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