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