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