Working towards issue #1105; this change enables hosted mode to decide which mozilla installation to load based on config files. This will allow flexibility so that the largest number of developers will experience no crashiness, but will also allow upgrading to 1.7.13 to support mouse wheel events.

Patch by: jat, scottb (just a little)
Review by: scottb


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1190 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/linux/src/com/google/gwt/dev/BootStrapPlatform.java b/dev/linux/src/com/google/gwt/dev/BootStrapPlatform.java
index c42f750..a5cc0c8 100644
--- a/dev/linux/src/com/google/gwt/dev/BootStrapPlatform.java
+++ b/dev/linux/src/com/google/gwt/dev/BootStrapPlatform.java
@@ -15,19 +15,42 @@
  */
 package com.google.gwt.dev;
 
+import com.google.gwt.dev.shell.moz.MozillaInstall;
+
 /**
  * Initializes low-level libraries for linux.
  */
 public class BootStrapPlatform {
 
+  /**
+   * Find a usable Mozilla installation and load it. Fail immediately, logging
+   * to stderr and exiting with a failure code, if we are unable to find or load
+   * it. If successful, store the loaded path in the property swt.mozilla.path
+   * so SWT's Browser object can use it.
+   */
   public static void go() {
-    // nothing to do
+    MozillaInstall mozInstall = MozillaInstall.find();
+    if (mozInstall == null) {
+      System.err.println("** Unable to find a usable Mozilla install **");
+      System.err.println("You may specify one in mozilla-hosted-browser.conf, "
+          + "see comments in the file for details.");
+      System.exit(1);
+    }
+    try {
+      mozInstall.load();
+    } catch (UnsatisfiedLinkError e) {
+      System.err.println("** Unable to load Mozilla for hosted mode **");
+      e.printStackTrace();
+      System.exit(1);
+    }
+    String mozillaPath = mozInstall.getPath();
+    System.setProperty("swt.mozilla.path", mozillaPath);
   }
 
   public static void maybeInitializeAWT() {
     // nothing to do
   }
-  
+
   public static void setSystemProperties() {
     // nothing to do
   }
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
index 81ebe73..937bbe1 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
@@ -81,6 +81,9 @@
 
   public BrowserWidgetMoz(Shell shell, BrowserWidgetHost host) {
     super(shell, host);
+    host.getLogger().log(TreeLogger.DEBUG,
+        "Using Mozilla install at " + MozillaInstall.getLoaded().getPath(),
+        null);
 
     // Expose a 'window.external' object factory. The created object's
     // gwtOnLoad() method will be called when a hosted mode application's
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
index b929a03..f4875a0 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
@@ -16,10 +16,7 @@
 package com.google.gwt.dev.shell.moz;
 
 import com.google.gwt.dev.shell.LowLevel;
-import com.google.gwt.util.tools.Utility;
 
-import java.io.File;
-import java.io.IOException;
 import java.util.Iterator;
 import java.util.Vector;
 
@@ -100,17 +97,6 @@
     }
   }
 
-  public static String getMozillaDirectory() {
-    String installPath = Utility.getInstallPath();
-    try {
-      // try to make absolute
-      installPath = new File(installPath).getCanonicalPath();
-    } catch (IOException e) {
-      // ignore problems, failures will occur when the libs try to load
-    }
-    return installPath + "/mozilla-1.7.13";
-  }
-
   public static synchronized void init() {
     // Force LowLevel initialization to load gwt-ll
     LowLevel.init();
@@ -239,7 +225,8 @@
    * @param jsthis the JS object with the named method
    * @param jsargs an array of arguments to the method
    */
-  private static void printInvocationParams(String methodName, JsValueMoz jsthis, JsValueMoz[] jsargs) {
+  private static void printInvocationParams(String methodName,
+      JsValueMoz jsthis, JsValueMoz[] jsargs) {
     System.out.println("LowLevelMoz.invoke:");
     System.out.println(" method = " + methodName);
     System.out.println(" # args = " + (jsargs.length));
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/MozillaInstall.java b/dev/linux/src/com/google/gwt/dev/shell/moz/MozillaInstall.java
new file mode 100644
index 0000000..e9aebdb
--- /dev/null
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/MozillaInstall.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.moz;
+
+import com.google.gwt.util.tools.Utility;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Utility class to represent a Mozilla installation, including its installation
+ * directory and a load order file. Instances are immutable, and static helper
+ * functions are provided to create instances.
+ */
+public class MozillaInstall {
+
+  // Default hosted browser config file.
+  private static final String CONFIG_FILENAME = "mozilla-hosted-browser.conf";
+
+  // Default load order file (optional).
+  private static final String LOAD_ORDER_CONFIG_FILE = "gwt-dl-loadorder.conf";
+
+  // Mozilla installation which was actually loaded.
+  private static MozillaInstall loadedInstallation = null;
+
+  /**
+   * Find a suitable mozilla install for GWT hosted mode.
+   * 
+   * This is done by reading the mozilla-hosted-browser.conf in the install
+   * directory, which contains one or more entries, each describing a Mozilla
+   * install to try and load. The first suitable (present and compiled against
+   * GTK2 instead of GTK1) one found is used.
+   * 
+   * Blank lines and lines beginning with # are ignored. The install directory
+   * is prepended to non-absolute paths.
+   * 
+   * @return a MozillaInstall instance or <code>null</code> if none could be
+   *         found
+   */
+  public static MozillaInstall find() {
+    String installPath = Utility.getInstallPath();
+    try {
+      // try to make absolute
+      installPath = new File(installPath).getCanonicalPath();
+    } catch (IOException e) {
+      /*
+       * We might have an exception here if a parent directory is not readable,
+       * so leave it non-absolute and hope the relative path works. If this
+       * fails, the libraries will fail to load when SWT initializes.
+       */
+    }
+
+    /*
+     * Read from the mozilla-hosted-browser.conf file and try to find a suitable
+     * mozilla installation. Return immediately if a suitable one is found.
+     */
+    try {
+      BufferedReader reader = new BufferedReader(new FileReader(installPath
+          + "/" + CONFIG_FILENAME));
+      try { // make sure we close the reader
+        String mozillaDir;
+        while ((mozillaDir = reader.readLine()) != null) {
+          if (mozillaDir.startsWith("#") || mozillaDir.matches("^\\s*$")) {
+            // lines beginning with # are comments, ignore blank lines
+            continue;
+          }
+          if (!mozillaDir.startsWith("/")) {
+            mozillaDir = installPath + "/" + mozillaDir;
+          }
+          MozillaInstall mozInstall = new MozillaInstall(mozillaDir);
+          if (mozInstall.isAcceptable()) {
+            return mozInstall;
+          }
+        }
+      } finally {
+        reader.close();
+      }
+    } catch (FileNotFoundException e) {
+      // fall through to common error-path code
+    } catch (IOException e) {
+      // fall through to common error-path code
+    }
+
+    // if we get here, nothing worked. Return failure.
+    return null;
+  }
+
+  /**
+   * @return the installation which was loaded, or null if none
+   */
+  public static MozillaInstall getLoaded() {
+    return loadedInstallation;
+  }
+
+  /**
+   * The absolute path to a directory containing a Mozilla install.
+   */
+  private String path;
+
+  /**
+   * Create a new instance. Private since this should only be created via the
+   * find factory method.
+   * 
+   * @param path absolute path to the directory containing the Mozilla install
+   */
+  private MozillaInstall(String path) {
+    this.path = path;
+  }
+
+  /**
+   * @return the path of this Mozilla install
+   */
+  public String getPath() {
+    return path;
+  }
+
+  /**
+   * Load any libraries required for this Mozilla install, reading from the
+   * loadOrderFile.
+   * 
+   * The format of the load order configuration file is simply one library path
+   * per line (if non-absolute, the install directory is prepended) to be
+   * loaded. This is necessary because we have to forcibly load some libraries
+   * to make sure they are used instead of system-supplied libraries.
+   * 
+   * Blank lines and lines beginning with # are ignored.
+   * 
+   * @throws UnsatisfiedLinkError if libxpcom.so failed to load
+   */
+  public void load() {
+    try {
+      /*
+       * Read the library load order configuration file to get the list of
+       * libraries which must be preloaded. This is required because Eclipse
+       * seems to always add the system mozilla directory to the
+       * java.library.path, which results in loading the system-supplied
+       * libraries to fulfill dependencies rather than the ones we supply. So,
+       * to get around this we preload individual libraries in a particular
+       * order to make sure they get pulled in properly.
+       * 
+       * The file has one line per library, each giving either an absolute path
+       * or a path relative to the browser directory, in the order the libraries
+       * should be loaded.
+       */
+      String loadOrderFile = path + "/" + LOAD_ORDER_CONFIG_FILE;
+      BufferedReader reader = new BufferedReader(new FileReader(loadOrderFile));
+      try {
+        // read each line and load the library specified
+        String library;
+        while ((library = reader.readLine()) != null) {
+          if (library.startsWith("#") || library.matches("^\\s*$")) {
+            // lines beginning with # are comments, ignore blank lines
+            continue;
+          }
+          if (!library.startsWith("/")) {
+            library = path + "/" + library;
+          }
+          System.load(library);
+        }
+      } finally {
+        reader.close();
+      }
+    } catch (FileNotFoundException e) {
+      // ignore error, assume system will load libraries properly
+    } catch (IOException e) {
+      // ignore error, assume system will load libraries properly
+    }
+    /*
+     * Forcibly load libxpcom.so. Since it has to be loaded before SWT loads its
+     * swt-mozilla library, load it here. This will also cause a failure early
+     * in the process if we have a problem rather than waiting until SWT starts
+     * up.
+     */
+    System.load(path + "/libxpcom.so");
+    loadedInstallation = this;
+  }
+
+  /**
+   * Checks to see if the specified path refers to an acceptable Mozilla
+   * install.
+   * 
+   * In this case, acceptable means it is present and was not compiled against
+   * GTK1.
+   * 
+   * Note: Embedding a Mozilla GTK1.2 causes a crash. The workaround is to check
+   * the version of GTK used by Mozilla by looking for the libwidget_gtk.so
+   * library used by Mozilla GTK1.2. Mozilla GTK2 uses the libwidget_gtk2.so
+   * library.
+   * 
+   * @return <code>true</code> if this Mozilla installation is acceptable for
+   *         use with GWT
+   */
+  private boolean isAcceptable() {
+    // make sure the main library exists
+    if (!new File(path, "libxpcom.so").exists()) {
+      return false;
+    }
+    // check to see if it was built against GTK1 instead of GTK2
+    if (new File(path, "components/libwidget_gtk.so").exists()) {
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/dev/linux/src/org/eclipse/swt/browser/Browser.java b/dev/linux/src/org/eclipse/swt/browser/Browser.java
index 3178ad6..0ae657e 100644
--- a/dev/linux/src/org/eclipse/swt/browser/Browser.java
+++ b/dev/linux/src/org/eclipse/swt/browser/Browser.java
@@ -129,10 +129,13 @@
 	int /*long*/[] result = new int /*long*/[1];
 	if (!mozilla) {
 		/*
-		* GOOGLE: We ship our own bundled version of Mozilla; ignore the
-		* environment and just use ours.
+		* GOOGLE: We ship our own bundled version of Mozilla; the
+		* bootstrap process stores the path of the one we loaded in
+		* the system property swt.mozilla.path, so we get it from
+		* there rather than using SWT's normal process to find it.
 		*/
-		String mozillaPath = LowLevelMoz.getMozillaDirectory();
+		String mozillaPath = System.getProperty("swt.mozilla.path");
+        
 		int ptr;
 		//String mozillaPath = null;
 		//int /*long*/ ptr = OS.getenv(Converter.wcsToMbcs(null, XPCOM.MOZILLA_FIVE_HOME, true));
@@ -147,48 +150,7 @@
 		//	SWT.error(SWT.ERROR_NO_HANDLES, null, " [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]"); //$NON-NLS-1$
 		//}
 
-		/*
-		* Note.  Embedding a Mozilla GTK1.2 causes a crash.  The workaround
-		* is to check the version of GTK used by Mozilla by looking for
-		* the libwidget_gtk.so library used by Mozilla GTK1.2. Mozilla GTK2
-		* uses the libwidget_gtk2.so library.   
-		*/
-		File file = new File(mozillaPath, "components/libwidget_gtk.so"); //$NON-NLS-1$
-		if (file.exists()) {
-			dispose();
-			SWT.error(SWT.ERROR_NO_HANDLES, null, " [Mozilla GTK2 required (GTK1.2 detected)]"); //$NON-NLS-1$							
-		}
-
 		try {
-			/*
-			 * GOOGLE: Eclipse seems to always add /usr/lib/mozilla onto the
-			 * java.library.path, which results in loading the system-supplied
-			 * libraries to fulfill dependencies rather than the ones we built.
-			 * So, to get around this, we load each library specifically.
-			 */
-			System.load(mozillaPath + "/libnspr4.so");
-			System.load(mozillaPath + "/libplc4.so");
-			System.load(mozillaPath + "/libplds4.so");
-
-			/*
-			* GOOGLE: For some reason, swt-mozilla won't load unless xpcom is
-			* loaded first. I don't know why it fails for me, or why it
-			* doesn't fail for the SWT guys, but this seems to fix it.
-			*
-			* This may in fact be related to the problem above.
-			*/
-			System.load (mozillaPath + "/libxpcom.so");
-			System.load(mozillaPath + "/libmozz.so");
-			System.load(mozillaPath + "/libmozjs.so");
-			System.load(mozillaPath + "/libgkgfx.so");
-			System.load(mozillaPath + "/components/libtypeaheadfind.so");
-			System.load(mozillaPath + "/components/libembedcomponents.so");
-			System.load(mozillaPath + "/components/libpref.so");
-			System.load(mozillaPath + "/components/libsystem-pref.so");
-			System.load(mozillaPath + "/components/libnecko.so");
-			System.load(mozillaPath + "/components/libcaps.so");
-			System.load(mozillaPath + "/components/libjsd.so");
-			System.load(mozillaPath + "/libgtkembedmoz.so");
 			Library.loadLibrary ("swt-mozilla"); //$NON-NLS-1$
 		} catch (UnsatisfiedLinkError e) {
 			try {
diff --git a/distro-source/linux/src/mozilla-hosted-browser.conf b/distro-source/linux/src/mozilla-hosted-browser.conf
new file mode 100644
index 0000000..743c8d0
--- /dev/null
+++ b/distro-source/linux/src/mozilla-hosted-browser.conf
@@ -0,0 +1,35 @@
+# This file specifies a list of possible mozilla installations to load, in
+# priority order.  Non-existent paths are ignored, and the next location is
+# searched.  A best effort attempt is also made to ignore paths containing
+# incompatible mozilla installations (for example, ones which use GTK1 instead
+# of GTK2, which is required).  The first apparently valid installation found
+# will be used (although it could still fail later).
+#
+# Non-absolute paths are relative to the directory containing this file (that
+# is, the GWT install directory).
+#
+# Non-system installations should contain a file named "gwt-dl-loadorder.conf".
+# The format of such a file is one shared library per line which dictates the
+# order in which that installation's shared libraries must be loaded to prevent
+# implicit loading of other libraries.  In other words, no library should be
+# loaded before its dependencies.  This is to prevent the LD_LIBRARY_PATH or
+# other system library load configuration from loading the default system
+# version of the library instead of the version in the target installation.
+
+
+# Prefer mozilla 1.7.13 if it exists, because it supports mouse wheel events.
+# If you need mouse wheel events, you can install the distribution available at:
+#
+#   http://google-web-toolkit.googlecode.com/svn/tools/redist/mozilla/mozilla-1.7.13.tar.gz
+#
+# However, this version may not run correctly on your system.  If it doesn't,
+# you can try installing a mozilla 1.7.13 built for your system.
+mozilla-1.7.13
+
+# This is the default mozilla that ships with GWT.
+mozilla-1.7.12
+
+# See if there are compatible mozilla distributions already installed.
+/usr/lib/mozilla-1.7.13
+/usr/lib/mozilla-1.7.12
+/usr/lib/mozilla