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