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