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 }