1 /*
   2  * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.applet;
  27 
  28 import java.applet.*;
  29 import java.awt.*;
  30 import java.awt.event.*;
  31 import java.awt.image.ColorModel;
  32 import java.awt.image.MemoryImageSource;
  33 import java.io.*;
  34 import java.lang.ref.WeakReference;
  35 import java.lang.reflect.InvocationTargetException;
  36 import java.lang.reflect.Method;
  37 import java.net.InetAddress;
  38 import java.net.JarURLConnection;
  39 import java.net.MalformedURLException;
  40 import java.net.SocketPermission;
  41 import java.net.URL;
  42 import java.net.UnknownHostException;
  43 import java.security.*;
  44 import java.util.*;
  45 import java.util.Collections;
  46 import java.util.Locale;
  47 import java.util.WeakHashMap;
  48 import sun.awt.AWTAccessor;
  49 import sun.awt.AppContext;
  50 import sun.awt.EmbeddedFrame;
  51 import sun.awt.SunToolkit;
  52 import sun.misc.MessageUtils;
  53 import sun.misc.PerformanceLogger;
  54 import sun.misc.Queue;
  55 import sun.security.util.SecurityConstants;
  56 
  57 /**
  58  * Applet panel class. The panel manages and manipulates the
  59  * applet as it is being loaded. It forks a separate thread in a new
  60  * thread group to call the applet's init(), start(), stop(), and
  61  * destroy() methods.
  62  *
  63  * @author      Arthur van Hoff
  64  */
  65 @SuppressWarnings("serial") // JDK implementation class
  66 public
  67 abstract class AppletPanel extends Panel implements AppletStub, Runnable {
  68 
  69     /**
  70      * The applet (if loaded).
  71      */
  72     Applet applet;
  73 
  74     /**
  75      * Applet will allow initialization.  Should be
  76      * set to false if loading a serialized applet
  77      * that was pickled in the init=true state.
  78      */
  79     protected boolean doInit = true;
  80 
  81 
  82     /**
  83      * The classloader for the applet.
  84      */
  85     protected AppletClassLoader loader;
  86 
  87     /* applet event ids */
  88     public final static int APPLET_DISPOSE = 0;
  89     public final static int APPLET_LOAD = 1;
  90     public final static int APPLET_INIT = 2;
  91     public final static int APPLET_START = 3;
  92     public final static int APPLET_STOP = 4;
  93     public final static int APPLET_DESTROY = 5;
  94     public final static int APPLET_QUIT = 6;
  95     public final static int APPLET_ERROR = 7;
  96 
  97     /* send to the parent to force relayout */
  98     public final static int APPLET_RESIZE = 51234;
  99 
 100     /* sent to a (distant) parent to indicate that the applet is being
 101      * loaded or as completed loading
 102      */
 103     public final static int APPLET_LOADING = 51235;
 104     public final static int APPLET_LOADING_COMPLETED = 51236;
 105 
 106     /**
 107      * The current status. One of:
 108      *    APPLET_DISPOSE,
 109      *    APPLET_LOAD,
 110      *    APPLET_INIT,
 111      *    APPLET_START,
 112      *    APPLET_STOP,
 113      *    APPLET_DESTROY,
 114      *    APPLET_ERROR.
 115      */
 116     protected int status;
 117 
 118     /**
 119      * The thread for the applet.
 120      */
 121     protected Thread handler;
 122 
 123 
 124     /**
 125      * The initial applet size.
 126      */
 127     Dimension defaultAppletSize = new Dimension(10, 10);
 128 
 129     /**
 130      * The current applet size.
 131      */
 132     Dimension currentAppletSize = new Dimension(10, 10);
 133 
 134     MessageUtils mu = new MessageUtils();
 135 
 136     /**
 137      * The thread to use during applet loading
 138      */
 139 
 140     Thread loaderThread = null;
 141 
 142     /**
 143      * Flag to indicate that a loading has been cancelled
 144      */
 145     boolean loadAbortRequest = false;
 146 
 147     /* abstract classes */
 148     abstract protected String getCode();
 149     abstract protected String getJarFiles();
 150     abstract protected String getSerializedObject();
 151 
 152     abstract public int    getWidth();
 153     abstract public int    getHeight();
 154     abstract public boolean hasInitialFocus();
 155 
 156     private static int threadGroupNumber = 0;
 157 
 158     protected void setupAppletAppContext() {
 159         // do nothing
 160     }
 161 
 162     /*
 163      * Creates a thread to run the applet. This method is called
 164      * each time an applet is loaded and reloaded.
 165      */
 166     synchronized void createAppletThread() {
 167         // Create a thread group for the applet, and start a new
 168         // thread to load the applet.
 169         String nm = "applet-" + getCode();
 170         loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
 171         loader.grab(); // Keep this puppy around!
 172 
 173         // 4668479: Option to turn off codebase lookup in AppletClassLoader
 174         // during resource requests. [stanley.ho]
 175         String param = getParameter("codebase_lookup");
 176 
 177         if (param != null && param.equals("false"))
 178             loader.setCodebaseLookup(false);
 179         else
 180             loader.setCodebaseLookup(true);
 181 
 182 
 183         ThreadGroup appletGroup = loader.getThreadGroup();
 184 
 185         handler = new Thread(appletGroup, this, "thread " + nm);
 186         // set the context class loader for this thread
 187         AccessController.doPrivileged(new PrivilegedAction() {
 188                 public Object run() {
 189                     handler.setContextClassLoader(loader);
 190                     return null;
 191                 }
 192             });
 193         handler.start();
 194     }
 195 
 196     void joinAppletThread() throws InterruptedException {
 197         if (handler != null) {
 198             handler.join();
 199             handler = null;
 200         }
 201     }
 202 
 203     void release() {
 204         if (loader != null) {
 205             loader.release();
 206             loader = null;
 207         }
 208     }
 209 
 210     /**
 211      * Construct an applet viewer and start the applet.
 212      */
 213     public void init() {
 214         try {
 215             // Get the width (if any)
 216             defaultAppletSize.width = getWidth();
 217             currentAppletSize.width = defaultAppletSize.width;
 218 
 219             // Get the height (if any)
 220             defaultAppletSize.height = getHeight();
 221             currentAppletSize.height = defaultAppletSize.height;
 222 
 223         } catch (NumberFormatException e) {
 224             // Turn on the error flag and let TagAppletPanel
 225             // do the right thing.
 226             status = APPLET_ERROR;
 227             showAppletStatus("badattribute.exception");
 228             showAppletLog("badattribute.exception");
 229             showAppletException(e);
 230         }
 231 
 232         setLayout(new BorderLayout());
 233 
 234         createAppletThread();
 235     }
 236 
 237     /**
 238      * Minimum size
 239      */
 240     public Dimension minimumSize() {
 241         return new Dimension(defaultAppletSize.width,
 242                              defaultAppletSize.height);
 243     }
 244 
 245     /**
 246      * Preferred size
 247      */
 248     public Dimension preferredSize() {
 249         return new Dimension(currentAppletSize.width,
 250                              currentAppletSize.height);
 251     }
 252 
 253     private AppletListener listeners;
 254 
 255     /**
 256      * AppletEvent Queue
 257      */
 258     private Queue queue = null;
 259 
 260 
 261     synchronized public void addAppletListener(AppletListener l) {
 262         listeners = AppletEventMulticaster.add(listeners, l);
 263     }
 264 
 265     synchronized public void removeAppletListener(AppletListener l) {
 266         listeners = AppletEventMulticaster.remove(listeners, l);
 267     }
 268 
 269     /**
 270      * Dispatch event to the listeners..
 271      */
 272     public void dispatchAppletEvent(int id, Object argument) {
 273         //System.out.println("SEND= " + id);
 274         if (listeners != null) {
 275             AppletEvent evt = new AppletEvent(this, id, argument);
 276             listeners.appletStateChanged(evt);
 277         }
 278     }
 279 
 280     /**
 281      * Send an event. Queue it for execution by the handler thread.
 282      */
 283     public void sendEvent(int id) {
 284         synchronized(this) {
 285             if (queue == null) {
 286                 //System.out.println("SEND0= " + id);
 287                 queue = new Queue();
 288             }
 289             Integer eventId = Integer.valueOf(id);
 290             queue.enqueue(eventId);
 291             notifyAll();
 292         }
 293         if (id == APPLET_QUIT) {
 294             try {
 295                 joinAppletThread(); // Let the applet event handler exit
 296             } catch (InterruptedException e) {
 297             }
 298 
 299             // AppletClassLoader.release() must be called by a Thread
 300             // not within the applet's ThreadGroup
 301             if (loader == null)
 302                 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
 303             release();
 304         }
 305     }
 306 
 307     /**
 308      * Get an event from the queue.
 309      */
 310     synchronized AppletEvent getNextEvent() throws InterruptedException {
 311         while (queue == null || queue.isEmpty()) {
 312             wait();
 313         }
 314         Integer eventId = (Integer)queue.dequeue();
 315         return new AppletEvent(this, eventId.intValue(), null);
 316     }
 317 
 318     boolean emptyEventQueue() {
 319         if ((queue == null) || (queue.isEmpty()))
 320             return true;
 321         else
 322             return false;
 323     }
 324 
 325     /**
 326      * This kludge is specific to get over AccessControlException thrown during
 327      * Applet.stop() or destroy() when static thread is suspended.  Set a flag
 328      * in AppletClassLoader to indicate that an
 329      * AccessControlException for RuntimePermission "modifyThread" or
 330      * "modifyThreadGroup" had occurred.
 331      */
 332      private void setExceptionStatus(AccessControlException e) {
 333      Permission p = e.getPermission();
 334      if (p instanceof RuntimePermission) {
 335          if (p.getName().startsWith("modifyThread")) {
 336              if (loader == null)
 337                  loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
 338              loader.setExceptionStatus();
 339          }
 340      }
 341      }
 342 
 343     /**
 344      * Execute applet events.
 345      * Here is the state transition diagram
 346      *
 347      *   Note: (XXX) is the action
 348      *         APPLET_XXX is the state
 349      *  (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
 350      *   applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
 351      *   destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
 352      *   APPLET_DISPOSE -->....
 353      *
 354      * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
 355      * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
 356      * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
 357      * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
 358      *
 359      * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
 360      * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
 361      * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
 362      *
 363      * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
 364      * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
 365      *
 366      * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
 367          * Same as APPLET_LOAD to
 368      * APPLET_DISPOSE since all of this are triggered by browser.
 369      *
 370      *
 371      */
 372     public void run() {
 373 
 374         Thread curThread = Thread.currentThread();
 375         if (curThread == loaderThread) {
 376             // if we are in the loader thread, cause
 377             // loading to occur.  We may exit this with
 378             // status being APPLET_DISPOSE, APPLET_ERROR,
 379             // or APPLET_LOAD
 380             runLoader();
 381             return;
 382         }
 383 
 384         boolean disposed = false;
 385         while (!disposed && !curThread.isInterrupted()) {
 386             AppletEvent evt;
 387             try {
 388                 evt = getNextEvent();
 389             } catch (InterruptedException e) {
 390                 showAppletStatus("bail");
 391                 return;
 392             }
 393 
 394             //showAppletStatus("EVENT = " + evt.getID());
 395             try {
 396                 switch (evt.getID()) {
 397                   case APPLET_LOAD:
 398                       if (!okToLoad()) {
 399                           break;
 400                       }
 401                       // This complexity allows loading of applets to be
 402                       // interruptable.  The actual thread loading runs
 403                       // in a separate thread, so it can be interrupted
 404                       // without harming the applet thread.
 405                       // So that we don't have to worry about
 406                       // concurrency issues, the main applet thread waits
 407                       // until the loader thread terminates.
 408                       // (one way or another).
 409                       if (loaderThread == null) {
 410                           // REMIND: do we want a name?
 411                           //System.out.println("------------------- loading applet");
 412                           setLoaderThread(new Thread(this));
 413                           loaderThread.start();
 414                           // we get to go to sleep while this runs
 415                           loaderThread.join();
 416                           setLoaderThread(null);
 417                       } else {
 418                           // REMIND: issue an error -- this case should never
 419                           // occur.
 420                       }
 421                       break;
 422 
 423                   case APPLET_INIT:
 424                     // AppletViewer "Restart" will jump from destroy method to
 425                     // init, that is why we need to check status w/ APPLET_DESTROY
 426                       if (status != APPLET_LOAD && status != APPLET_DESTROY) {
 427                           showAppletStatus("notloaded");
 428                           break;
 429                       }
 430                       applet.resize(defaultAppletSize);
 431                       if (doInit) {
 432                           if (PerformanceLogger.loggingEnabled()) {
 433                               PerformanceLogger.setTime("Applet Init");
 434                               PerformanceLogger.outputLog();
 435                           }
 436                           applet.init();
 437                       }
 438 
 439                       //Need the default(fallback) font to be created in this AppContext
 440                       Font f = getFont();
 441                       if (f == null ||
 442                           "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) &&
 443                           f.getSize() == 12 && f.getStyle() == Font.PLAIN) {
 444                           setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
 445                       }
 446 
 447                       doInit = true;    // allow restarts
 448 
 449                       // Validate the applet in event dispatch thread
 450                       // to avoid deadlock.
 451                       try {
 452                           final AppletPanel p = this;
 453                           Runnable r = new Runnable() {
 454                               public void run() {
 455                                   p.validate();
 456                               }
 457                           };
 458                           AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
 459                       }
 460                       catch(InterruptedException ie) {
 461                       }
 462                       catch(InvocationTargetException ite) {
 463                       }
 464 
 465                       status = APPLET_INIT;
 466                       showAppletStatus("inited");
 467                       break;
 468 
 469                   case APPLET_START:
 470                   {
 471                       if (status != APPLET_INIT && status != APPLET_STOP) {
 472                           showAppletStatus("notinited");
 473                           break;
 474                       }
 475                       applet.resize(currentAppletSize);
 476                       applet.start();
 477 
 478                       // Validate and show the applet in event dispatch thread
 479                       // to avoid deadlock.
 480                       try {
 481                           final AppletPanel p = this;
 482                           final Applet a = applet;
 483                           Runnable r = new Runnable() {
 484                               public void run() {
 485                                   p.validate();
 486                                   a.setVisible(true);
 487 
 488                                   // Fix for BugTraq ID 4041703.
 489                                   // Set the default focus for an applet.
 490                                   if (hasInitialFocus()) {
 491                                       setDefaultFocus();
 492                                   }
 493                               }
 494                           };
 495                           AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
 496                       }
 497                       catch(InterruptedException ie) {
 498                       }
 499                       catch(InvocationTargetException ite) {
 500                       }
 501 
 502                       status = APPLET_START;
 503                       showAppletStatus("started");
 504                       break;
 505                   }
 506 
 507                 case APPLET_STOP:
 508                     if (status != APPLET_START) {
 509                         showAppletStatus("notstarted");
 510                         break;
 511                     }
 512                     status = APPLET_STOP;
 513 
 514                     // Hide the applet in event dispatch thread
 515                     // to avoid deadlock.
 516                     try {
 517                         final Applet a = applet;
 518                         Runnable r = new Runnable() {
 519                             public void run() {
 520                                 a.setVisible(false);
 521                             }
 522                         };
 523                         AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
 524                     }
 525                     catch(InterruptedException ie) {
 526                     }
 527                     catch(InvocationTargetException ite) {
 528                     }
 529 
 530 
 531                     // During Applet.stop(), any AccessControlException on an involved Class remains in
 532                     // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
 533                     // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
 534                     // exceptionStatusSet flag to allow recognition of what had happened
 535                     // when reusing AppletClassLoader object.
 536                     try {
 537                         applet.stop();
 538                     } catch (java.security.AccessControlException e) {
 539                         setExceptionStatus(e);
 540                         // rethrow exception to be handled as it normally would be.
 541                         throw e;
 542                     }
 543                     showAppletStatus("stopped");
 544                     break;
 545 
 546                 case APPLET_DESTROY:
 547                     if (status != APPLET_STOP && status != APPLET_INIT) {
 548                         showAppletStatus("notstopped");
 549                         break;
 550                     }
 551                     status = APPLET_DESTROY;
 552 
 553                     // During Applet.destroy(), any AccessControlException on an involved Class remains in
 554                     // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
 555                     // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
 556                     // exceptionStatusSet flag to allow recognition of what had happened
 557                     // when reusing AppletClassLoader object.
 558                     try {
 559                         applet.destroy();
 560                     } catch (java.security.AccessControlException e) {
 561                         setExceptionStatus(e);
 562                         // rethrow exception to be handled as it normally would be.
 563                         throw e;
 564                     }
 565                     showAppletStatus("destroyed");
 566                     break;
 567 
 568                 case APPLET_DISPOSE:
 569                     if (status != APPLET_DESTROY && status != APPLET_LOAD) {
 570                         showAppletStatus("notdestroyed");
 571                         break;
 572                     }
 573                     status = APPLET_DISPOSE;
 574 
 575                     try {
 576                         final Applet a = applet;
 577                         Runnable r = new Runnable() {
 578                             public void run() {
 579                                 remove(a);
 580                             }
 581                         };
 582                         AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
 583                     }
 584                     catch(InterruptedException ie)
 585                     {
 586                     }
 587                     catch(InvocationTargetException ite)
 588                     {
 589                     }
 590                     applet = null;
 591                     showAppletStatus("disposed");
 592                     disposed = true;
 593                     break;
 594 
 595                 case APPLET_QUIT:
 596                     return;
 597                 }
 598             } catch (Exception e) {
 599                 status = APPLET_ERROR;
 600                 if (e.getMessage() != null) {
 601                     showAppletStatus("exception2", e.getClass().getName(),
 602                                      e.getMessage());
 603                 } else {
 604                     showAppletStatus("exception", e.getClass().getName());
 605                 }
 606                 showAppletException(e);
 607             } catch (ThreadDeath e) {
 608                 showAppletStatus("death");
 609                 return;
 610             } catch (Error e) {
 611                 status = APPLET_ERROR;
 612                 if (e.getMessage() != null) {
 613                     showAppletStatus("error2", e.getClass().getName(),
 614                                      e.getMessage());
 615                 } else {
 616                     showAppletStatus("error", e.getClass().getName());
 617                 }
 618                 showAppletException(e);
 619             }
 620             clearLoadAbortRequest();
 621         }
 622     }
 623 
 624     /**
 625      * Gets most recent focus owner component associated with the given window.
 626      * It does that without calling Window.getMostRecentFocusOwner since it
 627      * provides its own logic contradicting with setDefautlFocus. Instead, it
 628      * calls KeyboardFocusManager directly.
 629      */
 630     private Component getMostRecentFocusOwnerForWindow(Window w) {
 631         Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() {
 632                 public Object run() {
 633                     Method meth = null;
 634                     try {
 635                         meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class});
 636                         meth.setAccessible(true);
 637                     } catch (Exception e) {
 638                         // Must never happen
 639                         e.printStackTrace();
 640                     }
 641                     return meth;
 642                 }
 643             });
 644         if (meth != null) {
 645             // Meth refers static method
 646             try {
 647                 return (Component)meth.invoke(null, new Object[] {w});
 648             } catch (Exception e) {
 649                 // Must never happen
 650                 e.printStackTrace();
 651             }
 652         }
 653         // Will get here if exception was thrown or meth is null
 654         return w.getMostRecentFocusOwner();
 655     }
 656 
 657     /*
 658      * Fix for BugTraq ID 4041703.
 659      * Set the focus to a reasonable default for an Applet.
 660      */
 661     private void setDefaultFocus() {
 662         Component toFocus = null;
 663         Container parent = getParent();
 664 
 665         if(parent != null) {
 666             if (parent instanceof Window) {
 667                 toFocus = getMostRecentFocusOwnerForWindow((Window)parent);
 668                 if (toFocus == parent || toFocus == null) {
 669                     toFocus = parent.getFocusTraversalPolicy().
 670                         getInitialComponent((Window)parent);
 671                 }
 672             } else if (parent.isFocusCycleRoot()) {
 673                 toFocus = parent.getFocusTraversalPolicy().
 674                     getDefaultComponent(parent);
 675             }
 676         }
 677 
 678         if (toFocus != null) {
 679             if (parent instanceof EmbeddedFrame) {
 680                 ((EmbeddedFrame)parent).synthesizeWindowActivation(true);
 681             }
 682             // EmbeddedFrame might have focus before the applet was added.
 683             // Thus after its activation the most recent focus owner will be
 684             // restored. We need the applet's initial focusabled component to
 685             // be focused here.
 686             toFocus.requestFocusInWindow();
 687         }
 688     }
 689 
 690     /**
 691      * Load the applet into memory.
 692      * Runs in a seperate (and interruptible) thread from the rest of the
 693      * applet event processing so that it can be gracefully interrupted from
 694      * things like HotJava.
 695      */
 696     private void runLoader() {
 697         if (status != APPLET_DISPOSE) {
 698             showAppletStatus("notdisposed");
 699             return;
 700         }
 701 
 702         dispatchAppletEvent(APPLET_LOADING, null);
 703 
 704         // REMIND -- might be cool to visually indicate loading here --
 705         // maybe do animation?
 706         status = APPLET_LOAD;
 707 
 708         // Create a class loader
 709         loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
 710 
 711         // Load the archives if present.
 712         // REMIND - this probably should be done in a separate thread,
 713         // or at least the additional archives (epll).
 714 
 715         String code = getCode();
 716 
 717         // setup applet AppContext
 718         // this must be called before loadJarFiles
 719         setupAppletAppContext();
 720 
 721         try {
 722             loadJarFiles(loader);
 723             applet = createApplet(loader);
 724         } catch (ClassNotFoundException e) {
 725             status = APPLET_ERROR;
 726             showAppletStatus("notfound", code);
 727             showAppletLog("notfound", code);
 728             showAppletException(e);
 729             return;
 730         } catch (InstantiationException e) {
 731             status = APPLET_ERROR;
 732             showAppletStatus("nocreate", code);
 733             showAppletLog("nocreate", code);
 734             showAppletException(e);
 735             return;
 736         } catch (IllegalAccessException e) {
 737             status = APPLET_ERROR;
 738             showAppletStatus("noconstruct", code);
 739             showAppletLog("noconstruct", code);
 740             showAppletException(e);
 741             // sbb -- I added a return here
 742             return;
 743         } catch (Exception e) {
 744             status = APPLET_ERROR;
 745             showAppletStatus("exception", e.getMessage());
 746             showAppletException(e);
 747             return;
 748         } catch (ThreadDeath e) {
 749             status = APPLET_ERROR;
 750             showAppletStatus("death");
 751             return;
 752         } catch (Error e) {
 753             status = APPLET_ERROR;
 754             showAppletStatus("error", e.getMessage());
 755             showAppletException(e);
 756             return;
 757         } finally {
 758             // notify that loading is no longer going on
 759             dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
 760         }
 761 
 762         // Fixed #4508194: NullPointerException thrown during
 763         // quick page switch
 764         //
 765         if (applet != null)
 766         {
 767             // Stick it in the frame
 768             applet.setStub(this);
 769             applet.hide();
 770             add("Center", applet);
 771             showAppletStatus("loaded");
 772             validate();
 773         }
 774     }
 775 
 776     protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException,
 777                                                                          IllegalAccessException, IOException, InstantiationException, InterruptedException {
 778         final String serName = getSerializedObject();
 779         String code = getCode();
 780 
 781         if (code != null && serName != null) {
 782             System.err.println(amh.getMessage("runloader.err"));
 783 //          return null;
 784             throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
 785         }
 786         if (code == null && serName == null) {
 787             String msg = "nocode";
 788             status = APPLET_ERROR;
 789             showAppletStatus(msg);
 790             showAppletLog(msg);
 791             repaint();
 792         }
 793         if (code != null) {
 794             applet = (Applet)loader.loadCode(code).newInstance();
 795             doInit = true;
 796         } else {
 797             // serName is not null;
 798             try (InputStream is = AccessController.doPrivileged(
 799                     (PrivilegedAction<InputStream>)() -> loader.getResourceAsStream(serName));
 800                  ObjectInputStream ois = new AppletObjectInputStream(is, loader)) {
 801 
 802                 applet = (Applet) ois.readObject();
 803                 doInit = false; // skip over the first init
 804             }
 805         }
 806 
 807         // Determine the JDK level that the applet targets.
 808         // This is critical for enabling certain backward
 809         // compatibility switch if an applet is a JDK 1.1
 810         // applet. [stanley.ho]
 811         findAppletJDKLevel(applet);
 812 
 813         if (Thread.interrupted()) {
 814             try {
 815                 status = APPLET_DISPOSE; // APPLET_ERROR?
 816                 applet = null;
 817                 // REMIND: This may not be exactly the right thing: the
 818                 // status is set by the stop button and not necessarily
 819                 // here.
 820                 showAppletStatus("death");
 821             } finally {
 822                 Thread.currentThread().interrupt(); // resignal interrupt
 823             }
 824             return null;
 825         }
 826         return applet;
 827     }
 828 
 829     protected void loadJarFiles(AppletClassLoader loader) throws IOException,
 830                                                                  InterruptedException {
 831         // Load the archives if present.
 832         // REMIND - this probably should be done in a separate thread,
 833         // or at least the additional archives (epll).
 834         String jarFiles = getJarFiles();
 835 
 836         if (jarFiles != null) {
 837             StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
 838             while(st.hasMoreTokens()) {
 839                 String tok = st.nextToken().trim();
 840                 try {
 841                     loader.addJar(tok);
 842                 } catch (IllegalArgumentException e) {
 843                     // bad archive name
 844                     continue;
 845                 }
 846             }
 847         }
 848     }
 849 
 850     /**
 851      * Request that the loading of the applet be stopped.
 852      */
 853     protected synchronized void stopLoading() {
 854         // REMIND: fill in the body
 855         if (loaderThread != null) {
 856             //System.out.println("Interrupting applet loader thread: " + loaderThread);
 857             loaderThread.interrupt();
 858         } else {
 859             setLoadAbortRequest();
 860         }
 861     }
 862 
 863 
 864     protected synchronized boolean okToLoad() {
 865         return !loadAbortRequest;
 866     }
 867 
 868     protected synchronized void clearLoadAbortRequest() {
 869         loadAbortRequest = false;
 870     }
 871 
 872     protected synchronized void setLoadAbortRequest() {
 873         loadAbortRequest = true;
 874     }
 875 
 876 
 877     private synchronized void setLoaderThread(Thread loaderThread) {
 878         this.loaderThread = loaderThread;
 879     }
 880 
 881     /**
 882      * Return true when the applet has been started.
 883      */
 884     public boolean isActive() {
 885         return status == APPLET_START;
 886     }
 887 
 888 
 889     private EventQueue appEvtQ = null;
 890     /**
 891      * Is called when the applet wants to be resized.
 892      */
 893     public void appletResize(int width, int height) {
 894         currentAppletSize.width = width;
 895         currentAppletSize.height = height;
 896         final Dimension currentSize = new Dimension(currentAppletSize.width,
 897                                                     currentAppletSize.height);
 898 
 899         if(loader != null) {
 900             AppContext appCtxt = loader.getAppContext();
 901             if(appCtxt != null)
 902                 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
 903         }
 904 
 905         final AppletPanel ap = this;
 906         if (appEvtQ != null){
 907             appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
 908                                                   new Runnable(){
 909                                                       public void run(){
 910                                                           if(ap != null)
 911                                                           {
 912                                                               ap.dispatchAppletEvent(APPLET_RESIZE, currentSize);
 913                                                           }
 914                                                       }
 915                                                   }));
 916         }
 917     }
 918 
 919     public void setBounds(int x, int y, int width, int height) {
 920         super.setBounds(x, y, width, height);
 921         currentAppletSize.width = width;
 922         currentAppletSize.height = height;
 923     }
 924 
 925     public Applet getApplet() {
 926         return applet;
 927     }
 928 
 929     /**
 930      * Status line. Called by the AppletPanel to provide
 931      * feedback on the Applet's state.
 932      */
 933     protected void showAppletStatus(String status) {
 934         getAppletContext().showStatus(amh.getMessage(status));
 935     }
 936 
 937     protected void showAppletStatus(String status, Object arg) {
 938         getAppletContext().showStatus(amh.getMessage(status, arg));
 939     }
 940     protected void showAppletStatus(String status, Object arg1, Object arg2) {
 941         getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
 942     }
 943 
 944     /**
 945      * Called by the AppletPanel to print to the log.
 946      */
 947     protected void showAppletLog(String msg) {
 948         System.out.println(amh.getMessage(msg));
 949     }
 950 
 951     protected void showAppletLog(String msg, Object arg) {
 952         System.out.println(amh.getMessage(msg, arg));
 953     }
 954 
 955     /**
 956      * Called by the AppletPanel to provide
 957      * feedback when an exception has happened.
 958      */
 959     protected void showAppletException(Throwable t) {
 960         t.printStackTrace();
 961         repaint();
 962     }
 963 
 964     /**
 965      * Get caching key for classloader cache
 966      */
 967     public String getClassLoaderCacheKey()
 968     {
 969         /**
 970          * Fixed #4501142: Classlaoder sharing policy doesn't
 971          * take "archive" into account. This will be overridden
 972          * by Java Plug-in.                     [stanleyh]
 973          */
 974         return getCodeBase().toString();
 975     }
 976 
 977     /**
 978      * The class loaders
 979      */
 980     private static HashMap classloaders = new HashMap();
 981 
 982     /**
 983      * Flush a class loader.
 984      */
 985     public static synchronized void flushClassLoader(String key) {
 986         classloaders.remove(key);
 987     }
 988 
 989     /**
 990      * Flush all class loaders.
 991      */
 992     public static synchronized void flushClassLoaders() {
 993         classloaders = new HashMap();
 994     }
 995 
 996     /**
 997      * This method actually creates an AppletClassLoader.
 998      *
 999      * It can be override by subclasses (such as the Plug-in)
1000      * to provide different classloaders.
1001      */
1002     protected AppletClassLoader createClassLoader(final URL codebase) {
1003         return new AppletClassLoader(codebase);
1004     }
1005 
1006     /**
1007      * Get a class loader. Create in a restricted context
1008      */
1009     synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
1010         AppletClassLoader c = (AppletClassLoader)classloaders.get(key);
1011         if (c == null) {
1012             AccessControlContext acc =
1013                 getAccessControlContext(codebase);
1014             c = (AppletClassLoader)
1015                 AccessController.doPrivileged(new PrivilegedAction() {
1016                         public Object run() {
1017                             AppletClassLoader ac = createClassLoader(codebase);
1018                             /* Should the creation of the classloader be
1019                              * within the class synchronized block?  Since
1020                              * this class is used by the plugin, take care
1021                              * to avoid deadlocks, or specialize
1022                              * AppletPanel within the plugin.  It may take
1023                              * an arbitrary amount of time to create a
1024                              * class loader (involving getting Jar files
1025                              * etc.) and may block unrelated applets from
1026                              * finishing createAppletThread (due to the
1027                              * class synchronization). If
1028                              * createAppletThread does not finish quickly,
1029                              * the applet cannot process other messages,
1030                              * particularly messages such as destroy
1031                              * (which timeout when called from the browser).
1032                              */
1033                             synchronized (getClass()) {
1034                                 AppletClassLoader res =
1035                                     (AppletClassLoader)classloaders.get(key);
1036                                 if (res == null) {
1037                                     classloaders.put(key, ac);
1038                                     return ac;
1039                                 } else {
1040                                     return res;
1041                                 }
1042                             }
1043                         }
1044                     },acc);
1045         }
1046         return c;
1047     }
1048 
1049     /**
1050      * get the context for the AppletClassLoader we are creating.
1051      * the context is granted permission to create the class loader,
1052      * connnect to the codebase, and whatever else the policy grants
1053      * to all codebases.
1054      */
1055     private AccessControlContext getAccessControlContext(final URL codebase) {
1056 
1057         PermissionCollection perms = (PermissionCollection)
1058             AccessController.doPrivileged(new PrivilegedAction() {
1059                     public Object run() {
1060                         Policy p = java.security.Policy.getPolicy();
1061                         if (p != null) {
1062                             return p.getPermissions(new CodeSource(null,
1063                                                                    (java.security.cert.Certificate[]) null));
1064                         } else {
1065                             return null;
1066                         }
1067                     }
1068                 });
1069 
1070         if (perms == null)
1071             perms = new Permissions();
1072 
1073         //XXX: this is needed to be able to create the classloader itself!
1074 
1075         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
1076 
1077         Permission p;
1078         java.net.URLConnection urlConnection = null;
1079         try {
1080             urlConnection = codebase.openConnection();
1081             p = urlConnection.getPermission();
1082         } catch (java.io.IOException ioe) {
1083             p = null;
1084         }
1085 
1086         if (p != null)
1087             perms.add(p);
1088 
1089         if (p instanceof FilePermission) {
1090 
1091             String path = p.getName();
1092 
1093             int endIndex = path.lastIndexOf(File.separatorChar);
1094 
1095             if (endIndex != -1) {
1096                 path = path.substring(0, endIndex+1);
1097 
1098                 if (path.endsWith(File.separator)) {
1099                     path += "-";
1100                 }
1101                 perms.add(new FilePermission(path,
1102                                              SecurityConstants.FILE_READ_ACTION));
1103             }
1104         } else {
1105             URL locUrl = codebase;
1106             if (urlConnection instanceof JarURLConnection) {
1107                 locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
1108             }
1109             String host = locUrl.getHost();
1110             if (host != null && (host.length() > 0))
1111                 perms.add(new SocketPermission(host,
1112                                                SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
1113         }
1114 
1115         ProtectionDomain domain =
1116             new ProtectionDomain(new CodeSource(codebase,
1117                                                 (java.security.cert.Certificate[]) null), perms);
1118         AccessControlContext acc =
1119             new AccessControlContext(new ProtectionDomain[] { domain });
1120 
1121         return acc;
1122     }
1123 
1124     public Thread getAppletHandlerThread() {
1125         return handler;
1126     }
1127 
1128     public int getAppletWidth() {
1129         return currentAppletSize.width;
1130     }
1131 
1132     public int getAppletHeight() {
1133         return currentAppletSize.height;
1134     }
1135 
1136     public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
1137     {
1138         // Fixed #4754451: Applet can have methods running on main
1139         // thread event queue.
1140         //
1141         // The cause of this bug is that the frame of the applet
1142         // is created in main thread group. Thus, when certain
1143         // AWT/Swing events are generated, the events will be
1144         // dispatched through the wrong event dispatch thread.
1145         //
1146         // To fix this, we rearrange the AppContext with the frame,
1147         // so the proper event queue will be looked up.
1148         //
1149         // Swing also maintains a Frame list for the AppContext,
1150         // so we will have to rearrange it as well.
1151 
1152         // Check if frame's AppContext has already been set properly
1153         AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
1154 
1155         if (oldAppContext == newAppContext)
1156             return;
1157 
1158         // Synchronization on Window.class is needed for locking the
1159         // critical section of the window list in AppContext.
1160         synchronized (Window.class)
1161         {
1162             WeakReference weakRef = null;
1163             // Remove frame from the Window list in wrong AppContext
1164             {
1165                 // Lookup current frame's AppContext
1166                 Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
1167                 if (windowList != null) {
1168                     for (WeakReference ref : windowList) {
1169                         if (ref.get() == frame) {
1170                             weakRef = ref;
1171                             break;
1172                         }
1173                     }
1174                     // Remove frame from wrong AppContext
1175                     if (weakRef != null)
1176                         windowList.remove(weakRef);
1177                 }
1178             }
1179 
1180             // Put the frame into the applet's AppContext map
1181             SunToolkit.insertTargetMapping(frame, newAppContext);
1182 
1183             // Insert frame into the Window list in the applet's AppContext map
1184             {
1185                 Vector<WeakReference<Window>> windowList = (Vector)newAppContext.get(Window.class);
1186                 if (windowList == null) {
1187                     windowList = new Vector<WeakReference<Window>>();
1188                     newAppContext.put(Window.class, windowList);
1189                 }
1190                 // use the same weakRef here as it is used elsewhere
1191                 windowList.add(weakRef);
1192             }
1193         }
1194     }
1195 
1196     // Flag to indicate if applet is targeted for JDK 1.1.
1197     private boolean jdk11Applet = false;
1198 
1199     // Flag to indicate if applet is targeted for JDK 1.2.
1200     private boolean jdk12Applet = false;
1201 
1202     /**
1203      * Determine JDK level of an applet.
1204      */
1205     private void findAppletJDKLevel(Applet applet)
1206     {
1207         // To determine the JDK level of an applet, the
1208         // most reliable way is to check the major version
1209         // of the applet class file.
1210 
1211         // synchronized on applet class object, so calling from
1212         // different instances of the same applet will be
1213         // serialized.
1214         Class appletClass = applet.getClass();
1215 
1216         synchronized(appletClass)  {
1217             // Determine if the JDK level of an applet has been
1218             // checked before.
1219             Boolean jdk11Target = loader.isJDK11Target(appletClass);
1220             Boolean jdk12Target = loader.isJDK12Target(appletClass);
1221 
1222             // if applet JDK level has been checked before, retrieve
1223             // value and return.
1224             if (jdk11Target != null || jdk12Target != null) {
1225                 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
1226                 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
1227                 return;
1228             }
1229 
1230             String name = appletClass.getName();
1231 
1232             // first convert any '.' to '/'
1233             name = name.replace('.', '/');
1234 
1235             // append .class
1236             final String resourceName = name + ".class";
1237 
1238             byte[] classHeader = new byte[8];
1239 
1240             try (InputStream is = AccessController.doPrivileged(
1241                     (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) {
1242 
1243                 // Read the first 8 bytes of the class file
1244                 int byteRead = is.read(classHeader, 0, 8);
1245 
1246                 // return if the header is not read in entirely
1247                 // for some reasons.
1248                 if (byteRead != 8)
1249                     return;
1250             }
1251             catch (IOException e)   {
1252                 return;
1253             }
1254 
1255             // Check major version in class file header
1256             int major_version = readShort(classHeader, 6);
1257 
1258             // Major version in class file is as follows:
1259             //   45 - JDK 1.1
1260             //   46 - JDK 1.2
1261             //   47 - JDK 1.3
1262             //   48 - JDK 1.4
1263             //   49 - JDK 1.5
1264             if (major_version < 46)
1265                 jdk11Applet = true;
1266             else if (major_version == 46)
1267                 jdk12Applet = true;
1268 
1269             // Store applet JDK level in AppContext for later lookup,
1270             // e.g. page switch.
1271             loader.setJDK11Target(appletClass, jdk11Applet);
1272             loader.setJDK12Target(appletClass, jdk12Applet);
1273         }
1274     }
1275 
1276     /**
1277      * Return true if applet is targeted to JDK 1.1.
1278      */
1279     protected boolean isJDK11Applet()   {
1280         return jdk11Applet;
1281     }
1282 
1283     /**
1284      * Return true if applet is targeted to JDK1.2.
1285      */
1286     protected boolean isJDK12Applet()   {
1287         return jdk12Applet;
1288     }
1289 
1290     /**
1291      * Read short from byte array.
1292      */
1293     private int readShort(byte[] b, int off)    {
1294         int hi = readByte(b[off]);
1295         int lo = readByte(b[off + 1]);
1296         return (hi << 8) | lo;
1297     }
1298 
1299     private int readByte(byte b) {
1300         return ((int)b) & 0xFF;
1301     }
1302 
1303 
1304     private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
1305 }