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