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