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