Backed out changeset d7fc46501527 (Bug 642795) due to linux build bustage
authorDaniel Holbert <dholbert@cs.stanford.edu>
Fri, 01 Apr 2011 11:04:52 -0700
changeset 64608 84034146670ce04062981e930341691edf0cb184
parent 64607 d7fc46501527a4aeecdf3f69f908b250a305ef00
child 64609 f9e753853aa7cbc9495419bbe23ffcd1b7a2c181
push idunknown
push userunknown
push dateunknown
bugs642795
milestone2.2a1pre
backs outd7fc46501527a4aeecdf3f69f908b250a305ef00
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset d7fc46501527 (Bug 642795) due to linux build bustage
embedding/tests/winEmbed/winEmbed.cpp
xpcom/build/dlldeps.cpp
xpcom/glue/nsGREGlue.cpp
xpcom/glue/objs.mk
xpcom/glue/standalone/nsXPCOMGlue.h
xulrunner/app/Makefile.in
xulrunner/app/nsRegisterGRE.h
xulrunner/app/nsRegisterGREUnix.cpp
xulrunner/app/nsRegisterGREVoid.cpp
xulrunner/app/nsRegisterGREWin.cpp
xulrunner/app/nsXULRunnerApp.cpp
xulrunner/installer/Makefile.in
xulrunner/stub/nsXULStub.cpp
--- a/embedding/tests/winEmbed/winEmbed.cpp
+++ b/embedding/tests/winEmbed/winEmbed.cpp
@@ -170,62 +170,75 @@ int main(int argc, char *argv[])
     // should be compatible all the way up to (but not including) mozilla 2.0
     static const GREVersionRange vr = {
         "1.8a1",
         PR_TRUE,
         "2.0",
         PR_FALSE
     };
 
-    char path[_MAX_PATH];
-    GetModuleFileName(ghInstanceApp, self, sizeof(self));
-    lastslash = ns_strrpbrk(xpcomPath, "/\\");
+    char xpcomPath[_MAX_PATH];
+    rv = GRE_GetGREPathWithProperties(&vr, 1, nsnull, 0,
+                                      xpcomPath, sizeof(xpcomPath));
+    if (NS_FAILED(rv))
+        return 1;
+
+    char *lastslash = ns_strrpbrk(xpcomPath, "/\\");
     if (!lastslash)
-        return 7;
+        return 2;
 
-    strcpy(lastslash, "\\xulrunner\\xpcom.dll");
-
-    rv = XPCOMGlueStartup(path);
+    rv = XPCOMGlueStartup(xpcomPath);
     if (NS_FAILED(rv))
         return 3;
 
-    strcpy(lastslash, "\\xulrunner\\xul.dll");
+    *lastslash = '\0';
 
-    HINSTANCE xulModule = LoadLibraryEx(path, NULL, 0);
+    char xulPath[_MAX_PATH];
+    _snprintf(xulPath, sizeof(xulPath), "%s\\xul.dll", xpcomPath);
+    xulPath[sizeof(xulPath) - 1] = '\0';
+
+    HINSTANCE xulModule = LoadLibraryEx(xulPath, NULL, 0);
     if (!xulModule)
         return 4;
 
+    char temp[_MAX_PATH];
+    GetModuleFileName(xulModule, temp, sizeof(temp));
+
     XRE_InitEmbedding2 =
         (XRE_InitEmbedding2Type) GetProcAddress(xulModule, "XRE_InitEmbedding2");
     if (!XRE_InitEmbedding2) {
         fprintf(stderr, "Error: %i\n", GetLastError());
         return 5;
     }
 
     XRE_TermEmbedding =
         (XRE_TermEmbeddingType) GetProcAddress(xulModule, "XRE_TermEmbedding");
     if (!XRE_TermEmbedding) {
         fprintf(stderr, "Error: %i\n", GetLastError());
         return 5;
     }
 
     // Scope all the XPCOM stuff
     {
-        strcpy(lastslash, "\\xulrunner");
-
         nsCOMPtr<nsILocalFile> xuldir;
-        rv = NS_NewNativeLocalFile(nsCString(path), PR_FALSE,
+        rv = NS_NewNativeLocalFile(nsCString(xpcomPath), PR_FALSE,
                                    getter_AddRefs(xuldir));
         if (NS_FAILED(rv))
             return 6;
 
+        char self[_MAX_PATH];
+        GetModuleFileName(ghInstanceApp, self, sizeof(self));
+        lastslash = ns_strrpbrk(xpcomPath, "/\\");
+        if (!lastslash)
+            return 7;
+
         *lastslash = '\0';
 
         nsCOMPtr<nsILocalFile> appdir;
-        rv = NS_NewNativeLocalFile(nsCString(path), PR_FALSE,
+        rv = NS_NewNativeLocalFile(nsCString(self), PR_FALSE,
                                    getter_AddRefs(appdir));
         if (NS_FAILED(rv))
             return 8;
 
         rv = XRE_InitEmbedding2(xuldir, appdir, nsnull);
         if (NS_FAILED(rv))
             return 9;
 
--- a/xpcom/build/dlldeps.cpp
+++ b/xpcom/build/dlldeps.cpp
@@ -108,16 +108,17 @@
 
 using namespace mozilla;
 
 class nsCStringContainer : private nsStringContainer_base { };
 class nsStringContainer : private nsStringContainer_base { };
 
 void XXXNeverCalled()
 {
+    GRE_GetGREPathWithProperties(nsnull, 0, nsnull, 0, nsnull, 0);
     nsTextFormatter::snprintf(nsnull,0,nsnull);
     nsTextFormatter::smprintf(nsnull, nsnull);
     nsTextFormatter::smprintf_free(nsnull);
     nsVoidArray();
     nsSmallVoidArray();
     {
       nsTArray<PRBool> array1(1), array2(1);
       PRBool a, b, c;
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsGREGlue.cpp
@@ -0,0 +1,760 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Sean Su <ssu@netscape.com>
+ *   Benjamin Smedberg <benjamin@smedbergs.us>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsXPCOMGlue.h"
+
+#include "nsAutoPtr.h"
+#include "nsINIParser.h"
+#include "nsVersionComparator.h"
+#include "nsXPCOMPrivate.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#ifdef XP_WIN32
+# include <windows.h>
+# include <mbstring.h>
+# include <io.h>
+# define snprintf _snprintf
+# define R_OK 04
+#elif defined(XP_OS2)
+# define INCL_DOS
+# include <os2.h>
+#elif defined(XP_MACOSX)
+# include <CoreFoundation/CoreFoundation.h>
+# include <unistd.h>
+# include <dirent.h>
+#elif defined(XP_UNIX)
+# include <unistd.h>
+# include <sys/param.h>
+# include <dirent.h>
+#endif
+
+#include <sys/stat.h>
+
+/**
+ * Like strncat, appends a buffer to another buffer. This is where the
+ * similarity ends. Firstly, the "count" here is the total size of the buffer
+ * (not the number of chars to append. Secondly, the function returns PR_FALSE
+ * if the buffer is not long enough to hold the concatenated string.
+ */
+static PRBool safe_strncat(char *dest, const char *append, PRUint32 count)
+{
+  char *end = dest + count - 1;
+
+  // skip to the end of dest
+  while (*dest)
+    ++dest;
+
+  while (*append && dest < end) {
+    *dest = *append;
+    ++dest, ++append;
+  }
+
+  *dest = '\0';
+
+  return *append == '\0';
+}
+
+#ifdef XP_WIN
+static PRBool
+CheckVersion(const PRUnichar* toCheck,
+             const GREVersionRange *versions,
+             PRUint32 versionsLength);
+#endif
+static PRBool
+CheckVersion(const char* toCheck,
+             const GREVersionRange *versions,
+             PRUint32 versionsLength);
+
+
+#if defined(XP_MACOSX)
+
+static PRBool
+GRE_FindGREFramework(const char* rootPath,
+                     const GREVersionRange *versions,
+                     PRUint32 versionsLength,
+                     const GREProperty *properties,
+                     PRUint32 propertiesLength,
+                     char* buffer, PRUint32 buflen);
+
+#elif defined(XP_UNIX)
+
+static PRBool
+GRE_GetPathFromConfigDir(const char* dirname,
+                         const GREVersionRange *versions,
+                         PRUint32 versionsLength,
+                         const GREProperty *properties,
+                         PRUint32 propertiesLength,
+                         char* buffer, PRUint32 buflen);
+
+static PRBool
+GRE_GetPathFromConfigFile(const char* filename,
+                          const GREVersionRange *versions,
+                          PRUint32 versionsLength,
+                          const GREProperty *properties,
+                          PRUint32 propertiesLength,
+                          char* buffer, PRUint32 buflen);
+
+#elif defined(XP_WIN)
+
+static PRBool
+GRE_GetPathFromRegKey(HKEY aRegKey,
+                      const GREVersionRange *versions,
+                      PRUint32 versionsLength,
+                      const GREProperty *properties,
+                      PRUint32 propertiesLength,
+                      char* buffer, PRUint32 buflen);
+
+#endif
+
+nsresult
+GRE_GetGREPathWithProperties(const GREVersionRange *versions,
+                             PRUint32 versionsLength,
+                             const GREProperty *properties,
+                             PRUint32 propertiesLength,
+                             char *aBuffer, PRUint32 aBufLen)
+{
+#ifdef TARGET_XPCOM_ABI
+  // append the ABI to the properties to match only binary 
+  // compatible GREs
+  static const GREProperty kExtraProperty =
+    { "abi", TARGET_XPCOM_ABI };
+
+  nsAutoArrayPtr<GREProperty> allProperties(new GREProperty[propertiesLength + 1]);
+  if (!allProperties)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  for (PRUint32 i=0; i<propertiesLength; i++) {
+    allProperties[i].property = properties[i].property;
+    allProperties[i].value    = properties[i].value;
+  }
+  allProperties[propertiesLength].property = kExtraProperty.property;
+  allProperties[propertiesLength].value    = kExtraProperty.value;
+  PRUint32 allPropertiesLength = propertiesLength + 1;
+#else
+  const GREProperty *allProperties = properties;
+  PRUint32 allPropertiesLength = propertiesLength;
+#endif
+
+  // if GRE_HOME is in the environment, use that GRE
+  const char* env = getenv("GRE_HOME");
+  if (env && *env) {
+    char p[MAXPATHLEN];
+    snprintf(p, sizeof(p), "%s" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL, env);
+    p[sizeof(p) - 1] = '\0';
+
+#if XP_UNIX
+    if (realpath(p, aBuffer))
+      return NS_OK;
+#elif WINCE
+    if (p[0] != '\\') 
+    {
+      WCHAR dir[MAX_PATH];
+      WCHAR path[MAX_PATH];
+      MultiByteToWideChar(CP_ACP, 0, p, -1, path, MAX_PATH);
+      _wfullpath(dir,path,MAX_PATH);
+      WideCharToMultiByte(CP_ACP, 0, dir, -1, aBuffer, MAX_PATH, NULL, NULL);
+    }
+    else {
+      strcpy(aBuffer, p);
+    }
+    return NS_OK;
+#elif XP_WIN
+    if (_fullpath(aBuffer, p, aBufLen))
+      return NS_OK;
+#elif XP_OS2
+    // realpath on OS/2 returns a unix-ized path, so re-native-ize
+    if (realpath(p, aBuffer)) {
+      for (char* ptr = strchr(aBuffer, '/'); ptr; ptr = strchr(ptr, '/'))
+        *ptr = '\\';
+      return NS_OK;
+    }
+#else
+    // hope for the best
+    // xxxbsmedberg: other platforms should have a "make absolute" function
+#endif
+
+    if (strlen(p) >= aBufLen)
+      return NS_ERROR_FILE_NAME_TOO_LONG;
+
+    strcpy(aBuffer, p);
+
+    return NS_OK;
+  }
+
+  // the Gecko bits that sit next to the application or in the LD_LIBRARY_PATH
+  env = getenv("USE_LOCAL_GRE");
+  if (env && *env) {
+    *aBuffer = nsnull;
+    return NS_OK;
+  }
+
+#ifdef XP_MACOSX
+  aBuffer[0] = '\0';
+
+  // Check the bundle first, for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
+  CFBundleRef appBundle = CFBundleGetMainBundle();
+  if (appBundle) {
+    CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle);
+    CFURLRef absfwurl = nsnull;
+    if (fwurl) {
+      absfwurl = CFURLCopyAbsoluteURL(fwurl);
+      CFRelease(fwurl);
+    }
+
+    if (absfwurl) {
+      CFURLRef xulurl =
+        CFURLCreateCopyAppendingPathComponent(NULL, absfwurl,
+                                              CFSTR(GRE_FRAMEWORK_NAME),
+                                              PR_TRUE);
+
+      if (xulurl) {
+        CFURLRef xpcomurl =
+          CFURLCreateCopyAppendingPathComponent(NULL, xulurl,
+                                                CFSTR("libxpcom.dylib"),
+                                                PR_FALSE);
+
+        if (xpcomurl) {
+          char tbuffer[MAXPATHLEN];
+
+          if (CFURLGetFileSystemRepresentation(xpcomurl, PR_TRUE,
+                                               (UInt8*) tbuffer,
+                                               sizeof(tbuffer)) &&
+              access(tbuffer, R_OK | X_OK) == 0) {
+            if (!realpath(tbuffer, aBuffer)) {
+              aBuffer[0] = '\0';
+            }
+          }
+
+          CFRelease(xpcomurl);
+        }
+
+        CFRelease(xulurl);
+      }
+
+      CFRelease(absfwurl);
+    }
+  }
+
+  if (aBuffer[0])
+    return NS_OK;
+
+  // Check ~/Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
+  const char *home = getenv("HOME");
+  if (home && *home && GRE_FindGREFramework(home,
+                                            versions, versionsLength,
+                                            allProperties, allPropertiesLength,
+                                            aBuffer, aBufLen)) {
+    return NS_OK;
+  }
+
+  // Check /Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
+  if (GRE_FindGREFramework("",
+                           versions, versionsLength,
+                           allProperties, allPropertiesLength,
+                           aBuffer, aBufLen)) {
+    return NS_OK;
+  }
+
+#elif defined(XP_UNIX) 
+  env = getenv("MOZ_GRE_CONF");
+  if (env && GRE_GetPathFromConfigFile(env,
+                                       versions, versionsLength,
+                                       allProperties, allPropertiesLength,
+                                       aBuffer, aBufLen)) {
+    return NS_OK;
+  }
+
+  env = getenv("HOME");
+  if (env && *env) {
+    char buffer[MAXPATHLEN];
+
+    // Look in ~/.gre.config
+
+    snprintf(buffer, sizeof(buffer),
+             "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME, env);
+    
+    if (GRE_GetPathFromConfigFile(buffer,
+                                  versions, versionsLength,
+                                  allProperties, allPropertiesLength,
+                                  aBuffer, aBufLen)) {
+      return NS_OK;
+    }
+
+    // Look in ~/.gre.d/*.conf
+
+    snprintf(buffer, sizeof(buffer),
+             "%s" XPCOM_FILE_PATH_SEPARATOR GRE_USER_CONF_DIR, env);
+
+    if (GRE_GetPathFromConfigDir(buffer,
+                                 versions, versionsLength,
+                                 allProperties, allPropertiesLength,
+                                 aBuffer, aBufLen)) {
+      return NS_OK;
+    }
+  }
+
+  // Look for a global /etc/gre.conf file
+  if (GRE_GetPathFromConfigFile(GRE_CONF_PATH,
+                                versions, versionsLength,
+                                allProperties, allPropertiesLength,
+                                aBuffer, aBufLen)) {
+    return NS_OK;
+  }
+
+  // Look for a group of config files in /etc/gre.d/
+  if (GRE_GetPathFromConfigDir(GRE_CONF_DIR,
+                               versions, versionsLength,
+                               allProperties, allPropertiesLength,
+                               aBuffer, aBufLen)) {
+    return NS_OK;
+  }
+
+#elif defined(XP_WIN)
+  HKEY hRegKey = NULL;
+    
+  // A couple of key points here:
+  // 1. Note the usage of the "Software\\mozilla.org\\GRE" subkey - this allows
+  //    us to have multiple versions of GREs on the same machine by having
+  //    subkeys such as 1.0, 1.1, 2.0 etc. under it.
+  // 2. In this sample below we're looking for the location of GRE version 1.2
+  //    i.e. we're compatible with GRE 1.2 and we're trying to find it's install
+  //    location.
+  //
+  // Please see http://www.mozilla.org/projects/embedding/GRE.html for
+  // more info.
+  //
+  if (::RegOpenKeyExW(HKEY_CURRENT_USER, GRE_WIN_REG_LOC, 0,
+                      KEY_READ, &hRegKey) == ERROR_SUCCESS) {
+      PRBool ok = GRE_GetPathFromRegKey(hRegKey,
+                                        versions, versionsLength,
+                                        allProperties, allPropertiesLength,
+                                        aBuffer, aBufLen);
+      ::RegCloseKey(hRegKey);
+
+      if (ok)
+          return NS_OK;
+  }
+
+  if (::RegOpenKeyExW(HKEY_LOCAL_MACHINE, GRE_WIN_REG_LOC, 0,
+                      KEY_ENUMERATE_SUB_KEYS, &hRegKey) == ERROR_SUCCESS) {
+      PRBool ok = GRE_GetPathFromRegKey(hRegKey,
+                                        versions, versionsLength,
+                                        allProperties, allPropertiesLength,
+                                        aBuffer, aBufLen);
+      ::RegCloseKey(hRegKey);
+
+      if (ok)
+          return NS_OK;
+  }
+#endif
+
+  return NS_ERROR_FAILURE;
+}
+
+static PRBool
+CheckVersion(const char* toCheck,
+             const GREVersionRange *versions,
+             PRUint32 versionsLength)
+{
+  
+  for (const GREVersionRange *versionsEnd = versions + versionsLength;
+       versions < versionsEnd;
+       ++versions) {
+    PRInt32 c = NS_CompareVersions(toCheck, versions->lower);
+    if (c < 0)
+      continue;
+
+    if (!c && !versions->lowerInclusive)
+      continue;
+
+    c = NS_CompareVersions(toCheck, versions->upper);
+    if (c > 0)
+      continue;
+
+    if (!c && !versions->upperInclusive)
+      continue;
+
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+#ifdef XP_WIN
+
+// Allocate an array of characters using new[], converting from UTF8 to UTF-16.
+// @note Use nsAutoArrayPtr for this result.
+
+static PRUnichar*
+ConvertUTF8toNewUTF16(const char *cstr)
+{
+  int len = MultiByteToWideChar(CP_UTF8, 0, cstr, -1, NULL, 0);
+  WCHAR *wstr = new WCHAR[len];
+  MultiByteToWideChar(CP_UTF8, 0, cstr, -1, wstr, len);
+  return wstr;
+}
+
+typedef nsAutoArrayPtr<PRUnichar> AutoWString;
+
+static PRBool
+CheckVersion(const PRUnichar* toCheck,
+             const GREVersionRange *versions,
+             PRUint32 versionsLength)
+{
+  for (const GREVersionRange *versionsEnd = versions + versionsLength;
+       versions < versionsEnd;
+       ++versions) {
+      AutoWString wlower(ConvertUTF8toNewUTF16(versions->lower));
+      PRInt32 c = NS_CompareVersions(toCheck, wlower);
+      if (c < 0)
+        continue;
+
+      if (!c && !versions->lowerInclusive)
+        continue;
+
+      AutoWString wupper(ConvertUTF8toNewUTF16(versions->upper));
+      c = NS_CompareVersions(toCheck, wupper);
+      if (c > 0)
+        continue;
+
+      if (!c && !versions->upperInclusive)
+        continue;
+
+      return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+#endif
+
+
+#ifdef XP_MACOSX
+PRBool
+GRE_FindGREFramework(const char* rootPath,
+                     const GREVersionRange *versions,
+                     PRUint32 versionsLength,
+                     const GREProperty *properties,
+                     PRUint32 propertiesLength,
+                     char* buffer, PRUint32 buflen)
+{
+  PRBool found = PR_FALSE;
+
+  snprintf(buffer, buflen,
+           "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME "/Versions", rootPath);
+  DIR *dir = opendir(buffer);
+  if (dir) {
+    struct dirent *entry;
+    while (!found && (entry = readdir(dir))) {
+      if (CheckVersion(entry->d_name, versions, versionsLength)) {
+        snprintf(buffer, buflen,
+                 "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
+                 "/Versions/%s/" XPCOM_DLL, rootPath, entry->d_name);
+        if (access(buffer, R_OK | X_OK) == 0)
+          found = PR_TRUE;
+      }
+    }
+
+    closedir(dir);
+  }
+  
+  if (found)
+    return PR_TRUE;
+
+  buffer[0] = '\0';
+  return PR_FALSE;
+}
+    
+#elif defined(XP_UNIX)
+
+static PRBool IsConfFile(const char *filename)
+{
+  const char *dot = strrchr(filename, '.');
+
+  return (dot && strcmp(dot, ".conf") == 0);
+}
+
+PRBool
+GRE_GetPathFromConfigDir(const char* dirname,
+                         const GREVersionRange *versions,
+                         PRUint32 versionsLength,
+                         const GREProperty *properties,
+                         PRUint32 propertiesLength,
+                         char* buffer, PRUint32 buflen)
+{
+  // Open the directory provided and try to read any files in that
+  // directory that end with .conf.  We look for an entry that might
+  // point to the GRE that we're interested in.
+  DIR *dir = opendir(dirname);
+  if (!dir)
+    return nsnull;
+
+  PRBool found = PR_FALSE;
+  struct dirent *entry;
+
+  while (!found && (entry = readdir(dir))) {
+
+    // Only look for files that end in .conf
+    // IsConfFile will skip "." and ".."
+    if (!IsConfFile(entry->d_name))
+      continue;
+
+    char fullPath[MAXPATHLEN];
+    snprintf(fullPath, sizeof(fullPath), "%s" XPCOM_FILE_PATH_SEPARATOR "%s",
+             dirname, entry->d_name);
+
+    found = GRE_GetPathFromConfigFile(fullPath,
+                                      versions, versionsLength,
+                                      properties, propertiesLength,
+                                      buffer, buflen);
+  }
+
+  closedir(dir);
+
+  return found;
+}
+
+#define READ_BUFFER_SIZE 1024
+
+struct INIClosure
+{
+  nsINIParser           *parser;
+  const GREVersionRange *versions;
+  PRUint32               versionsLength;
+  const GREProperty     *properties;
+  PRUint32               propertiesLength;
+  char                  *pathBuffer;
+  PRUint32               buflen;
+  PRBool                 found;
+};
+
+static PRBool
+CheckINIHeader(const char *aHeader, void *aClosure)
+{
+  nsresult rv;
+
+  INIClosure *c = reinterpret_cast<INIClosure *>(aClosure);
+
+  if (!CheckVersion(aHeader, c->versions, c->versionsLength))
+    return PR_TRUE;
+
+  const GREProperty *properties = c->properties;
+  const GREProperty *endProperties = properties + c->propertiesLength;
+  for (; properties < endProperties; ++properties) {
+    char buffer[MAXPATHLEN];
+    rv = c->parser->GetString(aHeader, properties->property,
+                             buffer, sizeof(buffer));
+    if (NS_FAILED(rv))
+      return PR_TRUE;
+
+    if (strcmp(buffer, properties->value))
+      return PR_TRUE;
+  }
+
+  rv = c->parser->GetString(aHeader, "GRE_PATH", c->pathBuffer, c->buflen);
+  if (NS_FAILED(rv))
+    return PR_TRUE;
+
+  if (!safe_strncat(c->pathBuffer, "/" XPCOM_DLL, c->buflen) ||
+      access(c->pathBuffer, R_OK))
+    return PR_TRUE;
+
+  // We found a good GRE! Stop looking.
+  c->found = PR_TRUE;
+  return PR_FALSE;
+}
+
+PRBool
+GRE_GetPathFromConfigFile(const char* filename,
+                          const GREVersionRange *versions,
+                          PRUint32 versionsLength,
+                          const GREProperty *properties,
+                          PRUint32 propertiesLength,
+                          char* pathBuffer, PRUint32 buflen)
+{
+  nsINIParser parser;
+  nsresult rv = parser.Init(filename);
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  INIClosure c = {
+    &parser,
+    versions, versionsLength,
+    properties, propertiesLength,
+    pathBuffer, buflen,
+    PR_FALSE
+  };
+
+  parser.GetSections(CheckINIHeader, &c);
+  return c.found;
+}
+
+#elif defined(XP_WIN)
+
+static PRBool
+CopyWithEnvExpansion(PRUnichar* aDest, const PRUnichar* aSource, PRUint32 aBufLen,
+                     DWORD aType)
+{
+  switch (aType) {
+  case REG_SZ:
+    if (wcslen(aSource) >= aBufLen)
+      return PR_FALSE;
+
+    wcscpy(aDest, aSource);
+    return PR_TRUE;
+
+  case REG_EXPAND_SZ:
+    if (ExpandEnvironmentStringsW(aSource, aDest, aBufLen) > aBufLen)
+      return PR_FALSE;
+
+    return PR_TRUE;
+  };
+
+  // Whoops! We expected REG_SZ or REG_EXPAND_SZ, what happened here?
+
+  return PR_FALSE;
+}
+
+PRBool
+GRE_GetPathFromRegKey(HKEY aRegKey,
+                      const GREVersionRange *versions,
+                      PRUint32 versionsLength,
+                      const GREProperty *properties,
+                      PRUint32 propertiesLength,
+                      char* aBuffer, PRUint32 aBufLen)
+{
+  // Formerly, GREs were registered at the registry key
+  // HKLM/Software/mozilla.org/GRE/<version> valuepair GreHome=Path.
+  // Nowadays, they are registered in any subkey of
+  // Software/mozilla.org/GRE, with the following valuepairs:
+  //   Version=<version> (REG_SZ)
+  //   GreHome=<path>    (REG_SZ or REG_EXPAND_SZ)
+  //   <Property>=<value> (REG_SZ)
+  //
+  // Additional meta-info may be available in the future, including
+  // localization info and other information which might be pertinent
+  // to selecting one GRE over another.
+  //
+  // When a GRE is being registered, it should try to register itself at
+  // HKLM/Software/mozilla.org/GRE/<Version> first, to preserve compatibility
+  // with older glue. If this key is already taken (i.e. there is more than
+  // one GRE of that version installed), it should append a unique number to
+  // the version, for example:
+  //   1.1 (already in use), 1.1_1, 1.1_2, etc...
+
+  DWORD i = 0;
+  PRUnichar buffer[MAXPATHLEN + 1];
+
+  while (PR_TRUE) {
+    PRUnichar name[MAXPATHLEN + 1];
+    DWORD nameLen = MAXPATHLEN;
+    if (::RegEnumKeyExW(aRegKey, i, name, &nameLen, NULL, NULL, NULL, NULL) !=
+          ERROR_SUCCESS) {
+        break;
+    }
+
+    HKEY subKey = NULL;
+    if (::RegOpenKeyExW(aRegKey, name, 0, KEY_QUERY_VALUE, &subKey) !=
+          ERROR_SUCCESS) {
+        continue;
+    }
+
+    PRUnichar version[40];
+    DWORD versionlen = 40;
+    PRUnichar pathbuf[MAXPATHLEN + 1];
+    DWORD pathlen;
+    DWORD pathtype;
+
+    PRBool ok = PR_FALSE;
+
+    if (::RegQueryValueExW(subKey, L"Version", NULL, NULL,
+                           (BYTE*) version, &versionlen) == ERROR_SUCCESS &&
+          CheckVersion(version, versions, versionsLength)) {
+
+      ok = PR_TRUE;
+      const GREProperty *props = properties;
+      const GREProperty *propsEnd = properties + propertiesLength;
+      for (; ok && props < propsEnd; ++props) {
+        pathlen = MAXPATHLEN + 1;
+
+        AutoWString wproperty(ConvertUTF8toNewUTF16(props->property));
+        AutoWString wvalue(ConvertUTF8toNewUTF16(props->value));
+        if (::RegQueryValueExW(subKey, wproperty, NULL, &pathtype,
+                               (BYTE*) pathbuf, &pathlen) != ERROR_SUCCESS ||
+            wcscmp(pathbuf,  wvalue))
+            ok = PR_FALSE;
+      }
+
+      pathlen = sizeof(pathbuf);
+      if (ok &&
+          (!::RegQueryValueExW(subKey, L"GreHome", NULL, &pathtype,
+                              (BYTE*) pathbuf, &pathlen) == ERROR_SUCCESS ||
+           !*pathbuf ||
+           !CopyWithEnvExpansion(buffer, pathbuf, MAXPATHLEN, pathtype))) {
+        ok = PR_FALSE;
+      }
+      else if (!wcsncat(buffer, L"\\" LXPCOM_DLL, aBufLen) 
+#ifdef WINCE
+               || (GetFileAttributesW(buffer) == INVALID_FILE_ATTRIBUTES)
+#else
+               || _waccess(buffer, R_OK)
+#endif
+               ) {
+        ok = PR_FALSE;
+      }
+    }
+
+    RegCloseKey(subKey);
+
+    if (ok) {
+      WideCharToMultiByte(CP_UTF8, 0, buffer, -1, aBuffer, aBufLen, NULL, NULL);
+      return PR_TRUE;
+    }
+
+    ++i;
+  }
+
+  aBuffer[0] = '\0';
+
+  return PR_FALSE;
+}
+#endif // XP_WIN
--- a/xpcom/glue/objs.mk
+++ b/xpcom/glue/objs.mk
@@ -51,16 +51,17 @@ XPCOM_GLUE_SRC_LCPPSRCS =        \
   nsComponentManagerUtils.cpp    \
   nsEnumeratorUtils.cpp          \
   nsID.cpp                       \
   nsIInterfaceRequestorUtils.cpp \
   nsINIParser.cpp                \
   nsISupportsImpl.cpp            \
   nsMemory.cpp                   \
   nsWeakReference.cpp            \
+  nsGREGlue.cpp                  \
   nsVersionComparator.cpp        \
   nsTHashtable.cpp               \
   nsQuickSort.cpp                \
   nsVoidArray.cpp                \
   nsTArray.cpp                   \
   nsThreadUtils.cpp              \
   nsTObserverArray.cpp           \
   nsCycleCollectionParticipant.cpp \
--- a/xpcom/glue/standalone/nsXPCOMGlue.h
+++ b/xpcom/glue/standalone/nsXPCOMGlue.h
@@ -36,16 +36,64 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsXPCOMGlue_h__
 #define nsXPCOMGlue_h__
 
 #include "nscore.h"
 
+class nsILocalFile;
+
+/**
+ * The following function is available in both the standalone and
+ * dynamically linked versions of the glue.
+ */
+
+struct GREVersionRange {
+    const char *lower;
+    PRBool      lowerInclusive;
+    const char *upper;
+    PRBool      upperInclusive;
+};
+
+struct GREProperty {
+    const char *property;
+    const char *value;
+};
+
+/**
+ * Locate the path of the xpcom shared library from a GRE with specified
+ * properties.
+ * 
+ * @param versions         An array of version ranges: if any version range
+ *                         matches, the GRE is considered acceptable.
+ * @param versionsLength   The length of the versions array.
+ * @param properties       A null-terminated list of GRE property/value pairs
+ *                         which must all be satisfied.
+ * @param propertiesLength Length of the properties array.
+ * @param buffer           A buffer to be filled with the appropriate path. If
+ *                         the "local" GRE is specified (via the USE_LOCAL_GRE
+ *                         environment variable, for example), this buffer
+ *                         will be set to the empty string.
+ * @param buflen           The length of buffer. This must be at least
+ *                         PATH_MAX/MAXPATHLEN.
+ * @throws NS_ERROR_FAILURE if an appropriate GRE could not be found.
+ * @note The properties parameter is ignored on macintosh, because of the
+ *       manner in which the XUL frameworks are installed by version.
+ * @note Currently this uses a "first-fit" algorithm, it does not select
+ *       the newest available GRE.
+ */
+extern "C" NS_COM_GLUE nsresult
+GRE_GetGREPathWithProperties(const GREVersionRange *versions,
+                             PRUint32 versionsLength,
+                             const GREProperty *properties,
+                             PRUint32 propertiesLength,
+                             char *buffer, PRUint32 buflen);
+
 #ifdef XPCOM_GLUE
 
 /**
  * The following functions are only available in the standalone glue.
  */
 
 /**
  * Initialize the XPCOM glue by dynamically linking against the XPCOM
--- a/xulrunner/app/Makefile.in
+++ b/xulrunner/app/Makefile.in
@@ -68,16 +68,30 @@ endif
 
 ifdef TARGET_XPCOM_ABI
 DEFINES += -DTARGET_XPCOM_ABI=\"$(TARGET_XPCOM_ABI)\"
 endif
 
 
 CPPSRCS = nsXULRunnerApp.cpp
 
+ifneq (,$(filter WINNT WINCE,$(OS_ARCH)))
+CPPSRCS += nsRegisterGREWin.cpp
+else
+ifeq (OS2,$(OS_ARCH))
+CPPSRCS += nsRegisterGREVoid.cpp
+else
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+CPPSRCS += nsRegisterGREVoid.cpp
+else
+CPPSRCS += nsRegisterGREUnix.cpp
+endif
+endif
+endif
+
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/toolkit/xre \
   -I$(topsrcdir)/toolkit/profile \
   -I$(topsrcdir)/xpcom/base \
   $(NULL)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 TK_LIBS := -framework Cocoa $(TK_LIBS)
new file mode 100644
--- /dev/null
+++ b/xulrunner/app/nsRegisterGRE.h
@@ -0,0 +1,58 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla XULRunner.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsRegisterGRE_h__
+#define nsRegisterGRE_h__
+
+#include "nscore.h"
+#include "nsStringAPI.h"
+class nsIFile;
+struct GREProperty;
+
+/**
+ * @return PR_TRUE on success
+ */
+NS_HIDDEN_(PRBool)
+RegisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation,
+                  const GREProperty *aProperties, PRUint32 aPropertiesLen,
+                  const char *aGREMilestone);
+
+NS_HIDDEN_(void)
+UnregisterXULRunner(PRBool aUnregisterGlobally, nsIFile* aLocation,
+                    const char *aGREMilestone);
+
+#endif // nsRegisterGRE_h__
new file mode 100644
--- /dev/null
+++ b/xulrunner/app/nsRegisterGREUnix.cpp
@@ -0,0 +1,272 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla XULRunner.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsRegisterGRE.h"
+#include "nsXPCOMGlue.h"
+
+#include "nsXPCOM.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
+#include "mozilla/FileUtils.h"
+
+#include "nsAppRunner.h" // for MAXPATHLEN
+#include "nsStringAPI.h"
+#include "nsINIParser.h"
+#include "nsCOMPtr.h"
+
+#include "prio.h"
+#include "prprf.h"
+#include "prenv.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+// If we can't register <buildid>.conf, we try to create a unique filename
+// by looping through <buildid>_<int>.conf, but if something is seriously wrong
+// we stop at 1000
+#define UNIQ_LOOP_LIMIT 1000
+
+using namespace mozilla;
+
+static const char kRegFileGlobal[] = "global.reginfo";
+static const char kRegFileUser[] = "user.reginfo";
+
+static PRBool
+MakeConfFile(const char *regfile, const nsCString &greHome,
+             const GREProperty *aProperties, PRUint32 aPropertiesLen,
+             const char *aGREMilestone)
+{
+  // If the file exists, don't create it again!
+  if (access(regfile, R_OK) == 0)
+    return PR_FALSE;
+
+  PRBool ok = PR_TRUE;
+
+  { // scope "fd" so that we can delete the file if something goes wrong
+    AutoFDClose fd = PR_Open(regfile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+                             0664);
+    if (!fd)
+      return PR_FALSE;
+
+    static const char kHeader[] =
+      "# Registration file generated by xulrunner. Do not edit.\n\n"
+      "[%s]\n"
+      "GRE_PATH=%s\n";
+
+    if (PR_fprintf(fd, kHeader, aGREMilestone, greHome.get()) <= 0)
+      ok = PR_FALSE;
+
+    for (PRUint32 i = 0; i < aPropertiesLen; ++i) {
+      if (PR_fprintf(fd, "%s=%s\n",
+                     aProperties[i].property, aProperties[i].value) <= 0)
+        ok = PR_FALSE;
+    }
+  }
+
+  if (!ok)
+    PR_Delete(regfile);
+
+  return ok;
+}
+
+
+PRBool
+RegisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation,
+                  const GREProperty *aProperties, PRUint32 aPropertiesLen,
+                  const char *aGREMilestone)
+{
+  // Register ourself in /etc/gre.d or ~/.gre.d/ and record what key we created
+  // for future unregistration.
+
+  nsresult rv;
+
+  char root[MAXPATHLEN] = "/etc/gre.d";
+
+  if (!aRegisterGlobally) {
+    char *home = PR_GetEnv("HOME");
+    if (!home || !*home)
+      return PR_FALSE;
+
+    PR_snprintf(root, MAXPATHLEN, "%s/.gre.d", home);
+  }
+
+  nsCString greHome;
+  rv = aLocation->GetNativePath(greHome);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCOMPtr<nsIFile> savedInfoFile;
+  aLocation->Clone(getter_AddRefs(savedInfoFile));
+  nsCOMPtr<nsILocalFile> localSaved(do_QueryInterface(savedInfoFile));
+  if (!localSaved)
+    return PR_FALSE;
+
+  const char *infoname = aRegisterGlobally ? kRegFileGlobal : kRegFileUser;
+  localSaved->AppendNative(nsDependentCString(infoname));
+
+  AutoFDClose fd;
+  rv = localSaved->OpenNSPRFileDesc(PR_CREATE_FILE | PR_RDWR, 0664, &fd);
+  // XXX report error?
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  char keyName[MAXPATHLEN];
+
+  PRInt32 r = PR_Read(fd, keyName, MAXPATHLEN);
+  if (r < 0)
+    return PR_FALSE;
+
+  char regfile[MAXPATHLEN];
+
+  if (r > 0) {
+    keyName[r] = '\0';
+
+    PR_snprintf(regfile, MAXPATHLEN, "%s/%s.conf", root, keyName);
+
+    // There was already a .reginfo file, let's see if we are already
+    // registered.
+    if (access(regfile, R_OK) == 0) {
+      fprintf(stderr, "Warning: Configuration file '%s' already exists.\n"
+                      "No action was performed.\n", regfile);
+      return PR_FALSE;
+    }
+
+    rv = localSaved->OpenNSPRFileDesc(PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, 0664, &fd);
+    if (NS_FAILED(rv))
+      return PR_FALSE;
+  }
+
+  if (access(root, R_OK | X_OK) &&
+      mkdir(root, 0775)) {
+    fprintf(stderr, "Error: could not create '%s'.\n",
+            root);
+    return PR_FALSE;
+  }
+
+  PR_snprintf(regfile, MAXPATHLEN, "%s/%s.conf", root, aGREMilestone);
+  if (MakeConfFile(regfile, greHome, aProperties, aPropertiesLen,
+                   aGREMilestone)) {
+    PR_fprintf(fd, "%s", aGREMilestone);
+    return PR_TRUE;
+  }
+
+  for (int i = 0; i < UNIQ_LOOP_LIMIT; ++i) {
+    static char buildID[30];
+    sprintf(buildID, "%s_%i", aGREMilestone, i);
+
+    PR_snprintf(regfile, MAXPATHLEN, "%s/%s.conf", root, buildID);
+
+    if (MakeConfFile(regfile, greHome, aProperties, aPropertiesLen,
+                     aGREMilestone)) {
+      PR_Write(fd, buildID, strlen(buildID));
+      return PR_TRUE;
+    }
+  }
+
+  return PR_FALSE;
+}
+
+void
+UnregisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation,
+                    const char *aGREMilestone)
+{
+  nsresult rv;
+
+  char root[MAXPATHLEN] = "/etc/gre.d";
+
+  if (!aRegisterGlobally) {
+    char *home = PR_GetEnv("HOME");
+    if (!home || !*home)
+      return;
+
+    PR_snprintf(root, MAXPATHLEN, "%s/.gre.d", home);
+  }
+
+  nsCOMPtr<nsIFile> savedInfoFile;
+  aLocation->Clone(getter_AddRefs(savedInfoFile));
+  nsCOMPtr<nsILocalFile> localSaved (do_QueryInterface(savedInfoFile));
+  if (!localSaved)
+    return;
+
+  const char *infoname = aRegisterGlobally ? kRegFileGlobal : kRegFileUser;
+  localSaved->AppendNative(nsDependentCString(infoname));
+
+  PRFileDesc* fd = nsnull;
+  rv = localSaved->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+  if (NS_FAILED(rv)) {
+    // XXX report error?
+    return;
+  }
+
+  char keyName[MAXPATHLEN];
+  PRInt32 r = PR_Read(fd, keyName, MAXPATHLEN);
+  PR_Close(fd);
+
+  localSaved->Remove(PR_FALSE);
+
+  if (r <= 0)
+    return;
+
+  keyName[r] = '\0';
+
+  char regFile[MAXPATHLEN];
+  PR_snprintf(regFile, MAXPATHLEN, "%s/%s.conf", root, keyName);
+
+  nsCOMPtr<nsILocalFile> lf;
+  rv = NS_NewNativeLocalFile(nsDependentCString(regFile), PR_FALSE,
+                             getter_AddRefs(lf));
+  if (NS_FAILED(rv))
+    return;
+
+  nsINIParser p;
+  rv = p.Init(lf);
+  if (NS_FAILED(rv))
+    return;
+
+  rv = p.GetString(aGREMilestone, "GRE_PATH", root, MAXPATHLEN);
+  if (NS_FAILED(rv))
+    return;
+
+  rv = NS_NewNativeLocalFile(nsDependentCString(root), PR_TRUE,
+                             getter_AddRefs(lf));
+  if (NS_FAILED(rv))
+    return;
+
+  PRBool eq;
+  if (NS_SUCCEEDED(aLocation->Equals(lf, &eq)) && eq)
+    PR_Delete(regFile);
+}
new file mode 100644
--- /dev/null
+++ b/xulrunner/app/nsRegisterGREVoid.cpp
@@ -0,0 +1,57 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla XULRunner.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsRegisterGRE.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+RegisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation,
+                  const GREProperty *aProperties, PRUint32 aPropertiesLen,
+                  const char *aGREMilestone)
+{
+  fprintf(stderr, "Registration not implemented on this platform!\n");
+  return 1;
+}
+
+void
+UnregisterXULRunner(PRBool aUnregisterGlobally, nsIFile* aLocation,
+                    const char *aGREMilestone)
+{
+  fprintf(stderr, "Registration not implemented on this platform!\n");
+}
new file mode 100644
--- /dev/null
+++ b/xulrunner/app/nsRegisterGREWin.cpp
@@ -0,0 +1,288 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla XULRunner.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsRegisterGRE.h"
+
+#include "nsXPCOM.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
+
+#include "nsAppRunner.h" // for MAXPATHLEN
+#include "nsStringAPI.h"
+#include "nsXPCOMGlue.h"
+#include "nsCOMPtr.h"
+
+#include "prio.h"
+
+#include <windows.h>
+#include "malloc.h"
+
+static const wchar_t kRegKeyRoot[] = L"Software\\mozilla.org\\GRE";
+static const wchar_t kRegFileGlobal[] = L"global.reginfo";
+static const wchar_t kRegFileUser[] = L"user.reginfo";
+
+static nsresult
+MakeVersionKey(HKEY root, const wchar_t* keyname, const nsString &grehome,
+               const GREProperty *aProperties, PRUint32 aPropertiesLen,
+               const wchar_t *aGREMilestone)
+{
+  HKEY  subkey;
+  DWORD disp;
+  
+  if (::RegCreateKeyExW(root, keyname, NULL, NULL, 0, KEY_WRITE, NULL,
+                       &subkey, &disp) != ERROR_SUCCESS)
+    return NS_ERROR_FAILURE;
+
+  if (disp != REG_CREATED_NEW_KEY) {
+    ::RegCloseKey(subkey);
+    return NS_ERROR_FAILURE;
+  }
+
+  PRBool failed = PR_FALSE;
+  failed |= ::RegSetValueExW(subkey, L"Version", NULL, REG_SZ,
+			     (BYTE*) aGREMilestone,
+			     sizeof(PRUnichar) * (wcslen(aGREMilestone) + 1)) 
+    != ERROR_SUCCESS;
+  failed |= ::RegSetValueExW(subkey, L"GreHome", NULL, REG_SZ,
+			     (BYTE*) grehome.get(),
+			     sizeof(PRUnichar) * (grehome.Length() + 1)) 
+    != ERROR_SUCCESS;
+  
+  for (PRUint32 i = 0; i < aPropertiesLen; ++i) {
+    // Properties should be ascii only 
+    NS_ConvertASCIItoUTF16 prop(aProperties[i].property);
+    NS_ConvertASCIItoUTF16 val(aProperties[i].value);
+    failed |= ::RegSetValueExW(subkey, prop.get(), NULL, REG_SZ, 
+			       (BYTE*) val.get(), 
+			       sizeof(wchar_t)*(val.Length()+1)
+			       ) != ERROR_SUCCESS;
+  }
+
+  ::RegCloseKey(subkey);
+
+  if (failed) {
+    // we created a key but couldn't fill it properly: delete it
+    ::RegDeleteKeyW(root, keyname);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+int
+RegisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation,
+                  const GREProperty *aProperties, PRUint32 aPropertiesLen,
+                  const char *aGREMilestoneAscii)
+{
+  // Register ourself in the windows registry, and record what key we created
+  // for future unregistration.
+
+  nsresult rv;
+  PRBool irv;
+  int i;
+  NS_ConvertASCIItoUTF16 aGREMilestone(aGREMilestoneAscii);
+  nsString greHome;
+  rv = aLocation->GetPath(greHome);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCOMPtr<nsIFile> savedInfoFile;
+  aLocation->Clone(getter_AddRefs(savedInfoFile));
+  nsCOMPtr<nsILocalFile> localSaved(do_QueryInterface(savedInfoFile));
+  if (!localSaved)
+    return PR_FALSE;
+
+  const wchar_t *infoname = aRegisterGlobally ? kRegFileGlobal : kRegFileUser;
+  localSaved->Append(nsDependentString(infoname));
+
+  PRFileDesc* fd = nsnull;
+  rv = localSaved->OpenNSPRFileDesc(PR_CREATE_FILE | PR_RDWR, 0664, &fd);
+  if (NS_FAILED(rv)) {
+    // XXX report error?
+    return PR_FALSE;
+  }
+
+  HKEY rootKey = NULL;
+  wchar_t keyName[MAXPATHLEN];
+  PRInt32 r;
+
+  if (::RegCreateKeyExW(aRegisterGlobally ? HKEY_LOCAL_MACHINE :
+			HKEY_CURRENT_USER,
+			kRegKeyRoot, NULL, NULL, 0, KEY_WRITE,
+			NULL, &rootKey, NULL) != ERROR_SUCCESS) {
+    irv = PR_FALSE;
+    goto reg_end;
+  }
+
+  r = PR_Read(fd, keyName, MAXPATHLEN);
+  if (r < 0) {
+    irv = PR_FALSE;
+    goto reg_end;
+  }
+
+  if (r > 0) {
+    keyName[r] = '\0';
+
+    // There was already a .reginfo file, let's see if we are already
+    // registered.
+    HKEY existing = NULL;
+    if (::RegOpenKeyExW(rootKey, keyName, NULL, KEY_QUERY_VALUE, &existing) ==
+        ERROR_SUCCESS) {
+      fprintf(stderr, "Warning: Registry key Software\\mozilla.org\\GRE\\%s already exists.\n"
+              "No action was performed.\n",
+              keyName);
+      irv = PR_FALSE;
+      goto reg_end;
+    }
+
+    PR_Close(fd);
+    fd = nsnull;
+    
+    rv = localSaved->OpenNSPRFileDesc(PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, 0664, &fd);
+    if (NS_FAILED(rv)) {
+      // XXX report error?
+      irv = PR_FALSE;
+      goto reg_end;
+    }
+  }
+
+  wcscpy(keyName, aGREMilestone.get());
+  rv = MakeVersionKey(rootKey, keyName, greHome, aProperties, aPropertiesLen,
+                      aGREMilestone.get());
+  if (NS_SUCCEEDED(rv)) {
+    NS_ConvertUTF16toUTF8 keyNameAscii(keyName);
+    PR_Write(fd, keyNameAscii.get(), sizeof(char)*keyNameAscii.Length());
+    irv = PR_TRUE;
+    goto reg_end;
+  }
+  
+  for (i = 0; i < 1000; ++i) {
+    swprintf(keyName, L"%s_%i", aGREMilestone.get(),  i);
+    rv = MakeVersionKey(rootKey, keyName, greHome,
+                        aProperties, aPropertiesLen,
+                        aGREMilestone.get());
+    if (NS_SUCCEEDED(rv)) {
+      NS_ConvertUTF16toUTF8 keyNameAscii(keyName);
+      PR_Write(fd, keyNameAscii.get(), sizeof(char)*keyNameAscii.Length());
+      irv = PR_TRUE;
+      goto reg_end;
+    }
+  }
+
+  irv = PR_FALSE;
+
+reg_end:
+  if (fd)
+    PR_Close(fd);
+
+  if (rootKey)
+    ::RegCloseKey(rootKey);
+
+  return irv;
+}
+
+void
+UnregisterXULRunner(PRBool aGlobal, nsIFile* aLocation,
+                    const char *aGREMilestone)
+{
+  nsCOMPtr<nsIFile> savedInfoFile;
+  aLocation->Clone(getter_AddRefs(savedInfoFile));
+  nsCOMPtr<nsILocalFile> localSaved (do_QueryInterface(savedInfoFile));
+  if (!localSaved)
+    return;
+
+  const wchar_t *infoname = aGlobal ? kRegFileGlobal : kRegFileUser;
+  localSaved->Append(nsDependentString(infoname));
+
+  PRFileDesc* fd = nsnull;
+  nsresult rv = localSaved->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+  if (NS_FAILED(rv)) {
+    // XXX report error?
+    return;
+  }
+
+  wchar_t keyName[MAXPATHLEN];
+  PRInt32 r = PR_Read(fd, keyName, MAXPATHLEN);
+  PR_Close(fd);
+
+  localSaved->Remove(PR_FALSE);
+
+  if (r <= 0)
+    return;
+
+  keyName[r] = '\0';
+
+  HKEY rootKey = NULL;
+  if (::RegOpenKeyExW(aGlobal ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+		      kRegKeyRoot, 0, KEY_READ, &rootKey) != ERROR_SUCCESS)
+    return;
+
+  HKEY subKey = NULL;
+  if (::RegOpenKeyExW(rootKey, keyName, 0, KEY_READ, &subKey) == ERROR_SUCCESS) {
+
+    char regpath[MAXPATHLEN];
+    DWORD reglen = MAXPATHLEN;
+
+    if (::RegQueryValueExW(subKey, L"GreHome", NULL, NULL,
+                          (BYTE*) regpath, &reglen) == ERROR_SUCCESS) {
+
+      nsCOMPtr<nsILocalFile> regpathfile;
+      rv = NS_NewNativeLocalFile(nsDependentCString(regpath), PR_FALSE,
+                                 getter_AddRefs(regpathfile));
+
+      PRBool eq;
+      if (NS_SUCCEEDED(rv) && 
+          NS_SUCCEEDED(aLocation->Equals(regpathfile, &eq)) && !eq) {
+        // We think we registered for this key, but it doesn't point to
+        // us any more!
+        fprintf(stderr, "Warning: Registry key Software\\mozilla.org\\GRE\\%s points to\n"
+                        "alternate path '%s'; unregistration was not successful.\n",
+                keyName, regpath);
+
+        ::RegCloseKey(subKey);
+        ::RegCloseKey(rootKey);
+
+        return;
+      }
+    }
+
+    ::RegCloseKey(subKey);
+  }
+
+  ::RegDeleteKeyW(rootKey, keyName);
+  ::RegCloseKey(rootKey);
+}
--- a/xulrunner/app/nsXULRunnerApp.cpp
+++ b/xulrunner/app/nsXULRunnerApp.cpp
@@ -38,16 +38,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #include "nsXULAppAPI.h"
 #include "nsXPCOMGlue.h"
+#include "nsRegisterGRE.h"
 #include "nsAppRunner.h"
 #include "nsILocalFile.h"
 #include "nsIXULAppInstall.h"
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 #include "nsCRTGlue.h"
 #include "nsStringAPI.h"
 #include "nsServiceManagerUtils.h"
@@ -255,16 +256,26 @@ InstallXULApp(nsIFile* aXULRunnerDir,
   NS_ShutdownXPCOM(nsnull);
 
   if (NS_FAILED(rv))
     return 3;
 
   return 0;
 }
 
+static const GREProperty kGREProperties[] = {
+  { "xulrunner", "true" }
+#ifdef TARGET_XPCOM_ABI
+  , { "abi", TARGET_XPCOM_ABI }
+#endif
+#ifdef MOZ_JAVAXPCOM
+  , { "javaxpcom", "1" }
+#endif
+};
+
 class AutoAppData
 {
 public:
   AutoAppData(nsILocalFile* aINIFile) : mAppData(nsnull) {
     nsresult rv = XRE_CreateAppData(aINIFile, &mAppData);
     if (NS_FAILED(rv))
       mAppData = nsnull;
   }
@@ -301,16 +312,77 @@ int main(int argc, char* argv[])
   }
 
   if (argc > 1) {
     nsCAutoString milestone;
     nsresult rv = GetGREVersion(argv[0], &milestone, nsnull);
     if (NS_FAILED(rv))
       return 2;
 
+    PRBool registerGlobal = IsArg(argv[1], "register-global");
+    PRBool registerUser   = IsArg(argv[1], "register-user");
+    if (registerGlobal || registerUser) {
+      if (argc != 2) {
+        Usage(argv[0]);
+        return 1;
+      }
+
+      nsCOMPtr<nsIFile> regDir;
+      rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir));
+      if (NS_FAILED(rv))
+        return 2;
+
+      return RegisterXULRunner(registerGlobal, regDir,
+                               kGREProperties,
+                               NS_ARRAY_LENGTH(kGREProperties),
+                               milestone.get()) ? 0 : 2;
+    }
+
+    registerGlobal = IsArg(argv[1], "unregister-global");
+    registerUser   = IsArg(argv[1], "unregister-user");
+    if (registerGlobal || registerUser) {
+      if (argc != 2) {
+        Usage(argv[0]);
+        return 1;
+      }
+
+      nsCOMPtr<nsIFile> regDir;
+      rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir));
+      if (NS_FAILED(rv))
+        return 2;
+
+      UnregisterXULRunner(registerGlobal, regDir, milestone.get());
+      return 0;
+    }
+
+    if (IsArg(argv[1], "find-gre")) {
+      if (argc != 3) {
+        Usage(argv[0]);
+        return 1;
+      }
+
+      char path[MAXPATHLEN];
+      static const GREVersionRange vr = {
+        argv[2], PR_TRUE,
+        argv[2], PR_TRUE
+      };
+      static const GREProperty kProperties[] = {
+        { "xulrunner", "true" }
+      };
+
+      rv = GRE_GetGREPathWithProperties(&vr, 1, kProperties,
+                                        NS_ARRAY_LENGTH(kProperties),
+                                        path, sizeof(path));
+      if (NS_FAILED(rv))
+        return 1;
+
+      printf("%s\n", path);
+      return 0;
+    }
+
     if (IsArg(argv[1], "gre-version")) {
       if (argc != 2) {
         Usage(argv[0]);
         return 1;
       }
 
       printf("%s\n", milestone.get());
       return 0;
--- a/xulrunner/installer/Makefile.in
+++ b/xulrunner/installer/Makefile.in
@@ -62,16 +62,29 @@ PKG_DMG_SOURCE = $(STAGEPATH)xulrunner-p
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 INSTALL_SDK = 1
 
 include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
 
+$(MOZILLA_VERSION).system.conf: $(topsrcdir)/config/milestone.txt Makefile
+	printf "[%s]\nGRE_PATH=%s\nxulrunner=true\nabi=%s" \
+	  $(MOZILLA_VERSION) $(installdir) $(TARGET_XPCOM_ABI)> $@
+
+ifndef SKIP_GRE_REGISTRATION
+# to register xulrunner per-user, override this with $HOME/.gre.d
+regdir = /etc/gre.d
+
+install:: $(MOZILLA_VERSION).system.conf
+	$(NSINSTALL) -D $(DESTDIR)$(regdir)
+	$(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(regdir)
+endif
+
 # Add pkg-config files to the install:: target
 
 pkg_config_files = \
 	libxul.pc \
 	libxul-embedding.pc \
 	mozilla-js.pc \
 	mozilla-plugin.pc \
 	mozilla-gtkmozembed.pc \
--- a/xulrunner/stub/nsXULStub.cpp
+++ b/xulrunner/stub/nsXULStub.cpp
@@ -430,19 +430,64 @@ main(int argc, char **argv)
       // Forward the command-line and bail out
       ForwardToWindow(wnd);
       return 0;
     }
   }
 #endif
 
   if (!greFound) {
-    Output(PR_FALSE,
-           "Could not find the Mozilla runtime.\n");
+    char minVersion[VERSION_MAXLEN];
+
+    // If a gecko maxVersion is not specified, we assume that the app uses only
+    // frozen APIs, and is therefore compatible with any xulrunner 1.x.
+    char maxVersion[VERSION_MAXLEN] = "1.*";
+
+    GREVersionRange range = {
+      minVersion,
+      PR_TRUE,
+      maxVersion,
+      PR_TRUE
+    };
+
+    rv = parser.GetString("Gecko", "MinVersion", minVersion, sizeof(minVersion));
+    if (NS_FAILED(rv)) {
+      fprintf(stderr,
+              "The application.ini does not specify a [Gecko] MinVersion\n");
       return 1;
+    }
+
+    rv = parser.GetString("Gecko", "MaxVersion", maxVersion, sizeof(maxVersion));
+    if (NS_SUCCEEDED(rv))
+      range.upperInclusive = PR_TRUE;
+
+    static const GREProperty kProperties[] = {
+      { "xulrunner", "true" }
+    };
+
+    rv = GRE_GetGREPathWithProperties(&range, 1,
+                                      kProperties, NS_ARRAY_LENGTH(kProperties),
+                                      greDir, sizeof(greDir));
+    if (NS_FAILED(rv)) {
+      // XXXbsmedberg: Do something much smarter here: notify the
+      // user/offer to download/?
+
+      Output(PR_FALSE,
+             "Could not find compatible GRE between version %s and %s.\n",
+             range.lower, range.upper);
+      return 1;
+    }
+#ifdef XP_UNIX
+    // Using a symlinked greDir will fail during startup. Not sure why, but if
+    // we resolve the symlink, everything works as expected.
+    char resolved_greDir[MAXPATHLEN] = "";  
+    if (realpath(greDir, resolved_greDir) && *resolved_greDir) {
+      strncpy(greDir, resolved_greDir, MAXPATHLEN);
+    }
+#endif
   }
 
 #ifdef XP_OS2
   // On OS/2 we need to set BEGINLIBPATH to be able to find XULRunner DLLs
   strcpy(tmpPath, greDir);
   lastSlash = strrchr(tmpPath, PATH_SEPARATOR_CHAR);
   if (lastSlash) {
     *lastSlash = '\0';