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<String, String> 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<String, String> 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<String> 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<URL, AudioClip> 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 = 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<URL, AppletImageRef> 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 = 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<AppletPanel> 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<AppletPanel> e = appletPanels.elements() ; e.hasMoreElements() ;) { 434 AppletPanel p = 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<Applet> getApplets() { 459 AppletSecurity security = (AppletSecurity)System.getSecurityManager(); 460 Vector<Applet> v = new Vector<>(); 461 SocketPermission panelSp = 462 new SocketPermission(panel.getCodeBase().getHost(), "connect"); 463 464 for (Enumeration<AppletPanel> e = appletPanels.elements() ; e.hasMoreElements() ;) { 465 AppletPanel p = 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<String> getStreamKeys(){ 513 // We do nothing. 514 return null; 515 } 516 517 /** 518 * System parameters. 519 */ 520 static Hashtable<String, String> 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<String, String> atts) { 537 out.print("<applet"); 538 539 String v = atts.get("codebase"); 540 if (v != null) { 541 out.print(" codebase=\"" + v + "\""); 542 } 543 544 v = atts.get("code"); 545 if (v == null) { 546 v = "applet.class"; 547 } 548 out.print(" code=\"" + v + "\""); 549 v = atts.get("width"); 550 if (v == null) { 551 v = "150"; 552 } 553 out.print(" width=" + v); 554 555 v = atts.get("height"); 556 if (v == null) { 557 v = "100"; 558 } 559 out.print(" height=" + v); 560 561 v = 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<String> e = atts.keys() ; e.hasMoreElements() ;) { 572 String param = 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<Object>() { 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 @SuppressWarnings("unchecked") 706 Hashtable<String, String> tmp = (Hashtable<String, String>) panel.atts.clone(); 707 factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA, 708 panel.documentURL, tmp); 709 } 710 711 /** 712 * Show the applet tag. 713 */ 714 void appletTag() { 715 ByteArrayOutputStream out = new ByteArrayOutputStream(); 716 updateAtts(); 717 printTag(new PrintStream(out), panel.atts); 718 showStatus(amh.getMessage("applettag")); 719 720 Point p = location(); 721 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString()); 722 } 723 724 /** 725 * Show the applet info. 726 */ 727 void appletInfo() { 728 String str = panel.applet.getAppletInfo(); 729 if (str == null) { 730 str = amh.getMessage("appletinfo.applet"); 731 } 732 str += "\n\n"; 733 734 String atts[][] = panel.applet.getParameterInfo(); 735 if (atts != null) { 736 for (int i = 0 ; i < atts.length ; i++) { 737 str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n"; 738 } 739 } else { 740 str += amh.getMessage("appletinfo.param"); 741 } 742 743 Point p = location(); 744 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str); 745 746 } 747 748 /** 749 * Show character encoding type 750 */ 751 void appletCharacterEncoding() { 752 showStatus(amh.getMessage("appletencoding", encoding)); 753 } 754 755 /** 756 * Edit the applet. 757 */ 758 void appletEdit() { 759 } 760 761 /** 762 * Print the applet. 763 */ 764 void appletPrint() { 765 PrinterJob pj = PrinterJob.getPrinterJob(); 766 767 if (pj != null) { 768 PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); 769 if (pj.printDialog(aset)) { 770 pj.setPrintable(this); 771 try { 772 pj.print(aset); 773 statusMsgStream.println(amh.getMessage("appletprint.finish")); 774 } catch (PrinterException e) { 775 statusMsgStream.println(amh.getMessage("appletprint.fail")); 776 } 777 } else { 778 statusMsgStream.println(amh.getMessage("appletprint.cancel")); 779 } 780 } else { 781 statusMsgStream.println(amh.getMessage("appletprint.fail")); 782 } 783 } 784 785 @Override 786 public int print(Graphics graphics, PageFormat pf, int pageIndex) { 787 if (pageIndex > 0) { 788 return Printable.NO_SUCH_PAGE; 789 } else { 790 Graphics2D g2d = (Graphics2D)graphics; 791 g2d.translate(pf.getImageableX(), pf.getImageableY()); 792 panel.applet.printAll(graphics); 793 return Printable.PAGE_EXISTS; 794 } 795 } 796 797 /** 798 * Properties. 799 */ 800 static AppletProps props; 801 public static synchronized void networkProperties() { 802 if (props == null) { 803 props = new AppletProps(); 804 } 805 props.addNotify(); 806 props.setVisible(true); 807 } 808 809 /** 810 * Start the applet. 811 */ 812 void appletStart() { 813 panel.sendEvent(AppletPanel.APPLET_START); 814 } 815 816 /** 817 * Stop the applet. 818 */ 819 void appletStop() { 820 panel.sendEvent(AppletPanel.APPLET_STOP); 821 } 822 823 /** 824 * Shutdown a viewer. 825 * Stop, Destroy, Dispose and Quit a viewer 826 */ 827 private void appletShutdown(AppletPanel p) { 828 p.sendEvent(AppletPanel.APPLET_STOP); 829 p.sendEvent(AppletPanel.APPLET_DESTROY); 830 p.sendEvent(AppletPanel.APPLET_DISPOSE); 831 p.sendEvent(AppletPanel.APPLET_QUIT); 832 } 833 834 /** 835 * Close this viewer. 836 * Stop, Destroy, Dispose and Quit an AppletView, then 837 * reclaim resources and exit the program if this is 838 * the last applet. 839 */ 840 void appletClose() { 841 842 // The caller thread is event dispatch thread, so 843 // spawn a new thread to avoid blocking the event queue 844 // when calling appletShutdown. 845 // 846 final AppletPanel p = panel; 847 848 new Thread(new Runnable() 849 { 850 @Override 851 public void run() 852 { 853 appletShutdown(p); 854 appletPanels.removeElement(p); 855 dispose(); 856 857 if (countApplets() == 0) { 858 appletSystemExit(); 859 } 860 } 861 }).start(); 862 } 863 864 /** 865 * Exit the program. 866 * Exit from the program (if not stand alone) - do no clean-up 867 */ 868 private void appletSystemExit() { 869 if (factory.isStandalone()) 870 System.exit(0); 871 } 872 873 /** 874 * Quit all viewers. 875 * Shutdown all viewers properly then 876 * exit from the program (if not stand alone) 877 */ 878 protected void appletQuit() 879 { 880 // The caller thread is event dispatch thread, so 881 // spawn a new thread to avoid blocking the event queue 882 // when calling appletShutdown. 883 // 884 new Thread(new Runnable() 885 { 886 @Override 887 public void run() 888 { 889 for (Enumeration<AppletPanel> e = appletPanels.elements() ; e.hasMoreElements() ;) { 890 AppletPanel p = e.nextElement(); 891 appletShutdown(p); 892 } 893 appletSystemExit(); 894 } 895 }).start(); 896 } 897 898 /** 899 * Handle events. 900 */ 901 public void processUserAction(ActionEvent evt) { 902 903 String label = ((MenuItem)evt.getSource()).getLabel(); 904 905 if (amh.getMessage("menuitem.restart").equals(label)) { 906 appletRestart(); 907 return; 908 } 909 910 if (amh.getMessage("menuitem.reload").equals(label)) { 911 appletReload(); 912 return; 913 } 914 915 if (amh.getMessage("menuitem.clone").equals(label)) { 916 appletClone(); 917 return; 918 } 919 920 if (amh.getMessage("menuitem.stop").equals(label)) { 921 appletStop(); 922 return; 923 } 924 925 if (amh.getMessage("menuitem.save").equals(label)) { 926 appletSave(); 927 return; 928 } 929 930 if (amh.getMessage("menuitem.start").equals(label)) { 931 appletStart(); 932 return; 933 } 934 935 if (amh.getMessage("menuitem.tag").equals(label)) { 936 appletTag(); 937 return; 938 } 939 940 if (amh.getMessage("menuitem.info").equals(label)) { 941 appletInfo(); 942 return; 943 } 944 945 if (amh.getMessage("menuitem.encoding").equals(label)) { 946 appletCharacterEncoding(); 947 return; 948 } 949 950 if (amh.getMessage("menuitem.edit").equals(label)) { 951 appletEdit(); 952 return; 953 } 954 955 if (amh.getMessage("menuitem.print").equals(label)) { 956 appletPrint(); 957 return; 958 } 959 960 if (amh.getMessage("menuitem.props").equals(label)) { 961 networkProperties(); 962 return; 963 } 964 965 if (amh.getMessage("menuitem.close").equals(label)) { 966 appletClose(); 967 return; 968 } 969 970 if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) { 971 appletQuit(); 972 return; 973 } 974 //statusMsgStream.println("evt = " + evt); 975 } 976 977 /** 978 * How many applets are running? 979 */ 980 981 public static int countApplets() { 982 return appletPanels.size(); 983 } 984 985 986 /** 987 * The current character. 988 */ 989 static int c; 990 991 /** 992 * Scan spaces. 993 */ 994 public static void skipSpace(Reader in) throws IOException { 995 while ((c >= 0) && 996 ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) { 997 c = in.read(); 998 } 999 } 1000 1001 /** 1002 * Scan identifier 1003 */ 1004 public static String scanIdentifier(Reader in) throws IOException { 1005 StringBuilder sb = new StringBuilder(); 1006 while (true) { 1007 if (((c >= 'a') && (c <= 'z')) || 1008 ((c >= 'A') && (c <= 'Z')) || 1009 ((c >= '0') && (c <= '9')) || (c == '_')) { 1010 sb.append((char) c); 1011 c = in.read(); 1012 } else { 1013 return sb.toString(); 1014 } 1015 } 1016 } 1017 1018 /** 1019 * Scan tag 1020 */ 1021 public static Hashtable<String, String> scanTag(Reader in) throws IOException { 1022 Hashtable<String, String> atts = new Hashtable<>(); 1023 skipSpace(in); 1024 while (c >= 0 && c != '>') { 1025 String att = scanIdentifier(in); 1026 String val = ""; 1027 skipSpace(in); 1028 if (c == '=') { 1029 int quote = -1; 1030 c = in.read(); 1031 skipSpace(in); 1032 if ((c == '\'') || (c == '\"')) { 1033 quote = c; 1034 c = in.read(); 1035 } 1036 StringBuilder sb = new StringBuilder(); 1037 while ((c > 0) && 1038 (((quote < 0) && (c != ' ') && (c != '\t') && 1039 (c != '\n') && (c != '\r') && (c != '>')) 1040 || ((quote >= 0) && (c != quote)))) { 1041 sb.append((char) c); 1042 c = in.read(); 1043 } 1044 if (c == quote) { 1045 c = in.read(); 1046 } 1047 skipSpace(in); 1048 val = sb.toString(); 1049 } 1050 //statusMsgStream.println("PUT " + att + " = '" + val + "'"); 1051 if (! val.equals("")) { 1052 atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val); 1053 } 1054 while (true) { 1055 if ((c == '>') || (c < 0) || 1056 ((c >= 'a') && (c <= 'z')) || 1057 ((c >= 'A') && (c <= 'Z')) || 1058 ((c >= '0') && (c <= '9')) || (c == '_')) 1059 break; 1060 c = in.read(); 1061 } 1062 //skipSpace(in); 1063 } 1064 return atts; 1065 } 1066 1067 /* values used for placement of AppletViewer's frames */ 1068 private static int x = 0; 1069 private static int y = 0; 1070 private static final int XDELTA = 30; 1071 private static final int YDELTA = XDELTA; 1072 1073 static String encoding = null; 1074 1075 static private Reader makeReader(InputStream is) { 1076 if (encoding != null) { 1077 try { 1078 return new BufferedReader(new InputStreamReader(is, encoding)); 1079 } catch (IOException x) { } 1080 } 1081 InputStreamReader r = new InputStreamReader(is); 1082 encoding = r.getEncoding(); 1083 return new BufferedReader(r); 1084 } 1085 1086 /** 1087 * Scan an html file for <applet> tags 1088 */ 1089 public static void parse(URL url, String enc) throws IOException { 1090 encoding = enc; 1091 parse(url, System.out, new StdAppletViewerFactory()); 1092 } 1093 1094 public static void parse(URL url) throws IOException { 1095 parse(url, System.out, new StdAppletViewerFactory()); 1096 } 1097 1098 public static void parse(URL url, PrintStream statusMsgStream, 1099 AppletViewerFactory factory) throws IOException { 1100 // <OBJECT> <EMBED> tag flags 1101 boolean isAppletTag = false; 1102 boolean isObjectTag = false; 1103 boolean isEmbedTag = false; 1104 1105 // warning messages 1106 String requiresNameWarning = amh.getMessage("parse.warning.requiresname"); 1107 String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside"); 1108 String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode"); 1109 String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight"); 1110 String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth"); 1111 String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode"); 1112 String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight"); 1113 String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth"); 1114 String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode"); 1115 String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight"); 1116 String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth"); 1117 String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported"); 1118 1119 java.net.URLConnection conn = url.openConnection(); 1120 Reader in = makeReader(conn.getInputStream()); 1121 /* The original URL may have been redirected - this 1122 * sets it to whatever URL/codebase we ended up getting 1123 */ 1124 url = conn.getURL(); 1125 1126 int ydisp = 1; 1127 Hashtable<String, String> atts = null; 1128 1129 while(true) { 1130 c = in.read(); 1131 if (c == -1) 1132 break; 1133 1134 if (c == '<') { 1135 c = in.read(); 1136 if (c == '/') { 1137 c = in.read(); 1138 String nm = scanIdentifier(in); 1139 if (nm.equalsIgnoreCase("applet") || 1140 nm.equalsIgnoreCase("object") || 1141 nm.equalsIgnoreCase("embed")) { 1142 1143 // We can't test for a code tag until </OBJECT> 1144 // because it is a parameter, not an attribute. 1145 if(isObjectTag) { 1146 if (atts.get("code") == null && atts.get("object") == null) { 1147 statusMsgStream.println(objectRequiresCodeWarning); 1148 atts = null; 1149 } 1150 } 1151 1152 if (atts != null) { 1153 // XXX 5/18 In general this code just simply 1154 // shouldn't be part of parsing. It's presence 1155 // causes things to be a little too much of a 1156 // hack. 1157 factory.createAppletViewer(x, y, url, atts); 1158 x += XDELTA; 1159 y += YDELTA; 1160 // make sure we don't go too far! 1161 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 1162 if ((x > d.width - 300) || (y > d.height - 300)) { 1163 x = 0; 1164 y = 2 * ydisp * YDELTA; 1165 ydisp++; 1166 } 1167 } 1168 atts = null; 1169 isAppletTag = false; 1170 isObjectTag = false; 1171 isEmbedTag = false; 1172 } 1173 } 1174 else { 1175 String nm = scanIdentifier(in); 1176 if (nm.equalsIgnoreCase("param")) { 1177 Hashtable<String, String> t = scanTag(in); 1178 String att = t.get("name"); 1179 if (att == null) { 1180 statusMsgStream.println(requiresNameWarning); 1181 } else { 1182 String val = t.get("value"); 1183 if (val == null) { 1184 statusMsgStream.println(requiresNameWarning); 1185 } else if (atts != null) { 1186 atts.put(att.toLowerCase(), val); 1187 } else { 1188 statusMsgStream.println(paramOutsideWarning); 1189 } 1190 } 1191 } 1192 else if (nm.equalsIgnoreCase("applet")) { 1193 isAppletTag = true; 1194 atts = scanTag(in); 1195 if (atts.get("code") == null && atts.get("object") == null) { 1196 statusMsgStream.println(appletRequiresCodeWarning); 1197 atts = null; 1198 } else if (atts.get("width") == null) { 1199 statusMsgStream.println(appletRequiresWidthWarning); 1200 atts = null; 1201 } else if (atts.get("height") == null) { 1202 statusMsgStream.println(appletRequiresHeightWarning); 1203 atts = null; 1204 } 1205 } 1206 else if (nm.equalsIgnoreCase("object")) { 1207 isObjectTag = true; 1208 atts = scanTag(in); 1209 // The <OBJECT> attribute codebase isn't what 1210 // we want. If its defined, remove it. 1211 if(atts.get("codebase") != null) { 1212 atts.remove("codebase"); 1213 } 1214 1215 if (atts.get("width") == null) { 1216 statusMsgStream.println(objectRequiresWidthWarning); 1217 atts = null; 1218 } else if (atts.get("height") == null) { 1219 statusMsgStream.println(objectRequiresHeightWarning); 1220 atts = null; 1221 } 1222 } 1223 else if (nm.equalsIgnoreCase("embed")) { 1224 isEmbedTag = true; 1225 atts = scanTag(in); 1226 1227 if (atts.get("code") == null && atts.get("object") == null) { 1228 statusMsgStream.println(embedRequiresCodeWarning); 1229 atts = null; 1230 } else if (atts.get("width") == null) { 1231 statusMsgStream.println(embedRequiresWidthWarning); 1232 atts = null; 1233 } else if (atts.get("height") == null) { 1234 statusMsgStream.println(embedRequiresHeightWarning); 1235 atts = null; 1236 } 1237 } 1238 else if (nm.equalsIgnoreCase("app")) { 1239 statusMsgStream.println(appNotLongerSupportedWarning); 1240 Hashtable<String, String> atts2 = scanTag(in); 1241 nm = atts2.get("class"); 1242 if (nm != null) { 1243 atts2.remove("class"); 1244 atts2.put("code", nm + ".class"); 1245 } 1246 nm = atts2.get("src"); 1247 if (nm != null) { 1248 atts2.remove("src"); 1249 atts2.put("codebase", nm); 1250 } 1251 if (atts2.get("width") == null) { 1252 atts2.put("width", "100"); 1253 } 1254 if (atts2.get("height") == null) { 1255 atts2.put("height", "100"); 1256 } 1257 printTag(statusMsgStream, atts2); 1258 statusMsgStream.println(); 1259 } 1260 } 1261 } 1262 } 1263 in.close(); 1264 } 1265 1266 /** 1267 * Old main entry point. 1268 * 1269 * @deprecated 1270 */ 1271 @Deprecated 1272 public static void main(String argv[]) { 1273 // re-route everything to the new main entry point 1274 Main.main(argv); 1275 } 1276 1277 private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); 1278 1279 private static void checkConnect(URL url) 1280 { 1281 SecurityManager security = System.getSecurityManager(); 1282 if (security != null) { 1283 try { 1284 java.security.Permission perm = 1285 url.openConnection().getPermission(); 1286 if (perm != null) 1287 security.checkPermission(perm); 1288 else 1289 security.checkConnect(url.getHost(), url.getPort()); 1290 } catch (java.io.IOException ioe) { 1291 security.checkConnect(url.getHost(), url.getPort()); 1292 } 1293 } 1294 } 1295 }