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.util.*; 29 import java.io.*; 30 import java.awt.*; 31 import java.awt.event.*; 32 import java.awt.print.*; 33 import javax.print.attribute.*; 34 import java.applet.*; 35 import java.net.URL; 36 import java.net.MalformedURLException; 37 import java.net.SocketPermission; 38 import java.security.AccessController; 39 import java.security.PrivilegedAction; 40 import java.lang.reflect.InvocationTargetException; 41 import java.lang.reflect.Method; 42 import sun.awt.SunToolkit; 43 import sun.awt.AppContext; 44 import java.lang.ref.WeakReference; 45 46 /** 47 * A frame to show the applet tag in. 48 */ 49 @SuppressWarnings("serial") // JDK-implementation class 50 class TextFrame extends Frame { 51 52 /** 53 * Create the tag frame. 54 */ 55 TextFrame(int x, int y, String title, String text) { 56 setTitle(title); 57 TextArea txt = new TextArea(20, 60); 58 txt.setText(text); 59 txt.setEditable(false); 60 61 add("Center", txt); 62 63 Panel p = new Panel(); 64 add("South", p); 65 Button b = new Button(amh.getMessage("button.dismiss", "Dismiss")); 66 p.add(b); 67 68 class ActionEventListener implements ActionListener { 69 public void actionPerformed(ActionEvent evt) { 70 dispose(); 71 } 72 } 73 b.addActionListener(new ActionEventListener()); 74 75 pack(); 76 move(x, y); 77 setVisible(true); 78 79 WindowListener windowEventListener = new WindowAdapter() { 80 81 public void windowClosing(WindowEvent evt) { 82 dispose(); 83 } 84 }; 85 86 addWindowListener(windowEventListener); 87 } 88 private static AppletMessageHandler amh = new AppletMessageHandler("textframe"); 89 90 } 91 92 /** 93 * Lets us construct one using unix-style one shot behaviors 94 */ 95 96 class StdAppletViewerFactory implements AppletViewerFactory 97 { 98 public AppletViewer createAppletViewer(int x, int y, 99 URL doc, Hashtable atts) { 100 return new AppletViewer(x, y, doc, atts, System.out, this); 101 } 102 103 public MenuBar getBaseMenuBar() { 104 return new MenuBar(); 105 } 106 107 public boolean isStandalone() { 108 return true; 109 } 110 } 111 112 /** 113 * The applet viewer makes it possible to run a Java applet without using a browser. 114 * For details on the syntax that <B>appletviewer</B> supports, see 115 * <a href="../../../docs/tooldocs/appletviewertags.html">AppletViewer Tags</a>. 116 * (The document named appletviewertags.html in the JDK's docs/tooldocs directory, 117 * once the JDK docs have been installed.) 118 */ 119 @SuppressWarnings("serial") // JDK implementation class 120 public class AppletViewer extends Frame implements AppletContext, 121 Printable { 122 123 /** 124 * Some constants... 125 */ 126 private static String defaultSaveFile = "Applet.ser"; 127 128 /** 129 * The panel in which the applet is being displayed. 130 */ 131 AppletViewerPanel panel; 132 133 /** 134 * The status line. 135 */ 136 Label label; 137 138 /** 139 * output status messages to this stream 140 */ 141 142 PrintStream statusMsgStream; 143 144 /** 145 * For cloning 146 */ 147 AppletViewerFactory factory; 148 149 150 private final class UserActionListener implements ActionListener { 151 public void actionPerformed(ActionEvent evt) { 152 processUserAction(evt); 153 } 154 } 155 156 /** 157 * Create the applet viewer 158 */ 159 public AppletViewer(int x, int y, URL doc, Hashtable atts, 160 PrintStream statusMsgStream, AppletViewerFactory factory) { 161 this.factory = factory; 162 this.statusMsgStream = statusMsgStream; 163 setTitle(amh.getMessage("tool.title", atts.get("code"))); 164 165 MenuBar mb = factory.getBaseMenuBar(); 166 167 Menu m = new Menu(amh.getMessage("menu.applet")); 168 169 addMenuItem(m, "menuitem.restart"); 170 addMenuItem(m, "menuitem.reload"); 171 addMenuItem(m, "menuitem.stop"); 172 addMenuItem(m, "menuitem.save"); 173 addMenuItem(m, "menuitem.start"); 174 addMenuItem(m, "menuitem.clone"); 175 m.add(new MenuItem("-")); 176 addMenuItem(m, "menuitem.tag"); 177 addMenuItem(m, "menuitem.info"); 178 addMenuItem(m, "menuitem.edit").disable(); 179 addMenuItem(m, "menuitem.encoding"); 180 m.add(new MenuItem("-")); 181 addMenuItem(m, "menuitem.print"); 182 m.add(new MenuItem("-")); 183 addMenuItem(m, "menuitem.props"); 184 m.add(new MenuItem("-")); 185 addMenuItem(m, "menuitem.close"); 186 if (factory.isStandalone()) { 187 addMenuItem(m, "menuitem.quit"); 188 } 189 190 mb.add(m); 191 192 setMenuBar(mb); 193 194 add("Center", panel = new AppletViewerPanel(doc, atts)); 195 add("South", label = new Label(amh.getMessage("label.hello"))); 196 panel.init(); 197 appletPanels.addElement(panel); 198 199 pack(); 200 move(x, y); 201 setVisible(true); 202 203 WindowListener windowEventListener = new WindowAdapter() { 204 205 public void windowClosing(WindowEvent evt) { 206 appletClose(); 207 } 208 209 public void windowIconified(WindowEvent evt) { 210 appletStop(); 211 } 212 213 public void windowDeiconified(WindowEvent evt) { 214 appletStart(); 215 } 216 }; 217 218 class AppletEventListener implements AppletListener 219 { 220 final Frame frame; 221 222 public AppletEventListener(Frame frame) 223 { 224 this.frame = frame; 225 } 226 227 public void appletStateChanged(AppletEvent evt) 228 { 229 AppletPanel src = (AppletPanel)evt.getSource(); 230 231 switch (evt.getID()) { 232 case AppletPanel.APPLET_RESIZE: { 233 if(src != null) { 234 resize(preferredSize()); 235 validate(); 236 } 237 break; 238 } 239 case AppletPanel.APPLET_LOADING_COMPLETED: { 240 Applet a = src.getApplet(); // sun.applet.AppletPanel 241 242 // Fixed #4754451: Applet can have methods running on main 243 // thread event queue. 244 // 245 // The cause of this bug is that the frame of the applet 246 // is created in main thread group. Thus, when certain 247 // AWT/Swing events are generated, the events will be 248 // dispatched through the wrong event dispatch thread. 249 // 250 // To fix this, we rearrange the AppContext with the frame, 251 // so the proper event queue will be looked up. 252 // 253 // Swing also maintains a Frame list for the AppContext, 254 // so we will have to rearrange it as well. 255 // 256 if (a != null) 257 AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a)); 258 else 259 AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext()); 260 261 break; 262 } 263 } 264 } 265 }; 266 267 addWindowListener(windowEventListener); 268 panel.addAppletListener(new AppletEventListener(this)); 269 270 // Start the applet 271 showStatus(amh.getMessage("status.start")); 272 initEventQueue(); 273 } 274 275 // XXX 99/9/10 probably should be "private" 276 public MenuItem addMenuItem(Menu m, String s) { 277 MenuItem mItem = new MenuItem(amh.getMessage(s)); 278 mItem.addActionListener(new UserActionListener()); 279 return m.add(mItem); 280 } 281 282 /** 283 * Send the initial set of events to the appletviewer event queue. 284 * On start-up the current behaviour is to load the applet and call 285 * Applet.init() and Applet.start(). 286 */ 287 private void initEventQueue() { 288 // appletviewer.send.event is an undocumented and unsupported system 289 // property which is used exclusively for testing purposes. 290 String eventList = System.getProperty("appletviewer.send.event"); 291 292 if (eventList == null) { 293 // Add the standard events onto the event queue. 294 panel.sendEvent(AppletPanel.APPLET_LOAD); 295 panel.sendEvent(AppletPanel.APPLET_INIT); 296 panel.sendEvent(AppletPanel.APPLET_START); 297 } else { 298 // We're testing AppletViewer. Force the specified set of events 299 // onto the event queue, wait for the events to be processed, and 300 // exit. 301 302 // The list of events that will be executed is provided as a 303 // ","-separated list. No error-checking will be done on the list. 304 String [] events = splitSeparator(",", eventList); 305 306 for (int i = 0; i < events.length; i++) { 307 System.out.println("Adding event to queue: " + events[i]); 308 if (events[i].equals("dispose")) 309 panel.sendEvent(AppletPanel.APPLET_DISPOSE); 310 else if (events[i].equals("load")) 311 panel.sendEvent(AppletPanel.APPLET_LOAD); 312 else if (events[i].equals("init")) 313 panel.sendEvent(AppletPanel.APPLET_INIT); 314 else if (events[i].equals("start")) 315 panel.sendEvent(AppletPanel.APPLET_START); 316 else if (events[i].equals("stop")) 317 panel.sendEvent(AppletPanel.APPLET_STOP); 318 else if (events[i].equals("destroy")) 319 panel.sendEvent(AppletPanel.APPLET_DESTROY); 320 else if (events[i].equals("quit")) 321 panel.sendEvent(AppletPanel.APPLET_QUIT); 322 else if (events[i].equals("error")) 323 panel.sendEvent(AppletPanel.APPLET_ERROR); 324 else 325 // non-fatal error if we get an unrecognized event 326 System.out.println("Unrecognized event name: " + events[i]); 327 } 328 329 while (!panel.emptyEventQueue()) ; 330 appletSystemExit(); 331 } 332 } 333 334 /** 335 * Split a string based on the presence of a specified separator. Returns 336 * an array of arbitrary length. The end of each element in the array is 337 * indicated by the separator of the end of the string. If there is a 338 * separator immediately before the end of the string, the final element 339 * will be empty. None of the strings will contain the separator. Useful 340 * when separating strings such as "foo/bar/bas" using separator "/". 341 * 342 * @param sep The separator. 343 * @param s The string to split. 344 * @return An array of strings. Each string in the array is determined 345 * by the location of the provided sep in the original string, 346 * s. Whitespace not stripped. 347 */ 348 private String [] splitSeparator(String sep, String s) { 349 Vector v = new Vector(); 350 int tokenStart = 0; 351 int tokenEnd = 0; 352 353 while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) { 354 v.addElement(s.substring(tokenStart, tokenEnd)); 355 tokenStart = tokenEnd+1; 356 } 357 // Add the final element. 358 v.addElement(s.substring(tokenStart)); 359 360 String [] retVal = new String[v.size()]; 361 v.copyInto(retVal); 362 return retVal; 363 } 364 365 /* 366 * Methods for java.applet.AppletContext 367 */ 368 369 private static Map audioClips = new HashMap(); 370 371 /** 372 * Get an audio clip. 373 */ 374 public AudioClip getAudioClip(URL url) { 375 checkConnect(url); 376 synchronized (audioClips) { 377 AudioClip clip = (AudioClip)audioClips.get(url); 378 if (clip == null) { 379 audioClips.put(url, clip = new AppletAudioClip(url)); 380 } 381 return clip; 382 } 383 } 384 385 private static Map imageRefs = new HashMap(); 386 387 /** 388 * Get an image. 389 */ 390 public Image getImage(URL url) { 391 return getCachedImage(url); 392 } 393 394 /** 395 * Get an image. 396 */ 397 static Image getCachedImage(URL url) { 398 // System.getSecurityManager().checkConnection(url.getHost(), url.getPort()); 399 synchronized (imageRefs) { 400 AppletImageRef ref = (AppletImageRef)imageRefs.get(url); 401 if (ref == null) { 402 ref = new AppletImageRef(url); 403 imageRefs.put(url, ref); 404 } 405 return ref.get(); 406 } 407 } 408 409 /** 410 * Flush the image cache. 411 */ 412 static void flushImageCache() { 413 imageRefs.clear(); 414 } 415 416 static Vector appletPanels = new Vector(); 417 418 /** 419 * Get an applet by name. 420 */ 421 public Applet getApplet(String name) { 422 AppletSecurity security = (AppletSecurity)System.getSecurityManager(); 423 name = name.toLowerCase(); 424 SocketPermission panelSp = 425 new SocketPermission(panel.getCodeBase().getHost(), "connect"); 426 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { 427 AppletPanel p = (AppletPanel)e.nextElement(); 428 String param = p.getParameter("name"); 429 if (param != null) { 430 param = param.toLowerCase(); 431 } 432 if (name.equals(param) && 433 p.getDocumentBase().equals(panel.getDocumentBase())) { 434 435 SocketPermission sp = 436 new SocketPermission(p.getCodeBase().getHost(), "connect"); 437 438 if (panelSp.implies(sp)) { 439 return p.applet; 440 } 441 } 442 } 443 return null; 444 } 445 446 /** 447 * Return an enumeration of all the accessible 448 * applets on this page. 449 */ 450 public Enumeration getApplets() { 451 AppletSecurity security = (AppletSecurity)System.getSecurityManager(); 452 Vector v = new Vector(); 453 SocketPermission panelSp = 454 new SocketPermission(panel.getCodeBase().getHost(), "connect"); 455 456 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { 457 AppletPanel p = (AppletPanel)e.nextElement(); 458 if (p.getDocumentBase().equals(panel.getDocumentBase())) { 459 460 SocketPermission sp = 461 new SocketPermission(p.getCodeBase().getHost(), "connect"); 462 if (panelSp.implies(sp)) { 463 v.addElement(p.applet); 464 } 465 } 466 } 467 return v.elements(); 468 } 469 470 /** 471 * Ignore. 472 */ 473 public void showDocument(URL url) { 474 } 475 476 /** 477 * Ignore. 478 */ 479 public void showDocument(URL url, String target) { 480 } 481 482 /** 483 * Show status. 484 */ 485 public void showStatus(String status) { 486 label.setText(status); 487 } 488 489 public void setStream(String key, InputStream stream)throws IOException{ 490 // We do nothing. 491 } 492 493 public InputStream getStream(String key){ 494 // We do nothing. 495 return null; 496 } 497 498 public Iterator getStreamKeys(){ 499 // We do nothing. 500 return null; 501 } 502 503 /** 504 * System parameters. 505 */ 506 static Hashtable systemParam = new Hashtable(); 507 508 static { 509 systemParam.put("codebase", "codebase"); 510 systemParam.put("code", "code"); 511 systemParam.put("alt", "alt"); 512 systemParam.put("width", "width"); 513 systemParam.put("height", "height"); 514 systemParam.put("align", "align"); 515 systemParam.put("vspace", "vspace"); 516 systemParam.put("hspace", "hspace"); 517 } 518 519 /** 520 * Print the HTML tag. 521 */ 522 public static void printTag(PrintStream out, Hashtable atts) { 523 out.print("<applet"); 524 525 String v = (String)atts.get("codebase"); 526 if (v != null) { 527 out.print(" codebase=\"" + v + "\""); 528 } 529 530 v = (String)atts.get("code"); 531 if (v == null) { 532 v = "applet.class"; 533 } 534 out.print(" code=\"" + v + "\""); 535 v = (String)atts.get("width"); 536 if (v == null) { 537 v = "150"; 538 } 539 out.print(" width=" + v); 540 541 v = (String)atts.get("height"); 542 if (v == null) { 543 v = "100"; 544 } 545 out.print(" height=" + v); 546 547 v = (String)atts.get("name"); 548 if (v != null) { 549 out.print(" name=\"" + v + "\""); 550 } 551 out.println(">"); 552 553 // A very slow sorting algorithm 554 int len = atts.size(); 555 String params[] = new String[len]; 556 len = 0; 557 for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) { 558 String param = (String)e.nextElement(); 559 int i = 0; 560 for (; i < len ; i++) { 561 if (params[i].compareTo(param) >= 0) { 562 break; 563 } 564 } 565 System.arraycopy(params, i, params, i + 1, len - i); 566 params[i] = param; 567 len++; 568 } 569 570 for (int i = 0 ; i < len ; i++) { 571 String param = params[i]; 572 if (systemParam.get(param) == null) { 573 out.println("<param name=" + param + 574 " value=\"" + atts.get(param) + "\">"); 575 } 576 } 577 out.println("</applet>"); 578 } 579 580 /** 581 * Make sure the atrributes are uptodate. 582 */ 583 public void updateAtts() { 584 Dimension d = panel.size(); 585 Insets in = panel.insets(); 586 panel.atts.put("width", 587 Integer.toString(d.width - (in.left + in.right))); 588 panel.atts.put("height", 589 Integer.toString(d.height - (in.top + in.bottom))); 590 } 591 592 /** 593 * Restart the applet. 594 */ 595 void appletRestart() { 596 panel.sendEvent(AppletPanel.APPLET_STOP); 597 panel.sendEvent(AppletPanel.APPLET_DESTROY); 598 panel.sendEvent(AppletPanel.APPLET_INIT); 599 panel.sendEvent(AppletPanel.APPLET_START); 600 } 601 602 /** 603 * Reload the applet. 604 */ 605 void appletReload() { 606 panel.sendEvent(AppletPanel.APPLET_STOP); 607 panel.sendEvent(AppletPanel.APPLET_DESTROY); 608 panel.sendEvent(AppletPanel.APPLET_DISPOSE); 609 610 /** 611 * Fixed #4501142: Classlaoder sharing policy doesn't 612 * take "archive" into account. This will be overridden 613 * by Java Plug-in. [stanleyh] 614 */ 615 AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey()); 616 617 /* 618 * Make sure we don't have two threads running through the event queue 619 * at the same time. 620 */ 621 try { 622 panel.joinAppletThread(); 623 panel.release(); 624 } catch (InterruptedException e) { 625 return; // abort the reload 626 } 627 628 panel.createAppletThread(); 629 panel.sendEvent(AppletPanel.APPLET_LOAD); 630 panel.sendEvent(AppletPanel.APPLET_INIT); 631 panel.sendEvent(AppletPanel.APPLET_START); 632 } 633 634 /** 635 * Save the applet to a well known file (for now) as a serialized object 636 */ 637 void appletSave() { 638 AccessController.doPrivileged(new PrivilegedAction() { 639 640 public Object run() { 641 // XXX: this privileged block should be made smaller 642 // by initializing a private static variable with "user.dir" 643 644 // Applet needs to be stopped for serialization to succeed. 645 // Since panel.sendEvent only queues the event, there is a 646 // chance that the event will not be processed before 647 // serialization begins. However, by sending the event before 648 // FileDialog is created, enough time is given such that this 649 // situation is unlikely to ever occur. 650 651 panel.sendEvent(AppletPanel.APPLET_STOP); 652 FileDialog fd = new FileDialog(AppletViewer.this, 653 amh.getMessage("appletsave.filedialogtitle"), 654 FileDialog.SAVE); 655 // needed for a bug under Solaris... 656 fd.setDirectory(System.getProperty("user.dir")); 657 fd.setFile(defaultSaveFile); 658 fd.show(); 659 String fname = fd.getFile(); 660 if (fname == null) { 661 // Restart applet if Save is cancelled. 662 panel.sendEvent(AppletPanel.APPLET_START); 663 return null; // cancelled 664 } 665 String dname = fd.getDirectory(); 666 File file = new File(dname, fname); 667 668 try (FileOutputStream fos = new FileOutputStream(file); 669 BufferedOutputStream bos = new BufferedOutputStream(fos); 670 ObjectOutputStream os = new ObjectOutputStream(bos)) { 671 672 showStatus(amh.getMessage("appletsave.err1", panel.applet.toString(), file.toString())); 673 os.writeObject(panel.applet); 674 } catch (IOException ex) { 675 System.err.println(amh.getMessage("appletsave.err2", ex)); 676 } finally { 677 panel.sendEvent(AppletPanel.APPLET_START); 678 } 679 return null; 680 } 681 }); 682 } 683 684 /** 685 * Clone the viewer and the applet. 686 */ 687 void appletClone() { 688 Point p = location(); 689 updateAtts(); 690 factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA, 691 panel.documentURL, (Hashtable)panel.atts.clone()); 692 } 693 694 /** 695 * Show the applet tag. 696 */ 697 void appletTag() { 698 ByteArrayOutputStream out = new ByteArrayOutputStream(); 699 updateAtts(); 700 printTag(new PrintStream(out), panel.atts); 701 showStatus(amh.getMessage("applettag")); 702 703 Point p = location(); 704 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString()); 705 } 706 707 /** 708 * Show the applet info. 709 */ 710 void appletInfo() { 711 String str = panel.applet.getAppletInfo(); 712 if (str == null) { 713 str = amh.getMessage("appletinfo.applet"); 714 } 715 str += "\n\n"; 716 717 String atts[][] = panel.applet.getParameterInfo(); 718 if (atts != null) { 719 for (int i = 0 ; i < atts.length ; i++) { 720 str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n"; 721 } 722 } else { 723 str += amh.getMessage("appletinfo.param"); 724 } 725 726 Point p = location(); 727 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str); 728 729 } 730 731 /** 732 * Show character encoding type 733 */ 734 void appletCharacterEncoding() { 735 showStatus(amh.getMessage("appletencoding", encoding)); 736 } 737 738 /** 739 * Edit the applet. 740 */ 741 void appletEdit() { 742 } 743 744 /** 745 * Print the applet. 746 */ 747 void appletPrint() { 748 PrinterJob pj = PrinterJob.getPrinterJob(); 749 750 if (pj != null) { 751 PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); 752 if (pj.printDialog(aset)) { 753 pj.setPrintable(this); 754 try { 755 pj.print(aset); 756 statusMsgStream.println(amh.getMessage("appletprint.finish")); 757 } catch (PrinterException e) { 758 statusMsgStream.println(amh.getMessage("appletprint.fail")); 759 } 760 } else { 761 statusMsgStream.println(amh.getMessage("appletprint.cancel")); 762 } 763 } else { 764 statusMsgStream.println(amh.getMessage("appletprint.fail")); 765 } 766 } 767 768 public int print(Graphics graphics, PageFormat pf, int pageIndex) { 769 if (pageIndex > 0) { 770 return Printable.NO_SUCH_PAGE; 771 } else { 772 Graphics2D g2d = (Graphics2D)graphics; 773 g2d.translate(pf.getImageableX(), pf.getImageableY()); 774 panel.applet.printAll(graphics); 775 return Printable.PAGE_EXISTS; 776 } 777 } 778 779 /** 780 * Properties. 781 */ 782 static AppletProps props; 783 public static synchronized void networkProperties() { 784 if (props == null) { 785 props = new AppletProps(); 786 } 787 props.addNotify(); 788 props.setVisible(true); 789 } 790 791 /** 792 * Start the applet. 793 */ 794 void appletStart() { 795 panel.sendEvent(AppletPanel.APPLET_START); 796 } 797 798 /** 799 * Stop the applet. 800 */ 801 void appletStop() { 802 panel.sendEvent(AppletPanel.APPLET_STOP); 803 } 804 805 /** 806 * Shutdown a viewer. 807 * Stop, Destroy, Dispose and Quit a viewer 808 */ 809 private void appletShutdown(AppletPanel p) { 810 p.sendEvent(AppletPanel.APPLET_STOP); 811 p.sendEvent(AppletPanel.APPLET_DESTROY); 812 p.sendEvent(AppletPanel.APPLET_DISPOSE); 813 p.sendEvent(AppletPanel.APPLET_QUIT); 814 } 815 816 /** 817 * Close this viewer. 818 * Stop, Destroy, Dispose and Quit an AppletView, then 819 * reclaim resources and exit the program if this is 820 * the last applet. 821 */ 822 void appletClose() { 823 824 // The caller thread is event dispatch thread, so 825 // spawn a new thread to avoid blocking the event queue 826 // when calling appletShutdown. 827 // 828 final AppletPanel p = panel; 829 830 new Thread(new Runnable() 831 { 832 public void run() 833 { 834 appletShutdown(p); 835 appletPanels.removeElement(p); 836 dispose(); 837 838 if (countApplets() == 0) { 839 appletSystemExit(); 840 } 841 } 842 }).start(); 843 } 844 845 /** 846 * Exit the program. 847 * Exit from the program (if not stand alone) - do no clean-up 848 */ 849 private void appletSystemExit() { 850 if (factory.isStandalone()) 851 System.exit(0); 852 } 853 854 /** 855 * Quit all viewers. 856 * Shutdown all viewers properly then 857 * exit from the program (if not stand alone) 858 */ 859 protected void appletQuit() 860 { 861 // The caller thread is event dispatch thread, so 862 // spawn a new thread to avoid blocking the event queue 863 // when calling appletShutdown. 864 // 865 new Thread(new Runnable() 866 { 867 public void run() 868 { 869 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { 870 AppletPanel p = (AppletPanel)e.nextElement(); 871 appletShutdown(p); 872 } 873 appletSystemExit(); 874 } 875 }).start(); 876 } 877 878 /** 879 * Handle events. 880 */ 881 public void processUserAction(ActionEvent evt) { 882 883 String label = ((MenuItem)evt.getSource()).getLabel(); 884 885 if (amh.getMessage("menuitem.restart").equals(label)) { 886 appletRestart(); 887 return; 888 } 889 890 if (amh.getMessage("menuitem.reload").equals(label)) { 891 appletReload(); 892 return; 893 } 894 895 if (amh.getMessage("menuitem.clone").equals(label)) { 896 appletClone(); 897 return; 898 } 899 900 if (amh.getMessage("menuitem.stop").equals(label)) { 901 appletStop(); 902 return; 903 } 904 905 if (amh.getMessage("menuitem.save").equals(label)) { 906 appletSave(); 907 return; 908 } 909 910 if (amh.getMessage("menuitem.start").equals(label)) { 911 appletStart(); 912 return; 913 } 914 915 if (amh.getMessage("menuitem.tag").equals(label)) { 916 appletTag(); 917 return; 918 } 919 920 if (amh.getMessage("menuitem.info").equals(label)) { 921 appletInfo(); 922 return; 923 } 924 925 if (amh.getMessage("menuitem.encoding").equals(label)) { 926 appletCharacterEncoding(); 927 return; 928 } 929 930 if (amh.getMessage("menuitem.edit").equals(label)) { 931 appletEdit(); 932 return; 933 } 934 935 if (amh.getMessage("menuitem.print").equals(label)) { 936 appletPrint(); 937 return; 938 } 939 940 if (amh.getMessage("menuitem.props").equals(label)) { 941 networkProperties(); 942 return; 943 } 944 945 if (amh.getMessage("menuitem.close").equals(label)) { 946 appletClose(); 947 return; 948 } 949 950 if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) { 951 appletQuit(); 952 return; 953 } 954 //statusMsgStream.println("evt = " + evt); 955 } 956 957 /** 958 * How many applets are running? 959 */ 960 961 public static int countApplets() { 962 return appletPanels.size(); 963 } 964 965 966 /** 967 * The current character. 968 */ 969 static int c; 970 971 /** 972 * Scan spaces. 973 */ 974 public static void skipSpace(Reader in) throws IOException { 975 while ((c >= 0) && 976 ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) { 977 c = in.read(); 978 } 979 } 980 981 /** 982 * Scan identifier 983 */ 984 public static String scanIdentifier(Reader in) throws IOException { 985 StringBuffer buf = new StringBuffer(); 986 while (true) { 987 if (((c >= 'a') && (c <= 'z')) || 988 ((c >= 'A') && (c <= 'Z')) || 989 ((c >= '0') && (c <= '9')) || (c == '_')) { 990 buf.append((char)c); 991 c = in.read(); 992 } else { 993 return buf.toString(); 994 } 995 } 996 } 997 998 /** 999 * Scan tag 1000 */ 1001 public static Hashtable scanTag(Reader in) throws IOException { 1002 Hashtable atts = new Hashtable(); 1003 skipSpace(in); 1004 while (c >= 0 && c != '>') { 1005 String att = scanIdentifier(in); 1006 String val = ""; 1007 skipSpace(in); 1008 if (c == '=') { 1009 int quote = -1; 1010 c = in.read(); 1011 skipSpace(in); 1012 if ((c == '\'') || (c == '\"')) { 1013 quote = c; 1014 c = in.read(); 1015 } 1016 StringBuffer buf = new StringBuffer(); 1017 while ((c > 0) && 1018 (((quote < 0) && (c != ' ') && (c != '\t') && 1019 (c != '\n') && (c != '\r') && (c != '>')) 1020 || ((quote >= 0) && (c != quote)))) { 1021 buf.append((char)c); 1022 c = in.read(); 1023 } 1024 if (c == quote) { 1025 c = in.read(); 1026 } 1027 skipSpace(in); 1028 val = buf.toString(); 1029 } 1030 //statusMsgStream.println("PUT " + att + " = '" + val + "'"); 1031 if (! val.equals("")) { 1032 atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val); 1033 } 1034 while (true) { 1035 if ((c == '>') || (c < 0) || 1036 ((c >= 'a') && (c <= 'z')) || 1037 ((c >= 'A') && (c <= 'Z')) || 1038 ((c >= '0') && (c <= '9')) || (c == '_')) 1039 break; 1040 c = in.read(); 1041 } 1042 //skipSpace(in); 1043 } 1044 return atts; 1045 } 1046 1047 /* values used for placement of AppletViewer's frames */ 1048 private static int x = 0; 1049 private static int y = 0; 1050 private static final int XDELTA = 30; 1051 private static final int YDELTA = XDELTA; 1052 1053 static String encoding = null; 1054 1055 static private Reader makeReader(InputStream is) { 1056 if (encoding != null) { 1057 try { 1058 return new BufferedReader(new InputStreamReader(is, encoding)); 1059 } catch (IOException x) { } 1060 } 1061 InputStreamReader r = new InputStreamReader(is); 1062 encoding = r.getEncoding(); 1063 return new BufferedReader(r); 1064 } 1065 1066 /** 1067 * Scan an html file for <applet> tags 1068 */ 1069 public static void parse(URL url, String enc) throws IOException { 1070 encoding = enc; 1071 parse(url, System.out, new StdAppletViewerFactory()); 1072 } 1073 1074 public static void parse(URL url) throws IOException { 1075 parse(url, System.out, new StdAppletViewerFactory()); 1076 } 1077 1078 public static void parse(URL url, PrintStream statusMsgStream, 1079 AppletViewerFactory factory) throws IOException { 1080 // <OBJECT> <EMBED> tag flags 1081 boolean isAppletTag = false; 1082 boolean isObjectTag = false; 1083 boolean isEmbedTag = false; 1084 1085 // warning messages 1086 String requiresNameWarning = amh.getMessage("parse.warning.requiresname"); 1087 String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside"); 1088 String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode"); 1089 String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight"); 1090 String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth"); 1091 String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode"); 1092 String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight"); 1093 String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth"); 1094 String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode"); 1095 String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight"); 1096 String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth"); 1097 String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported"); 1098 1099 java.net.URLConnection conn = url.openConnection(); 1100 Reader in = makeReader(conn.getInputStream()); 1101 /* The original URL may have been redirected - this 1102 * sets it to whatever URL/codebase we ended up getting 1103 */ 1104 url = conn.getURL(); 1105 1106 int ydisp = 1; 1107 Hashtable atts = null; 1108 1109 while(true) { 1110 c = in.read(); 1111 if (c == -1) 1112 break; 1113 1114 if (c == '<') { 1115 c = in.read(); 1116 if (c == '/') { 1117 c = in.read(); 1118 String nm = scanIdentifier(in); 1119 if (nm.equalsIgnoreCase("applet") || 1120 nm.equalsIgnoreCase("object") || 1121 nm.equalsIgnoreCase("embed")) { 1122 1123 // We can't test for a code tag until </OBJECT> 1124 // because it is a parameter, not an attribute. 1125 if(isObjectTag) { 1126 if (atts.get("code") == null && atts.get("object") == null) { 1127 statusMsgStream.println(objectRequiresCodeWarning); 1128 atts = null; 1129 } 1130 } 1131 1132 if (atts != null) { 1133 // XXX 5/18 In general this code just simply 1134 // shouldn't be part of parsing. It's presence 1135 // causes things to be a little too much of a 1136 // hack. 1137 factory.createAppletViewer(x, y, url, atts); 1138 x += XDELTA; 1139 y += YDELTA; 1140 // make sure we don't go too far! 1141 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 1142 if ((x > d.width - 300) || (y > d.height - 300)) { 1143 x = 0; 1144 y = 2 * ydisp * YDELTA; 1145 ydisp++; 1146 } 1147 } 1148 atts = null; 1149 isAppletTag = false; 1150 isObjectTag = false; 1151 isEmbedTag = false; 1152 } 1153 } 1154 else { 1155 String nm = scanIdentifier(in); 1156 if (nm.equalsIgnoreCase("param")) { 1157 Hashtable t = scanTag(in); 1158 String att = (String)t.get("name"); 1159 if (att == null) { 1160 statusMsgStream.println(requiresNameWarning); 1161 } else { 1162 String val = (String)t.get("value"); 1163 if (val == null) { 1164 statusMsgStream.println(requiresNameWarning); 1165 } else if (atts != null) { 1166 atts.put(att.toLowerCase(), val); 1167 } else { 1168 statusMsgStream.println(paramOutsideWarning); 1169 } 1170 } 1171 } 1172 else if (nm.equalsIgnoreCase("applet")) { 1173 isAppletTag = true; 1174 atts = scanTag(in); 1175 if (atts.get("code") == null && atts.get("object") == null) { 1176 statusMsgStream.println(appletRequiresCodeWarning); 1177 atts = null; 1178 } else if (atts.get("width") == null) { 1179 statusMsgStream.println(appletRequiresWidthWarning); 1180 atts = null; 1181 } else if (atts.get("height") == null) { 1182 statusMsgStream.println(appletRequiresHeightWarning); 1183 atts = null; 1184 } 1185 } 1186 else if (nm.equalsIgnoreCase("object")) { 1187 isObjectTag = true; 1188 atts = scanTag(in); 1189 // The <OBJECT> attribute codebase isn't what 1190 // we want. If its defined, remove it. 1191 if(atts.get("codebase") != null) { 1192 atts.remove("codebase"); 1193 } 1194 1195 if (atts.get("width") == null) { 1196 statusMsgStream.println(objectRequiresWidthWarning); 1197 atts = null; 1198 } else if (atts.get("height") == null) { 1199 statusMsgStream.println(objectRequiresHeightWarning); 1200 atts = null; 1201 } 1202 } 1203 else if (nm.equalsIgnoreCase("embed")) { 1204 isEmbedTag = true; 1205 atts = scanTag(in); 1206 1207 if (atts.get("code") == null && atts.get("object") == null) { 1208 statusMsgStream.println(embedRequiresCodeWarning); 1209 atts = null; 1210 } else if (atts.get("width") == null) { 1211 statusMsgStream.println(embedRequiresWidthWarning); 1212 atts = null; 1213 } else if (atts.get("height") == null) { 1214 statusMsgStream.println(embedRequiresHeightWarning); 1215 atts = null; 1216 } 1217 } 1218 else if (nm.equalsIgnoreCase("app")) { 1219 statusMsgStream.println(appNotLongerSupportedWarning); 1220 Hashtable atts2 = scanTag(in); 1221 nm = (String)atts2.get("class"); 1222 if (nm != null) { 1223 atts2.remove("class"); 1224 atts2.put("code", nm + ".class"); 1225 } 1226 nm = (String)atts2.get("src"); 1227 if (nm != null) { 1228 atts2.remove("src"); 1229 atts2.put("codebase", nm); 1230 } 1231 if (atts2.get("width") == null) { 1232 atts2.put("width", "100"); 1233 } 1234 if (atts2.get("height") == null) { 1235 atts2.put("height", "100"); 1236 } 1237 printTag(statusMsgStream, atts2); 1238 statusMsgStream.println(); 1239 } 1240 } 1241 } 1242 } 1243 in.close(); 1244 } 1245 1246 /** 1247 * Old main entry point. 1248 * 1249 * @deprecated 1250 */ 1251 @Deprecated 1252 public static void main(String argv[]) { 1253 // re-route everything to the new main entry point 1254 Main.main(argv); 1255 } 1256 1257 private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); 1258 1259 private static void checkConnect(URL url) 1260 { 1261 SecurityManager security = System.getSecurityManager(); 1262 if (security != null) { 1263 try { 1264 java.security.Permission perm = 1265 url.openConnection().getPermission(); 1266 if (perm != null) 1267 security.checkPermission(perm); 1268 else 1269 security.checkConnect(url.getHost(), url.getPort()); 1270 } catch (java.io.IOException ioe) { 1271 security.checkConnect(url.getHost(), url.getPort()); 1272 } 1273 } 1274 } 1275 }