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", "deprecation"}) // 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     public AppletViewer(int x, int y, URL doc, Hashtable<String, String> atts,
 161                         PrintStream statusMsgStream, AppletViewerFactory factory) {
 162         this.factory = factory;
 163         this.statusMsgStream = statusMsgStream;
 164         setTitle(amh.getMessage("tool.title", atts.get("code")));
 165 
 166         MenuBar mb = factory.getBaseMenuBar();
 167 
 168         Menu m = new Menu(amh.getMessage("menu.applet"));
 169 
 170         addMenuItem(m, "menuitem.restart");
 171         addMenuItem(m, "menuitem.reload");
 172         addMenuItem(m, "menuitem.stop");
 173         addMenuItem(m, "menuitem.save");
 174         addMenuItem(m, "menuitem.start");
 175         addMenuItem(m, "menuitem.clone");
 176         m.add(new MenuItem("-"));
 177         addMenuItem(m, "menuitem.tag");
 178         addMenuItem(m, "menuitem.info");
 179         addMenuItem(m, "menuitem.edit").disable();
 180         addMenuItem(m, "menuitem.encoding");
 181         m.add(new MenuItem("-"));
 182         addMenuItem(m, "menuitem.print");
 183         m.add(new MenuItem("-"));
 184         addMenuItem(m, "menuitem.props");
 185         m.add(new MenuItem("-"));
 186         addMenuItem(m, "menuitem.close");
 187         if (factory.isStandalone()) {
 188             addMenuItem(m, "menuitem.quit");
 189         }
 190 
 191         mb.add(m);
 192 
 193         setMenuBar(mb);
 194 
 195         add("Center", panel = new AppletViewerPanel(doc, atts));
 196         add("South", label = new Label(amh.getMessage("label.hello")));
 197         panel.init();
 198         appletPanels.addElement(panel);
 199 
 200         pack();
 201         move(x, y);
 202         setVisible(true);
 203 
 204         WindowListener windowEventListener = new WindowAdapter() {
 205 
 206             @Override
 207             public void windowClosing(WindowEvent evt) {
 208                 appletClose();
 209             }
 210 
 211             @Override
 212             public void windowIconified(WindowEvent evt) {
 213                 appletStop();
 214             }
 215 
 216             @Override
 217             public void windowDeiconified(WindowEvent evt) {
 218                 appletStart();
 219             }
 220         };
 221 
 222         class AppletEventListener implements AppletListener
 223         {
 224             final Frame frame;
 225 
 226             public AppletEventListener(Frame frame)
 227             {
 228                 this.frame = frame;
 229             }
 230 
 231             @Override
 232             @SuppressWarnings("deprecation")
 233             public void appletStateChanged(AppletEvent evt)
 234             {
 235                 AppletPanel src = (AppletPanel)evt.getSource();
 236 
 237                 switch (evt.getID()) {
 238                     case AppletPanel.APPLET_RESIZE: {
 239                         if(src != null) {
 240                             resize(preferredSize());
 241                             validate();
 242                         }
 243                         break;
 244                     }
 245                     case AppletPanel.APPLET_LOADING_COMPLETED: {
 246                         Applet a = src.getApplet(); // sun.applet.AppletPanel
 247 
 248                         // Fixed #4754451: Applet can have methods running on main
 249                         // thread event queue.
 250                         //
 251                         // The cause of this bug is that the frame of the applet
 252                         // is created in main thread group. Thus, when certain
 253                         // AWT/Swing events are generated, the events will be
 254                         // dispatched through the wrong event dispatch thread.
 255                         //
 256                         // To fix this, we rearrange the AppContext with the frame,
 257                         // so the proper event queue will be looked up.
 258                         //
 259                         // Swing also maintains a Frame list for the AppContext,
 260                         // so we will have to rearrange it as well.
 261                         //
 262                         if (a != null)
 263                             AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a));
 264                         else
 265                             AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext());
 266 
 267                         break;
 268                     }
 269                 }
 270             }
 271         };
 272 
 273         addWindowListener(windowEventListener);
 274         panel.addAppletListener(new AppletEventListener(this));
 275 
 276         // Start the applet
 277         showStatus(amh.getMessage("status.start"));
 278         initEventQueue();
 279     }
 280 
 281     // XXX 99/9/10 probably should be "private"
 282     public MenuItem addMenuItem(Menu m, String s) {
 283         MenuItem mItem = new MenuItem(amh.getMessage(s));
 284         mItem.addActionListener(new UserActionListener());
 285         return m.add(mItem);
 286     }
 287 
 288     /**
 289      * Send the initial set of events to the appletviewer event queue.
 290      * On start-up the current behaviour is to load the applet and call
 291      * Applet.init() and Applet.start().
 292      */
 293     private void initEventQueue() {
 294         // appletviewer.send.event is an undocumented and unsupported system
 295         // property which is used exclusively for testing purposes.
 296         String eventList = System.getProperty("appletviewer.send.event");
 297 
 298         if (eventList == null) {
 299             // Add the standard events onto the event queue.
 300             panel.sendEvent(AppletPanel.APPLET_LOAD);
 301             panel.sendEvent(AppletPanel.APPLET_INIT);
 302             panel.sendEvent(AppletPanel.APPLET_START);
 303         } else {
 304             // We're testing AppletViewer.  Force the specified set of events
 305             // onto the event queue, wait for the events to be processed, and
 306             // exit.
 307 
 308             // The list of events that will be executed is provided as a
 309             // ","-separated list.  No error-checking will be done on the list.
 310             String [] events = splitSeparator(",", eventList);
 311 
 312             for (int i = 0; i < events.length; i++) {
 313                 System.out.println("Adding event to queue: " + events[i]);
 314                 if (events[i].equals("dispose"))
 315                     panel.sendEvent(AppletPanel.APPLET_DISPOSE);
 316                 else if (events[i].equals("load"))
 317                     panel.sendEvent(AppletPanel.APPLET_LOAD);
 318                 else if (events[i].equals("init"))
 319                     panel.sendEvent(AppletPanel.APPLET_INIT);
 320                 else if (events[i].equals("start"))
 321                     panel.sendEvent(AppletPanel.APPLET_START);
 322                 else if (events[i].equals("stop"))
 323                     panel.sendEvent(AppletPanel.APPLET_STOP);
 324                 else if (events[i].equals("destroy"))
 325                     panel.sendEvent(AppletPanel.APPLET_DESTROY);
 326                 else if (events[i].equals("quit"))
 327                     panel.sendEvent(AppletPanel.APPLET_QUIT);
 328                 else if (events[i].equals("error"))
 329                     panel.sendEvent(AppletPanel.APPLET_ERROR);
 330                 else
 331                     // non-fatal error if we get an unrecognized event
 332                     System.out.println("Unrecognized event name: " + events[i]);
 333             }
 334 
 335             while (!panel.emptyEventQueue()) ;
 336             appletSystemExit();
 337         }
 338     }
 339 
 340     /**
 341      * Split a string based on the presence of a specified separator.  Returns
 342      * an array of arbitrary length.  The end of each element in the array is
 343      * indicated by the separator of the end of the string.  If there is a
 344      * separator immediately before the end of the string, the final element
 345      * will be empty.  None of the strings will contain the separator.  Useful
 346      * when separating strings such as "foo/bar/bas" using separator "/".
 347      *
 348      * @param sep  The separator.
 349      * @param s    The string to split.
 350      * @return     An array of strings.  Each string in the array is determined
 351      *             by the location of the provided sep in the original string,
 352      *             s.  Whitespace not stripped.
 353      */
 354     private String [] splitSeparator(String sep, String s) {
 355         Vector<String> v = new Vector<>();
 356         int tokenStart = 0;
 357         int tokenEnd   = 0;
 358 
 359         while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) {
 360             v.addElement(s.substring(tokenStart, tokenEnd));
 361             tokenStart = tokenEnd+1;
 362         }
 363         // Add the final element.
 364         v.addElement(s.substring(tokenStart));
 365 
 366         String [] retVal = new String[v.size()];
 367         v.copyInto(retVal);
 368         return retVal;
 369     }
 370 
 371     /*
 372      * Methods for java.applet.AppletContext
 373      */
 374 
 375     private static Map<URL, AudioClip> audioClips = new HashMap<>();
 376 
 377     /**
 378      * Get an audio clip.
 379      */
 380     @Override
 381     public AudioClip getAudioClip(URL url) {
 382         checkConnect(url);
 383         synchronized (audioClips) {
 384             AudioClip clip = audioClips.get(url);
 385             if (clip == null) {
 386                 audioClips.put(url, clip = new AppletAudioClip(url));
 387             }
 388             return clip;
 389         }
 390     }
 391 
 392     private static Map<URL, AppletImageRef> imageRefs = new HashMap<>();
 393 
 394     /**
 395      * Get an image.
 396      */
 397     @Override
 398     public Image getImage(URL url) {
 399         return getCachedImage(url);
 400     }
 401 
 402     /**
 403      * Get an image.
 404      */
 405     static Image getCachedImage(URL url) {
 406         // System.getSecurityManager().checkConnection(url.getHost(), url.getPort());
 407         synchronized (imageRefs) {
 408             AppletImageRef ref = imageRefs.get(url);
 409             if (ref == null) {
 410                 ref = new AppletImageRef(url);
 411                 imageRefs.put(url, ref);
 412             }
 413             return ref.get();
 414         }
 415     }
 416 
 417     /**
 418      * Flush the image cache.
 419      */
 420     static void flushImageCache() {
 421         imageRefs.clear();
 422     }
 423 
 424     static Vector<AppletPanel> appletPanels = new Vector<>();
 425 
 426     /**
 427      * Get an applet by name.
 428      */
 429     @Override
 430     public Applet getApplet(String name) {
 431         AppletSecurity security = (AppletSecurity)System.getSecurityManager();
 432         name = name.toLowerCase();
 433         SocketPermission panelSp =
 434             new SocketPermission(panel.getCodeBase().getHost(), "connect");
 435         for (Enumeration<AppletPanel> e = appletPanels.elements() ; e.hasMoreElements() ;) {
 436             AppletPanel p = e.nextElement();
 437             String param = p.getParameter("name");
 438             if (param != null) {
 439                 param = param.toLowerCase();
 440             }
 441             if (name.equals(param) &&
 442                 p.getDocumentBase().equals(panel.getDocumentBase())) {
 443 
 444                 SocketPermission sp =
 445                     new SocketPermission(p.getCodeBase().getHost(), "connect");
 446 
 447                 if (panelSp.implies(sp)) {
 448                     return p.applet;
 449                 }
 450             }
 451         }
 452         return null;
 453     }
 454 
 455     /**
 456      * Return an enumeration of all the accessible
 457      * applets on this page.
 458      */
 459     @Override
 460     public Enumeration<Applet> getApplets() {
 461         AppletSecurity security = (AppletSecurity)System.getSecurityManager();
 462         Vector<Applet> v = new Vector<>();
 463         SocketPermission panelSp =
 464             new SocketPermission(panel.getCodeBase().getHost(), "connect");
 465 
 466         for (Enumeration<AppletPanel> e = appletPanels.elements() ; e.hasMoreElements() ;) {
 467             AppletPanel p = e.nextElement();
 468             if (p.getDocumentBase().equals(panel.getDocumentBase())) {
 469 
 470                 SocketPermission sp =
 471                     new SocketPermission(p.getCodeBase().getHost(), "connect");
 472                 if (panelSp.implies(sp)) {
 473                     v.addElement(p.applet);
 474                 }
 475             }
 476         }
 477         return v.elements();
 478     }
 479 
 480     /**
 481      * Ignore.
 482      */
 483     @Override
 484     public void showDocument(URL url) {
 485     }
 486 
 487     /**
 488      * Ignore.
 489      */
 490     @Override
 491     public void showDocument(URL url, String target) {
 492     }
 493 
 494     /**
 495      * Show status.
 496      */
 497     @Override
 498     public void showStatus(String status) {
 499         label.setText(status);
 500     }
 501 
 502     @Override
 503     public void setStream(String key, InputStream stream)throws IOException{
 504         // We do nothing.
 505     }
 506 
 507     @Override
 508     public InputStream getStream(String key){
 509         // We do nothing.
 510         return null;
 511     }
 512 
 513     @Override
 514     public Iterator<String> getStreamKeys(){
 515         // We do nothing.
 516         return null;
 517     }
 518 
 519     /**
 520      * System parameters.
 521      */
 522     static Hashtable<String, String> systemParam = new Hashtable<>();
 523 
 524     static {
 525         systemParam.put("codebase", "codebase");
 526         systemParam.put("code", "code");
 527         systemParam.put("alt", "alt");
 528         systemParam.put("width", "width");
 529         systemParam.put("height", "height");
 530         systemParam.put("align", "align");
 531         systemParam.put("vspace", "vspace");
 532         systemParam.put("hspace", "hspace");
 533     }
 534 
 535     /**
 536      * Print the HTML tag.
 537      */
 538     public static void printTag(PrintStream out, Hashtable<String, String> atts) {
 539         out.print("<applet");
 540 
 541         String v = atts.get("codebase");
 542         if (v != null) {
 543             out.print(" codebase=\"" + v + "\"");
 544         }
 545 
 546         v = atts.get("code");
 547         if (v == null) {
 548             v = "applet.class";
 549         }
 550         out.print(" code=\"" + v + "\"");
 551         v = atts.get("width");
 552         if (v == null) {
 553             v = "150";
 554         }
 555         out.print(" width=" + v);
 556 
 557         v = atts.get("height");
 558         if (v == null) {
 559             v = "100";
 560         }
 561         out.print(" height=" + v);
 562 
 563         v = atts.get("name");
 564         if (v != null) {
 565             out.print(" name=\"" + v + "\"");
 566         }
 567         out.println(">");
 568 
 569         // A very slow sorting algorithm
 570         int len = atts.size();
 571         String params[] = new String[len];
 572         len = 0;
 573         for (Enumeration<String> e = atts.keys() ; e.hasMoreElements() ;) {
 574             String param = e.nextElement();
 575             int i = 0;
 576             for (; i < len ; i++) {
 577                 if (params[i].compareTo(param) >= 0) {
 578                     break;
 579                 }
 580             }
 581             System.arraycopy(params, i, params, i + 1, len - i);
 582             params[i] = param;
 583             len++;
 584         }
 585 
 586         for (int i = 0 ; i < len ; i++) {
 587             String param = params[i];
 588             if (systemParam.get(param) == null) {
 589                 out.println("<param name=" + param +
 590                             " value=\"" + atts.get(param) + "\">");
 591             }
 592         }
 593         out.println("</applet>");
 594     }
 595 
 596     /**
 597      * Make sure the atrributes are uptodate.
 598      */
 599     @SuppressWarnings("deprecation")
 600     public void updateAtts() {
 601         Dimension d = panel.size();
 602         Insets in = panel.insets();
 603         panel.atts.put("width",
 604                        Integer.toString(d.width - (in.left + in.right)));
 605         panel.atts.put("height",
 606                        Integer.toString(d.height - (in.top + in.bottom)));
 607     }
 608 
 609     /**
 610      * Restart the applet.
 611      */
 612     void appletRestart() {
 613         panel.sendEvent(AppletPanel.APPLET_STOP);
 614         panel.sendEvent(AppletPanel.APPLET_DESTROY);
 615         panel.sendEvent(AppletPanel.APPLET_INIT);
 616         panel.sendEvent(AppletPanel.APPLET_START);
 617     }
 618 
 619     /**
 620      * Reload the applet.
 621      */
 622     void appletReload() {
 623         panel.sendEvent(AppletPanel.APPLET_STOP);
 624         panel.sendEvent(AppletPanel.APPLET_DESTROY);
 625         panel.sendEvent(AppletPanel.APPLET_DISPOSE);
 626 
 627         /**
 628          * Fixed #4501142: Classloader sharing policy doesn't
 629          * take "archive" into account. This will be overridden
 630          * by Java Plug-in.                     [stanleyh]
 631          */
 632         AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey());
 633 
 634         /*
 635          * Make sure we don't have two threads running through the event queue
 636          * at the same time.
 637          */
 638         try {
 639             panel.joinAppletThread();
 640             panel.release();
 641         } catch (InterruptedException e) {
 642             return;   // abort the reload
 643         }
 644 
 645         panel.createAppletThread();
 646         panel.sendEvent(AppletPanel.APPLET_LOAD);
 647         panel.sendEvent(AppletPanel.APPLET_INIT);
 648         panel.sendEvent(AppletPanel.APPLET_START);
 649     }
 650 
 651     /**
 652      * Save the applet to a well known file (for now) as a serialized object
 653      */
 654     @SuppressWarnings("deprecation")
 655     void appletSave() {
 656         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 657 
 658             @Override
 659             public Object run() {
 660                 // XXX: this privileged block should be made smaller
 661                 // by initializing a private static variable with "user.dir"
 662 
 663                 // Applet needs to be stopped for serialization to succeed.
 664                 // Since panel.sendEvent only queues the event, there is a
 665                 // chance that the event will not be processed before
 666                 // serialization begins.  However, by sending the event before
 667                 // FileDialog is created, enough time is given such that this
 668                 // situation is unlikely to ever occur.
 669 
 670                 panel.sendEvent(AppletPanel.APPLET_STOP);
 671                 FileDialog fd = new FileDialog(AppletViewer.this,
 672                                                amh.getMessage("appletsave.filedialogtitle"),
 673                                                FileDialog.SAVE);
 674                 // needed for a bug under Solaris...
 675                 fd.setDirectory(System.getProperty("user.dir"));
 676                 fd.setFile(defaultSaveFile);
 677                 fd.show();
 678                 String fname = fd.getFile();
 679                 if (fname == null) {
 680                     // Restart applet if Save is cancelled.
 681                     panel.sendEvent(AppletPanel.APPLET_START);
 682                     return null;                // cancelled
 683                 }
 684                 String dname = fd.getDirectory();
 685                 File file = new File(dname, fname);
 686 
 687                 try (FileOutputStream fos = new FileOutputStream(file);
 688                      BufferedOutputStream bos = new BufferedOutputStream(fos);
 689                      ObjectOutputStream os = new ObjectOutputStream(bos)) {
 690 
 691                     showStatus(amh.getMessage("appletsave.err1", panel.applet.toString(), file.toString()));
 692                     os.writeObject(panel.applet);
 693                 } catch (IOException ex) {
 694                     System.err.println(amh.getMessage("appletsave.err2", ex));
 695                 } finally {
 696                     panel.sendEvent(AppletPanel.APPLET_START);
 697                 }
 698                 return null;
 699             }
 700         });
 701     }
 702 
 703     /**
 704      * Clone the viewer and the applet.
 705      */
 706     @SuppressWarnings("deprecation")
 707     void appletClone() {
 708         Point p = location();
 709         updateAtts();
 710         @SuppressWarnings("unchecked")
 711         Hashtable<String, String> tmp = (Hashtable<String, String>) panel.atts.clone();
 712         factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA,
 713                                    panel.documentURL, tmp);
 714     }
 715 
 716     /**
 717      * Show the applet tag.
 718      */
 719     @SuppressWarnings("deprecation")
 720     void appletTag() {
 721         ByteArrayOutputStream out = new ByteArrayOutputStream();
 722         updateAtts();
 723         printTag(new PrintStream(out), panel.atts);
 724         showStatus(amh.getMessage("applettag"));
 725 
 726         Point p = location();
 727         new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString());
 728     }
 729 
 730     /**
 731      * Show the applet info.
 732      */
 733     @SuppressWarnings("deprecation")
 734     void appletInfo() {
 735         String str = panel.applet.getAppletInfo();
 736         if (str == null) {
 737             str = amh.getMessage("appletinfo.applet");
 738         }
 739         str += "\n\n";
 740 
 741         String atts[][] = panel.applet.getParameterInfo();
 742         if (atts != null) {
 743             for (int i = 0 ; i < atts.length ; i++) {
 744                 str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n";
 745             }
 746         } else {
 747             str += amh.getMessage("appletinfo.param");
 748         }
 749 
 750         Point p = location();
 751         new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str);
 752 
 753     }
 754 
 755     /**
 756      * Show character encoding type
 757      */
 758     void appletCharacterEncoding() {
 759         showStatus(amh.getMessage("appletencoding", encoding));
 760     }
 761 
 762     /**
 763      * Edit the applet.
 764      */
 765     void appletEdit() {
 766     }
 767 
 768     /**
 769      * Print the applet.
 770      */
 771     void appletPrint() {
 772         PrinterJob pj = PrinterJob.getPrinterJob();
 773 
 774         if (pj != null) {
 775             PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
 776             if (pj.printDialog(aset)) {
 777                 pj.setPrintable(this);
 778                 try {
 779                     pj.print(aset);
 780                     statusMsgStream.println(amh.getMessage("appletprint.finish"));
 781                 } catch (PrinterException e) {
 782                    statusMsgStream.println(amh.getMessage("appletprint.fail"));
 783                 }
 784             } else {
 785                 statusMsgStream.println(amh.getMessage("appletprint.cancel"));
 786             }
 787         } else {
 788             statusMsgStream.println(amh.getMessage("appletprint.fail"));
 789         }
 790     }
 791 
 792     @Override
 793     public int print(Graphics graphics, PageFormat pf, int pageIndex) {
 794         if (pageIndex > 0) {
 795             return Printable.NO_SUCH_PAGE;
 796         } else {
 797             Graphics2D g2d = (Graphics2D)graphics;
 798             g2d.translate(pf.getImageableX(), pf.getImageableY());
 799             panel.applet.printAll(graphics);
 800             return Printable.PAGE_EXISTS;
 801         }
 802     }
 803 
 804     /**
 805      * Properties.
 806      */
 807     static AppletProps props;
 808     public static synchronized void networkProperties() {
 809         if (props == null) {
 810             props = new AppletProps();
 811         }
 812         props.addNotify();
 813         props.setVisible(true);
 814     }
 815 
 816     /**
 817      * Start the applet.
 818      */
 819     void appletStart() {
 820         panel.sendEvent(AppletPanel.APPLET_START);
 821     }
 822 
 823     /**
 824      * Stop the applet.
 825      */
 826     void appletStop() {
 827         panel.sendEvent(AppletPanel.APPLET_STOP);
 828     }
 829 
 830     /**
 831      * Shutdown a viewer.
 832      * Stop, Destroy, Dispose and Quit a viewer
 833      */
 834     private void appletShutdown(AppletPanel p) {
 835         p.sendEvent(AppletPanel.APPLET_STOP);
 836         p.sendEvent(AppletPanel.APPLET_DESTROY);
 837         p.sendEvent(AppletPanel.APPLET_DISPOSE);
 838         p.sendEvent(AppletPanel.APPLET_QUIT);
 839     }
 840 
 841     /**
 842      * Close this viewer.
 843      * Stop, Destroy, Dispose and Quit an AppletView, then
 844      * reclaim resources and exit the program if this is
 845      * the last applet.
 846      */
 847     void appletClose() {
 848 
 849         // The caller thread is event dispatch thread, so
 850         // spawn a new thread to avoid blocking the event queue
 851         // when calling appletShutdown.
 852         //
 853         final AppletPanel p = panel;
 854 
 855         new Thread(null, new Runnable()
 856         {
 857             @Override
 858             public void run()
 859             {
 860                 appletShutdown(p);
 861                 appletPanels.removeElement(p);
 862                 dispose();
 863 
 864                 if (countApplets() == 0) {
 865                     appletSystemExit();
 866                 }
 867             }
 868         },
 869         "AppletCloser", 0, false).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(null, 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         },
 904          "AppletQuit", 0, false).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 }