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