1 /* 2 * Copyright (c) 1995, 2014, 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.MessageUtils; 46 import sun.misc.PerformanceLogger; 47 import sun.misc.Queue; 48 import sun.security.util.SecurityConstants; 49 50 /** 51 * Applet panel class. The panel manages and manipulates the 52 * applet as it is being loaded. It forks a separate thread in a new 53 * thread group to call the applet's init(), start(), stop(), and 54 * destroy() methods. 55 * 56 * @author Arthur van Hoff 57 */ 58 @SuppressWarnings("serial") // JDK implementation class 59 public 60 abstract class AppletPanel extends Panel implements AppletStub, Runnable { 61 62 /** 63 * The applet (if loaded). 64 */ 65 Applet applet; 66 67 /** 68 * Applet will allow initialization. Should be 69 * set to false if loading a serialized applet 70 * that was pickled in the init=true state. 71 */ 72 protected boolean doInit = true; 73 74 75 /** 76 * The classloader for the applet. 77 */ 78 protected AppletClassLoader loader; 79 80 /* applet event ids */ 81 public final static int APPLET_DISPOSE = 0; 82 public final static int APPLET_LOAD = 1; 83 public final static int APPLET_INIT = 2; 84 public final static int APPLET_START = 3; 85 public final static int APPLET_STOP = 4; 86 public final static int APPLET_DESTROY = 5; 87 public final static int APPLET_QUIT = 6; 88 public final static int APPLET_ERROR = 7; 89 90 /* send to the parent to force relayout */ 91 public final static int APPLET_RESIZE = 51234; 92 93 /* sent to a (distant) parent to indicate that the applet is being 94 * loaded or as completed loading 95 */ 96 public final static int APPLET_LOADING = 51235; 97 public final static int APPLET_LOADING_COMPLETED = 51236; 98 99 /** 100 * The current status. One of: 101 * APPLET_DISPOSE, 102 * APPLET_LOAD, 103 * APPLET_INIT, 104 * APPLET_START, 105 * APPLET_STOP, 106 * APPLET_DESTROY, 107 * APPLET_ERROR. 108 */ 109 protected int status; 110 111 /** 112 * The thread for the applet. 113 */ 114 protected Thread handler; 115 116 117 /** 118 * The initial applet size. 119 */ 120 Dimension defaultAppletSize = new Dimension(10, 10); 121 122 /** 123 * The current applet size. 124 */ 125 Dimension currentAppletSize = new Dimension(10, 10); 126 127 MessageUtils mu = new MessageUtils(); 128 129 /** 130 * The thread to use during applet loading 131 */ 132 133 Thread loaderThread = null; 134 135 /** 136 * Flag to indicate that a loading has been cancelled 137 */ 138 boolean loadAbortRequest = false; 139 140 /* abstract classes */ 141 abstract protected String getCode(); 142 abstract protected String getJarFiles(); 143 abstract protected String getSerializedObject(); 144 145 @Override 146 abstract public int getWidth(); 147 @Override 148 abstract public int getHeight(); 149 abstract public boolean hasInitialFocus(); 150 151 private static int threadGroupNumber = 0; 152 153 protected void setupAppletAppContext() { 154 // do nothing 155 } 156 157 /* 158 * Creates a thread to run the applet. This method is called 159 * each time an applet is loaded and reloaded. 160 */ 161 synchronized void createAppletThread() { 162 // Create a thread group for the applet, and start a new 163 // thread to load the applet. 164 String nm = "applet-" + getCode(); 165 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 166 loader.grab(); // Keep this puppy around! 167 168 // 4668479: Option to turn off codebase lookup in AppletClassLoader 169 // during resource requests. [stanley.ho] 170 String param = getParameter("codebase_lookup"); 171 172 if (param != null && param.equals("false")) 173 loader.setCodebaseLookup(false); 174 else 175 loader.setCodebaseLookup(true); 176 177 178 ThreadGroup appletGroup = loader.getThreadGroup(); 179 180 handler = new Thread(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 Thread(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 ((EmbeddedFrame)parent).synthesizeWindowActivation(true); 690 } 691 // EmbeddedFrame might have focus before the applet was added. 692 // Thus after its activation the most recent focus owner will be 693 // restored. We need the applet's initial focusabled component to 694 // be focused here. 695 toFocus.requestFocusInWindow(); 696 } 697 } 698 699 /** 700 * Load the applet into memory. 701 * Runs in a seperate (and interruptible) thread from the rest of the 702 * applet event processing so that it can be gracefully interrupted from 703 * things like HotJava. 704 */ 705 @SuppressWarnings("deprecation") 706 private void runLoader() { 707 if (status != APPLET_DISPOSE) { 708 showAppletStatus("notdisposed"); 709 return; 710 } 711 712 dispatchAppletEvent(APPLET_LOADING, null); 713 714 // REMIND -- might be cool to visually indicate loading here -- 715 // maybe do animation? 716 status = APPLET_LOAD; 717 718 // Create a class loader 719 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 720 721 // Load the archives if present. 722 // REMIND - this probably should be done in a separate thread, 723 // or at least the additional archives (epll). 724 725 String code = getCode(); 726 727 // setup applet AppContext 728 // this must be called before loadJarFiles 729 setupAppletAppContext(); 730 731 try { 732 loadJarFiles(loader); 733 applet = createApplet(loader); 734 } catch (ClassNotFoundException e) { 735 status = APPLET_ERROR; 736 showAppletStatus("notfound", code); 737 showAppletLog("notfound", code); 738 showAppletException(e); 739 return; 740 } catch (InstantiationException e) { 741 status = APPLET_ERROR; 742 showAppletStatus("nocreate", code); 743 showAppletLog("nocreate", code); 744 showAppletException(e); 745 return; 746 } catch (IllegalAccessException e) { 747 status = APPLET_ERROR; 748 showAppletStatus("noconstruct", code); 749 showAppletLog("noconstruct", code); 750 showAppletException(e); 751 // sbb -- I added a return here 752 return; 753 } catch (Exception e) { 754 status = APPLET_ERROR; 755 showAppletStatus("exception", e.getMessage()); 756 showAppletException(e); 757 return; 758 } catch (ThreadDeath e) { 759 status = APPLET_ERROR; 760 showAppletStatus("death"); 761 return; 762 } catch (Error e) { 763 status = APPLET_ERROR; 764 showAppletStatus("error", e.getMessage()); 765 showAppletException(e); 766 return; 767 } finally { 768 // notify that loading is no longer going on 769 dispatchAppletEvent(APPLET_LOADING_COMPLETED, null); 770 } 771 772 // Fixed #4508194: NullPointerException thrown during 773 // quick page switch 774 // 775 if (applet != null) 776 { 777 // Stick it in the frame 778 applet.setStub(this); 779 applet.hide(); 780 add("Center", applet); 781 showAppletStatus("loaded"); 782 validate(); 783 } 784 } 785 786 protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException, 787 IllegalAccessException, IOException, InstantiationException, InterruptedException { 788 final String serName = getSerializedObject(); 789 String code = getCode(); 790 791 if (code != null && serName != null) { 792 System.err.println(amh.getMessage("runloader.err")); 793 // return null; 794 throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both."); 795 } 796 if (code == null && serName == null) { 797 String msg = "nocode"; 798 status = APPLET_ERROR; 799 showAppletStatus(msg); 800 showAppletLog(msg); 801 repaint(); 802 } 803 if (code != null) { 804 applet = (Applet)loader.loadCode(code).newInstance(); 805 doInit = true; 806 } else { 807 // serName is not null; 808 try (InputStream is = AccessController.doPrivileged( 809 (PrivilegedAction<InputStream>)() -> loader.getResourceAsStream(serName)); 810 ObjectInputStream ois = new AppletObjectInputStream(is, loader)) { 811 812 applet = (Applet) ois.readObject(); 813 doInit = false; // skip over the first init 814 } 815 } 816 817 // Determine the JDK level that the applet targets. 818 // This is critical for enabling certain backward 819 // compatibility switch if an applet is a JDK 1.1 820 // applet. [stanley.ho] 821 findAppletJDKLevel(applet); 822 823 if (Thread.interrupted()) { 824 try { 825 status = APPLET_DISPOSE; // APPLET_ERROR? 826 applet = null; 827 // REMIND: This may not be exactly the right thing: the 828 // status is set by the stop button and not necessarily 829 // here. 830 showAppletStatus("death"); 831 } finally { 832 Thread.currentThread().interrupt(); // resignal interrupt 833 } 834 return null; 835 } 836 return applet; 837 } 838 839 protected void loadJarFiles(AppletClassLoader loader) throws IOException, 840 InterruptedException { 841 // Load the archives if present. 842 // REMIND - this probably should be done in a separate thread, 843 // or at least the additional archives (epll). 844 String jarFiles = getJarFiles(); 845 846 if (jarFiles != null) { 847 StringTokenizer st = new StringTokenizer(jarFiles, ",", false); 848 while(st.hasMoreTokens()) { 849 String tok = st.nextToken().trim(); 850 try { 851 loader.addJar(tok); 852 } catch (IllegalArgumentException e) { 853 // bad archive name 854 continue; 855 } 856 } 857 } 858 } 859 860 /** 861 * Request that the loading of the applet be stopped. 862 */ 863 protected synchronized void stopLoading() { 864 // REMIND: fill in the body 865 if (loaderThread != null) { 866 //System.out.println("Interrupting applet loader thread: " + loaderThread); 867 loaderThread.interrupt(); 868 } else { 869 setLoadAbortRequest(); 870 } 871 } 872 873 874 protected synchronized boolean okToLoad() { 875 return !loadAbortRequest; 876 } 877 878 protected synchronized void clearLoadAbortRequest() { 879 loadAbortRequest = false; 880 } 881 882 protected synchronized void setLoadAbortRequest() { 883 loadAbortRequest = true; 884 } 885 886 887 private synchronized void setLoaderThread(Thread loaderThread) { 888 this.loaderThread = loaderThread; 889 } 890 891 /** 892 * Return true when the applet has been started. 893 */ 894 @Override 895 public boolean isActive() { 896 return status == APPLET_START; 897 } 898 899 900 private EventQueue appEvtQ = null; 901 /** 902 * Is called when the applet wants to be resized. 903 */ 904 @Override 905 public void appletResize(int width, int height) { 906 currentAppletSize.width = width; 907 currentAppletSize.height = height; 908 final Dimension currentSize = new Dimension(currentAppletSize.width, 909 currentAppletSize.height); 910 911 if(loader != null) { 912 AppContext appCtxt = loader.getAppContext(); 913 if(appCtxt != null) 914 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY); 915 } 916 917 final AppletPanel ap = this; 918 if (appEvtQ != null){ 919 appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), 920 new Runnable() { 921 @Override 922 public void run() { 923 if (ap != null) { 924 ap.dispatchAppletEvent( 925 APPLET_RESIZE, 926 currentSize); 927 } 928 } 929 })); 930 } 931 } 932 933 @Override 934 public void setBounds(int x, int y, int width, int height) { 935 super.setBounds(x, y, width, height); 936 currentAppletSize.width = width; 937 currentAppletSize.height = height; 938 } 939 940 public Applet getApplet() { 941 return applet; 942 } 943 944 /** 945 * Status line. Called by the AppletPanel to provide 946 * feedback on the Applet's state. 947 */ 948 protected void showAppletStatus(String status) { 949 getAppletContext().showStatus(amh.getMessage(status)); 950 } 951 952 protected void showAppletStatus(String status, Object arg) { 953 getAppletContext().showStatus(amh.getMessage(status, arg)); 954 } 955 protected void showAppletStatus(String status, Object arg1, Object arg2) { 956 getAppletContext().showStatus(amh.getMessage(status, arg1, arg2)); 957 } 958 959 /** 960 * Called by the AppletPanel to print to the log. 961 */ 962 protected void showAppletLog(String msg) { 963 System.out.println(amh.getMessage(msg)); 964 } 965 966 protected void showAppletLog(String msg, Object arg) { 967 System.out.println(amh.getMessage(msg, arg)); 968 } 969 970 /** 971 * Called by the AppletPanel to provide 972 * feedback when an exception has happened. 973 */ 974 protected void showAppletException(Throwable t) { 975 t.printStackTrace(); 976 repaint(); 977 } 978 979 /** 980 * Get caching key for classloader cache 981 */ 982 public String getClassLoaderCacheKey() 983 { 984 /** 985 * Fixed #4501142: Classloader sharing policy doesn't 986 * take "archive" into account. This will be overridden 987 * by Java Plug-in. [stanleyh] 988 */ 989 return getCodeBase().toString(); 990 } 991 992 /** 993 * The class loaders 994 */ 995 private static HashMap<String, AppletClassLoader> classloaders = new HashMap<>(); 996 997 /** 998 * Flush a class loader. 999 */ 1000 public static synchronized void flushClassLoader(String key) { 1001 classloaders.remove(key); 1002 } 1003 1004 /** 1005 * Flush all class loaders. 1006 */ 1007 public static synchronized void flushClassLoaders() { 1008 classloaders = new HashMap<>(); 1009 } 1010 1011 /** 1012 * This method actually creates an AppletClassLoader. 1013 * 1014 * It can be override by subclasses (such as the Plug-in) 1015 * to provide different classloaders. 1016 */ 1017 protected AppletClassLoader createClassLoader(final URL codebase) { 1018 return new AppletClassLoader(codebase); 1019 } 1020 1021 /** 1022 * Get a class loader. Create in a restricted context 1023 */ 1024 synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) { 1025 AppletClassLoader c = classloaders.get(key); 1026 if (c == null) { 1027 AccessControlContext acc = 1028 getAccessControlContext(codebase); 1029 c = AccessController.doPrivileged( 1030 new PrivilegedAction<AppletClassLoader>() { 1031 @Override 1032 public AppletClassLoader run() { 1033 AppletClassLoader ac = createClassLoader(codebase); 1034 /* Should the creation of the classloader be 1035 * within the class synchronized block? Since 1036 * this class is used by the plugin, take care 1037 * to avoid deadlocks, or specialize 1038 * AppletPanel within the plugin. It may take 1039 * an arbitrary amount of time to create a 1040 * class loader (involving getting Jar files 1041 * etc.) and may block unrelated applets from 1042 * finishing createAppletThread (due to the 1043 * class synchronization). If 1044 * createAppletThread does not finish quickly, 1045 * the applet cannot process other messages, 1046 * particularly messages such as destroy 1047 * (which timeout when called from the browser). 1048 */ 1049 synchronized (getClass()) { 1050 AppletClassLoader res = classloaders.get(key); 1051 if (res == null) { 1052 classloaders.put(key, ac); 1053 return ac; 1054 } else { 1055 return res; 1056 } 1057 } 1058 } 1059 },acc); 1060 } 1061 return c; 1062 } 1063 1064 /** 1065 * get the context for the AppletClassLoader we are creating. 1066 * the context is granted permission to create the class loader, 1067 * connnect to the codebase, and whatever else the policy grants 1068 * to all codebases. 1069 */ 1070 private AccessControlContext getAccessControlContext(final URL codebase) { 1071 1072 PermissionCollection perms = AccessController.doPrivileged( 1073 new PrivilegedAction<PermissionCollection>() { 1074 @Override 1075 public PermissionCollection run() { 1076 Policy p = java.security.Policy.getPolicy(); 1077 if (p != null) { 1078 return p.getPermissions(new CodeSource(null, 1079 (java.security.cert.Certificate[]) null)); 1080 } else { 1081 return null; 1082 } 1083 } 1084 }); 1085 1086 if (perms == null) 1087 perms = new Permissions(); 1088 1089 //XXX: this is needed to be able to create the classloader itself! 1090 1091 perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); 1092 1093 Permission p; 1094 java.net.URLConnection urlConnection = null; 1095 try { 1096 urlConnection = codebase.openConnection(); 1097 p = urlConnection.getPermission(); 1098 } catch (java.io.IOException ioe) { 1099 p = null; 1100 } 1101 1102 if (p != null) 1103 perms.add(p); 1104 1105 if (p instanceof FilePermission) { 1106 1107 String path = p.getName(); 1108 1109 int endIndex = path.lastIndexOf(File.separatorChar); 1110 1111 if (endIndex != -1) { 1112 path = path.substring(0, endIndex+1); 1113 1114 if (path.endsWith(File.separator)) { 1115 path += "-"; 1116 } 1117 perms.add(new FilePermission(path, 1118 SecurityConstants.FILE_READ_ACTION)); 1119 } 1120 } else { 1121 URL locUrl = codebase; 1122 if (urlConnection instanceof JarURLConnection) { 1123 locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); 1124 } 1125 String host = locUrl.getHost(); 1126 if (host != null && (host.length() > 0)) 1127 perms.add(new SocketPermission(host, 1128 SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION)); 1129 } 1130 1131 ProtectionDomain domain = 1132 new ProtectionDomain(new CodeSource(codebase, 1133 (java.security.cert.Certificate[]) null), perms); 1134 AccessControlContext acc = 1135 new AccessControlContext(new ProtectionDomain[] { domain }); 1136 1137 return acc; 1138 } 1139 1140 public Thread getAppletHandlerThread() { 1141 return handler; 1142 } 1143 1144 public int getAppletWidth() { 1145 return currentAppletSize.width; 1146 } 1147 1148 public int getAppletHeight() { 1149 return currentAppletSize.height; 1150 } 1151 1152 public static void changeFrameAppContext(Frame frame, AppContext newAppContext) 1153 { 1154 // Fixed #4754451: Applet can have methods running on main 1155 // thread event queue. 1156 // 1157 // The cause of this bug is that the frame of the applet 1158 // is created in main thread group. Thus, when certain 1159 // AWT/Swing events are generated, the events will be 1160 // dispatched through the wrong event dispatch thread. 1161 // 1162 // To fix this, we rearrange the AppContext with the frame, 1163 // so the proper event queue will be looked up. 1164 // 1165 // Swing also maintains a Frame list for the AppContext, 1166 // so we will have to rearrange it as well. 1167 1168 // Check if frame's AppContext has already been set properly 1169 AppContext oldAppContext = SunToolkit.targetToAppContext(frame); 1170 1171 if (oldAppContext == newAppContext) 1172 return; 1173 1174 // Synchronization on Window.class is needed for locking the 1175 // critical section of the window list in AppContext. 1176 synchronized (Window.class) 1177 { 1178 WeakReference<Window> weakRef = null; 1179 // Remove frame from the Window list in wrong AppContext 1180 { 1181 // Lookup current frame's AppContext 1182 @SuppressWarnings("unchecked") 1183 Vector<WeakReference<Window>> windowList = 1184 (Vector<WeakReference<Window>>)oldAppContext.get(Window.class); 1185 if (windowList != null) { 1186 for (WeakReference<Window> ref : windowList) { 1187 if (ref.get() == frame) { 1188 weakRef = ref; 1189 break; 1190 } 1191 } 1192 // Remove frame from wrong AppContext 1193 if (weakRef != null) 1194 windowList.remove(weakRef); 1195 } 1196 } 1197 1198 // Put the frame into the applet's AppContext map 1199 SunToolkit.insertTargetMapping(frame, newAppContext); 1200 1201 // Insert frame into the Window list in the applet's AppContext map 1202 { 1203 @SuppressWarnings("unchecked") 1204 Vector<WeakReference<Window>> windowList = 1205 (Vector<WeakReference<Window>>)newAppContext.get(Window.class); 1206 if (windowList == null) { 1207 windowList = new Vector<WeakReference<Window>>(); 1208 newAppContext.put(Window.class, windowList); 1209 } 1210 // use the same weakRef here as it is used elsewhere 1211 windowList.add(weakRef); 1212 } 1213 } 1214 } 1215 1216 // Flag to indicate if applet is targeted for JDK 1.1. 1217 private boolean jdk11Applet = false; 1218 1219 // Flag to indicate if applet is targeted for JDK 1.2. 1220 private boolean jdk12Applet = false; 1221 1222 /** 1223 * Determine JDK level of an applet. 1224 */ 1225 private void findAppletJDKLevel(Applet applet) 1226 { 1227 // To determine the JDK level of an applet, the 1228 // most reliable way is to check the major version 1229 // of the applet class file. 1230 1231 // synchronized on applet class object, so calling from 1232 // different instances of the same applet will be 1233 // serialized. 1234 Class<?> appletClass = applet.getClass(); 1235 1236 synchronized(appletClass) { 1237 // Determine if the JDK level of an applet has been 1238 // checked before. 1239 Boolean jdk11Target = loader.isJDK11Target(appletClass); 1240 Boolean jdk12Target = loader.isJDK12Target(appletClass); 1241 1242 // if applet JDK level has been checked before, retrieve 1243 // value and return. 1244 if (jdk11Target != null || jdk12Target != null) { 1245 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue(); 1246 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue(); 1247 return; 1248 } 1249 1250 String name = appletClass.getName(); 1251 1252 // first convert any '.' to '/' 1253 name = name.replace('.', '/'); 1254 1255 // append .class 1256 final String resourceName = name + ".class"; 1257 1258 byte[] classHeader = new byte[8]; 1259 1260 try (InputStream is = AccessController.doPrivileged( 1261 (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) { 1262 1263 // Read the first 8 bytes of the class file 1264 int byteRead = is.read(classHeader, 0, 8); 1265 1266 // return if the header is not read in entirely 1267 // for some reasons. 1268 if (byteRead != 8) 1269 return; 1270 } 1271 catch (IOException e) { 1272 return; 1273 } 1274 1275 // Check major version in class file header 1276 int major_version = readShort(classHeader, 6); 1277 1278 // Major version in class file is as follows: 1279 // 45 - JDK 1.1 1280 // 46 - JDK 1.2 1281 // 47 - JDK 1.3 1282 // 48 - JDK 1.4 1283 // 49 - JDK 1.5 1284 if (major_version < 46) 1285 jdk11Applet = true; 1286 else if (major_version == 46) 1287 jdk12Applet = true; 1288 1289 // Store applet JDK level in AppContext for later lookup, 1290 // e.g. page switch. 1291 loader.setJDK11Target(appletClass, jdk11Applet); 1292 loader.setJDK12Target(appletClass, jdk12Applet); 1293 } 1294 } 1295 1296 /** 1297 * Return true if applet is targeted to JDK 1.1. 1298 */ 1299 protected boolean isJDK11Applet() { 1300 return jdk11Applet; 1301 } 1302 1303 /** 1304 * Return true if applet is targeted to JDK1.2. 1305 */ 1306 protected boolean isJDK12Applet() { 1307 return jdk12Applet; 1308 } 1309 1310 /** 1311 * Read short from byte array. 1312 */ 1313 private int readShort(byte[] b, int off) { 1314 int hi = readByte(b[off]); 1315 int lo = readByte(b[off + 1]); 1316 return (hi << 8) | lo; 1317 } 1318 1319 private int readByte(byte b) { 1320 return ((int)b) & 0xFF; 1321 } 1322 1323 1324 private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel"); 1325 }