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