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