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