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