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