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