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", "deprecation"}) // 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         return AWTAccessor.getKeyboardFocusManagerAccessor()
 620                 .getMostRecentFocusOwner(w);
 621     }
 622 
 623     /*
 624      * Fix for BugTraq ID 4041703.
 625      * Set the focus to a reasonable default for an Applet.
 626      */
 627     private void setDefaultFocus() {
 628         Component toFocus = null;
 629         Container parent = getParent();
 630 
 631         if(parent != null) {
 632             if (parent instanceof Window) {
 633                 toFocus = getMostRecentFocusOwnerForWindow((Window)parent);
 634                 if (toFocus == parent || toFocus == null) {
 635                     toFocus = parent.getFocusTraversalPolicy().
 636                         getInitialComponent((Window)parent);
 637                 }
 638             } else if (parent.isFocusCycleRoot()) {
 639                 toFocus = parent.getFocusTraversalPolicy().
 640                     getDefaultComponent(parent);
 641             }
 642         }
 643 
 644         if (toFocus != null) {
 645             if (parent instanceof EmbeddedFrame) {
 646                 ((EmbeddedFrame) parent).synthesizeWindowActivation(true);
 647             }
 648             // EmbeddedFrame might have focus before the applet was added.
 649             // Thus after its activation the most recent focus owner will be
 650             // restored. We need the applet's initial focusabled component to
 651             // be focused here.
 652             toFocus.requestFocusInWindow();
 653         }
 654     }
 655 
 656     /**
 657      * Load the applet into memory.
 658      * Runs in a seperate (and interruptible) thread from the rest of the
 659      * applet event processing so that it can be gracefully interrupted from
 660      * things like HotJava.
 661      */
 662     @SuppressWarnings("deprecation")
 663     private void runLoader() {
 664         if (status != APPLET_DISPOSE) {
 665             showAppletStatus("notdisposed");
 666             return;
 667         }
 668 
 669         dispatchAppletEvent(APPLET_LOADING, null);
 670 
 671         // REMIND -- might be cool to visually indicate loading here --
 672         // maybe do animation?
 673         status = APPLET_LOAD;
 674 
 675         // Create a class loader
 676         loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
 677 
 678         // Load the archives if present.
 679         // REMIND - this probably should be done in a separate thread,
 680         // or at least the additional archives (epll).
 681 
 682         String code = getCode();
 683 
 684         // setup applet AppContext
 685         // this must be called before loadJarFiles
 686         setupAppletAppContext();
 687 
 688         try {
 689             loadJarFiles(loader);
 690             applet = createApplet(loader);
 691         } catch (ClassNotFoundException e) {
 692             status = APPLET_ERROR;
 693             showAppletStatus("notfound", code);
 694             showAppletLog("notfound", code);
 695             showAppletException(e);
 696             return;
 697         } catch (InstantiationException e) {
 698             status = APPLET_ERROR;
 699             showAppletStatus("nocreate", code);
 700             showAppletLog("nocreate", code);
 701             showAppletException(e);
 702             return;
 703         } catch (IllegalAccessException e) {
 704             status = APPLET_ERROR;
 705             showAppletStatus("noconstruct", code);
 706             showAppletLog("noconstruct", code);
 707             showAppletException(e);
 708             // sbb -- I added a return here
 709             return;
 710         } catch (Exception e) {
 711             status = APPLET_ERROR;
 712             showAppletStatus("exception", e.getMessage());
 713             showAppletException(e);
 714             return;
 715         } catch (ThreadDeath e) {
 716             status = APPLET_ERROR;
 717             showAppletStatus("death");
 718             return;
 719         } catch (Error e) {
 720             status = APPLET_ERROR;
 721             showAppletStatus("error", e.getMessage());
 722             showAppletException(e);
 723             return;
 724         } finally {
 725             // notify that loading is no longer going on
 726             dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
 727         }
 728 
 729         // Fixed #4508194: NullPointerException thrown during
 730         // quick page switch
 731         //
 732         if (applet != null)
 733         {
 734             // Stick it in the frame
 735             applet.setStub(this);
 736             applet.hide();
 737             add("Center", applet);
 738             showAppletStatus("loaded");
 739             validate();
 740         }
 741     }
 742 
 743     protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException,
 744                                                                          IllegalAccessException, IOException, InstantiationException, InterruptedException {
 745         String code = getCode();
 746 
 747         if (code != null) {
 748             applet = (Applet)loader.loadCode(code).newInstance();
 749         } else {
 750             String msg = "nocode";
 751             status = APPLET_ERROR;
 752             showAppletStatus(msg);
 753             showAppletLog(msg);
 754             repaint();
 755         }
 756 
 757         // Determine the JDK level that the applet targets.
 758         // This is critical for enabling certain backward
 759         // compatibility switch if an applet is a JDK 1.1
 760         // applet. [stanley.ho]
 761         findAppletJDKLevel(applet);
 762 
 763         if (Thread.interrupted()) {
 764             try {
 765                 status = APPLET_DISPOSE; // APPLET_ERROR?
 766                 applet = null;
 767                 // REMIND: This may not be exactly the right thing: the
 768                 // status is set by the stop button and not necessarily
 769                 // here.
 770                 showAppletStatus("death");
 771             } finally {
 772                 Thread.currentThread().interrupt(); // resignal interrupt
 773             }
 774             return null;
 775         }
 776         return applet;
 777     }
 778 
 779     protected void loadJarFiles(AppletClassLoader loader) throws IOException,
 780                                                                  InterruptedException {
 781         // Load the archives if present.
 782         // REMIND - this probably should be done in a separate thread,
 783         // or at least the additional archives (epll).
 784         String jarFiles = getJarFiles();
 785 
 786         if (jarFiles != null) {
 787             StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
 788             while(st.hasMoreTokens()) {
 789                 String tok = st.nextToken().trim();
 790                 try {
 791                     loader.addJar(tok);
 792                 } catch (IllegalArgumentException e) {
 793                     // bad archive name
 794                     continue;
 795                 }
 796             }
 797         }
 798     }
 799 
 800     /**
 801      * Request that the loading of the applet be stopped.
 802      */
 803     protected synchronized void stopLoading() {
 804         // REMIND: fill in the body
 805         if (loaderThread != null) {
 806             //System.out.println("Interrupting applet loader thread: " + loaderThread);
 807             loaderThread.interrupt();
 808         } else {
 809             setLoadAbortRequest();
 810         }
 811     }
 812 
 813 
 814     protected synchronized boolean okToLoad() {
 815         return !loadAbortRequest;
 816     }
 817 
 818     protected synchronized void clearLoadAbortRequest() {
 819         loadAbortRequest = false;
 820     }
 821 
 822     protected synchronized void setLoadAbortRequest() {
 823         loadAbortRequest = true;
 824     }
 825 
 826 
 827     private synchronized void setLoaderThread(Thread loaderThread) {
 828         this.loaderThread = loaderThread;
 829     }
 830 
 831     /**
 832      * Return true when the applet has been started.
 833      */
 834     @Override
 835     public boolean isActive() {
 836         return status == APPLET_START;
 837     }
 838 
 839 
 840     private EventQueue appEvtQ = null;
 841     /**
 842      * Is called when the applet wants to be resized.
 843      */
 844     @Override
 845     public void appletResize(int width, int height) {
 846         currentAppletSize.width = width;
 847         currentAppletSize.height = height;
 848         final Dimension currentSize = new Dimension(currentAppletSize.width,
 849                                                     currentAppletSize.height);
 850 
 851         if(loader != null) {
 852             AppContext appCtxt = loader.getAppContext();
 853             if(appCtxt != null)
 854                 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
 855         }
 856 
 857         final AppletPanel ap = this;
 858         if (appEvtQ != null){
 859             appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
 860                                                   new Runnable() {
 861                                                       @Override
 862                                                       public void run() {
 863                                                           if (ap != null) {
 864                                                               ap.dispatchAppletEvent(
 865                                                                       APPLET_RESIZE,
 866                                                                       currentSize);
 867                                                           }
 868                                                       }
 869                                                   }));
 870         }
 871     }
 872 
 873     @Override
 874     public void setBounds(int x, int y, int width, int height) {
 875         super.setBounds(x, y, width, height);
 876         currentAppletSize.width = width;
 877         currentAppletSize.height = height;
 878     }
 879 
 880     public Applet getApplet() {
 881         return applet;
 882     }
 883 
 884     /**
 885      * Status line. Called by the AppletPanel to provide
 886      * feedback on the Applet's state.
 887      */
 888     protected void showAppletStatus(String status) {
 889         getAppletContext().showStatus(amh.getMessage(status));
 890     }
 891 
 892     protected void showAppletStatus(String status, Object arg) {
 893         getAppletContext().showStatus(amh.getMessage(status, arg));
 894     }
 895     protected void showAppletStatus(String status, Object arg1, Object arg2) {
 896         getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
 897     }
 898 
 899     /**
 900      * Called by the AppletPanel to print to the log.
 901      */
 902     protected void showAppletLog(String msg) {
 903         System.out.println(amh.getMessage(msg));
 904     }
 905 
 906     protected void showAppletLog(String msg, Object arg) {
 907         System.out.println(amh.getMessage(msg, arg));
 908     }
 909 
 910     /**
 911      * Called by the AppletPanel to provide
 912      * feedback when an exception has happened.
 913      */
 914     protected void showAppletException(Throwable t) {
 915         t.printStackTrace();
 916         repaint();
 917     }
 918 
 919     /**
 920      * Get caching key for classloader cache
 921      */
 922     public String getClassLoaderCacheKey()
 923     {
 924         /**
 925          * Fixed #4501142: Classloader sharing policy doesn't
 926          * take "archive" into account. This will be overridden
 927          * by Java Plug-in.                     [stanleyh]
 928          */
 929         return getCodeBase().toString();
 930     }
 931 
 932     /**
 933      * The class loaders
 934      */
 935     private static HashMap<String, AppletClassLoader> classloaders = new HashMap<>();
 936 
 937     /**
 938      * Flush a class loader.
 939      */
 940     public static synchronized void flushClassLoader(String key) {
 941         classloaders.remove(key);
 942     }
 943 
 944     /**
 945      * Flush all class loaders.
 946      */
 947     public static synchronized void flushClassLoaders() {
 948         classloaders = new HashMap<>();
 949     }
 950 
 951     /**
 952      * This method actually creates an AppletClassLoader.
 953      *
 954      * It can be override by subclasses (such as the Plug-in)
 955      * to provide different classloaders.
 956      */
 957     protected AppletClassLoader createClassLoader(final URL codebase) {
 958         return new AppletClassLoader(codebase);
 959     }
 960 
 961     /**
 962      * Get a class loader. Create in a restricted context
 963      */
 964     synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
 965         AppletClassLoader c = classloaders.get(key);
 966         if (c == null) {
 967             AccessControlContext acc =
 968                 getAccessControlContext(codebase);
 969             c = AccessController.doPrivileged(
 970                     new PrivilegedAction<AppletClassLoader>() {
 971                         @Override
 972                         public AppletClassLoader run() {
 973                             AppletClassLoader ac = createClassLoader(codebase);
 974                             /* Should the creation of the classloader be
 975                              * within the class synchronized block?  Since
 976                              * this class is used by the plugin, take care
 977                              * to avoid deadlocks, or specialize
 978                              * AppletPanel within the plugin.  It may take
 979                              * an arbitrary amount of time to create a
 980                              * class loader (involving getting Jar files
 981                              * etc.) and may block unrelated applets from
 982                              * finishing createAppletThread (due to the
 983                              * class synchronization). If
 984                              * createAppletThread does not finish quickly,
 985                              * the applet cannot process other messages,
 986                              * particularly messages such as destroy
 987                              * (which timeout when called from the browser).
 988                              */
 989                             synchronized (getClass()) {
 990                                 AppletClassLoader res = classloaders.get(key);
 991                                 if (res == null) {
 992                                     classloaders.put(key, ac);
 993                                     return ac;
 994                                 } else {
 995                                     return res;
 996                                 }
 997                             }
 998                         }
 999                     },acc);
1000         }
1001         return c;
1002     }
1003 
1004     /**
1005      * get the context for the AppletClassLoader we are creating.
1006      * the context is granted permission to create the class loader,
1007      * connnect to the codebase, and whatever else the policy grants
1008      * to all codebases.
1009      */
1010     private AccessControlContext getAccessControlContext(final URL codebase) {
1011 
1012         PermissionCollection perms = AccessController.doPrivileged(
1013                 new PrivilegedAction<PermissionCollection>() {
1014                     @Override
1015                     public PermissionCollection run() {
1016                         Policy p = java.security.Policy.getPolicy();
1017                         if (p != null) {
1018                             return p.getPermissions(new CodeSource(null,
1019                                                                    (java.security.cert.Certificate[]) null));
1020                         } else {
1021                             return null;
1022                         }
1023                     }
1024                 });
1025 
1026         if (perms == null)
1027             perms = new Permissions();
1028 
1029         //XXX: this is needed to be able to create the classloader itself!
1030 
1031         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
1032 
1033         Permission p;
1034         java.net.URLConnection urlConnection = null;
1035         try {
1036             urlConnection = codebase.openConnection();
1037             p = urlConnection.getPermission();
1038         } catch (java.io.IOException ioe) {
1039             p = null;
1040         }
1041 
1042         if (p != null)
1043             perms.add(p);
1044 
1045         if (p instanceof FilePermission) {
1046 
1047             String path = p.getName();
1048 
1049             int endIndex = path.lastIndexOf(File.separatorChar);
1050 
1051             if (endIndex != -1) {
1052                 path = path.substring(0, endIndex+1);
1053 
1054                 if (path.endsWith(File.separator)) {
1055                     path += "-";
1056                 }
1057                 perms.add(new FilePermission(path,
1058                                              SecurityConstants.FILE_READ_ACTION));
1059             }
1060         } else {
1061             URL locUrl = codebase;
1062             if (urlConnection instanceof JarURLConnection) {
1063                 locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
1064             }
1065             String host = locUrl.getHost();
1066             if (host != null && (host.length() > 0))
1067                 perms.add(new SocketPermission(host,
1068                                                SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
1069         }
1070 
1071         ProtectionDomain domain =
1072             new ProtectionDomain(new CodeSource(codebase,
1073                                                 (java.security.cert.Certificate[]) null), perms);
1074         AccessControlContext acc =
1075             new AccessControlContext(new ProtectionDomain[] { domain });
1076 
1077         return acc;
1078     }
1079 
1080     public Thread getAppletHandlerThread() {
1081         return handler;
1082     }
1083 
1084     public int getAppletWidth() {
1085         return currentAppletSize.width;
1086     }
1087 
1088     public int getAppletHeight() {
1089         return currentAppletSize.height;
1090     }
1091 
1092     public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
1093     {
1094         // Fixed #4754451: Applet can have methods running on main
1095         // thread event queue.
1096         //
1097         // The cause of this bug is that the frame of the applet
1098         // is created in main thread group. Thus, when certain
1099         // AWT/Swing events are generated, the events will be
1100         // dispatched through the wrong event dispatch thread.
1101         //
1102         // To fix this, we rearrange the AppContext with the frame,
1103         // so the proper event queue will be looked up.
1104         //
1105         // Swing also maintains a Frame list for the AppContext,
1106         // so we will have to rearrange it as well.
1107 
1108         // Check if frame's AppContext has already been set properly
1109         AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
1110 
1111         if (oldAppContext == newAppContext)
1112             return;
1113 
1114         // Synchronization on Window.class is needed for locking the
1115         // critical section of the window list in AppContext.
1116         synchronized (Window.class)
1117         {
1118             WeakReference<Window> weakRef = null;
1119             // Remove frame from the Window list in wrong AppContext
1120             {
1121                 // Lookup current frame's AppContext
1122                 @SuppressWarnings("unchecked")
1123                 Vector<WeakReference<Window>> windowList =
1124                     (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
1125                 if (windowList != null) {
1126                     for (WeakReference<Window> ref : windowList) {
1127                         if (ref.get() == frame) {
1128                             weakRef = ref;
1129                             break;
1130                         }
1131                     }
1132                     // Remove frame from wrong AppContext
1133                     if (weakRef != null)
1134                         windowList.remove(weakRef);
1135                 }
1136             }
1137 
1138             // Put the frame into the applet's AppContext map
1139             SunToolkit.insertTargetMapping(frame, newAppContext);
1140 
1141             // Insert frame into the Window list in the applet's AppContext map
1142             {
1143                 @SuppressWarnings("unchecked")
1144                 Vector<WeakReference<Window>> windowList =
1145                     (Vector<WeakReference<Window>>)newAppContext.get(Window.class);
1146                 if (windowList == null) {
1147                     windowList = new Vector<WeakReference<Window>>();
1148                     newAppContext.put(Window.class, windowList);
1149                 }
1150                 // use the same weakRef here as it is used elsewhere
1151                 windowList.add(weakRef);
1152             }
1153         }
1154     }
1155 
1156     // Flag to indicate if applet is targeted for JDK 1.1.
1157     private boolean jdk11Applet = false;
1158 
1159     // Flag to indicate if applet is targeted for JDK 1.2.
1160     private boolean jdk12Applet = false;
1161 
1162     /**
1163      * Determine JDK level of an applet.
1164      */
1165     private void findAppletJDKLevel(Applet applet)
1166     {
1167         // To determine the JDK level of an applet, the
1168         // most reliable way is to check the major version
1169         // of the applet class file.
1170 
1171         // synchronized on applet class object, so calling from
1172         // different instances of the same applet will be
1173         // serialized.
1174         Class<?> appletClass = applet.getClass();
1175 
1176         synchronized(appletClass)  {
1177             // Determine if the JDK level of an applet has been
1178             // checked before.
1179             Boolean jdk11Target = loader.isJDK11Target(appletClass);
1180             Boolean jdk12Target = loader.isJDK12Target(appletClass);
1181 
1182             // if applet JDK level has been checked before, retrieve
1183             // value and return.
1184             if (jdk11Target != null || jdk12Target != null) {
1185                 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
1186                 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
1187                 return;
1188             }
1189 
1190             String name = appletClass.getName();
1191 
1192             // first convert any '.' to '/'
1193             name = name.replace('.', '/');
1194 
1195             // append .class
1196             final String resourceName = name + ".class";
1197 
1198             byte[] classHeader = new byte[8];
1199 
1200             try (InputStream is = AccessController.doPrivileged(
1201                     (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) {
1202 
1203                 // Read the first 8 bytes of the class file
1204                 int byteRead = is.read(classHeader, 0, 8);
1205 
1206                 // return if the header is not read in entirely
1207                 // for some reasons.
1208                 if (byteRead != 8)
1209                     return;
1210             }
1211             catch (IOException e)   {
1212                 return;
1213             }
1214 
1215             // Check major version in class file header
1216             int major_version = readShort(classHeader, 6);
1217 
1218             // Major version in class file is as follows:
1219             //   45 - JDK 1.1
1220             //   46 - JDK 1.2
1221             //   47 - JDK 1.3
1222             //   48 - JDK 1.4
1223             //   49 - JDK 1.5
1224             if (major_version < 46)
1225                 jdk11Applet = true;
1226             else if (major_version == 46)
1227                 jdk12Applet = true;
1228 
1229             // Store applet JDK level in AppContext for later lookup,
1230             // e.g. page switch.
1231             loader.setJDK11Target(appletClass, jdk11Applet);
1232             loader.setJDK12Target(appletClass, jdk12Applet);
1233         }
1234     }
1235 
1236     /**
1237      * Return true if applet is targeted to JDK 1.1.
1238      */
1239     protected boolean isJDK11Applet()   {
1240         return jdk11Applet;
1241     }
1242 
1243     /**
1244      * Return true if applet is targeted to JDK1.2.
1245      */
1246     protected boolean isJDK12Applet()   {
1247         return jdk12Applet;
1248     }
1249 
1250     /**
1251      * Read short from byte array.
1252      */
1253     private int readShort(byte[] b, int off)    {
1254         int hi = readByte(b[off]);
1255         int lo = readByte(b[off + 1]);
1256         return (hi << 8) | lo;
1257     }
1258 
1259     private int readByte(byte b) {
1260         return ((int)b) & 0xFF;
1261     }
1262 
1263 
1264     private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
1265 }