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