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