001: /*
002: ** Luxor - XML User Interface Language (XUL) Toolkit
003: ** Copyright (c) 2001, 2002 by Gerald Bauer
004: **
005: ** This program is free software.
006: **
007: ** You may redistribute it and/or modify it under the terms of the GNU
008: ** General Public License as published by the Free Software Foundation.
009: ** Version 2 of the license should be included with this distribution in
010: ** the file LICENSE, as well as License.html. If the license is not
011: ** included with this distribution, you may find a copy at the FSF web
012: ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
013: ** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA.
014: **
015: ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
016: ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
017: ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
018: ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
019: ** REDISTRIBUTION OF THIS SOFTWARE.
020: **
021: */
022:
023: package luxor.browser.albert;
024:
025: import java.io.File;
026: import java.io.FileNotFoundException;
027: import java.io.IOException;
028: import java.lang.reflect.Constructor;
029: import java.lang.reflect.Field;
030: import java.lang.reflect.InvocationTargetException;
031: import java.lang.reflect.Method;
032:
033: /**
034: * BrowserLauncher is a class that provides one static method, openURL, which
035: * opens the default web browser for the current user of the system to the
036: * given URL. It may support other protocols depending on the system -- mailto,
037: * ftp, etc. -- but that has not been rigorously tested and is not guaranteed
038: * to work. <p>
039: *
040: * Yes, this is platform-specific code, and yes, it may rely on classes on
041: * certain platforms that are not part of the standard JDK. What we're trying
042: * to do, though, is to take something that's frequently desirable but
043: * inherently platform-specific -- opening a default browser -- and allow
044: * programmers (you, for example) to do so without worrying about dropping into
045: * native code or doing anything else similarly evil. <p>
046: *
047: * Anyway, this code is completely in Java and will run on all JDK
048: * 1.1-compliant systems without modification or a need for additional
049: * libraries. All classes that are required on certain platforms to allow this
050: * to run are dynamically loaded at runtime via reflection and, if not found,
051: * will not cause this to do anything other than returning an error when
052: * opening the browser. <p>
053: *
054: * There are certain system requirements for this class, as it's running
055: * through Runtime.exec(), which is Java's way of making a native system call.
056: * Currently, this requires that a Macintosh have a Finder which supports the
057: * GURL event, which is true for Mac OS 8.0 and 8.1 systems that have the
058: * Internet Scripting AppleScript dictionary installed in the Scripting
059: * Additions folder in the Extensions folder (which is installed by default as
060: * far as I know under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later
061: * systems. On Windows, it only runs under Win32 systems (Windows 95, 98, and
062: * NT 4.0, as well as later versions of all). On other systems, this drops back
063: * from the inherently platform-sensitive concept of a default browser and
064: * simply attempts to launch Netscape via a shell command. <p>
065: *
066: * This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu)
067: * and may be redistributed or modified in any form without restrictions as
068: * long as the portion of this comment from this paragraph through the end of
069: * the comment is not removed. The author requests that he be notified of any
070: * application, applet, or other binary that makes use of this code, but that's
071: * more out of curiosity than anything and is not required. This software
072: * includes no warranty. The author is not repsonsible for any loss of data or
073: * functionality or any adverse or unexpected effects of using this software.
074: * <p>
075: *
076: * Credits: <br>
077: * Steven Spencer, JavaWorld magazine (<a
078: * href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java
079: * Tip 66</a> ) <br>
080: * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea
081: * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
082: *
083: *@author Eric Albert (<a href="mailto:ejalbert@cs.stanford.edu">
084: * ejalbert@cs.stanford.edu</a> )
085: *@version 1.4b1 (Released June 20, 2001)
086: */
087: public class BrowserLauncher {
088:
089: /**
090: * The creator code of the Finder on a Macintosh, which is needed to send
091: * AppleEvents to the application.
092: */
093: private final static String FINDER_CREATOR = "MACS";
094:
095: /**
096: * The file type of the Finder on a Macintosh. Hardcoding "Finder" would
097: * keep non-U.S. English systems from working properly.
098: */
099: private final static String FINDER_TYPE = "FNDR";
100:
101: /**
102: * The first parameter that needs to be passed into Runtime.exec() to open
103: * the default web browser on Windows.
104: */
105: private final static String FIRST_WINDOWS_PARAMETER = "/c";
106:
107: /**
108: * The name for the AppleEvent type corresponding to a GetURL event.
109: */
110: private final static String GURL_EVENT = "GURL";
111:
112: /**
113: * The framework to reference on Mac OS X
114: */
115: private final static String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
116:
117: /**
118: * JVM constant for MRJ 2.0
119: */
120: private final static int MRJ_2_0 = 0;
121:
122: /**
123: * JVM constant for MRJ 2.1 or later
124: */
125: private final static int MRJ_2_1 = 1;
126:
127: /**
128: * JVM constant for Java on Mac OS X 10.0 (MRJ 3.0)
129: */
130: private final static int MRJ_3_0 = 3;
131:
132: /**
133: * JVM constant for MRJ 3.1
134: */
135: private final static int MRJ_3_1 = 4;
136: private final static String NETSCAPE_OPEN_PARAMETER_END = ")'";
137: private final static String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
138:
139: /**
140: * The shell parameters for Netscape that opens a given URL in an
141: * already-open copy of Netscape on many command-line systems.
142: */
143: private final static String NETSCAPE_REMOTE_PARAMETER = "-remote";
144:
145: /**
146: * JVM constant for any other platform
147: */
148: private final static int OTHER = -1;
149:
150: /**
151: * The second parameter for Runtime.exec() on Windows.
152: */
153: private final static String SECOND_WINDOWS_PARAMETER = "start";
154:
155: /**
156: * The third parameter for Runtime.exec() on Windows. This is a "title"
157: * parameter that the command line expects. Setting this parameter allows
158: * URLs containing spaces to work.
159: */
160: private final static String THIRD_WINDOWS_PARAMETER = "\"\"";
161:
162: /**
163: * JVM constant for any Windows 9x JVM
164: */
165: private final static int WINDOWS_9x = 6;
166:
167: /**
168: * JVM constant for any Windows NT JVM
169: */
170: private final static int WINDOWS_NT = 5;
171:
172: /**
173: * The com.apple.MacOS.AEDesc class
174: */
175: private static Class aeDescClass;
176:
177: /**
178: * The <init>(String) method of com.apple.MacOS.AEDesc
179: */
180: private static Constructor aeDescConstructor;
181:
182: /**
183: * The <init>(int) method of com.apple.MacOS.AETarget
184: */
185: private static Constructor aeTargetConstructor;
186:
187: /**
188: * The <init>(int, int, int) method of com.apple.MacOS.AppleEvent
189: */
190: private static Constructor appleEventConstructor;
191:
192: /**
193: * The browser for the system
194: */
195: private static Object browser;
196:
197: /**
198: * The message from any exception thrown throughout the initialization
199: * process.
200: */
201: private static String errorMessage;
202:
203: /**
204: * The findFolder method of com.apple.mrj.MRJFileUtils
205: */
206: private static Method findFolder;
207:
208: /**
209: * The getFileCreator method of com.apple.mrj.MRJFileUtils
210: */
211: private static Method getFileCreator;
212:
213: /**
214: * The getFileType method of com.apple.mrj.MRJFileUtils
215: */
216: private static Method getFileType;
217:
218: /**
219: * The Java virtual machine that we are running on. Actually, in most cases
220: * we only care about the operating system, but some operating systems
221: * require us to switch on the VM.
222: */
223: private static int jvm;
224:
225: /**
226: * The kAnyTransactionID AppleEvent code
227: */
228: private static Integer kAnyTransactionID;
229:
230: /**
231: * The kAutoGenerateReturnID AppleEvent code
232: */
233: private static Integer kAutoGenerateReturnID;
234:
235: /**
236: * Actually an MRJOSType pointing to the System Folder on a Macintosh
237: */
238: private static Object kSystemFolderType;
239:
240: /**
241: * The keyDirectObject AppleEvent parameter type
242: */
243: private static Integer keyDirectObject;
244:
245: /**
246: * The linkage object required for JDirect 3 on Mac OS X.
247: */
248: private static Object linkage;
249:
250: /**
251: * Caches whether any classes, methods, and fields that are not part of the
252: * JDK and need to be dynamically loaded at runtime loaded successfully. <p>
253: *
254: * Note that if this is <code>false</code>, <code>openURL()</code> will
255: * always return an IOException.
256: */
257: private static boolean loadedWithoutErrors;
258:
259: /**
260: * The makeOSType method of com.apple.MacOS.OSUtils
261: */
262: private static Method makeOSType;
263:
264: /**
265: * The com.apple.mrj.MRJFileUtils class
266: */
267: private static Class mrjFileUtilsClass;
268:
269: /**
270: * The com.apple.mrj.MRJOSType class
271: */
272: private static Class mrjOSTypeClass;
273:
274: /**
275: * The openURL method of com.apple.mrj.MRJFileUtils
276: */
277: private static Method openURL;
278:
279: /**
280: * The putParameter method of com.apple.MacOS.AppleEvent
281: */
282: private static Method putParameter;
283:
284: /**
285: * The sendNoReply method of com.apple.MacOS.AppleEvent
286: */
287: private static Method sendNoReply;
288:
289: /**
290: * This class should be never be instantiated; this just ensures so.
291: */
292: private BrowserLauncher() {
293: }
294:
295: /**
296: * Attempts to open the default web browser to the given URL.
297: *
298: *@param url The URL to open
299: *@throws IOException If the web browser could not be located or does not
300: * run
301: */
302: public static void openURL(String url) throws IOException {
303: if (!loadedWithoutErrors) {
304: throw new IOException("Exception in finding browser: "
305: + errorMessage);
306: }
307: Object browser = locateBrowser();
308: if (browser == null) {
309: throw new IOException("Unable to locate browser: "
310: + errorMessage);
311: }
312:
313: switch (jvm) {
314: case MRJ_2_0:
315: Object aeDesc = null;
316: try {
317: aeDesc = aeDescConstructor
318: .newInstance(new Object[] { url });
319: putParameter.invoke(browser, new Object[] {
320: keyDirectObject, aeDesc });
321: sendNoReply.invoke(browser, new Object[] {});
322: } catch (InvocationTargetException ite) {
323: throw new IOException(
324: "InvocationTargetException while creating AEDesc: "
325: + ite.getMessage());
326: } catch (IllegalAccessException iae) {
327: throw new IOException(
328: "IllegalAccessException while building AppleEvent: "
329: + iae.getMessage());
330: } catch (InstantiationException ie) {
331: throw new IOException(
332: "InstantiationException while creating AEDesc: "
333: + ie.getMessage());
334: } finally {
335: aeDesc = null;
336: // Encourage it to get disposed if it was created
337: browser = null;
338: // Ditto
339: }
340: break;
341: case MRJ_2_1:
342: Runtime.getRuntime().exec(
343: new String[] { (String) browser, url });
344: break;
345: case MRJ_3_0:
346: int[] instance = new int[1];
347: int result = ICStart(instance, 0);
348: if (result == 0) {
349: int[] selectionStart = new int[] { 0 };
350: byte[] urlBytes = url.getBytes();
351: int[] selectionEnd = new int[] { urlBytes.length };
352: result = ICLaunchURL(instance[0], new byte[] { 0 },
353: urlBytes, urlBytes.length, selectionStart,
354: selectionEnd);
355: if (result == 0) {
356: // Ignore the return value; the URL was launched successfully
357: // regardless of what happens here.
358: ICStop(instance);
359: } else {
360: throw new IOException("Unable to launch URL: "
361: + result);
362: }
363: } else {
364: throw new IOException(
365: "Unable to create an Internet Config instance: "
366: + result);
367: }
368: break;
369: case MRJ_3_1:
370: try {
371: openURL.invoke(null, new Object[] { url });
372: } catch (InvocationTargetException ite) {
373: throw new IOException(
374: "InvocationTargetException while calling openURL: "
375: + ite.getMessage());
376: } catch (IllegalAccessException iae) {
377: throw new IOException(
378: "IllegalAccessException while calling openURL: "
379: + iae.getMessage());
380: }
381: break;
382: case WINDOWS_NT:
383: case WINDOWS_9x:
384: // Add quotes around the URL to allow ampersands and other special
385: // characters to work.
386: Process process = Runtime.getRuntime().exec(
387: new String[] { (String) browser,
388: FIRST_WINDOWS_PARAMETER,
389: SECOND_WINDOWS_PARAMETER,
390: THIRD_WINDOWS_PARAMETER, '"' + url + '"' });
391: // This avoids a memory leak on some versions of Java on Windows.
392: // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>.
393: try {
394: process.waitFor();
395: process.exitValue();
396: } catch (InterruptedException ie) {
397: throw new IOException(
398: "InterruptedException while launching browser: "
399: + ie.getMessage());
400: }
401: break;
402: case OTHER:
403: // Assume that we're on Unix and that Netscape is installed
404:
405: // First, attempt to open the URL in a currently running session of Netscape
406: process = Runtime.getRuntime().exec(
407: new String[] {
408: (String) browser,
409: NETSCAPE_REMOTE_PARAMETER,
410: NETSCAPE_OPEN_PARAMETER_START + url
411: + NETSCAPE_OPEN_PARAMETER_END });
412: try {
413: int exitCode = process.waitFor();
414: if (exitCode != 0) {
415: // if Netscape was not open
416: Runtime.getRuntime().exec(
417: new String[] { (String) browser, url });
418: }
419: } catch (InterruptedException ie) {
420: throw new IOException(
421: "InterruptedException while launching browser: "
422: + ie.getMessage());
423: }
424: break;
425: default:
426: // This should never occur, but if it does, we'll try the simplest thing possible
427: Runtime.getRuntime().exec(
428: new String[] { (String) browser, url });
429: break;
430: }
431: }
432:
433: private native static int ICLaunchURL(int instance, byte[] hint,
434: byte[] data, int len, int[] selectionStart,
435: int[] selectionEnd);
436:
437: /**
438: * Methods required for Mac OS X. The presence of native methods does not
439: * cause any problems on other platforms.
440: */
441: private native static int ICStart(int[] instance, int signature);
442:
443: private native static int ICStop(int[] instance);
444:
445: /**
446: * Called by a static initializer to load any classes, fields, and methods
447: * required at runtime to locate the user's web browser.
448: *
449: *@return <code>true</code> if all intialization succeeded <code>false</code>
450: * if any portion of the initialization failed
451: */
452: private static boolean loadClasses() {
453: switch (jvm) {
454: case MRJ_2_0:
455: try {
456: Class aeTargetClass = Class
457: .forName("com.apple.MacOS.AETarget");
458: Class osUtilsClass = Class
459: .forName("com.apple.MacOS.OSUtils");
460: Class appleEventClass = Class
461: .forName("com.apple.MacOS.AppleEvent");
462: Class aeClass = Class.forName("com.apple.MacOS.ae");
463: aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
464:
465: aeTargetConstructor = aeTargetClass
466: .getDeclaredConstructor(new Class[] { int.class });
467: appleEventConstructor = appleEventClass
468: .getDeclaredConstructor(new Class[] {
469: int.class, int.class, aeTargetClass,
470: int.class, int.class });
471: aeDescConstructor = aeDescClass
472: .getDeclaredConstructor(new Class[] { String.class });
473:
474: makeOSType = osUtilsClass.getDeclaredMethod(
475: "makeOSType", new Class[] { String.class });
476: putParameter = appleEventClass.getDeclaredMethod(
477: "putParameter", new Class[] { int.class,
478: aeDescClass });
479: sendNoReply = appleEventClass.getDeclaredMethod(
480: "sendNoReply", new Class[] {});
481:
482: Field keyDirectObjectField = aeClass
483: .getDeclaredField("keyDirectObject");
484: keyDirectObject = (Integer) keyDirectObjectField
485: .get(null);
486: Field autoGenerateReturnIDField = appleEventClass
487: .getDeclaredField("kAutoGenerateReturnID");
488: kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
489: .get(null);
490: Field anyTransactionIDField = appleEventClass
491: .getDeclaredField("kAnyTransactionID");
492: kAnyTransactionID = (Integer) anyTransactionIDField
493: .get(null);
494: } catch (ClassNotFoundException cnfe) {
495: errorMessage = cnfe.getMessage();
496: return false;
497: } catch (NoSuchMethodException nsme) {
498: errorMessage = nsme.getMessage();
499: return false;
500: } catch (NoSuchFieldException nsfe) {
501: errorMessage = nsfe.getMessage();
502: return false;
503: } catch (IllegalAccessException iae) {
504: errorMessage = iae.getMessage();
505: return false;
506: }
507: break;
508: case MRJ_2_1:
509: try {
510: mrjFileUtilsClass = Class
511: .forName("com.apple.mrj.MRJFileUtils");
512: mrjOSTypeClass = Class
513: .forName("com.apple.mrj.MRJOSType");
514: Field systemFolderField = mrjFileUtilsClass
515: .getDeclaredField("kSystemFolderType");
516: kSystemFolderType = systemFolderField.get(null);
517: findFolder = mrjFileUtilsClass.getDeclaredMethod(
518: "findFolder", new Class[] { mrjOSTypeClass });
519: getFileCreator = mrjFileUtilsClass.getDeclaredMethod(
520: "getFileCreator", new Class[] { File.class });
521: getFileType = mrjFileUtilsClass.getDeclaredMethod(
522: "getFileType", new Class[] { File.class });
523: } catch (ClassNotFoundException cnfe) {
524: errorMessage = cnfe.getMessage();
525: return false;
526: } catch (NoSuchFieldException nsfe) {
527: errorMessage = nsfe.getMessage();
528: return false;
529: } catch (NoSuchMethodException nsme) {
530: errorMessage = nsme.getMessage();
531: return false;
532: } catch (SecurityException se) {
533: errorMessage = se.getMessage();
534: return false;
535: } catch (IllegalAccessException iae) {
536: errorMessage = iae.getMessage();
537: return false;
538: }
539: break;
540: case MRJ_3_0:
541: try {
542: Class linker = Class
543: .forName("com.apple.mrj.jdirect.Linker");
544: Constructor constructor = linker
545: .getConstructor(new Class[] { Class.class });
546: linkage = constructor
547: .newInstance(new Object[] { BrowserLauncher.class });
548: } catch (ClassNotFoundException cnfe) {
549: errorMessage = cnfe.getMessage();
550: return false;
551: } catch (NoSuchMethodException nsme) {
552: errorMessage = nsme.getMessage();
553: return false;
554: } catch (InvocationTargetException ite) {
555: errorMessage = ite.getMessage();
556: return false;
557: } catch (InstantiationException ie) {
558: errorMessage = ie.getMessage();
559: return false;
560: } catch (IllegalAccessException iae) {
561: errorMessage = iae.getMessage();
562: return false;
563: }
564: break;
565: case MRJ_3_1:
566: try {
567: mrjFileUtilsClass = Class
568: .forName("com.apple.mrj.MRJFileUtils");
569: openURL = mrjFileUtilsClass.getDeclaredMethod(
570: "openURL", new Class[] { String.class });
571: } catch (ClassNotFoundException cnfe) {
572: errorMessage = cnfe.getMessage();
573: return false;
574: } catch (NoSuchMethodException nsme) {
575: errorMessage = nsme.getMessage();
576: return false;
577: }
578: break;
579: default:
580: break;
581: }
582: return true;
583: }
584:
585: /**
586: * Attempts to locate the default web browser on the local system. Caches
587: * results so it only locates the browser once for each use of this class
588: * per JVM instance.
589: *
590: *@return The browser for the system. Note that this may not be what you
591: * would consider to be a standard web browser; instead, it's the
592: * application that gets called to open the default web browser. In some
593: * cases, this will be a non-String object that provides the means of
594: * calling the default browser.
595: */
596: private static Object locateBrowser() {
597: if (browser != null) {
598: return browser;
599: }
600: switch (jvm) {
601: case MRJ_2_0:
602: try {
603: Integer finderCreatorCode = (Integer) makeOSType
604: .invoke(null, new Object[] { FINDER_CREATOR });
605: Object aeTarget = aeTargetConstructor
606: .newInstance(new Object[] { finderCreatorCode });
607: Integer gurlType = (Integer) makeOSType.invoke(null,
608: new Object[] { GURL_EVENT });
609: Object appleEvent = appleEventConstructor
610: .newInstance(new Object[] { gurlType, gurlType,
611: aeTarget, kAutoGenerateReturnID,
612: kAnyTransactionID });
613: // Don't set browser = appleEvent because then the next time we call
614: // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
615: // added the relevant parameter. Instead, regenerate the AppleEvent every time.
616: // There's probably a way to do this better; if any has any ideas, please let
617: // me know.
618: return appleEvent;
619: } catch (IllegalAccessException iae) {
620: browser = null;
621: errorMessage = iae.getMessage();
622: return browser;
623: } catch (InstantiationException ie) {
624: browser = null;
625: errorMessage = ie.getMessage();
626: return browser;
627: } catch (InvocationTargetException ite) {
628: browser = null;
629: errorMessage = ite.getMessage();
630: return browser;
631: }
632: case MRJ_2_1:
633: File systemFolder;
634: try {
635: systemFolder = (File) findFolder.invoke(null,
636: new Object[] { kSystemFolderType });
637: } catch (IllegalArgumentException iare) {
638: browser = null;
639: errorMessage = iare.getMessage();
640: return browser;
641: } catch (IllegalAccessException iae) {
642: browser = null;
643: errorMessage = iae.getMessage();
644: return browser;
645: } catch (InvocationTargetException ite) {
646: browser = null;
647: errorMessage = ite.getTargetException().getClass()
648: + ": " + ite.getTargetException().getMessage();
649: return browser;
650: }
651: String[] systemFolderFiles = systemFolder.list();
652: // Avoid a FilenameFilter because that can't be stopped mid-list
653: for (int i = 0; i < systemFolderFiles.length; i++) {
654: try {
655: File file = new File(systemFolder,
656: systemFolderFiles[i]);
657: if (!file.isFile()) {
658: continue;
659: }
660: // We're looking for a file with a creator code of 'MACS' and
661: // a type of 'FNDR'. Only requiring the type results in non-Finder
662: // applications being picked up on certain Mac OS 9 systems,
663: // especially German ones, and sending a GURL event to those
664: // applications results in a logout under Multiple Users.
665: Object fileType = getFileType.invoke(null,
666: new Object[] { file });
667: if (FINDER_TYPE.equals(fileType.toString())) {
668: Object fileCreator = getFileCreator.invoke(
669: null, new Object[] { file });
670: if (FINDER_CREATOR.equals(fileCreator
671: .toString())) {
672: browser = file.toString();
673: // Actually the Finder, but that's OK
674: return browser;
675: }
676: }
677: } catch (IllegalArgumentException iare) {
678: browser = browser;
679: errorMessage = iare.getMessage();
680: return null;
681: } catch (IllegalAccessException iae) {
682: browser = null;
683: errorMessage = iae.getMessage();
684: return browser;
685: } catch (InvocationTargetException ite) {
686: browser = null;
687: errorMessage = ite.getTargetException().getClass()
688: + ": "
689: + ite.getTargetException().getMessage();
690: return browser;
691: }
692: }
693: browser = null;
694: break;
695: case MRJ_3_0:
696: case MRJ_3_1:
697: browser = "";
698: // Return something non-null
699: break;
700: case WINDOWS_NT:
701: browser = "cmd.exe";
702: break;
703: case WINDOWS_9x:
704: browser = "command.com";
705: break;
706: case OTHER:
707: default:
708: browser = "netscape";
709: break;
710: }
711: return browser;
712: }
713:
714: static {
715: loadedWithoutErrors = true;
716: String osName = System.getProperty("os.name");
717: if (osName.startsWith("Mac OS")) {
718: String mrjVersion = System.getProperty("mrj.version");
719: String majorMRJVersion = mrjVersion.substring(0, 3);
720: try {
721: double version = Double.valueOf(majorMRJVersion)
722: .doubleValue();
723: if (version == 2) {
724: jvm = MRJ_2_0;
725: } else if (version >= 2.1 && version < 3) {
726: // Assume that all 2.x versions of MRJ work the same. MRJ 2.1 actually
727: // works via Runtime.exec() and 2.2 supports that but has an openURL() method
728: // as well that we currently ignore.
729: jvm = MRJ_2_1;
730: } else if (version == 3.0) {
731: jvm = MRJ_3_0;
732: } else if (version >= 3.1) {
733: // Assume that all 3.1 and later versions of MRJ work the same.
734: jvm = MRJ_3_1;
735: } else {
736: loadedWithoutErrors = false;
737: errorMessage = "Unsupported MRJ version: "
738: + version;
739: }
740: } catch (NumberFormatException nfe) {
741: loadedWithoutErrors = false;
742: errorMessage = "Invalid MRJ version: " + mrjVersion;
743: }
744: } else if (osName.startsWith("Windows")) {
745: if (osName.indexOf("9") != -1) {
746: jvm = WINDOWS_9x;
747: } else {
748: jvm = WINDOWS_NT;
749: }
750: } else {
751: jvm = OTHER;
752: }
753:
754: if (loadedWithoutErrors) {
755: // if we haven't hit any errors yet
756: loadedWithoutErrors = loadClasses();
757: }
758: }
759: }
|