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