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