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             @SuppressWarnings("deprecation")
 775             Object tmp = loader.loadCode(code).newInstance();
 776             applet = (Applet)tmp;
 777         } else {
 778             String msg = "nocode";
 779             status = APPLET_ERROR;
 780             showAppletStatus(msg);
 781             showAppletLog(msg);
 782             repaint();
 783         }
 784 
 785         // Determine the JDK level that the applet targets.
 786         // This is critical for enabling certain backward
 787         // compatibility switch if an applet is a JDK 1.1
 788         // applet. [stanley.ho]
 789         findAppletJDKLevel(applet);
 790 
 791         if (Thread.interrupted()) {
 792             try {
 793                 status = APPLET_DISPOSE; // APPLET_ERROR?
 794                 applet = null;
 795                 // REMIND: This may not be exactly the right thing: the
 796                 // status is set by the stop button and not necessarily
 797                 // here.
 798                 showAppletStatus("death");
 799             } finally {
 800                 Thread.currentThread().interrupt(); // resignal interrupt
 801             }
 802             return null;
 803         }
 804         return applet;
 805     }
 806 
 807     protected void loadJarFiles(AppletClassLoader loader) throws IOException,
 808                                                                  InterruptedException {
 809         // Load the archives if present.
 810         // REMIND - this probably should be done in a separate thread,
 811         // or at least the additional archives (epll).
 812         String jarFiles = getJarFiles();
 813 
 814         if (jarFiles != null) {
 815             StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
 816             while(st.hasMoreTokens()) {
 817                 String tok = st.nextToken().trim();
 818                 try {
 819                     loader.addJar(tok);
 820                 } catch (IllegalArgumentException e) {
 821                     // bad archive name
 822                     continue;
 823                 }
 824             }
 825         }
 826     }
 827 
 828     /**
 829      * Request that the loading of the applet be stopped.
 830      */
 831     protected synchronized void stopLoading() {
 832         // REMIND: fill in the body
 833         if (loaderThread != null) {
 834             //System.out.println("Interrupting applet loader thread: " + loaderThread);
 835             loaderThread.interrupt();
 836         } else {
 837             setLoadAbortRequest();
 838         }
 839     }
 840 
 841 
 842     protected synchronized boolean okToLoad() {
 843         return !loadAbortRequest;
 844     }
 845 
 846     protected synchronized void clearLoadAbortRequest() {
 847         loadAbortRequest = false;
 848     }
 849 
 850     protected synchronized void setLoadAbortRequest() {
 851         loadAbortRequest = true;
 852     }
 853 
 854 
 855     private synchronized void setLoaderThread(Thread loaderThread) {
 856         this.loaderThread = loaderThread;
 857     }
 858 
 859     /**
 860      * Return true when the applet has been started.
 861      */
 862     @Override
 863     public boolean isActive() {
 864         return status == APPLET_START;
 865     }
 866 
 867 
 868     private EventQueue appEvtQ = null;
 869     /**
 870      * Is called when the applet wants to be resized.
 871      */
 872     @Override
 873     public void appletResize(int width, int height) {
 874         currentAppletSize.width = width;
 875         currentAppletSize.height = height;
 876         final Dimension currentSize = new Dimension(currentAppletSize.width,
 877                                                     currentAppletSize.height);
 878 
 879         if(loader != null) {
 880             AppContext appCtxt = loader.getAppContext();
 881             if(appCtxt != null)
 882                 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
 883         }
 884 
 885         final AppletPanel ap = this;
 886         if (appEvtQ != null){
 887             appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
 888                                                   new Runnable() {
 889                                                       @Override
 890                                                       public void run() {
 891                                                           if (ap != null) {
 892                                                               ap.dispatchAppletEvent(
 893                                                                       APPLET_RESIZE,
 894                                                                       currentSize);
 895                                                           }
 896                                                       }
 897                                                   }));
 898         }
 899     }
 900 
 901     @Override
 902     public void setBounds(int x, int y, int width, int height) {
 903         super.setBounds(x, y, width, height);
 904         currentAppletSize.width = width;
 905         currentAppletSize.height = height;
 906     }
 907 
 908     public Applet getApplet() {
 909         return applet;
 910     }
 911 
 912     /**
 913      * Status line. Called by the AppletPanel to provide
 914      * feedback on the Applet's state.
 915      */
 916     protected void showAppletStatus(String status) {
 917         getAppletContext().showStatus(amh.getMessage(status));
 918     }
 919 
 920     protected void showAppletStatus(String status, Object arg) {
 921         getAppletContext().showStatus(amh.getMessage(status, arg));
 922     }
 923     protected void showAppletStatus(String status, Object arg1, Object arg2) {
 924         getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
 925     }
 926 
 927     /**
 928      * Called by the AppletPanel to print to the log.
 929      */
 930     protected void showAppletLog(String msg) {
 931         System.out.println(amh.getMessage(msg));
 932     }
 933 
 934     protected void showAppletLog(String msg, Object arg) {
 935         System.out.println(amh.getMessage(msg, arg));
 936     }
 937 
 938     /**
 939      * Called by the AppletPanel to provide
 940      * feedback when an exception has happened.
 941      */
 942     protected void showAppletException(Throwable t) {
 943         t.printStackTrace();
 944         repaint();
 945     }
 946 
 947     /**
 948      * Get caching key for classloader cache
 949      */
 950     public String getClassLoaderCacheKey()
 951     {
 952         /**
 953          * Fixed #4501142: Classloader sharing policy doesn't
 954          * take "archive" into account. This will be overridden
 955          * by Java Plug-in.                     [stanleyh]
 956          */
 957         return getCodeBase().toString();
 958     }
 959 
 960     /**
 961      * The class loaders
 962      */
 963     private static HashMap<String, AppletClassLoader> classloaders = new HashMap<>();
 964 
 965     /**
 966      * Flush a class loader.
 967      */
 968     public static synchronized void flushClassLoader(String key) {
 969         classloaders.remove(key);
 970     }
 971 
 972     /**
 973      * Flush all class loaders.
 974      */
 975     public static synchronized void flushClassLoaders() {
 976         classloaders = new HashMap<>();
 977     }
 978 
 979     /**
 980      * This method actually creates an AppletClassLoader.
 981      *
 982      * It can be override by subclasses (such as the Plug-in)
 983      * to provide different classloaders.
 984      */
 985     protected AppletClassLoader createClassLoader(final URL codebase) {
 986         return new AppletClassLoader(codebase);
 987     }
 988 
 989     /**
 990      * Get a class loader. Create in a restricted context
 991      */
 992     synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
 993         AppletClassLoader c = classloaders.get(key);
 994         if (c == null) {
 995             AccessControlContext acc =
 996                 getAccessControlContext(codebase);
 997             c = AccessController.doPrivileged(
 998                     new PrivilegedAction<AppletClassLoader>() {
 999                         @Override
1000                         public AppletClassLoader run() {
1001                             AppletClassLoader ac = createClassLoader(codebase);
1002                             /* Should the creation of the classloader be
1003                              * within the class synchronized block?  Since
1004                              * this class is used by the plugin, take care
1005                              * to avoid deadlocks, or specialize
1006                              * AppletPanel within the plugin.  It may take
1007                              * an arbitrary amount of time to create a
1008                              * class loader (involving getting Jar files
1009                              * etc.) and may block unrelated applets from
1010                              * finishing createAppletThread (due to the
1011                              * class synchronization). If
1012                              * createAppletThread does not finish quickly,
1013                              * the applet cannot process other messages,
1014                              * particularly messages such as destroy
1015                              * (which timeout when called from the browser).
1016                              */
1017                             synchronized (getClass()) {
1018                                 AppletClassLoader res = classloaders.get(key);
1019                                 if (res == null) {
1020                                     classloaders.put(key, ac);
1021                                     return ac;
1022                                 } else {
1023                                     return res;
1024                                 }
1025                             }
1026                         }
1027                     },acc);
1028         }
1029         return c;
1030     }
1031 
1032     /**
1033      * get the context for the AppletClassLoader we are creating.
1034      * the context is granted permission to create the class loader,
1035      * connnect to the codebase, and whatever else the policy grants
1036      * to all codebases.
1037      */
1038     private AccessControlContext getAccessControlContext(final URL codebase) {
1039 
1040         PermissionCollection perms = AccessController.doPrivileged(
1041                 new PrivilegedAction<PermissionCollection>() {
1042                     @Override
1043                     public PermissionCollection run() {
1044                         Policy p = java.security.Policy.getPolicy();
1045                         if (p != null) {
1046                             return p.getPermissions(new CodeSource(null,
1047                                                                    (java.security.cert.Certificate[]) null));
1048                         } else {
1049                             return null;
1050                         }
1051                     }
1052                 });
1053 
1054         if (perms == null)
1055             perms = new Permissions();
1056 
1057         //XXX: this is needed to be able to create the classloader itself!
1058 
1059         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
1060 
1061         Permission p;
1062         java.net.URLConnection urlConnection = null;
1063         try {
1064             urlConnection = codebase.openConnection();
1065             p = urlConnection.getPermission();
1066         } catch (java.io.IOException ioe) {
1067             p = null;
1068         }
1069 
1070         if (p != null)
1071             perms.add(p);
1072 
1073         if (p instanceof FilePermission) {
1074 
1075             String path = p.getName();
1076 
1077             int endIndex = path.lastIndexOf(File.separatorChar);
1078 
1079             if (endIndex != -1) {
1080                 path = path.substring(0, endIndex+1);
1081 
1082                 if (path.endsWith(File.separator)) {
1083                     path += "-";
1084                 }
1085                 perms.add(new FilePermission(path,
1086                                              SecurityConstants.FILE_READ_ACTION));
1087             }
1088         } else {
1089             URL locUrl = codebase;
1090             if (urlConnection instanceof JarURLConnection) {
1091                 locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
1092             }
1093             String host = locUrl.getHost();
1094             if (host != null && (host.length() > 0))
1095                 perms.add(new SocketPermission(host,
1096                                                SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
1097         }
1098 
1099         ProtectionDomain domain =
1100             new ProtectionDomain(new CodeSource(codebase,
1101                                                 (java.security.cert.Certificate[]) null), perms);
1102         AccessControlContext acc =
1103             new AccessControlContext(new ProtectionDomain[] { domain });
1104 
1105         return acc;
1106     }
1107 
1108     public Thread getAppletHandlerThread() {
1109         return handler;
1110     }
1111 
1112     public int getAppletWidth() {
1113         return currentAppletSize.width;
1114     }
1115 
1116     public int getAppletHeight() {
1117         return currentAppletSize.height;
1118     }
1119 
1120     public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
1121     {
1122         // Fixed #4754451: Applet can have methods running on main
1123         // thread event queue.
1124         //
1125         // The cause of this bug is that the frame of the applet
1126         // is created in main thread group. Thus, when certain
1127         // AWT/Swing events are generated, the events will be
1128         // dispatched through the wrong event dispatch thread.
1129         //
1130         // To fix this, we rearrange the AppContext with the frame,
1131         // so the proper event queue will be looked up.
1132         //
1133         // Swing also maintains a Frame list for the AppContext,
1134         // so we will have to rearrange it as well.
1135 
1136         // Check if frame's AppContext has already been set properly
1137         AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
1138 
1139         if (oldAppContext == newAppContext)
1140             return;
1141 
1142         // Synchronization on Window.class is needed for locking the
1143         // critical section of the window list in AppContext.
1144         synchronized (Window.class)
1145         {
1146             WeakReference<Window> weakRef = null;
1147             // Remove frame from the Window list in wrong AppContext
1148             {
1149                 // Lookup current frame's AppContext
1150                 @SuppressWarnings("unchecked")
1151                 Vector<WeakReference<Window>> windowList =
1152                     (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
1153                 if (windowList != null) {
1154                     for (WeakReference<Window> ref : windowList) {
1155                         if (ref.get() == frame) {
1156                             weakRef = ref;
1157                             break;
1158                         }
1159                     }
1160                     // Remove frame from wrong AppContext
1161                     if (weakRef != null)
1162                         windowList.remove(weakRef);
1163                 }
1164             }
1165 
1166             // Put the frame into the applet's AppContext map
1167             SunToolkit.insertTargetMapping(frame, newAppContext);
1168 
1169             // Insert frame into the Window list in the applet's AppContext map
1170             {
1171                 @SuppressWarnings("unchecked")
1172                 Vector<WeakReference<Window>> windowList =
1173                     (Vector<WeakReference<Window>>)newAppContext.get(Window.class);
1174                 if (windowList == null) {
1175                     windowList = new Vector<WeakReference<Window>>();
1176                     newAppContext.put(Window.class, windowList);
1177                 }
1178                 // use the same weakRef here as it is used elsewhere
1179                 windowList.add(weakRef);
1180             }
1181         }
1182     }
1183 
1184     // Flag to indicate if applet is targeted for JDK 1.1.
1185     private boolean jdk11Applet = false;
1186 
1187     // Flag to indicate if applet is targeted for JDK 1.2.
1188     private boolean jdk12Applet = false;
1189 
1190     /**
1191      * Determine JDK level of an applet.
1192      */
1193     private void findAppletJDKLevel(Applet applet)
1194     {
1195         // To determine the JDK level of an applet, the
1196         // most reliable way is to check the major version
1197         // of the applet class file.
1198 
1199         // synchronized on applet class object, so calling from
1200         // different instances of the same applet will be
1201         // serialized.
1202         Class<?> appletClass = applet.getClass();
1203 
1204         synchronized(appletClass)  {
1205             // Determine if the JDK level of an applet has been
1206             // checked before.
1207             Boolean jdk11Target = loader.isJDK11Target(appletClass);
1208             Boolean jdk12Target = loader.isJDK12Target(appletClass);
1209 
1210             // if applet JDK level has been checked before, retrieve
1211             // value and return.
1212             if (jdk11Target != null || jdk12Target != null) {
1213                 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
1214                 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
1215                 return;
1216             }
1217 
1218             String name = appletClass.getName();
1219 
1220             // first convert any '.' to '/'
1221             name = name.replace('.', '/');
1222 
1223             // append .class
1224             final String resourceName = name + ".class";
1225 
1226             byte[] classHeader = new byte[8];
1227 
1228             try (InputStream is = AccessController.doPrivileged(
1229                     (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) {
1230 
1231                 // Read the first 8 bytes of the class file
1232                 int byteRead = is.read(classHeader, 0, 8);
1233 
1234                 // return if the header is not read in entirely
1235                 // for some reasons.
1236                 if (byteRead != 8)
1237                     return;
1238             }
1239             catch (IOException e)   {
1240                 return;
1241             }
1242 
1243             // Check major version in class file header
1244             int major_version = readShort(classHeader, 6);
1245 
1246             // Major version in class file is as follows:
1247             //   45 - JDK 1.1
1248             //   46 - JDK 1.2
1249             //   47 - JDK 1.3
1250             //   48 - JDK 1.4
1251             //   49 - JDK 1.5
1252             if (major_version < 46)
1253                 jdk11Applet = true;
1254             else if (major_version == 46)
1255                 jdk12Applet = true;
1256 
1257             // Store applet JDK level in AppContext for later lookup,
1258             // e.g. page switch.
1259             loader.setJDK11Target(appletClass, jdk11Applet);
1260             loader.setJDK12Target(appletClass, jdk12Applet);
1261         }
1262     }
1263 
1264     /**
1265      * Return true if applet is targeted to JDK 1.1.
1266      */
1267     protected boolean isJDK11Applet()   {
1268         return jdk11Applet;
1269     }
1270 
1271     /**
1272      * Return true if applet is targeted to JDK1.2.
1273      */
1274     protected boolean isJDK12Applet()   {
1275         return jdk12Applet;
1276     }
1277 
1278     /**
1279      * Read short from byte array.
1280      */
1281     private int readShort(byte[] b, int off)    {
1282         int hi = readByte(b[off]);
1283         int lo = readByte(b[off + 1]);
1284         return (hi << 8) | lo;
1285     }
1286 
1287     private int readByte(byte b) {
1288         return ((int)b) & 0xFF;
1289     }
1290 
1291 
1292     private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
1293 }