Add an l10n strategy for web-based protocol handler updating (bug 395277); r=myk, sr=biesi, r=gavin (for browser/ pieces), a=blocking-1.9+; late-l10n checkin
authordmose@mozilla.org
Tue, 19 Feb 2008 20:46:35 -0800
changeset 11902 fceac97e2b4b4de189541be857504041e0965eb4
parent 11901 b99e687758b2f630bf9c00f790452d3928b7475a
child 11903 b3bf015414dbf7e24fd9ddd721821ef8ebd35a3b
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersmyk, biesi, gavin, blocking-1.9
bugs395277
milestone1.9b4pre
Add an l10n strategy for web-based protocol handler updating (bug 395277); r=myk, sr=biesi, r=gavin (for browser/ pieces), a=blocking-1.9+; late-l10n checkin
browser/app/profile/firefox.js
browser/locales/en-US/chrome/browser-region/region.properties
uriloader/exthandler/beos/nsOSHelperAppService.cpp
uriloader/exthandler/beos/nsOSHelperAppService.h
uriloader/exthandler/mac/nsOSHelperAppService.cpp
uriloader/exthandler/mac/nsOSHelperAppService.h
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsHandlerService.js
uriloader/exthandler/nsIExternalProtocolService.idl
uriloader/exthandler/os2/nsOSHelperAppService.cpp
uriloader/exthandler/os2/nsOSHelperAppService.h
uriloader/exthandler/unix/nsOSHelperAppService.cpp
uriloader/exthandler/unix/nsOSHelperAppService.h
uriloader/exthandler/win/nsOSHelperAppService.cpp
uriloader/exthandler/win/nsOSHelperAppService.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -523,29 +523,47 @@ pref("browser.contentHandlers.types.4.ty
 pref("browser.contentHandlers.types.5.title", "chrome://browser-region/locale/region.properties");
 pref("browser.contentHandlers.types.5.uri", "chrome://browser-region/locale/region.properties");
 pref("browser.contentHandlers.types.5.type", "application/vnd.mozilla.maybe.feed");
 
 pref("browser.feeds.handler", "ask");
 pref("browser.videoFeeds.handler", "ask");
 pref("browser.audioFeeds.handler", "ask");
 
-// For now, this is living in content rather than in locales, as per Pike.
-// Eventually it will get merged into region.properties; see bug 395277.
-//
-// At startup, if the handler service notices that the version number here
-// is newer than the version number in the handler service datastore, it will
-// add any handlers it finds in the prefs (as seeded by this file) to its
-// datastore.  
-pref("gecko.handlerService.defaultHandlersVersion", "0");
-//
+// At startup, if the handler service notices that the version number in the
+// region.properties file is newer than the version number in the handler
+// service datastore, it will add any new handlers it finds in the prefs (as
+// seeded by this file) to its datastore.  
+pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties");
+
 // The default set of web-based protocol handlers shown in the application
-// selection dialog
-pref("gecko.handlerService.schemes.webcal.0.name", "WebCal Test Handler");
-pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "http://handler-test.mozilla.org/webcal?url=%s");
+// selection dialog for webcal: ; I've arbitrarily picked 4 default handlers
+// per protocol, but if some locale wants more than that (or defaults for some
+// protocol not currently listed here), we should go ahead and add those.
+
+// webcal
+pref("gecko.handlerService.schemes.webcal.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// mailto
+pref("gecko.handlerService.schemes.mailto.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
 
 // By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
 pref("gecko.handlerService.allowRegisterFromDifferentHost", false);
 
 #ifdef MOZ_SAFE_BROWSING
 // Safe browsing does nothing unless this pref is set
 pref("browser.safebrowsing.enabled", true);
 
--- a/browser/locales/en-US/chrome/browser-region/region.properties
+++ b/browser/locales/en-US/chrome/browser-region/region.properties
@@ -11,8 +11,19 @@ browser.contentHandlers.types.0.title=Bl
 browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s
 browser.contentHandlers.types.1.title=My Yahoo
 browser.contentHandlers.types.1.uri=http://add.my.yahoo.com/rss?url=%s
 browser.contentHandlers.types.2.title=Google
 browser.contentHandlers.types.2.uri=http://fusion.google.com/add?feedurl=%s
 
 # Keyword URL (for location bar searches)
 keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=
+
+# increment this number when anything gets changed in the list below.  This will
+# cause Firefox to re-read these prefs and inject any new handlers into the 
+# profile database.  Note that "new" is defined as "has a different URL"; this
+# means that it's not possible to update the name of existing handler, so 
+# don't make any spelling errors here.
+gecko.handlerService.defaultHandlersVersion=0
+
+# The default set of protocol handlers for webcal
+gecko.handlerService.schemes.webcal.0.name=30 Boxes
+gecko.handlerService.schemes.webcal.0.uriTemplate=http://30boxes.com/external/widget?refer=ff&url=%s
--- a/uriloader/exthandler/beos/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/beos/nsOSHelperAppService.cpp
@@ -227,37 +227,38 @@ nsOSHelperAppService::GetMIMEInfoFromOS(
     return nsnull;
   NS_ADDREF(mi);
   if (!aFileExt.IsEmpty())
     mi->AppendExtension(aFileExt);
 
   return mi;
 }
 
-already_AddRefed<nsIHandlerInfo>
-nsOSHelperAppService::GetProtocolInfoFromOS(const nsACString &aScheme,
-                                            PRBool *found)
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                   PRBool *found,
+                                                   nsIHandlerInfo **_retval)
 {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
                                         found);
   if (NS_FAILED(rv))
-    return nsnull;
+    return rv;
 
   nsMIMEInfoBeOS *handlerInfo =
 	new nsMIMEInfoBeOS(aScheme, nsMIMEInfoBase::eProtocolInfo);
   NS_ENSURE_TRUE(handlerInfo, nsnull);
-  NS_ADDREF(handlerInfo);
+  NS_ADDREF(*_retval = handlerInfo);
 
   if (!*found) {
     // Code that calls this requires an object regardless if the OS has
     // something for us, so we return the empty object.
-    return handlerInfo;
+    return rv;
   }
 
   nsAutoString desc;
   GetApplicationDescription(aScheme, desc);
   handlerInfo->SetDefaultDescription(desc);
 
-  return handlerInfo;
+  return rv;
 }
 
--- a/uriloader/exthandler/beos/nsOSHelperAppService.h
+++ b/uriloader/exthandler/beos/nsOSHelperAppService.h
@@ -49,18 +49,19 @@ class nsMIMEInfoBeOS;
 
 class nsOSHelperAppService : public nsExternalHelperAppService
 {
 public:
 	nsOSHelperAppService();
 	virtual ~nsOSHelperAppService();
 
 	already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, PRBool *aFound);
-	already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-	                                                       PRBool *found);
+	NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+	                                        PRBool *found,
+	                                        nsIHandlerInfo **_retval);
 
 	// override nsIExternalProtocolService methods
 	nsresult OSProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists);
 
 protected:
 	nsresult SetMIMEInfoForType(const char *aMIMEType, nsMIMEInfoBeOS **_retval);
 	nsresult GetMimeInfoFromExtension(const char *aFileExt, nsMIMEInfoBeOS **_retval);
 	nsresult GetMimeInfoFromMIMEType(const char *aMIMEType, nsMIMEInfoBeOS **_retval);
--- a/uriloader/exthandler/mac/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -287,43 +288,44 @@ nsOSHelperAppService::GetMIMEInfoFromOS(
 
     if (!aFileExt.IsEmpty())
       mimeInfo->AppendExtension(aFileExt);
   }
   
   return mimeInfo;
 }
 
-already_AddRefed<nsIHandlerInfo>
-nsOSHelperAppService::GetProtocolInfoFromOS(const nsACString &aScheme,
-                                            PRBool *found)
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                   PRBool *found,
+                                                   nsIHandlerInfo **_retval)
 {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
                                         found);
   if (NS_FAILED(rv))
-    return nsnull;
+    return rv;
 
   nsMIMEInfoMac *handlerInfo =
     new nsMIMEInfoMac(aScheme, nsMIMEInfoBase::eProtocolInfo);
   NS_ENSURE_TRUE(handlerInfo, nsnull);
-  NS_ADDREF(handlerInfo);
+  NS_ADDREF(*_retval = handlerInfo);
 
   if (!*found) {
     // Code that calls this requires an object regardless if the OS has
     // something for us, so we return the empty object.
-    return handlerInfo;
+    return NS_OK;
   }
 
   nsAutoString desc;
   GetApplicationDescription(aScheme, desc);
   handlerInfo->SetDefaultDescription(desc);
 
-  return handlerInfo;
+  return NS_OK;
 }
 
 // we never want to use a hard coded value for the creator and file type for the mac. always look these values up
 // from internet config.
 void nsOSHelperAppService::UpdateCreatorInfo(nsIMIMEInfo * aMIMEInfo)
 {
   PRUint32 macCreatorType;
   PRUint32 macFileType;
--- a/uriloader/exthandler/mac/nsOSHelperAppService.h
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.h
@@ -18,16 +18,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
  *   Asaf Romano <mozilla.mano@sent.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -56,18 +57,19 @@ public:
   virtual ~nsOSHelperAppService();
 
   // override nsIExternalProtocolService methods
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
   
   // method overrides --> used to hook the mime service into internet config....
   NS_IMETHOD GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo);
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, PRBool * aFound);
-  already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-                                                         PRBool *found);
+  NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                          PRBool *found,
+                                          nsIHandlerInfo **_retval);
 
   // GetFileTokenForPath must be implemented by each platform. 
   // platformAppPath --> a platform specific path to an application that we got out of the 
   //                     rdf data source. This can be a mac file spec, a unix path or a windows path depending on the platform
   // aFile --> an nsIFile representation of that platform application path.
   virtual nsresult GetFileTokenForPath(const PRUnichar * platformAppPath, nsIFile ** aFile);
 
   nsresult OSProtocolHandlerExists(const char * aScheme,
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -701,18 +701,18 @@ nsresult nsExternalHelperAppService::Get
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 // begin external protocol service default implementation...
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 NS_IMETHODIMP nsExternalHelperAppService::ExternalProtocolHandlerExists(const char * aProtocolScheme,
                                                                         PRBool * aHandlerExists)
 {
   nsCOMPtr<nsIHandlerInfo> handlerInfo;
-  nsresult rv = GetProtocolHandlerInfo(
-      nsDependentCString(aProtocolScheme), getter_AddRefs(handlerInfo));
+  nsresult rv = GetProtocolHandlerInfo(nsDependentCString(aProtocolScheme), 
+                                       getter_AddRefs(handlerInfo));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // See if we have any known possible handler apps for this
   nsCOMPtr<nsIMutableArray> possibleHandlers;
   handlerInfo->GetPossibleApplicationHandlers(getter_AddRefs(possibleHandlers));
 
   PRUint32 length;
   possibleHandlers->GetLength(&length);
@@ -885,64 +885,82 @@ nsresult nsExternalHelperAppService::Exp
 }
 
 static const char kExternalWarningPrefPrefix[] = 
   "network.protocol-handler.warn-external.";
 static const char kExternalWarningDefaultPref[] = 
   "network.protocol-handler.warn-external-default";
 
 NS_IMETHODIMP
-nsExternalHelperAppService::GetProtocolHandlerInfo(const nsACString &aScheme, 
+nsExternalHelperAppService::GetProtocolHandlerInfo(const nsACString &aScheme,
                                                    nsIHandlerInfo **aHandlerInfo)
 {
   // XXX enterprise customers should be able to turn this support off with a
   // single master pref (maybe use one of the "exposed" prefs here?)
 
   PRBool exists;
-  *aHandlerInfo = GetProtocolInfoFromOS(aScheme, &exists).get();
-  if (!(*aHandlerInfo)) {
+  nsresult rv = GetProtocolHandlerInfoFromOS(aScheme, &exists, aHandlerInfo);
+  if (NS_FAILED(rv)) {
     // Either it knows nothing, or we ran out of memory
     return NS_ERROR_FAILURE;
   }
-
-  nsresult rv = NS_ERROR_FAILURE;
+  
+  rv = NS_ERROR_FAILURE;
   nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
   if (handlerSvc)
     rv = handlerSvc->FillHandlerInfo(*aHandlerInfo, EmptyCString());
+
+  if (NS_SUCCEEDED(rv))
+    return NS_OK;
   
+  return SetProtocolHandlerDefaults(*aHandlerInfo, exists);
+}
+
+NS_IMETHODIMP
+nsExternalHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                         PRBool *found,
+                                                         nsIHandlerInfo **aHandlerInfo)
+{
+  // intended to be implemented by the subclass
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsExternalHelperAppService::SetProtocolHandlerDefaults(nsIHandlerInfo *aHandlerInfo,
+                                                       const PRBool aOSHandlerExists)
+{
   // this type isn't in our database, so we've only got an OS default handler,
   // if one exists
-  if (NS_FAILED(rv)) {
-    
-    if (exists) {
-      // we've got a default, so use it
-      (*aHandlerInfo)->SetPreferredAction(nsIHandlerInfo::useSystemDefault);
+
+  if (aOSHandlerExists) {
+    // we've got a default, so use it
+    aHandlerInfo->SetPreferredAction(nsIHandlerInfo::useSystemDefault);
 
-      // whether or not to ask the user depends on the warning preference
-
-      nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
-      if (!prefs)
-        return NS_OK; // deny if we can't check prefs
+    // whether or not to ask the user depends on the warning preference
+    nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+    if (!prefs)
+      return NS_OK; // deny if we can't check prefs
 
-      nsCAutoString warningPref(kExternalWarningPrefPrefix);
-      warningPref += aScheme;
-      PRBool warn = PR_TRUE;
-      rv = prefs->GetBoolPref(warningPref.get(), &warn);
-      if (NS_FAILED(rv))
-      {
-        // no scheme-specific value, check the default
-        prefs->GetBoolPref(kExternalWarningDefaultPref, &warn);
-      }
-      (*aHandlerInfo)->SetAlwaysAskBeforeHandling(warn);
-    } else {
-      // If no OS default existed, we set the preferred action to alwaysAsk. 
-      // This really means not initialized (i.e. there's no available handler)
-      // to all the code...
-      (*aHandlerInfo)->SetPreferredAction(nsIHandlerInfo::alwaysAsk);
+    nsCAutoString scheme;
+    aHandlerInfo->GetType(scheme);
+    
+    nsCAutoString warningPref(kExternalWarningPrefPrefix);
+    warningPref += scheme;
+    PRBool warn = PR_TRUE;
+    nsresult rv = prefs->GetBoolPref(warningPref.get(), &warn);
+    if (NS_FAILED(rv)) {
+      // no scheme-specific value, check the default
+      prefs->GetBoolPref(kExternalWarningDefaultPref, &warn);
     }
+    aHandlerInfo->SetAlwaysAskBeforeHandling(warn);
+  } else {
+    // If no OS default existed, we set the preferred action to alwaysAsk. 
+    // This really means not initialized (i.e. there's no available handler)
+    // to all the code...
+    aHandlerInfo->SetPreferredAction(nsIHandlerInfo::alwaysAsk);
   }
 
   return NS_OK;
 }
  
 // XPCOM profile change observer
 NS_IMETHODIMP
 nsExternalHelperAppService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData )
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -125,26 +125,16 @@ public:
    *         returning one is an out-of-memory error.
    *         If null, the value of aFound is unspecified.
    */
   virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
                                                           const nsACString& aFileExt,
                                                           PRBool     * aFound) = 0;
 
   /**
-   * Given a scheme, looks up the protocol info from the OS.  This should be
-   * overridden by each OS's implementation.
-   *
-   * @param aScheme The protocol scheme we are looking for.
-   * @return An nsIHanderInfo for the protocol.
-   */
-  virtual already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-                                                                 PRBool *found) = 0;
-
-  /**
    * Given a string identifying an application, create an nsIFile representing
    * it. This function should look in $PATH for the application.
    * The base class implementation will first try to interpret platformAppPath
    * as an absolute path, and if that fails it will look for a file next to the
    * mozilla executable. Subclasses can override this method if they want a
    * different behaviour.
    * @param platformAppPath A platform specific path to an application that we
    *                        got out of the rdf data source. This can be a mac
--- a/uriloader/exthandler/nsHandlerService.js
+++ b/uriloader/exthandler/nsHandlerService.js
@@ -44,18 +44,20 @@ const Cr = Components.results;
 
 const CLASS_MIMEINFO        = "mimetype";
 const CLASS_PROTOCOLINFO    = "scheme";
 
 
 // namespace prefix
 const NC_NS                 = "http://home.netscape.com/NC-rdf#";
 
-// the most recent default handlers that have been injected
-const NC_DEFAULT_HANDLERS_VERSION = NC_NS + "defaultHandlersVersion";
+// the most recent default handlers that have been injected.  Note that
+// this is used to construct an RDF resource, which needs to have NC_NS
+// prepended, since that hasn't been done yet
+const DEFAULT_HANDLERS_VERSION = "defaultHandlersVersion";
 
 // type list properties
 
 const NC_MIME_TYPES         = NC_NS + "MIME-types";
 const NC_PROTOCOL_SCHEMES   = NC_NS + "Protocol-Schemes";
 
 // content type ("type") properties
 
@@ -135,32 +137,40 @@ HandlerService.prototype = {
     try {
       var defaultHandlersVersion = this._datastoreDefaultHandlersVersion;
     } catch(ex) {
       // accessing the datastore failed, we can't update anything
       return;
     }
 
     try {
-      // if the default prefs have changed, inject any new default handers
-      // into the datastore
+      // if we don't have the current version of the default prefs for
+      // this locale, inject any new default handers into the datastore
       if (defaultHandlersVersion < this._prefsDefaultHandlersVersion) {
+
         // set the new version first so that if we recurse we don't
         // call _injectNewDefaults several times
         this._datastoreDefaultHandlersVersion =
           this._prefsDefaultHandlersVersion;
         this._injectNewDefaults();
-      }
+      } 
     } catch (ex) {
       // if injecting the defaults failed, set the version back to the
       // previous value
-      this._datastoreDefaultHandlersVersion = defaultHandlersVersion;      
+      this._datastoreDefaultHandlersVersion = defaultHandlersVersion;
     }
   },
-  
+
+  get _currentLocale() {
+    var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].
+                         getService(Ci.nsIXULChromeRegistry);
+    var currentLocale = chromeRegistry.getSelectedLocale("global");
+    return currentLocale;
+  }, 
+
   _destroy: function HS__destroy() {
     this._observerSvc.removeObserver(this, "profile-before-change");
     this._observerSvc.removeObserver(this, "xpcom-shutdown");
     this._observerSvc.removeObserver(this, "profile-do-change");
 
     // XXX Should we also null references to all the services that get stored
     // by our memoizing getters in the Convenience Getters section?
   },
@@ -178,85 +188,104 @@ HandlerService.prototype = {
       handler.QueryInterface(Ci.nsIHandlerApp);
       if (handler.equals(aHandler))
         return true;
     }
     
     return false;
   },
 
+  // note that this applies to the current locale only 
   get _datastoreDefaultHandlersVersion() {
-    var version = this._getValue("urn:root", NC_DEFAULT_HANDLERS_VERSION); 
+    var version = this._getValue("urn:root", NC_NS + this._currentLocale +
+                                             "_" + DEFAULT_HANDLERS_VERSION);
     
     return version ? version : -1;
   },
 
   set _datastoreDefaultHandlersVersion(aNewVersion) {
-    return this._setLiteral("urn:root", NC_DEFAULT_HANDLERS_VERSION, 
-                            aNewVersion);
+    return this._setLiteral("urn:root", NC_NS + this._currentLocale + "_" + 
+                            DEFAULT_HANDLERS_VERSION, aNewVersion);
   },
 
   get _prefsDefaultHandlersVersion() {
     // get handler service pref branch
     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
                   getService(Ci.nsIPrefService);
     var handlerSvcBranch = prefSvc.getBranch("gecko.handlerService.");
-  
+
     // get the version of the preferences for this locale
-    var version = handlerSvcBranch.getComplexValue("defaultHandlersVersion",
-                                                   Ci.nsISupportsString).data;
-                                                   
-    return version;                                                   
+    return Number(handlerSvcBranch.
+                  getComplexValue("defaultHandlersVersion", 
+                                  Ci.nsIPrefLocalizedString).data);
   },
   
   _injectNewDefaults: function HS__injectNewDefaults() {
     // get handler service pref branch
     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
                   getService(Ci.nsIPrefService);
 
     let schemesPrefBranch = prefSvc.getBranch("gecko.handlerService.schemes.");
     let schemePrefList = schemesPrefBranch.getChildList("", {}); 
 
-    let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
-                   getService(Ci.nsIExternalProtocolService);
-
     var schemes = {};
 
     // read all the scheme prefs into a hash
     for each (var schemePrefName in schemePrefList) {
 
       let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
 
-      if (!(scheme in schemes))
-        schemes[scheme] = {};
-      if (!(handlerNumber in schemes[scheme]))
-        schemes[scheme][handlerNumber] = {};
+      try {
+        var attrData =
+          schemesPrefBranch.getComplexValue(schemePrefName,
+                                            Ci.nsIPrefLocalizedString).data;
+        if (!(scheme in schemes))
+          schemes[scheme] = {};
+  
+        if (!(handlerNumber in schemes[scheme]))
+          schemes[scheme][handlerNumber] = {};
         
-      schemes[scheme][handlerNumber][attribute] = 
-        schemesPrefBranch.getComplexValue(schemePrefName,
-                                          Ci.nsISupportsString).data;
+        schemes[scheme][handlerNumber][attribute] = attrData;
+      } catch (ex) {}
     }
 
+    let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
+                   getService(Ci.nsIExternalProtocolService);
     for (var scheme in schemes) {
 
-      // get a protocol info object for that scheme and cache the possible
-      // handlers to avoid extra xpconnect traversals
-      let protoInfo = protoSvc.getProtocolHandlerInfo(scheme);  
+      // This clause is essentially a reimplementation of 
+      // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
+      // Necessary because calling that from here would make XPConnect barf
+      // when getService tried to re-enter the constructor for this
+      // service.
+      let osDefaultHandlerFound = {};
+      let protoInfo = protoSvc.getProtocolHandlerInfoFromOS(scheme, 
+                               osDefaultHandlerFound);
+      
+      try {
+        this.fillHandlerInfo(protoInfo, null);
+      } catch (ex) {
+        // pick some sane defaults
+        protoSvc.setProtocolHandlerDefaults(protoInfo, 
+                                            osDefaultHandlerFound.value);
+      }
+
+      // cache the possible handlers to avoid extra xpconnect traversals.      
       let possibleHandlers = protoInfo.possibleApplicationHandlers;
 
       for each (var handlerPrefs in schemes[scheme]) {
 
         let handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
                          createInstance(Ci.nsIWebHandlerApp);
 
         handlerApp.uriTemplate = handlerPrefs.uriTemplate;
         handlerApp.name = handlerPrefs.name;                
 
         if (!this._isInHandlerArray(possibleHandlers, handlerApp)) {
-             possibleHandlers.appendElement(handlerApp, false);
+          possibleHandlers.appendElement(handlerApp, false);
         }
       }
 
       this.store(protoInfo);
     }
   },
 
   //**************************************************************************//
--- a/uriloader/exthandler/nsIExternalProtocolService.idl
+++ b/uriloader/exthandler/nsIExternalProtocolService.idl
@@ -50,17 +50,17 @@ interface nsIHandlerInfo;
  * The external protocol service is used for finding and launching
  * web handlers (a la registerProtocolHandler in the HTML5 draft) or 
  * platform-specific applications for handling particular protocols.
  *
  * You can ask the external protocol service if it has an external
  * handler for a given protocol scheme. And you can ask it to load 
  * the url using the default handler.
  */
-[scriptable, uuid(7968ffa9-bb51-4b2d-aad2-8ea46c15d27f)]
+[scriptable, uuid(70f93b7a-3ec6-4bcb-b093-92d9984c9f83)]
 interface nsIExternalProtocolService : nsISupports
 {
   /**
    * Check whether a handler for a specific protocol exists.  Specifically,
    * this looks to see whether there are any known possible application handlers
    * in either the nsIHandlerService datastore or registered with the OS.
    *
    * @param aProtocolScheme The scheme from a url: http, ftp, mailto, etc.
@@ -95,16 +95,40 @@ interface nsIExternalProtocolService : n
    * of the URI syntax, not part of the scheme itself (i.e. pass "mailto" not
    * "mailto:").
    * 
    * @return the handler, if any; otherwise a default handler
    */
   nsIHandlerInfo getProtocolHandlerInfo(in ACString aProtocolScheme);
 
   /**
+   * Given a scheme, looks up the protocol info from the OS.  This should be
+   * overridden by each OS's implementation.
+   *
+   * @param aScheme The protocol scheme we are looking for.
+   * @param aFound  Was an OS default handler for this scheme found?
+   * @return An nsIHanderInfo for the protocol.
+   */
+  nsIHandlerInfo getProtocolHandlerInfoFromOS(in ACString aProtocolScheme,
+                                              out boolean aFound);
+
+  /** 
+   * Set some sane defaults for a protocol handler object.
+   * 
+   * @param aHandlerInfo      nsIHandlerInfo object, as returned by 
+   *                          getProtocolHandlerInfoFromOS
+   * @param aOSHandlerExists  was the object above created for an extant
+   *                          OS default handler?  This is generally the
+   *                          value of the aFound out param from
+   *                          getProtocolHandlerInfoFromOS.
+   */
+  void setProtocolHandlerDefaults(in nsIHandlerInfo aHandlerInfo,
+                                  in boolean aOSHandlerExists);
+
+  /**
    * Used to load a url via an external protocol handler (if one exists)
    *
    * @param aURL The url to load
    * @deprecated Use LoadURI instead (See Bug 389565 for removal)
    */
    void loadUrl (in nsIURI aURL);
 
   /**
--- a/uriloader/exthandler/os2/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.cpp
@@ -20,16 +20,17 @@
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
  *   Boris Zbarsky <bzbarsky@mit.edu>  (Added mailcap and mime.types support)
  *   Peter Weilbacher <mozilla@Weilbacher.org>
  *   Rich Walsh <dragtext@e-vertise.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -1602,43 +1603,44 @@ nsOSHelperAppService::GetMIMEInfoFromOS(
     // Copy the attributes of retval onto miByExt, to return it
     retval->CopyBasicDataTo(miByExt);
 
     miByExt.swap(retval);
   }
   return retval;
 }
 
-already_AddRefed<nsIHandlerInfo>
-nsOSHelperAppService::GetProtocolInfoFromOS(const nsACString &aScheme,
-                                            PRBool *found)
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                   PRBool *found,
+                                                   nsIHandlerInfo **_retval)
 {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
                                         found);
   if (NS_FAILED(rv))
-    return nsnull;
+    return rv;
 
   nsMIMEInfoOS2 *handlerInfo =
     new nsMIMEInfoOS2(aScheme, nsMIMEInfoBase::eProtocolInfo);
   NS_ENSURE_TRUE(handlerInfo, nsnull);
-  NS_ADDREF(handlerInfo);
+  NS_ADDREF(*_retval = handlerInfo);
 
   if (!*found) {
     // Code that calls this requires an object regardless if the OS has
     // something for us, so we return the empty object.
-    return handlerInfo;
+    return NS_OK;
   }
 
   nsAutoString desc;
   GetApplicationDescription(aScheme, desc);
   handlerInfo->SetDefaultDescription(desc);
 
-  return handlerInfo;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
 {
   nsresult rv;
   nsCOMPtr<nsIPrefService> thePrefsService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
--- a/uriloader/exthandler/os2/nsOSHelperAppService.h
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.h
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -63,18 +64,19 @@ public:
 
   // method overrides for mime.types and mime.info look up steps
   NS_IMETHOD GetFromTypeAndExtension(const nsACString& aMIMEType,
                                      const nsACString& aFileExt,
                                      nsIMIMEInfo **_retval);
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMimeType,
                                                   const nsACString& aFileExt,
                                                   PRBool     *aFound);
-  already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-                                                         PRBool *found);
+  NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                          PRBool *found,
+                                          nsIHandlerInfo **_retval);
 
   // override nsIExternalProtocolService methods
   NS_IMETHODIMP GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
 
   nsresult OSProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists);
 
 protected:
   already_AddRefed<nsMIMEInfoOS2> GetFromType(const nsCString& aMimeType);
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -1625,46 +1625,47 @@ nsOSHelperAppService::GetMIMEInfoFromOS(
     // Copy the attributes of retval onto miByExt, to return it
     retval->CopyBasicDataTo(miByExt);
 
     miByExt.swap(retval);
   }
   return retval;
 }
 
-already_AddRefed<nsIHandlerInfo>
-nsOSHelperAppService::GetProtocolInfoFromOS(const nsACString &aScheme,
-                                            PRBool *found)
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                   PRBool *found,
+                                                   nsIHandlerInfo **_retval)
 {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   // We must check that a registered handler exists so that gnome_url_show
   // doesn't fallback to gnomevfs.
   // See nsGNOMERegistry::LoadURL and bug 389632.
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
                                         found);
   if (NS_FAILED(rv))
-    return nsnull;
+    return rv;
 
   nsMIMEInfoUnix *handlerInfo =
     new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo);
   NS_ENSURE_TRUE(handlerInfo, nsnull);
-  NS_ADDREF(handlerInfo);
+  NS_ADDREF(*_retval = handlerInfo);
 
   if (!*found) {
     // Code that calls this requires an object regardless if the OS has
     // something for us, so we return the empty object.
-    return handlerInfo;
+    return NS_OK;
   }
 
   nsAutoString desc;
   GetApplicationDescription(aScheme, desc);
   handlerInfo->SetDefaultDescription(desc);
 
-  return handlerInfo;
+  return NS_OK;
 }
 
 void
 nsOSHelperAppService::FixFilePermissions(nsILocalFile* aFile)
 {
   aFile->SetPermissions(mPermissions); 
 }
 
--- a/uriloader/exthandler/unix/nsOSHelperAppService.h
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.h
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -56,18 +57,19 @@ class nsOSHelperAppService : public nsEx
 public:
   nsOSHelperAppService();
   virtual ~nsOSHelperAppService();
 
   // method overrides for mime.types and mime.info look up steps
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMimeType,
                                                   const nsACString& aFileExt,
                                                   PRBool     *aFound);
-  already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-                                                         PRBool *found);
+  NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                          PRBool *found,
+                                          nsIHandlerInfo **_retval);
 
   // override nsIExternalProtocolService methods
   nsresult OSProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists);
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
 
   // GetFileTokenForPath must be implemented by each platform. 
   // platformAppPath --> a platform specific path to an application that we got out of the 
   //                     rdf data source. This can be a mac file spec, a unix path or a windows path depending on the platform
--- a/uriloader/exthandler/win/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
+ *   Dan Mosedale <dmose@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -582,37 +583,38 @@ already_AddRefed<nsIMIMEInfo> nsOSHelper
     nsAutoString desc;
     miByExt->GetDefaultDescription(desc);
 
     mi->SetDefaultDescription(desc);
   }
   return mi;
 }
 
-already_AddRefed<nsIHandlerInfo>
-nsOSHelperAppService::GetProtocolInfoFromOS(const nsACString &aScheme,
-                                            PRBool *found)
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+                                                   PRBool *found,
+                                                   nsIHandlerInfo **_retval)
 {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
                                         found);
   if (NS_FAILED(rv))
-    return nsnull;
+    return rv;
 
   nsMIMEInfoWin *handlerInfo =
     new nsMIMEInfoWin(aScheme, nsMIMEInfoBase::eProtocolInfo);
   NS_ENSURE_TRUE(handlerInfo, nsnull);
-  NS_ADDREF(handlerInfo);
+  NS_ADDREF(*_retval = handlerInfo);
 
   if (!*found) {
     // Code that calls this requires an object regardless if the OS has
     // something for us, so we return the empty object.
-    return handlerInfo;
+    return NS_OK;
   }
 
   nsAutoString desc;
   GetApplicationDescription(aScheme, desc);
   handlerInfo->SetDefaultDescription(desc);
 
-  return handlerInfo;
+  return NS_OK;
 }
 
--- a/uriloader/exthandler/win/nsOSHelperAppService.h
+++ b/uriloader/exthandler/win/nsOSHelperAppService.h
@@ -58,18 +58,19 @@ public:
 
   // override nsIExternalProtocolService methods
   nsresult OSProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists);
   nsresult LoadUriInternal(nsIURI * aURL);
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
 
   // method overrides for windows registry look up steps....
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, PRBool *aFound);
-  already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
-                                                         PRBool *found);
+  NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme, 
+                                          PRBool *found,
+                                          nsIHandlerInfo **_retval);
 
   /** Get the string value of a registry value and store it in result.
    * @return PR_TRUE on success, PR_FALSE on failure
    */
   static PRBool GetValueString(HKEY hKey, const PRUnichar* pValueName, nsAString& result);
 
   // Removes registry command handler parameters, quotes, and expands environment strings.
   static PRBool CleanupCmdHandlerPath(nsAString& aCommandHandler);