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