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(null, 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 }, 870 "AppletCloser", 0, false).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 Thread(null, 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 }, 905 "AppletQuit", 0, false).start(); 906 } 907 908 /** 909 * Handle events. 910 */ 911 public void processUserAction(ActionEvent evt) { 912 913 String label = ((MenuItem)evt.getSource()).getLabel(); 914 915 if (amh.getMessage("menuitem.restart").equals(label)) { 916 appletRestart(); 917 return; 918 } 919 920 if (amh.getMessage("menuitem.reload").equals(label)) { 921 appletReload(); 922 return; 923 } 924 925 if (amh.getMessage("menuitem.clone").equals(label)) { 926 appletClone(); 927 return; 928 } 929 930 if (amh.getMessage("menuitem.stop").equals(label)) { 931 appletStop(); 932 return; 933 } 934 935 if (amh.getMessage("menuitem.save").equals(label)) { 936 appletSave(); 937 return; 938 } 939 940 if (amh.getMessage("menuitem.start").equals(label)) { 941 appletStart(); 942 return; 943 } 944 945 if (amh.getMessage("menuitem.tag").equals(label)) { 946 appletTag(); 947 return; 948 } 949 950 if (amh.getMessage("menuitem.info").equals(label)) { 951 appletInfo(); 952 return; 953 } 954 955 if (amh.getMessage("menuitem.encoding").equals(label)) { 956 appletCharacterEncoding(); 957 return; 958 } 959 960 if (amh.getMessage("menuitem.edit").equals(label)) { 961 appletEdit(); 962 return; 963 } 964 965 if (amh.getMessage("menuitem.print").equals(label)) { 966 appletPrint(); 967 return; 968 } 969 970 if (amh.getMessage("menuitem.props").equals(label)) { 971 networkProperties(); 972 return; 973 } 974 975 if (amh.getMessage("menuitem.close").equals(label)) { 976 appletClose(); 977 return; 978 } 979 980 if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) { 981 appletQuit(); 982 return; 983 } 984 //statusMsgStream.println("evt = " + evt); 985 } 986 987 /** 988 * How many applets are running? 989 */ 990 991 public static int countApplets() { 992 return appletPanels.size(); 993 } 994 995 996 /** 997 * The current character. 998 */ 999 static int c; 1000 1001 /** 1002 * Scan spaces. 1003 */ 1004 public static void skipSpace(Reader in) throws IOException { 1005 while ((c >= 0) && 1006 ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) { 1007 c = in.read(); 1008 } 1009 } 1010 1011 /** 1012 * Scan identifier 1013 */ 1014 public static String scanIdentifier(Reader in) throws IOException { 1015 StringBuilder sb = new StringBuilder(); 1016 while (true) { 1017 if (((c >= 'a') && (c <= 'z')) || 1018 ((c >= 'A') && (c <= 'Z')) || 1019 ((c >= '0') && (c <= '9')) || (c == '_')) { 1020 sb.append((char) c); 1021 c = in.read(); 1022 } else { 1023 return sb.toString(); 1024 } 1025 } 1026 } 1027 1028 /** 1029 * Scan tag 1030 */ 1031 public static Hashtable<String, String> scanTag(Reader in) throws IOException { 1032 Hashtable<String, String> atts = new Hashtable<>(); 1033 skipSpace(in); 1034 while (c >= 0 && c != '>') { 1035 String att = scanIdentifier(in); 1036 String val = ""; 1037 skipSpace(in); 1038 if (c == '=') { 1039 int quote = -1; 1040 c = in.read(); 1041 skipSpace(in); 1042 if ((c == '\'') || (c == '\"')) { 1043 quote = c; 1044 c = in.read(); 1045 } 1046 StringBuilder sb = new StringBuilder(); 1047 while ((c > 0) && 1048 (((quote < 0) && (c != ' ') && (c != '\t') && 1049 (c != '\n') && (c != '\r') && (c != '>')) 1050 || ((quote >= 0) && (c != quote)))) { 1051 sb.append((char) c); 1052 c = in.read(); 1053 } 1054 if (c == quote) { 1055 c = in.read(); 1056 } 1057 skipSpace(in); 1058 val = sb.toString(); 1059 } 1060 //statusMsgStream.println("PUT " + att + " = '" + val + "'"); 1061 if (! val.equals("")) { 1062 atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val); 1063 } 1064 while (true) { 1065 if ((c == '>') || (c < 0) || 1066 ((c >= 'a') && (c <= 'z')) || 1067 ((c >= 'A') && (c <= 'Z')) || 1068 ((c >= '0') && (c <= '9')) || (c == '_')) 1069 break; 1070 c = in.read(); 1071 } 1072 //skipSpace(in); 1073 } 1074 return atts; 1075 } 1076 1077 /* values used for placement of AppletViewer's frames */ 1078 private static int x = 0; 1079 private static int y = 0; 1080 private static final int XDELTA = 30; 1081 private static final int YDELTA = XDELTA; 1082 1083 static String encoding = null; 1084 1085 private static Reader makeReader(InputStream is) { 1086 if (encoding != null) { 1087 try { 1088 return new BufferedReader(new InputStreamReader(is, encoding)); 1089 } catch (IOException x) { } 1090 } 1091 InputStreamReader r = new InputStreamReader(is); 1092 encoding = r.getEncoding(); 1093 return new BufferedReader(r); 1094 } 1095 1096 /** 1097 * Scan an html file for {@code <applet>} tags 1098 */ 1099 public static void parse(URL url, String enc) throws IOException { 1100 encoding = enc; 1101 parse(url, System.out, new StdAppletViewerFactory()); 1102 } 1103 1104 public static void parse(URL url) throws IOException { 1105 parse(url, System.out, new StdAppletViewerFactory()); 1106 } 1107 1108 public static void parse(URL url, PrintStream statusMsgStream, 1109 AppletViewerFactory factory) throws IOException { 1110 // <OBJECT> <EMBED> tag flags 1111 boolean isAppletTag = false; 1112 boolean isObjectTag = false; 1113 boolean isEmbedTag = false; 1114 1115 // warning messages 1116 String requiresNameWarning = amh.getMessage("parse.warning.requiresname"); 1117 String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside"); 1118 String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode"); 1119 String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight"); 1120 String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth"); 1121 String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode"); 1122 String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight"); 1123 String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth"); 1124 String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode"); 1125 String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight"); 1126 String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth"); 1127 String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported"); 1128 1129 java.net.URLConnection conn = url.openConnection(); 1130 Reader in = makeReader(conn.getInputStream()); 1131 /* The original URL may have been redirected - this 1132 * sets it to whatever URL/codebase we ended up getting 1133 */ 1134 url = conn.getURL(); 1135 1136 int ydisp = 1; 1137 Hashtable<String, String> atts = null; 1138 1139 while(true) { 1140 c = in.read(); 1141 if (c == -1) 1142 break; 1143 1144 if (c == '<') { 1145 c = in.read(); 1146 if (c == '/') { 1147 c = in.read(); 1148 String nm = scanIdentifier(in); 1149 if (nm.equalsIgnoreCase("applet") || 1150 nm.equalsIgnoreCase("object") || 1151 nm.equalsIgnoreCase("embed")) { 1152 1153 // We can't test for a code tag until </OBJECT> 1154 // because it is a parameter, not an attribute. 1155 if(isObjectTag) { 1156 if (atts.get("code") == null && atts.get("object") == null) { 1157 statusMsgStream.println(objectRequiresCodeWarning); 1158 atts = null; 1159 } 1160 } 1161 1162 if (atts != null) { 1163 // XXX 5/18 In general this code just simply 1164 // shouldn't be part of parsing. It's presence 1165 // causes things to be a little too much of a 1166 // hack. 1167 factory.createAppletViewer(x, y, url, atts); 1168 x += XDELTA; 1169 y += YDELTA; 1170 // make sure we don't go too far! 1171 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 1172 if ((x > d.width - 300) || (y > d.height - 300)) { 1173 x = 0; 1174 y = 2 * ydisp * YDELTA; 1175 ydisp++; 1176 } 1177 } 1178 atts = null; 1179 isAppletTag = false; 1180 isObjectTag = false; 1181 isEmbedTag = false; 1182 } 1183 } 1184 else { 1185 String nm = scanIdentifier(in); 1186 if (nm.equalsIgnoreCase("param")) { 1187 Hashtable<String, String> t = scanTag(in); 1188 String att = t.get("name"); 1189 if (att == null) { 1190 statusMsgStream.println(requiresNameWarning); 1191 } else { 1192 String val = t.get("value"); 1193 if (val == null) { 1194 statusMsgStream.println(requiresNameWarning); 1195 } else if (atts != null) { 1196 atts.put(att.toLowerCase(), val); 1197 } else { 1198 statusMsgStream.println(paramOutsideWarning); 1199 } 1200 } 1201 } 1202 else if (nm.equalsIgnoreCase("applet")) { 1203 isAppletTag = true; 1204 atts = scanTag(in); 1205 if (atts.get("code") == null && atts.get("object") == null) { 1206 statusMsgStream.println(appletRequiresCodeWarning); 1207 atts = null; 1208 } else if (atts.get("width") == null) { 1209 statusMsgStream.println(appletRequiresWidthWarning); 1210 atts = null; 1211 } else if (atts.get("height") == null) { 1212 statusMsgStream.println(appletRequiresHeightWarning); 1213 atts = null; 1214 } 1215 } 1216 else if (nm.equalsIgnoreCase("object")) { 1217 isObjectTag = true; 1218 atts = scanTag(in); 1219 // The <OBJECT> attribute codebase isn't what 1220 // we want. If its defined, remove it. 1221 if(atts.get("codebase") != null) { 1222 atts.remove("codebase"); 1223 } 1224 1225 if (atts.get("width") == null) { 1226 statusMsgStream.println(objectRequiresWidthWarning); 1227 atts = null; 1228 } else if (atts.get("height") == null) { 1229 statusMsgStream.println(objectRequiresHeightWarning); 1230 atts = null; 1231 } 1232 } 1233 else if (nm.equalsIgnoreCase("embed")) { 1234 isEmbedTag = true; 1235 atts = scanTag(in); 1236 1237 if (atts.get("code") == null && atts.get("object") == null) { 1238 statusMsgStream.println(embedRequiresCodeWarning); 1239 atts = null; 1240 } else if (atts.get("width") == null) { 1241 statusMsgStream.println(embedRequiresWidthWarning); 1242 atts = null; 1243 } else if (atts.get("height") == null) { 1244 statusMsgStream.println(embedRequiresHeightWarning); 1245 atts = null; 1246 } 1247 } 1248 else if (nm.equalsIgnoreCase("app")) { 1249 statusMsgStream.println(appNotLongerSupportedWarning); 1250 Hashtable<String, String> atts2 = scanTag(in); 1251 nm = atts2.get("class"); 1252 if (nm != null) { 1253 atts2.remove("class"); 1254 atts2.put("code", nm + ".class"); 1255 } 1256 nm = atts2.get("src"); 1257 if (nm != null) { 1258 atts2.remove("src"); 1259 atts2.put("codebase", nm); 1260 } 1261 if (atts2.get("width") == null) { 1262 atts2.put("width", "100"); 1263 } 1264 if (atts2.get("height") == null) { 1265 atts2.put("height", "100"); 1266 } 1267 printTag(statusMsgStream, atts2); 1268 statusMsgStream.println(); 1269 } 1270 } 1271 } 1272 } 1273 in.close(); 1274 } 1275 1276 /** 1277 * Old main entry point. 1278 * 1279 * @deprecated 1280 */ 1281 @Deprecated 1282 public static void main(String argv[]) { 1283 // re-route everything to the new main entry point 1284 Main.main(argv); 1285 } 1286 1287 private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); 1288 1289 private static void checkConnect(URL url) 1290 { 1291 SecurityManager security = System.getSecurityManager(); 1292 if (security != null) { 1293 try { 1294 java.security.Permission perm = 1295 url.openConnection().getPermission(); 1296 if (perm != null) 1297 security.checkPermission(perm); 1298 else 1299 security.checkConnect(url.getHost(), url.getPort()); 1300 } catch (java.io.IOException ioe) { 1301 security.checkConnect(url.getHost(), url.getPort()); 1302 } 1303 } 1304 } 1305 }