bug 393492: move mimeTypes.rdf datasource access code from nsExternalHelperAppService.cpp to nsHandlerService.js, fixing bug 393500 (auto-add preferred handler to possible handlers when retrieving them, since they won't be in there already on legacy datasources) in the process; r=biesi; sr=dmose; a=bsmedberg for 1.9
authormyk@mozilla.org
Tue, 04 Sep 2007 13:45:57 -0700
changeset 5657 20a2d91b2c3fe2f04bf05afe1ab87f45967f3c85
parent 5656 9f6792af2176d8e35f195909ecf2cbff6ebcd7c1
child 5658 a2fb3139bb1e6459a08acda5e14fcbf4d81b1c92
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, dmose, bsmedberg
bugs393492, 393500
milestone1.9a8pre
bug 393492: move mimeTypes.rdf datasource access code from nsExternalHelperAppService.cpp to nsHandlerService.js, fixing bug 393500 (auto-add preferred handler to possible handlers when retrieving them, since they won't be in there already on legacy datasources) in the process; r=biesi; sr=dmose; a=bsmedberg for 1.9
netwerk/mime/public/nsIMIMEInfo.idl
uriloader/exthandler/Makefile.in
uriloader/exthandler/nsCExternalHandlerService.idl
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsHandlerService.js
uriloader/exthandler/nsHelperAppRDF.h
uriloader/exthandler/nsIHandlerService.idl
uriloader/exthandler/nsLocalHandlerApp.cpp
uriloader/exthandler/nsWebHandlerApp.js
uriloader/exthandler/tests/unit/test_handlerService.js
--- a/netwerk/mime/public/nsIMIMEInfo.idl
+++ b/netwerk/mime/public/nsIMIMEInfo.idl
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mosedale <dmose@mozilla.org>
+ *   Myk Melez <myk@mozilla.org>
  *
  * 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
@@ -227,29 +228,42 @@ interface nsIMIMEInfo : nsIHandlerInfo {
      * @throw NS_ERROR_INVALID_ARG if action is not valid for this function.
      * Other exceptions may be thrown.
      */
     void launchWithFile(in nsIFile aFile);
 };
 
 /**
  * nsIHandlerApp represents an external application that can handle content
- * of some sort (either a MIME-type or a protocol).  Note that for theoretical
- * cleanliness, nsI{Local,Web}HandlerApp really ought to inherit from
- * nsIHandlerApp.  After that's done, we should also try and make
- * nsIWebContentHandlerInfo inherit from or possibly be replaced by
- * nsIWebHandlerApp.
+ * of some sort (either a MIME type or a protocol).
+ *
+ * FIXME: now that we've made nsIWebHandlerApp inherit from nsIHandlerApp,
+ * we should also try to make nsIWebContentHandlerInfo inherit from or possibly
+ * be replaced by nsIWebHandlerApp (bug 394710).
  */
-[scriptable, uuid(08a543dc-081f-4933-b325-252cf68d6ad9)]
+[scriptable, uuid(b504f39e-d88a-4435-8e0d-e13f1070f7e7)]
 interface nsIHandlerApp : nsISupports {
 
     /**
      * Human readable name for the handler
      */
     attribute AString name;
+
+    /**
+     * Whether or not the given handler app is logically equivalent to the
+     * invokant (i.e. they represent the same app).
+     * 
+     * Two apps are the same if they are both either local or web handlers
+     * and their executables/URI templates are the same in a string comparison.
+     *
+     * @param aHandlerApp the handler app to compare to the invokant
+     *
+     * @returns true if the two are logically equivalent, false otherwise
+     */
+    boolean equals(in nsIHandlerApp aHandlerApp);
 };
 
 /**
  * nsILocalHandlerApp is a local OS-level executable
  */
 [scriptable, uuid(9812be73-273c-478c-8170-c3e0db08ae7c)]
 interface nsILocalHandlerApp : nsIHandlerApp {
 
--- a/uriloader/exthandler/Makefile.in
+++ b/uriloader/exthandler/Makefile.in
@@ -72,17 +72,16 @@ MODULE = exthandler
 LIBRARY_NAME    = exthandler_s
 LIBXUL_LIBRARY  = 1
 REQUIRES	= xpcom \
 		  string \
 		  unicharutil \
 		  mimetype \
 		  uriloader \
 		  necko \
-		  rdf \
 		  webshell \
 		  plugin \
 		  pref \
 		  intl \
 		  uconv \
 		  docshell \
 		  windowwatcher \
 		  embed_base \
--- a/uriloader/exthandler/nsCExternalHandlerService.idl
+++ b/uriloader/exthandler/nsCExternalHandlerService.idl
@@ -50,16 +50,19 @@ nsIExternalHelperAppService
 
 /* A7F800E0-4306-11d4-98D0-001083010E9B */
 #define NS_EXTERNALHELPERAPPSERVICE_CID   \
  { 0xa7f800e0, 0x4306, 0x11d4, { 0x98, 0xd0, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b } }
 
 #define NS_EXTERNALHELPERAPPSERVICE_CONTRACTID \
 "@mozilla.org/uriloader/external-helper-app-service;1"
 
+#define NS_HANDLERSERVICE_CONTRACTID \
+"@mozilla.org/uriloader/handler-service;1"
+
 #define NS_EXTERNALPROTOCOLSERVICE_CONTRACTID \
 "@mozilla.org/uriloader/external-protocol-service;1"
 
 #define NS_MIMESERVICE_CONTRACTID \
 "@mozilla.org/mime;1"
 
 #define NS_EXTERNALPROTOCOLHANDLER_CID	\
 { 0xbd6390c8, 0xfbea, 0x11d4, {0x98, 0xf6, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b } }
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -63,24 +63,18 @@
 #include "nsReadableUtils.h"
 #include "nsIRequest.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsIMutableArray.h"
 
-// used to manage our in memory data source of helper applications
-#ifdef MOZ_RDF
-#include "nsRDFCID.h"
-#include "rdf.h"
-#include "nsIRDFService.h"
-#include "nsIRDFRemoteDataSource.h"
-#include "nsHelperAppRDF.h"
-#endif //MOZ_RDF
+// used to access our datastore of user-configured helper applications
+#include "nsIHandlerService.h"
 #include "nsIMIMEInfo.h"
 #include "nsIRefreshURI.h" // XXX needed to redirect according to Refresh: URI
 #include "nsIDocumentLoader.h" // XXX needed to get orig. channel and assoc. refresh uri
 #include "nsIHelperAppLauncherDialog.h"
 #include "nsIContentDispatchChooser.h"
 #include "nsNetUtil.h"
 #include "nsIIOService.h"
 #include "nsNetCID.h"
@@ -135,19 +129,16 @@ PRLogModuleInfo* nsExternalHelperAppServ
 // Using 3 instead of PR_LOG_WARN because we don't output warnings
 #define LOG(args) PR_LOG(mLog, 3, args)
 #define LOG_ENABLED() PR_LOG_TEST(mLog, 3)
 
 static const char NEVER_ASK_PREF_BRANCH[] = "browser.helperApps.neverAsk.";
 static const char NEVER_ASK_FOR_SAVE_TO_DISK_PREF[] = "saveToDisk";
 static const char NEVER_ASK_FOR_OPEN_FILE_PREF[]    = "openFile";
 
-#ifdef MOZ_RDF
-static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
-#endif
 static NS_DEFINE_CID(kPluginManagerCID, NS_PLUGINMANAGER_CID);
 
 /**
  * Contains a pointer to the helper app service, set in its constructor
  */
 nsExternalHelperAppService* gExtProtSvc;
 
 // Helper functions for Content-Disposition headers
@@ -491,17 +482,16 @@ NS_IMPL_ISUPPORTS6(
   nsIExternalHelperAppService,
   nsPIExternalAppLauncher,
   nsIExternalProtocolService,
   nsIMIMEService,
   nsIObserver,
   nsISupportsWeakReference)
 
 nsExternalHelperAppService::nsExternalHelperAppService()
-: mDataSourceInitialized(PR_FALSE)
 {
   gExtProtSvc = this;
 }
 nsresult nsExternalHelperAppService::Init()
 {
   // Add an observer for profile change
   nsresult rv = NS_OK;
   nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1", &rv);
@@ -518,81 +508,16 @@ nsresult nsExternalHelperAppService::Ini
   return obs->AddObserver(this, "profile-before-change", PR_TRUE);
 }
 
 nsExternalHelperAppService::~nsExternalHelperAppService()
 {
   gExtProtSvc = nsnull;
 }
 
-nsresult nsExternalHelperAppService::InitDataSource()
-{
-#ifdef MOZ_RDF
-  nsresult rv = NS_OK;
-
-  // don't re-initialize the data source if we've already done so...
-  if (mDataSourceInitialized)
-    return NS_OK;
-
-  nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Get URI of the mimeTypes.rdf data source.  Do this the same way it's done in
-  // pref-applications-edit.xul, for example, to ensure we get the same data source!
-  // Note that the way it was done previously (using nsIFileSpec) worked better, but it
-  // produced a different uri (it had "file:///C|" instead of "file:///C:".  What can you do?
-  nsCOMPtr<nsIFile> mimeTypesFile;
-  rv = NS_GetSpecialDirectory(NS_APP_USER_MIMETYPES_50_FILE, getter_AddRefs(mimeTypesFile));
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  // Get file url spec to be used to initialize the DS.
-  nsCAutoString urlSpec;
-  rv = NS_GetURLSpecFromFile(mimeTypesFile, urlSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Get the data source; if it is going to be created, then load is synchronous.
-  rv = rdf->GetDataSourceBlocking( urlSpec.get(), getter_AddRefs( mOverRideDataSource ) );
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // initialize our resources if we haven't done so already...
-  if (!kNC_Description)
-  {
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_DESCRIPTION),
-                     getter_AddRefs(kNC_Description));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_VALUE),
-                     getter_AddRefs(kNC_Value));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_FILEEXTENSIONS),
-                     getter_AddRefs(kNC_FileExtensions));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_PATH),
-                     getter_AddRefs(kNC_Path));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_SAVETODISK),
-                     getter_AddRefs(kNC_SaveToDisk));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_USESYSTEMDEFAULT),
-                     getter_AddRefs(kNC_UseSystemDefault));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_HANDLEINTERNAL),
-                     getter_AddRefs(kNC_HandleInternal));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_ALWAYSASK),
-                     getter_AddRefs(kNC_AlwaysAsk));  
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_POSSIBLEAPPLICATION),
-                     getter_AddRefs(kNC_PossibleApplication));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_PRETTYNAME),
-                     getter_AddRefs(kNC_PrettyName));
-    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_URITEMPLATE),
-                     getter_AddRefs(kNC_UriTemplate));
-     
-  }
-  
-  mDataSourceInitialized = PR_TRUE;
-
-  return rv;
-#else
-  return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
 NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType,
                                                     nsIRequest *aRequest,
                                                     nsIInterfaceRequestor *aWindowContext,
                                                     nsIStreamListener ** aStreamListener)
 {
   nsAutoString fileName;
   nsCAutoString fileExtension;
   PRUint32 reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
@@ -720,456 +645,16 @@ NS_IMETHODIMP nsExternalHelperAppService
         aEncodingType.LowerCaseEqualsASCII(nonDecodableExtensions[i].mMimeType)) {
       *aApplyDecoding = PR_FALSE;
       break;
     }
   }
   return NS_OK;
 }
 
-#ifdef MOZ_RDF
-nsresult nsExternalHelperAppService::FillMIMEExtensionProperties(
-  nsIRDFResource * aContentTypeNodeResource, nsIRDFService * aRDFService,
-  nsIMIMEInfo * aMIMEInfo)
-{
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsIRDFNode> target;
-  nsCOMPtr<nsIRDFLiteral> literal;
-  const PRUnichar * stringValue;
-  
-  rv = InitDataSource();
-  if (NS_FAILED(rv)) return NS_OK;
-
-  // iterate over all the file type extensions...
-  nsCOMPtr<nsISimpleEnumerator> fileExtensions;
-  mOverRideDataSource->GetTargets(aContentTypeNodeResource, kNC_FileExtensions, PR_TRUE, getter_AddRefs(fileExtensions));
-
-  PRBool hasMoreElements = PR_FALSE;
-  nsCAutoString fileExtension; 
-  nsCOMPtr<nsISupports> element;
-
-  if (fileExtensions)
-  {
-    fileExtensions->HasMoreElements(&hasMoreElements);
-    while (hasMoreElements)
-    { 
-      fileExtensions->GetNext(getter_AddRefs(element));
-      if (element)
-      {
-        literal = do_QueryInterface(element);
-        if (!literal) return NS_ERROR_FAILURE;
-
-        literal->GetValueConst(&stringValue);
-        CopyUTF16toUTF8(stringValue, fileExtension);
-        if (!fileExtension.IsEmpty())
-          aMIMEInfo->AppendExtension(fileExtension);
-      }
-  
-      fileExtensions->HasMoreElements(&hasMoreElements);
-    } // while we have more extensions to parse....
-  }
-
-  return rv;
-}
-
-nsresult nsExternalHelperAppService::FillLiteralValueFromTarget(
-  nsIRDFResource * aSource, nsIRDFResource * aProperty,
-  const PRUnichar ** aLiteralValue)
-{
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsIRDFLiteral> literal;
-  nsCOMPtr<nsIRDFNode> target;
-
-  *aLiteralValue = nsnull;
-  rv = InitDataSource();
-  if (NS_FAILED(rv)) return rv;
-
-  mOverRideDataSource->GetTarget(aSource, aProperty, PR_TRUE, getter_AddRefs(target));
-  if (target)
-  {
-    literal = do_QueryInterface(target);    
-    if (!literal)
-      return NS_ERROR_FAILURE;
-    literal->GetValueConst(aLiteralValue);
-  }
-  else
-    rv = NS_ERROR_FAILURE;
-
-  return rv;
-}
-
-nsresult nsExternalHelperAppService::FillHandlerAppFromSource(
-  nsIRDFResource * aSource, nsIHandlerApp ** aHandlerApp)
-{
-  nsresult rv = NS_OK;
-
-  const PRUnichar * appName = nsnull;
-  FillLiteralValueFromTarget(aSource, kNC_PrettyName, &appName);
-
-  // if we've got a path name, this must be a local app
-  const PRUnichar * path = nsnull;
-  FillLiteralValueFromTarget(aSource, kNC_Path, &path);
-  if (path && path[0])
-  {
-    nsCOMPtr<nsIFile> application;
-    GetFileTokenForPath(path, getter_AddRefs(application));
-    if (application) {
-      nsLocalHandlerApp *handlerApp(new nsLocalHandlerApp(appName, application));
-      if (!handlerApp) { 
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      NS_ADDREF(*aHandlerApp = handlerApp);
-    }
-  } else {
-    // if we got here, there's no path name in the RDF graph, so this must 
-    // be a web app
-    const PRUnichar * uriTemplate = nsnull;
-    FillLiteralValueFromTarget(aSource, kNC_UriTemplate, &uriTemplate);
-    if (uriTemplate && uriTemplate[0]) {
-      nsCOMPtr<nsIWebHandlerApp> handlerApp =
-        do_CreateInstance(NS_WEBHANDLERAPP_CONTRACTID, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      handlerApp->SetName(nsDependentString(appName));
-      handlerApp->SetUriTemplate(NS_ConvertUTF16toUTF8(uriTemplate));
-
-      NS_ADDREF(*aHandlerApp = handlerApp);
-    } else {
-      return NS_ERROR_FAILURE; // no path name _and_ no uri template
-    }
-  }
-
-  return rv;
-}
-
-nsresult nsExternalHelperAppService::FillPossibleAppsFromSource(
-  nsIRDFResource * aSource, nsIMutableArray * aPossibleApps)
-{
-  nsresult rv = NS_OK;
-
-  rv = InitDataSource();
-  if (NS_FAILED(rv)) return rv;
-
-  nsCOMPtr<nsISimpleEnumerator> possibleAppResources;
-  rv = mOverRideDataSource->GetTargets(aSource, kNC_PossibleApplication, PR_TRUE,
-                                       getter_AddRefs(possibleAppResources));
-  if (NS_FAILED(rv)) return rv;
-
-  PRBool more;
-  nsCOMPtr<nsISupports> supports;
-  while (NS_SUCCEEDED(possibleAppResources->HasMoreElements(&more)) && more) {
-    rv = possibleAppResources->GetNext(getter_AddRefs(supports));
-    if (NS_FAILED(rv)) return rv;
-
-    nsCOMPtr<nsIRDFResource> possibleAppResource = do_QueryInterface(supports);
-
-    if (possibleAppResource) {
-      nsCOMPtr<nsIHandlerApp> possibleApp;
-      rv = FillHandlerAppFromSource(possibleAppResource, getter_AddRefs(possibleApp));
-      // It's ok if we failed to get one of the possible apps, as some of the
-      // others might still be good, so merely continue on failure.
-      if (NS_FAILED(rv)) continue;
-
-      rv = aPossibleApps->AppendElement(possibleApp, PR_FALSE);
-      if (NS_FAILED(rv)) return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-
-nsresult nsExternalHelperAppService::FillContentHandlerProperties(
-  const char * aContentType, const char * aTypeNodePrefix,
-  nsIRDFService * aRDFService, nsIHandlerInfo * aHandlerInfo)
-{
-  nsCOMPtr<nsIRDFNode> target;
-  nsCOMPtr<nsIRDFLiteral> literal;
-  const PRUnichar * stringValue = nsnull;
-  nsresult rv = NS_OK;
-
-  rv = InitDataSource();
-  if (NS_FAILED(rv)) return rv;
-
-  nsCAutoString contentTypeHandlerNodeName(aTypeNodePrefix);
-  contentTypeHandlerNodeName.Append(NC_HANDLER_SUFFIX);
-  contentTypeHandlerNodeName.Append(aContentType);
-
-  nsCOMPtr<nsIRDFResource> contentTypeHandlerNodeResource;
-  aRDFService->GetResource(contentTypeHandlerNodeName, getter_AddRefs(contentTypeHandlerNodeResource));
-  NS_ENSURE_TRUE(contentTypeHandlerNodeResource, NS_ERROR_FAILURE); // that's not good! we have an error in the rdf file
-
-  // now process the application handler information
-  aHandlerInfo->SetPreferredAction(nsIHandlerInfo::useHelperApp);
-
-  // save to disk
-  FillLiteralValueFromTarget(contentTypeHandlerNodeResource,kNC_SaveToDisk, &stringValue);
-  NS_NAMED_LITERAL_STRING(trueString, "true");
-  NS_NAMED_LITERAL_STRING(falseString, "false");
-  if (stringValue && trueString.Equals(stringValue))
-       aHandlerInfo->SetPreferredAction(nsIHandlerInfo::saveToDisk);
-
-  // use system default
-  FillLiteralValueFromTarget(contentTypeHandlerNodeResource,kNC_UseSystemDefault, &stringValue);
-  if (stringValue && trueString.Equals(stringValue))
-      aHandlerInfo->SetPreferredAction(nsIHandlerInfo::useSystemDefault);
-
-  // handle internal
-  FillLiteralValueFromTarget(contentTypeHandlerNodeResource,kNC_HandleInternal, &stringValue);
-  if (stringValue && trueString.Equals(stringValue))
-       aHandlerInfo->SetPreferredAction(nsIHandlerInfo::handleInternally);
-  
-  // always ask
-  FillLiteralValueFromTarget(contentTypeHandlerNodeResource,kNC_AlwaysAsk, &stringValue);
-  // Only skip asking if we are absolutely sure the user does not want
-  // to be asked.  Any sort of bofus data should mean we ask.
-  aHandlerInfo->SetAlwaysAskBeforeHandling(!stringValue ||
-                                           !falseString.Equals(stringValue));
-
-
-  // now digest the external application information
-
-  // Clear out any possibly set applications to match the datasource.
-  aHandlerInfo->SetPreferredApplicationHandler(nsnull);
-
-  // Get the preferred application, if any.
-  nsCAutoString externalAppNodeName(aTypeNodePrefix);
-  externalAppNodeName.AppendLiteral(NC_EXTERNALAPP_SUFFIX);
-  externalAppNodeName.Append(aContentType);
-  nsCOMPtr<nsIRDFResource> externalAppNodeResource;
-  aRDFService->GetResource(externalAppNodeName, getter_AddRefs(externalAppNodeResource));
-
-  if (externalAppNodeResource)
-  {
-    nsCOMPtr<nsIHandlerApp> preferredApp;
-    rv = FillHandlerAppFromSource(externalAppNodeResource, getter_AddRefs(preferredApp));
-
-    if (NS_SUCCEEDED(rv))
-      aHandlerInfo->SetPreferredApplicationHandler(preferredApp);
-  }
-
-  // Get the possible applications, if any.
-  nsCOMPtr<nsIMutableArray> possibleApps;
-  rv = aHandlerInfo->GetPossibleApplicationHandlers(getter_AddRefs(possibleApps));
-  if (NS_FAILED(rv)) return rv;
-
-  // Clear out any possibly set applications to match the datasource.
-  possibleApps->Clear();
-
-  rv = FillPossibleAppsFromSource(contentTypeHandlerNodeResource, possibleApps);
-
-  return rv;
-}
-#endif /* MOZ_RDF */
-
-PRBool nsExternalHelperAppService::MIMETypeIsInDataSource(const char * aContentType)
-{
-#ifdef MOZ_RDF
-  nsresult rv = InitDataSource();
-  if (NS_FAILED(rv)) return PR_FALSE;
-  
-  if (mOverRideDataSource)
-  {
-    // Get the RDF service.
-    nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID, &rv);
-    if (NS_FAILED(rv)) return PR_FALSE;
-    
-    // Build uri for the mimetype resource.
-    nsCAutoString contentTypeNodeName(NC_CONTENT_NODE_PREFIX);
-    nsCAutoString contentType(aContentType);
-    ToLowerCase(contentType);
-    contentTypeNodeName.Append(contentType);
-    
-    // Get the mime type resource.
-    nsCOMPtr<nsIRDFResource> contentTypeNodeResource;
-    rv = rdf->GetResource(contentTypeNodeName, getter_AddRefs(contentTypeNodeResource));
-    if (NS_FAILED(rv)) return PR_FALSE;
-    
-    // Test that there's a #value arc from the mimetype resource to the mimetype literal string.
-    nsCOMPtr<nsIRDFLiteral> mimeLiteral;
-    NS_ConvertUTF8toUTF16 mimeType(contentType);
-    rv = rdf->GetLiteral( mimeType.get(), getter_AddRefs( mimeLiteral ) );
-    if (NS_FAILED(rv)) return PR_FALSE;
-    
-    PRBool exists = PR_FALSE;
-    rv = mOverRideDataSource->HasAssertion(contentTypeNodeResource, kNC_Value, mimeLiteral, PR_TRUE, &exists );
-    
-    if (NS_SUCCEEDED(rv) && exists) return PR_TRUE;
-  }
-#endif
-  return PR_FALSE;
-}
-
-nsresult nsExternalHelperAppService::FillMIMEInfoForMimeTypeFromDS(
-  const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo)
-{
-#ifdef MOZ_RDF
-  NS_ENSURE_ARG_POINTER(aMIMEInfo);
-  nsresult rv = InitDataSource();
-  if (NS_FAILED(rv)) return rv;
-
-  // can't do anything if we have no datasource...
-  if (!mOverRideDataSource)
-    return NS_ERROR_FAILURE;
-
-  // Get the RDF service.
-  nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // get lowercase typename & uri for the handlertype resource.
-  nsCAutoString typeNodeName(NC_CONTENT_NODE_PREFIX);
-  nsCAutoString type(aContentType);
-  ToLowerCase(type);
-  typeNodeName.Append(type);
-
-  // Get the handler type resource.
-  nsCOMPtr<nsIRDFResource> typeNodeResource;
-  rv = rdf->GetResource(typeNodeName, getter_AddRefs(typeNodeResource));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // fill in MIME-specific extension info
-  rv = FillMIMEExtensionProperties(typeNodeResource, rdf, aMIMEInfo);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return FillHandlerInfoForTypeFromDS(typeNodeResource.get(), type, rdf, 
-                                      NC_CONTENT_NODE_PREFIX, aMIMEInfo); 
-#else
-  return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-nsresult nsExternalHelperAppService::FillProtoInfoForSchemeFromDS(
-    const nsACString& aType, nsIHandlerInfo * aHandlerInfo)
-{
-#ifdef MOZ_RDF
-  NS_ENSURE_ARG_POINTER(aHandlerInfo);
-  nsresult rv = InitDataSource();
-  if (NS_FAILED(rv)) return rv;
-
-  // can't do anything if we have no datasource...
-  if (!mOverRideDataSource)
-    return NS_ERROR_FAILURE;
-
-  // Get the RDF service.
-  nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // get lowercase typename & get uri for the handlertype resource.
-  nsCAutoString typeNodeName(NC_SCHEME_NODE_PREFIX);
-  nsCAutoString type(aType);
-  ToLowerCase(type);
-  typeNodeName.Append(type);
-
-  // Get the handler type resource.
-  nsCOMPtr<nsIRDFResource> typeNodeResource;
-  rv = rdf->GetResource(typeNodeName, getter_AddRefs(typeNodeResource));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return FillHandlerInfoForTypeFromDS(typeNodeResource.get(), type, rdf,
-                                      NC_SCHEME_NODE_PREFIX, aHandlerInfo);
-#else
-  return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-#ifdef MOZ_RDF
-nsresult nsExternalHelperAppService::FillHandlerInfoForTypeFromDS(
-  nsIRDFResource *aTypeNodeResource, const nsCAutoString &aType, 
-  nsIRDFService *rdf, const char *aTypeNodePrefix,
-  nsIHandlerInfo * aHandlerInfo)
-{
-
-  // we need a way to determine if this type resource is really in the graph
-  // or not... Test that there's a #value arc from the type resource to the
-  // type literal string.
-  nsCOMPtr<nsIRDFLiteral> typeLiteral;
-  NS_ConvertUTF8toUTF16 UTF16Type(aType);
-  nsresult rv = rdf->GetLiteral( UTF16Type.get(), 
-                                 getter_AddRefs( typeLiteral ) );
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  PRBool exists = PR_FALSE;
-  rv = mOverRideDataSource->HasAssertion(aTypeNodeResource, kNC_Value,
-                                         typeLiteral, PR_TRUE, &exists );
-  if (NS_SUCCEEDED(rv) && exists)
-  {
-     // fill in the handlerinfo based on the values from the data source
-
-     // set the pretty name description, if nonempty
-     const PRUnichar *stringValue;
-     FillLiteralValueFromTarget(aTypeNodeResource, kNC_Description, 
-                                &stringValue);
-     if (stringValue && *stringValue)
-       aHandlerInfo->SetDescription(nsDependentString(stringValue));
-
-     rv = FillContentHandlerProperties(aType.get(), aTypeNodePrefix,
-                                       rdf, aHandlerInfo);
-
-  } // if we have a node in the graph for this content type
-  // If we had success, but entry doesn't exist, we don't want to return success
-  else if (NS_SUCCEEDED(rv)) {
-    rv = NS_ERROR_NOT_AVAILABLE;
-  }
-
-  return rv;
-}
-#endif /* MOZ_RDF */
-
-nsresult nsExternalHelperAppService::FillMIMEInfoForExtensionFromDS(
-  const nsACString& aFileExtension, nsIMIMEInfo * aMIMEInfo)
-{
-  nsCAutoString type;
-  PRBool found = GetTypeFromDS(aFileExtension, type);
-  if (!found)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  return FillMIMEInfoForMimeTypeFromDS(type, aMIMEInfo);
-}
-
-PRBool nsExternalHelperAppService::GetTypeFromDS(const nsACString& aExtension,
-                                                 nsACString& aType)
-{
-#ifdef MOZ_RDF
-  nsresult rv = InitDataSource();
-  if (NS_FAILED(rv))
-    return PR_FALSE;
-
-  // Can't do anything without a datasource
-  if (!mOverRideDataSource)
-    return PR_FALSE;
-
-  // Get the RDF service.
-  nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID, &rv);
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  NS_ConvertUTF8toUTF16 extension(aExtension);
-  ToLowerCase(extension);
-  nsCOMPtr<nsIRDFLiteral> extensionLiteral;
-  rv = rdf->GetLiteral(extension.get(), getter_AddRefs(extensionLiteral));
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  nsCOMPtr<nsIRDFResource> contentTypeNodeResource;
-  rv = mOverRideDataSource->GetSource(kNC_FileExtensions,
-                                      extensionLiteral,
-                                      PR_TRUE,
-                                      getter_AddRefs(contentTypeNodeResource));
-  nsCAutoString contentTypeStr;
-  if (NS_SUCCEEDED(rv) && contentTypeNodeResource)
-  {
-    const PRUnichar* contentType = nsnull;
-    rv = FillLiteralValueFromTarget(contentTypeNodeResource, kNC_Value, &contentType);
-    if (contentType) {
-      LossyCopyUTF16toASCII(contentType, aType);
-      return PR_TRUE;
-    }
-  }  // if we have a node in the graph for this extension
-#endif /* MOZ_RDF */
-  return PR_FALSE;
-}
-
 nsresult nsExternalHelperAppService::GetFileTokenForPath(const PRUnichar * aPlatformAppPath,
                                                          nsIFile ** aFile)
 {
   nsDependentString platformAppPath(aPlatformAppPath);
   // First, check if we have an absolute path
   nsILocalFile* localFile = nsnull;
   nsresult rv = NS_NewLocalFile(platformAppPath, PR_TRUE, &localFile);
   if (NS_SUCCEEDED(rv)) {
@@ -1408,51 +893,45 @@ nsExternalHelperAppService::GetProtocolH
 
   PRBool exists;
   *aHandlerInfo = GetProtocolInfoFromOS(aScheme, &exists).get();
   if (!(*aHandlerInfo)) {
     // Either it knows nothing, or we ran out of memory
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = FillProtoInfoForSchemeFromDS(aScheme, *aHandlerInfo);     
-  if (NS_ERROR_NOT_AVAILABLE == rv) {
+  nsresult rv = NS_ERROR_FAILURE;
+  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  if (handlerSvc)
+    rv = handlerSvc->FillHandlerInfo(*aHandlerInfo, EmptyCString());
+
+  if (NS_FAILED(rv)) {
     // We don't know it, so we always ask the user.  By the time we call this
     // method, we already have checked if we should open this protocol and ask
     // the user, so these defaults are OK.
     // XXX this is a bit different than the MIME system, so we may want to look
     //     into this more in the future.
     (*aHandlerInfo)->SetAlwaysAskBeforeHandling(PR_TRUE);
     // If no OS default existed, we set the preferred action to alwaysAsk.  This
     // really means not initialized to all the code...
     if (exists)
       (*aHandlerInfo)->SetPreferredAction(nsIHandlerInfo::useSystemDefault);
     else
       (*aHandlerInfo)->SetPreferredAction(nsIHandlerInfo::alwaysAsk);
-  } else if (NS_FAILED(rv)) {
-    NS_RELEASE(*aHandlerInfo);
-    return rv;
   }
 
   return NS_OK;
 }
  
 // XPCOM profile change observer
 NS_IMETHODIMP
 nsExternalHelperAppService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData )
 {
   if (!strcmp(aTopic, "profile-before-change")) {
     ExpungeTemporaryFiles();
-#ifdef MOZ_RDF
-    nsCOMPtr <nsIRDFRemoteDataSource> flushableDataSource = do_QueryInterface(mOverRideDataSource);
-    if (flushableDataSource)
-      flushableDataSource->Flush();
-    mOverRideDataSource = nsnull;
-    mDataSourceInitialized = PR_FALSE;
-#endif
   }
   return NS_OK;
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 // begin external app handler implementation 
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -1850,22 +1329,27 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
   // So let's find out whether the user wants to be prompted.  If he does not,
   // check mReason and the preferred action to see what we should do.
 
   PRBool alwaysAsk = PR_TRUE;
   mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
   if (alwaysAsk)
   {
     // But we *don't* ask if this mimeInfo didn't come from
-    // our mimeTypes.rdf data source and the user has said
+    // our user configuration datastore and the user has said
     // at some point in the distant past that they don't
     // want to be asked.  The latter fact would have been
     // stored in pref strings back in the old days.
     NS_ASSERTION(gExtProtSvc, "Service gone away!?");
-    if (!gExtProtSvc->MIMETypeIsInDataSource(MIMEType.get()))
+
+    PRBool mimeTypeIsInDatastore = PR_FALSE;
+    nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+    if (handlerSvc)
+      handlerSvc->Exists(mMimeInfo, &mimeTypeIsInDatastore);
+    if (!handlerSvc || !mimeTypeIsInDatastore)
     {
       if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_SAVE_TO_DISK_PREF, MIMEType.get()))
       {
         // Don't need to ask after all.
         alwaysAsk = PR_FALSE;
         // Make sure action matches pref (save to disk).
         mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
       }
@@ -2768,30 +2252,37 @@ NS_IMETHODIMP nsExternalHelperAppService
   // (1) Ask the OS for a mime info
   PRBool found;
   *_retval = GetMIMEInfoFromOS(typeToUse, aFileExt, &found).get();
   LOG(("OS gave back 0x%p - found: %i\n", *_retval, found));
   // If we got no mimeinfo, something went wrong. Probably lack of memory.
   if (!*_retval)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  // (2) Now, let's see if we can find something in our datasource
+  // (2) Now, let's see if we can find something in our datastore
   // This will not overwrite the OS information that interests us
   // (i.e. default application, default app. description)
-  nsresult rv = FillMIMEInfoForMimeTypeFromDS(typeToUse, *_retval);
-  found = found || NS_SUCCEEDED(rv);
-
-  LOG(("Data source: Via type: retval 0x%08x\n", rv));
+  nsresult rv;
+  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  if (handlerSvc) {
+    rv = handlerSvc->FillHandlerInfo(*_retval, EmptyCString());
+    LOG(("Data source: Via type: retval 0x%08x\n", rv));
+    found = found || NS_SUCCEEDED(rv);
 
-  if (!found || NS_FAILED(rv)) {
-    // No type match, try extension match
-    if (!aFileExt.IsEmpty()) {
-      rv = FillMIMEInfoForExtensionFromDS(aFileExt, *_retval);
-      LOG(("Data source: Via ext: retval 0x%08x\n", rv));
-      found = found || NS_SUCCEEDED(rv);
+    if (!found || NS_FAILED(rv)) {
+      // No type match, try extension match
+      if (!aFileExt.IsEmpty()) {
+        nsCAutoString overrideType;
+        rv = handlerSvc->GetTypeFromExtension(aFileExt, overrideType);
+        if (NS_SUCCEEDED(rv)) {
+          rv = handlerSvc->FillHandlerInfo(*_retval, overrideType);
+          LOG(("Data source: Via ext: retval 0x%08x\n", rv));
+          found = found || NS_SUCCEEDED(rv);
+        }
+      }
     }
   }
 
   // (3) No match yet. Ask extras.
   if (!found) {
     rv = NS_ERROR_FAILURE;
 #ifdef XP_WIN
     /* XXX Gross hack to wallpaper over the most common Win32
@@ -2835,38 +2326,41 @@ NS_IMETHODIMP nsExternalHelperAppService
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExternalHelperAppService::GetTypeFromExtension(const nsACString& aFileExt, nsACString& aContentType) 
 {
   // OK. We want to try the following sources of mimetype information, in this order:
   // 1. defaultMimeEntries array
-  // 2. User-set preferences (mimeTypes.rdf)
+  // 2. User-set preferences (managed by the handler service)
   // 3. OS-provided information
   // 4. our "extras" array
   // 5. Information from plugins
   // 6. The "ext-to-type-mapping" category
 
   nsresult rv = NS_OK;
   // First of all, check our default entries
   for (size_t i = 0; i < NS_ARRAY_LENGTH(defaultMimeEntries); i++)
   {
     if (aFileExt.LowerCaseEqualsASCII(defaultMimeEntries[i].mFileExtension)) {
       aContentType = defaultMimeEntries[i].mMimeType;
       return rv;
     }
   }
 
-  // Check RDF DS
-  PRBool found = GetTypeFromDS(aFileExt, aContentType);
-  if (found)
+  // Check user-set prefs
+  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  if (handlerSvc)
+    rv = handlerSvc->GetTypeFromExtension(aFileExt, aContentType);
+  if (NS_SUCCEEDED(rv))
     return NS_OK;
 
   // Ask OS.
+  PRBool found = PR_FALSE;
   nsCOMPtr<nsIMIMEInfo> mi = GetMIMEInfoFromOS(EmptyCString(), aFileExt, &found);
   if (mi && found)
     return mi->GetMIMEType(aContentType);
 
   // Check extras array.
   found = GetTypeFromExtras(aFileExt, aContentType);
   if (found)
     return NS_OK;
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -64,29 +64,25 @@
 #include "nsIOutputStream.h"
 #include "nsString.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILocalFile.h"
 #include "nsIChannel.h"
 #include "nsITimer.h"
 
-#ifdef MOZ_RDF
-#include "nsIRDFDataSource.h"
-#include "nsIRDFResource.h"
-#endif
+#include "nsIHandlerService.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsCOMArray.h"
 #include "nsWeakReference.h"
 #include "nsIPrompt.h"
 
 class nsExternalAppHandler;
 class nsIMIMEInfo;
-class nsIRDFService;
 class nsITransfer;
 class nsIDOMWindowInternal;
 
 /**
  * The helper app service. Responsible for handling content that Mozilla
  * itself can not handle
  */
 class nsExternalHelperAppService
@@ -102,89 +98,24 @@ public:
   NS_DECL_NSIEXTERNALHELPERAPPSERVICE
   NS_DECL_NSPIEXTERNALAPPLAUNCHER
   NS_DECL_NSIEXTERNALPROTOCOLSERVICE
   NS_DECL_NSIMIMESERVICE
   NS_DECL_NSIOBSERVER
 
   nsExternalHelperAppService();
   virtual ~nsExternalHelperAppService();
-  /**
-   * Initializes the RDF datasource from the profile.
-   * @retval NS_OK Loading was successful
-   * @retval errorcode Loading failed
-   * @see mOverRideDataSource
-   */
-  NS_HIDDEN_(nsresult) InitDataSource();
 
   /**
    * Initializes internal state. Will be called automatically when
    * this service is first instantiated.
    */
   NS_HIDDEN_(nsresult) Init();
  
   /**
-   * Given an existing MIME info object and a MIME type, fill in any user
-   * override info from the in-memory data source.
-   *
-   * @param aContentType  The MIME content-type 
-   * @param aMIMEInfo     The mime info to fill with the information
-   */
-  NS_HIDDEN_(nsresult) FillMIMEInfoForMimeTypeFromDS(
-    const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo);
-
-  /**
-   * Given an existing protocol info object and a protocol scheme, fill in
-   * any user override info from the in-memory data source.
-   *
-   * @param aScheme   The protocol scheme
-   * @param aMIMEInfo The mime info to fill with the information
-   */
-  NS_HIDDEN_(nsresult) FillProtoInfoForSchemeFromDS(
-    const nsACString& aScheme, nsIHandlerInfo * aMIMEInfo);
-
-#ifdef MOZ_RDF
-  /**
-   * Fill in the generic handler info stuff; called by Fill*InfoFor*FromDS.
-   * 
-   * @param aTypeNodeResource  RDF resource representing the top level scheme
-   *                           or MIME-type node in the graph
-   * @param aType              content-type or scheme name 
-   * @param aRDFService        the RDF service
-   * @param aTypeNodePrefix    One of NC_{CONTENT,SCHEME}_NODE_PREFIX
-   * @param aHandlerInfo       object to be filled in
-   */
-  NS_HIDDEN_(nsresult) FillHandlerInfoForTypeFromDS(
-    nsIRDFResource *aTypeNodeResource, const nsCAutoString& aType,
-    nsIRDFService *aRDFService, const char *aTypeNodePrefix, 
-    nsIHandlerInfo * aHandlerInfo);
-#endif
-    
-  /**
-   * Given an extension, look up the user override information to see if we
-   * have a mime info object representing this extension. The user over ride
-   * information is contained in an in-memory data source.
-   *
-   * Does not change the MIME Type of the MIME Info.
-   *
-   * @param aMIMEInfo The mime info to fill with the information
-   */
-  NS_HIDDEN_(nsresult) FillMIMEInfoForExtensionFromDS(
-    const nsACString& aFileExtension, nsIMIMEInfo * aMIMEInfo);
-
-  /**
-   * Looks up the MIME Type for a given extension in the RDF Datasource.
-   * @param aExtension The extension to look for
-   * @param aType [out] The type, if found
-   * @return PR_TRUE if found, PR_FALSE otherwise
-   */
-  NS_HIDDEN_(PRBool) GetTypeFromDS(const nsACString& aFileExtension,
-                                   nsACString& aType);
-
-  /**
    * Given a mimetype and an extension, looks up a mime info from the OS.
    * The mime type is given preference. This function follows the same rules
    * as nsIMIMEService::GetFromTypeAndExtension.
    * This is supposed to be overridden by the platform-specific
    * nsOSHelperAppService!
    * @param aFileExt The file extension; may be empty. UTF-8 encoded.
    * @param [out] aFound
    *        Should be set to PR_TRUE if the os has a mapping, to
@@ -220,93 +151,21 @@ public:
    *                        file spec, a unix path or a windows path depending
    *                        on the platform
    * @param aFile           [out] An nsIFile representation of that platform
    *                        application path.
    */
   virtual nsresult GetFileTokenForPath(const PRUnichar * platformAppPath,
                                        nsIFile ** aFile);
 
-  /**
-   * Helper routine used to test whether a given mime type is in our
-   * mimeTypes.rdf data source
-   */
-  NS_HIDDEN_(PRBool) MIMETypeIsInDataSource(const char * aContentType);
-
   virtual NS_HIDDEN_(nsresult) OSProtocolHandlerExists(const char *aScheme,
                                                        PRBool *aExists) = 0;
 
 protected:
   /**
-   * Pointer to the datasource that contains the user override information.
-   * @see InitDataSource
-   */
-#ifdef MOZ_RDF
-  nsCOMPtr<nsIRDFDataSource> mOverRideDataSource;
-
-  nsCOMPtr<nsIRDFResource> kNC_Description;
-  nsCOMPtr<nsIRDFResource> kNC_Value;
-  nsCOMPtr<nsIRDFResource> kNC_FileExtensions;
-  nsCOMPtr<nsIRDFResource> kNC_Path;
-  nsCOMPtr<nsIRDFResource> kNC_UseSystemDefault;
-  nsCOMPtr<nsIRDFResource> kNC_SaveToDisk;
-  nsCOMPtr<nsIRDFResource> kNC_AlwaysAsk;
-  nsCOMPtr<nsIRDFResource> kNC_HandleInternal;
-  nsCOMPtr<nsIRDFResource> kNC_PrettyName;
-  nsCOMPtr<nsIRDFResource> kNC_UriTemplate;
-  nsCOMPtr<nsIRDFResource> kNC_PossibleApplication;
-#endif
-
-  /**
-   * Whether mOverRideDataSource is initialized
-   */
-  PRBool mDataSourceInitialized;
-
-  /**
-   * Helper routines for digesting the data source and filling in a handler
-   * info object for a given content type inside that data source. The content
-   * type of the handler info object will not be changed.
-   */
-#ifdef MOZ_RDF
-  NS_HIDDEN_(nsresult) FillMIMEExtensionProperties(
-    nsIRDFResource * aContentTypeNodeResource, nsIRDFService * aRDFService,
-    nsIMIMEInfo * aMIMEInfo);
-  
-  /**
-   * @see FillMIMEExtensionProperties
-   */
-  NS_HIDDEN_(nsresult) FillContentHandlerProperties(const char * aContentType,
-                                                    const char * aNodePrefix,
-                                                    nsIRDFService * aRDFService,
-                                                    nsIHandlerInfo * aHandler);
-
-  /**
-   * A small helper function which gets the target for a given source and
-   * property. QIs to a literal and returns a CONST ptr to the string value
-   * of that target
-   */
-  NS_HIDDEN_(nsresult) FillLiteralValueFromTarget(nsIRDFResource * aSource,
-                                                  nsIRDFResource * aProperty,
-                                                  const PRUnichar ** aLiteralValue);
-
-  /**
-   * Returns the nsIHandlerApp represented by the source node.
-   */
-  NS_HIDDEN_(nsresult) FillHandlerAppFromSource(nsIRDFResource * aSource,
-                                                nsIHandlerApp ** aHandlerApp);
-
-  /**
-   * Returns an array of nsIHandlerApp objects representing possible apps
-   * for the handler represented by the source node.
-   */
-  NS_HIDDEN_(nsresult) FillPossibleAppsFromSource(nsIRDFResource * aSource,
-                                                  nsIMutableArray * aPossibleApps);
-#endif
-
-  /**
    * Searches the "extra" array of MIMEInfo objects for an object
    * with a specific type. If found, it will modify the passed-in
    * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
    * will be untouched.
    * @param aContentType The type to search for.
    * @param aMIMEInfo    [inout] The mime info, if found
    */
   NS_HIDDEN_(nsresult) FillMIMEInfoForMimeTypeFromExtras(
--- a/uriloader/exthandler/nsHandlerService.js
+++ b/uriloader/exthandler/nsHandlerService.js
@@ -51,16 +51,20 @@ const NC_NS                 = "http://ho
 
 const NC_MIME_TYPES         = NC_NS + "MIME-types";
 const NC_PROTOCOL_SCHEMES   = NC_NS + "Protocol-Schemes";
 
 // content type ("type") properties
 
 // nsIHandlerInfo::type
 const NC_VALUE              = NC_NS + "value";
+const NC_DESCRIPTION        = NC_NS + "description";
+
+// additional extensions
+const NC_FILE_EXTENSIONS    = NC_NS + "fileExtensions";
 
 // references nsIHandlerInfo record
 const NC_HANDLER_INFO       = NC_NS + "handlerProp";
 
 // handler info ("info") properties
 
 // nsIHandlerInfo::preferredAction
 const NC_SAVE_TO_DISK       = NC_NS + "saveToDisk";
@@ -84,39 +88,138 @@ const NC_PATH               = NC_NS + "p
 
 // nsIWebHandlerApp::uriTemplate
 const NC_URI_TEMPLATE       = NC_NS + "uriTemplate";
 
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 
-function HandlerService() {}
+function HandlerService() {
+  this._init();
+}
 
 HandlerService.prototype = {
   //**************************************************************************//
   // XPCOM Plumbing
 
   classDescription: "Handler Service",
   classID:          Components.ID("{32314cc8-22f7-4f7f-a645-1a45453ba6a6}"),
   contractID:       "@mozilla.org/uriloader/handler-service;1",
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIHandlerService]),
 
 
   //**************************************************************************//
+  // Initialization & Destruction
+  
+  _init: function HS__init() {
+    // Observe profile-before-change so we can switch to the datasource
+    // in the new profile when the user changes profiles.
+    this._observerSvc.addObserver(this, "profile-before-change", false);
+
+    // Observe xpcom-shutdown so we can remove these observers
+    // when the application shuts down.
+    this._observerSvc.addObserver(this, "xpcom-shutdown", false);
+  },
+
+  _destroy: function HS__destroy() {
+    this._observerSvc.removeObserver(this, "profile-before-change");
+    this._observerSvc.removeObserver(this, "xpcom-shutdown");
+
+    // XXX Should we also null references to all the services that get stored
+    // by our memoizing getters in the Convenience Getters section?
+  },
+
+  _onProfileChange: function HS__onProfileChange() {
+    // Lose our reference to the datasource so we reacquire it
+    // from the new profile the next time we need it.
+    this.__ds = null;
+  },
+
+
+  //**************************************************************************//
+  // nsIObserver
+  
+  observe: function HS__observe(subject, topic, data) {
+    switch(topic) {
+      case "profile-before-change":
+        this._onProfileChange();
+        break;
+      case "xpcom-shutdown":
+        this._destroy();
+        break;
+    }
+  },
+
+
+  //**************************************************************************//
   // nsIHandlerService
 
   enumerate: function HS_enumerate() {
     var handlers = Cc["@mozilla.org/array;1"].
                    createInstance(Ci.nsIMutableArray);
     this._appendHandlers(handlers, CLASS_MIMEINFO);
     this._appendHandlers(handlers, CLASS_PROTOCOLINFO);
     return handlers.enumerate();
   },
 
+  fillHandlerInfo: function HS_fillHandlerInfo(aHandlerInfo, aOverrideType) {
+    var type = aOverrideType || aHandlerInfo.type;
+    var typeID = this._getTypeID(this._getClass(aHandlerInfo), type);
+
+    // Determine whether or not information about this handler is available
+    // in the datastore by looking for its "value" property, which stores its
+    // type and should always be present.
+    if (!this._hasValue(typeID, NC_VALUE))
+      throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+    // Retrieve the human-readable description of the type.
+    if (this._hasValue(typeID, NC_DESCRIPTION))
+      aHandlerInfo.description = this._getValue(typeID, NC_DESCRIPTION);
+
+    // Note: for historical reasons, we don't actually check that the type
+    // record has a "handlerProp" property referencing the info record.  It's
+    // unclear whether or not we should start doing this check; perhaps some
+    // legacy datasources don't have such references.
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), type);
+
+    aHandlerInfo.preferredAction = this._retrievePreferredAction(infoID);
+
+    var preferredHandlerID =
+      this._getPreferredHandlerID(this._getClass(aHandlerInfo), type);
+
+    // Retrieve the preferred handler.
+    // Note: for historical reasons, we don't actually check that the info
+    // record has an "externalApplication" property referencing the preferred
+    // handler record.  It's unclear whether or not we should start doing
+    // this check; perhaps some legacy datasources don't have such references.
+    aHandlerInfo.preferredApplicationHandler =
+      this._retrieveHandlerApp(preferredHandlerID);
+
+    // Fill the array of possible handlers with the ones in the datastore.
+    this._fillPossibleHandlers(infoID,
+                               aHandlerInfo.possibleApplicationHandlers,
+                               aHandlerInfo.preferredApplicationHandler);
+
+    // Retrieve the "always ask" flag.
+    // Note: we only set the flag to false if we are absolutely sure the user
+    // does not want to be asked.  Any sort of bogus data should mean we ask.
+    // So there must be an "alwaysAsk" property in the datastore for the handler
+    // info object, and it must be set to "false", in order for us not to ask.
+    aHandlerInfo.alwaysAskBeforeHandling =
+      !this._hasValue(infoID, NC_ALWAYS_ASK) ||
+      this._getValue(infoID, NC_ALWAYS_ASK) != "false";
+
+    // If the object represents a MIME type handler, then also retrieve
+    // any file extensions.
+    if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
+      for each (let fileExtension in this._retrieveFileExtensions(typeID))
+        aHandlerInfo.appendExtension(fileExtension);
+  },
+
   store: function HS_store(aHandlerInfo) {
     // FIXME: when we switch from RDF to something with transactions (like
     // SQLite), enclose the following changes in a transaction so they all
     // get rolled back if any of them fail and we don't leave the datastore
     // in an inconsistent state.
 
     this._ensureRecordsForType(aHandlerInfo);
     this._storePreferredAction(aHandlerInfo);
@@ -125,24 +228,30 @@ HandlerService.prototype = {
     this._storeAlwaysAsk(aHandlerInfo);
 
     // Write the changes to the database immediately so we don't lose them
     // if the application crashes.
     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
       this._ds.Flush();
   },
 
+  exists: function HS_exists(aHandlerInfo) {
+    var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
+    return this._hasLiteralAssertion(typeID, NC_VALUE, aHandlerInfo.type);
+  },
+
   remove: function HS_remove(aHandlerInfo) {
-    var preferredHandlerID = this._getPreferredHandlerID(aHandlerInfo);
+    var preferredHandlerID =
+      this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._removeAssertions(preferredHandlerID);
 
-    var infoID = this._getInfoID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._removeAssertions(infoID);
 
-    var typeID = this._getTypeID(aHandlerInfo);
+    var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._removeAssertions(typeID);
 
     // Now that there's no longer a handler for this type, remove the type
     // from the list of types for which there are known handlers.
     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
     var type = this._rdf.GetResource(typeID);
     var typeIndex = typeList.IndexOf(type);
     if (typeIndex != -1)
@@ -151,22 +260,190 @@ HandlerService.prototype = {
     // Write the changes to the database immediately so we don't lose them
     // if the application crashes.
     // XXX If we're removing a bunch of handlers at once, will flushing
     // after every removal cause a significant performance hit?
     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
       this._ds.Flush();
   },
 
+  getTypeFromExtension: function HS_getTypeFromExtension(aFileExtension) {
+    var fileExtension = aFileExtension.toLowerCase();
+    var typeID;
+
+    if (this._existsLiteralTarget(NC_FILE_EXTENSIONS, fileExtension))
+      typeID = this._getSourceForLiteral(NC_FILE_EXTENSIONS, fileExtension);
+
+    if (typeID && this._hasValue(typeID, NC_VALUE)) {
+      let type = this._getValue(typeID, NC_VALUE);
+      if (type == "")
+        throw Cr.NS_ERROR_FAILURE;
+      return type;
+    }
+
+    throw Cr.NS_ERROR_NOT_AVAILABLE;
+  },
+
+
+  //**************************************************************************//
+  // Retrieval Methods
+
+  /**
+   * Retrieve the preferred action for the info record with the given ID.
+   *
+   * @param aInfoID  {string}  the info record ID
+   *
+   * @returns  {integer}  the preferred action enumeration value
+   */
+  _retrievePreferredAction: function HS__retrievePreferredAction(aInfoID) {
+    if (this._getValue(aInfoID, NC_SAVE_TO_DISK) == "true")
+      return Ci.nsIHandlerInfo.saveToDisk;
+    
+    if (this._getValue(aInfoID, NC_USE_SYSTEM_DEFAULT) == "true")
+      return Ci.nsIHandlerInfo.useSystemDefault;
+    
+    if (this._getValue(aInfoID, NC_HANDLE_INTERNALLY) == "true")
+      return Ci.nsIHandlerInfo.handleInternal;
+
+    return Ci.nsIHandlerInfo.useHelperApp;
+  },
+
+  /**
+   * Fill an array of possible handlers with the handlers for the given info ID.
+   *
+   * @param aInfoID            {string}           the ID of the info record
+   * @param aPossibleHandlers  {nsIMutableArray}  the array of possible handlers
+   * @param aPreferredHandler  {nsIHandlerApp}    the preferred handler, if any
+   */
+  _fillPossibleHandlers: function HS__fillPossibleHandlers(aInfoID,
+                                                           aPossibleHandlers,
+                                                           aPreferredHandler) {
+    // The set of possible handlers should include the preferred handler,
+    // but legacy datastores (from before we added possible handlers) won't
+    // include the preferred handler, so check if it's included as we build
+    // the list of handlers, and, if it's not included, add it to the list.
+    if (aPreferredHandler)
+      aPossibleHandlers.appendElement(aPreferredHandler, false);
+
+    var possibleHandlerTargets = this._getTargets(aInfoID, NC_POSSIBLE_APP);
+
+    while (possibleHandlerTargets.hasMoreElements()) {
+      let possibleHandlerTarget = possibleHandlerTargets.getNext();
+      if (!(possibleHandlerTarget instanceof Ci.nsIRDFResource))
+        continue;
+
+      let possibleHandlerID = possibleHandlerTarget.ValueUTF8;
+      let possibleHandler = this._retrieveHandlerApp(possibleHandlerID);
+      if (possibleHandler && (!aPreferredHandler ||
+                              !possibleHandler.equals(aPreferredHandler)))
+        aPossibleHandlers.appendElement(possibleHandler, false);
+    }
+  },
+
+  /**
+   * Retrieve the handler app object with the given ID.
+   *
+   * @param aHandlerAppID  {string}  the ID of the handler app to retrieve
+   *
+   * @returns  {nsIHandlerApp}  the handler app, if any; otherwise null
+   */
+  _retrieveHandlerApp: function HS__retrieveHandlerApp(aHandlerAppID) {
+    var handlerApp;
+
+    // If it has a path, it's a local handler; otherwise, it's a web handler.
+    if (this._hasValue(aHandlerAppID, NC_PATH)) {
+      let executable =
+        this._getFileWithPath(this._getValue(aHandlerAppID, NC_PATH));
+      if (!executable)
+        return null;
+
+      handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+                   createInstance(Ci.nsILocalHandlerApp);
+      handlerApp.executable = executable;
+    }
+    else if (this._hasValue(aHandlerAppID, NC_URI_TEMPLATE)) {
+      let uriTemplate = this._getValue(aHandlerAppID, NC_URI_TEMPLATE);
+      if (!uriTemplate)
+        return null;
+
+      handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+                   createInstance(Ci.nsIWebHandlerApp);
+      handlerApp.uriTemplate = uriTemplate;
+    }
+    else
+      return null;
+
+    handlerApp.name = this._getValue(aHandlerAppID, NC_PRETTY_NAME);
+
+    return handlerApp;
+  },
+
+  /*
+   * Retrieve file extensions, if any, for the MIME type with the given type ID.
+   *
+   * @param aTypeID  {string}  the type record ID
+   */
+  _retrieveFileExtensions: function HS__retrieveFileExtensions(aTypeID) {
+    var fileExtensions = [];
+
+    var fileExtensionTargets = this._getTargets(aTypeID, NC_FILE_EXTENSIONS);
+
+    while (fileExtensionTargets.hasMoreElements()) {
+      let fileExtensionTarget = fileExtensionTargets.getNext();
+      if (fileExtensionTarget instanceof Ci.nsIRDFLiteral &&
+          fileExtensionTarget.Value != "")
+        fileExtensions.push(fileExtensionTarget.Value);
+    }
+
+    return fileExtensions;
+  },
+
+  /**
+   * Get the file with the given path.  This is not as simple as merely
+   * initializing a local file object with the path, because the path might be
+   * relative to the current process directory, in which case we have to
+   * construct a path starting from that directory.
+   *
+   * @param aPath  {string}  a path to a file
+   *
+   * @returns {nsILocalFile} the file, or null if the file does not exist
+   */
+  _getFileWithPath: function HS__getFileWithPath(aPath) {
+    var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+
+    try {
+      file.initWithPath(aPath);
+
+      if (file.exists())
+        return file;
+    }
+    catch(ex) {
+      // Note: for historical reasons, we don't actually check to see
+      // if the exception is NS_ERROR_FILE_UNRECOGNIZED_PATH, which is what
+      // nsILocalFile::initWithPath throws when a path is relative.
+
+      file = this._dirSvc.get("XCurProcD", Ci.nsIFile);
+
+      try {
+        file.append(aPath);
+        if (file.exists())
+          return file;
+      }
+      catch(ex) {}
+    }
+
+    return null;
+  },
+
 
   //**************************************************************************//
   // Storage Methods
 
   _storePreferredAction: function HS__storePreferredAction(aHandlerInfo) {
-    var infoID = this._getInfoID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
 
     switch(aHandlerInfo.preferredAction) {
       case Ci.nsIHandlerInfo.saveToDisk:
         this._setLiteral(infoID, NC_SAVE_TO_DISK, "true");
         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
         break;
 
@@ -191,18 +468,20 @@ HandlerService.prototype = {
         this._removeTarget(infoID, NC_SAVE_TO_DISK);
         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
         break;
     }
   },
 
   _storePreferredHandler: function HS__storePreferredHandler(aHandlerInfo) {
-    var infoID = this._getInfoID(aHandlerInfo);
-    var handlerID = this._getPreferredHandlerID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
+    var handlerID =
+      this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
+
     var handler = aHandlerInfo.preferredApplicationHandler;
 
     if (handler) {
       this._storeHandlerApp(handlerID, handler);
 
       // Make this app be the preferred app for the handler info.
       //
       // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores
@@ -223,39 +502,39 @@ HandlerService.prototype = {
 
   /**
    * Store the list of possible handler apps for the content type represented
    * by the given handler info object.
    *
    * @param aHandlerInfo  {nsIHandlerInfo}  the handler info object
    */
   _storePossibleHandlers: function HS__storePossibleHandlers(aHandlerInfo) {
-    var infoID = this._getInfoID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
 
     // First, retrieve the set of handler apps currently stored for the type,
     // keeping track of their IDs in a hash that we'll use to determine which
     // ones are no longer valid and should be removed.
     var currentHandlerApps = {};
     var currentHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
     while (currentHandlerTargets.hasMoreElements()) {
       let handlerApp = currentHandlerTargets.getNext();
       if (handlerApp instanceof Ci.nsIRDFResource) {
-        let handlerAppID = handlerApp.Value;
+        let handlerAppID = handlerApp.ValueUTF8;
         currentHandlerApps[handlerAppID] = true;
       }
     }
 
     // Next, store any new handler apps.
     var newHandlerApps =
       aHandlerInfo.possibleApplicationHandlers.enumerate();
     while (newHandlerApps.hasMoreElements()) {
       let handlerApp =
         newHandlerApps.getNext().QueryInterface(Ci.nsIHandlerApp);
       let handlerAppID = this._getPossibleHandlerAppID(handlerApp);
-      if (!this._hasResourceTarget(infoID, NC_POSSIBLE_APP, handlerAppID)) {
+      if (!this._hasResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID)) {
         this._storeHandlerApp(handlerAppID, handlerApp);
         this._addResourceTarget(infoID, NC_POSSIBLE_APP, handlerAppID);
       }
       delete currentHandlerApps[handlerAppID];
     }
 
     // Finally, remove any old handler apps that aren't being used anymore,
     // and if those handler apps aren't being used by any other type either,
@@ -298,26 +577,46 @@ HandlerService.prototype = {
     else {
       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
       this._setLiteral(aHandlerAppID, NC_URI_TEMPLATE, aHandlerApp.uriTemplate);
       this._removeTarget(aHandlerAppID, NC_PATH);
     }
   },
 
   _storeAlwaysAsk: function HS__storeAlwaysAsk(aHandlerInfo) {
-    var infoID = this._getInfoID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._setLiteral(infoID,
                      NC_ALWAYS_ASK,
                      aHandlerInfo.alwaysAskBeforeHandling ? "true" : "false");
   },
 
 
   //**************************************************************************//
   // Convenience Getters
 
+  // Observer Service
+  __observerSvc: null,
+  get _observerSvc() {
+    if (!this.__observerSvc)
+      this.__observerSvc =
+        Cc["@mozilla.org/observer-service;1"].
+        getService(Ci.nsIObserverService);
+    return this.__observerSvc;
+  },
+
+  // Directory Service
+  __dirSvc: null,
+  get _dirSvc() {
+    if (!this.__dirSvc)
+      this.__dirSvc =
+        Cc["@mozilla.org/file/directory_service;1"].
+        getService(Ci.nsIProperties);
+    return this.__dirSvc;
+  },
+
   // MIME Service
   __mimeSvc: null,
   get _mimeSvc() {
     if (!this.__mimeSvc)
       this.__mimeSvc =
         Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
         getService(Ci.nsIMIMEService);
     return this.__mimeSvc;
@@ -350,84 +649,83 @@ HandlerService.prototype = {
                               getService(Ci.nsIRDFContainerUtils);
     return this.__containerUtils;
   },
 
   // RDF datasource containing content handling config (i.e. mimeTypes.rdf)
   __ds: null,
   get _ds() {
     if (!this.__ds) {
-      var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
-                        getService(Ci.nsIProperties);
-      var file = fileLocator.get("UMimTyp", Ci.nsIFile);
+      var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
       // FIXME: make this a memoizing getter if we use it anywhere else.
       var ioService = Cc["@mozilla.org/network/io-service;1"].
                       getService(Ci.nsIIOService);
       var fileHandler = ioService.getProtocolHandler("file").
                         QueryInterface(Ci.nsIFileProtocolHandler);
       this.__ds =
         this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
     }
 
     return this.__ds;
   },
 
 
   //**************************************************************************//
-  // Storage Utils
+  // Datastore Utils
 
   /**
    * Get the string identifying whether this is a MIME or a protocol handler.
    * This string is used in the URI IDs of various RDF properties.
    * 
    * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the class
    * 
-   * @returns {string} the ID
+   * @returns {string} the class
    */
   _getClass: function HS__getClass(aHandlerInfo) {
     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
       return CLASS_MIMEINFO;
     else
       return CLASS_PROTOCOLINFO;
   },
 
   /**
    * Return the unique identifier for a content type record, which stores
-   * the value field plus a reference to the type's handler.
-   * 
+   * the value field plus a reference to the content type's handler info record.
+   *
    * |urn:<class>:<type>|
-   * 
+   *
    * XXX: should this be a property of nsIHandlerInfo?
-   * 
-   * @param aHandlerInfo {nsIHandlerInfo} the type for which to get the ID
-   * 
+   *
+   * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
+   * @param aType  {string} the type (a MIME type or protocol scheme)
+   *
    * @returns {string} the ID
    */
-  _getTypeID: function HS__getTypeID(aHandlerInfo) {
-    return "urn:" + this._getClass(aHandlerInfo) + ":" + aHandlerInfo.type;
+  _getTypeID: function HS__getTypeID(aClass, aType) {
+    return "urn:" + aClass + ":" + aType;
   },
 
   /**
-   * Return the unique identifier for a type info record, which stores
+   * Return the unique identifier for a handler info record, which stores
    * the preferredAction and alwaysAsk fields plus a reference to the preferred
-   * handler.  Roughly equivalent to the nsIHandlerInfo interface.
-   * 
+   * handler app.  Roughly equivalent to the nsIHandlerInfo interface.
+   *
    * |urn:<class>:handler:<type>|
-   * 
+   *
    * FIXME: the type info record should be merged into the type record,
    * since there's a one to one relationship between them, and this record
    * merely stores additional attributes of a content type.
-   * 
-   * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the ID
-   * 
+   *
+   * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
+   * @param aType  {string} the type (a MIME type or protocol scheme)
+   *
    * @returns {string} the ID
    */
-  _getInfoID: function HS__getInfoID(aHandlerInfo) {
-    return "urn:" + this._getClass(aHandlerInfo) + ":handler:" +
-           aHandlerInfo.type;
+  _getInfoID: function HS__getInfoID(aClass, aType) {
+    return "urn:" + aClass + ":handler:" + aType;
   },
 
   /**
    * Return the unique identifier for a preferred handler record, which stores
    * information about the preferred handler for a given content type, including
    * its human-readable name and the path to its executable (for a local app)
    * or its URI template (for a web app).
    * 
@@ -436,23 +734,23 @@ HandlerService.prototype = {
    * XXX: should this be a property of nsIHandlerApp?
    *
    * FIXME: this should be an arbitrary ID, and we should retrieve it from
    * the datastore for a given content type via the NC:ExternalApplication
    * property rather than looking for a specific ID, so a handler doesn't
    * have to change IDs when it goes from being a possible handler to being
    * the preferred one (once we support possible handlers).
    * 
-   * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the ID
+   * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
+   * @param aType  {string} the type (a MIME type or protocol scheme)
    * 
    * @returns {string} the ID
    */
-  _getPreferredHandlerID: function HS__getPreferredHandlerID(aHandlerInfo) {
-    return "urn:" + this._getClass(aHandlerInfo) + ":externalApplication:" +
-           aHandlerInfo.type;
+  _getPreferredHandlerID: function HS__getPreferredHandlerID(aClass, aType) {
+    return "urn:" + aClass + ":externalApplication:" + aType;
   },
 
   /**
    * Return the unique identifier for a handler app record, which stores
    * information about a possible handler for one or more content types,
    * including its human-readable name and the path to its executable (for a
    * local app) or its URI template (for a web app).
    *
@@ -482,20 +780,16 @@ HandlerService.prototype = {
    * |urn:<class>s|
    * |urn:<class>s:root|
    * 
    * @param aClass {string} the class for which to retrieve a list of types
    *
    * @returns {nsIRDFContainer} the list of types
    */
   _ensureAndGetTypeList: function HS__ensureAndGetTypeList(aClass) {
-    // FIXME: once nsIHandlerInfo supports retrieving the scheme
-    // (and differentiating between MIME and protocol content types),
-    // implement support for protocols.
-
     var source = this._rdf.GetResource("urn:" + aClass + "s");
     var property =
       this._rdf.GetResource(aClass == CLASS_MIMEINFO ? NC_MIME_TYPES
                                                      : NC_PROTOCOL_SCHEMES);
     var target = this._rdf.GetResource("urn:" + aClass + "s:root");
 
     // Make sure we have an arc from the source to the target.
     if (!this._ds.HasAssertion(source, property, target, true))
@@ -526,39 +820,40 @@ HandlerService.prototype = {
    * @param aHandlerInfo {nsIHandlerInfo} the type to make sure has a record
    */
   _ensureRecordsForType: function HS__ensureRecordsForType(aHandlerInfo) {
     // Get the list of types.
     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
 
     // If there's already a record in the datastore for this type, then we
     // don't need to do anything more.
-    var typeID = this._getTypeID(aHandlerInfo);
+    var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     var type = this._rdf.GetResource(typeID);
     if (typeList.IndexOf(type) != -1)
       return;
 
     // Create a basic type record for this type.
     typeList.AppendElement(type);
     this._setLiteral(typeID, NC_VALUE, aHandlerInfo.type);
     
     // Create a basic info record for this type.
-    var infoID = this._getInfoID(aHandlerInfo);
+    var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._setLiteral(infoID, NC_ALWAYS_ASK, "false");
     this._setResource(typeID, NC_HANDLER_INFO, infoID);
     // XXX Shouldn't we set preferredAction to useSystemDefault?
     // That's what it is if there's no record in the datastore; why should it
     // change to useHelperApp just because we add a record to the datastore?
     
     // Create a basic preferred handler record for this type.
     // XXX Not sure this is necessary, since preferred handlers are optional,
     // and nsExternalHelperAppService::FillHandlerInfoForTypeFromDS doesn't seem
     // to require the record , but downloadactions.js::_ensureMIMERegistryEntry
     // used to create it, so we'll do the same.
-    var preferredHandlerID = this._getPreferredHandlerID(aHandlerInfo);
+    var preferredHandlerID =
+      this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
     this._setLiteral(preferredHandlerID, NC_PATH, "");
     this._setResource(infoID, NC_PREFERRED_APP, preferredHandlerID);
   },
 
   /**
    * Append known handlers of the given class to the given array.  The class
    * can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO.
    *
@@ -581,26 +876,39 @@ HandlerService.prototype = {
 
       // Get the value of the element's NC:value property, which contains
       // the MIME type or scheme for which we're retrieving a handler info.
       var type = this._getValue(element.ValueUTF8, NC_VALUE);
       if (!type)
         continue;
 
       var handler;
-      if (typeList.Resource.Value == "urn:mimetypes:root")
+      if (typeList.Resource.ValueUTF8 == "urn:mimetypes:root")
         handler = this._mimeSvc.getFromTypeAndExtension(type, null);
       else
         handler = this._protocolSvc.getProtocolHandlerInfo(type);
 
       aHandlers.appendElement(handler, false);
     }
   },
 
   /**
+   * Whether or not a property of an RDF source has a value.
+   *
+   * @param sourceURI   {string}  the URI of the source
+   * @param propertyURI {string}  the URI of the property
+   * @returns           {boolean} whether or not the property has a value
+   */
+  _hasValue: function HS__hasValue(sourceURI, propertyURI) {
+    var source = this._rdf.GetResource(sourceURI);
+    var property = this._rdf.GetResource(propertyURI);
+    return this._ds.hasArcOut(source, property);
+  },
+
+  /**
    * Get the value of a property of an RDF source.
    *
    * @param sourceURI   {string} the URI of the source
    * @param propertyURI {string} the URI of the property
    * @returns           {string} the value of the property
    */
   _getValue: function HS__getValue(sourceURI, propertyURI) {
     var source = this._rdf.GetResource(sourceURI);
@@ -704,36 +1012,85 @@ HandlerService.prototype = {
     var target = this._rdf.GetResource(targetURI);
     
     this._ds.Assert(source, property, target, true);
   },
 
   /**
    * Whether or not a property of an RDF source has a given resource target.
    * 
-   * The difference between this method and _setResource is that this one adds
-   * an assertion even if one already exists, which allows its callers to make
-   * sets of assertions (i.e. to set a property to multiple targets).
-   *
    * @param sourceURI   {string} the URI of the source
    * @param propertyURI {string} the URI of the property
    * @param targetURI   {string} the URI of the target
    *
    * @returns {boolean} whether or not there is such an assertion
    */
-  _hasResourceTarget: function HS__hasResourceTarget(sourceURI, propertyURI,
-                                                     targetURI) {
+  _hasResourceAssertion: function HS__hasResourceAssertion(sourceURI,
+                                                           propertyURI,
+                                                           targetURI) {
     var source = this._rdf.GetResource(sourceURI);
     var property = this._rdf.GetResource(propertyURI);
     var target = this._rdf.GetResource(targetURI);
 
     return this._ds.HasAssertion(source, property, target, true);
   },
 
   /**
+   * Whether or not a property of an RDF source has a given literal value.
+   * 
+   * @param sourceURI   {string} the URI of the source
+   * @param propertyURI {string} the URI of the property
+   * @param value       {string} the literal value
+   *
+   * @returns {boolean} whether or not there is such an assertion
+   */
+  _hasLiteralAssertion: function HS__hasLiteralAssertion(sourceURI,
+                                                         propertyURI,
+                                                         value) {
+    var source = this._rdf.GetResource(sourceURI);
+    var property = this._rdf.GetResource(propertyURI);
+    var target = this._rdf.GetLiteral(value);
+
+    return this._ds.HasAssertion(source, property, target, true);
+  },
+
+  /**
+   * Whether or not there is an RDF source that has the given property set to
+   * the given literal value.
+   * 
+   * @param propertyURI {string} the URI of the property
+   * @param value       {string} the literal value
+   *
+   * @returns {boolean} whether or not there is a source
+   */
+  _existsLiteralTarget: function HS__existsLiteralTarget(propertyURI, value) {
+    var property = this._rdf.GetResource(propertyURI);
+    var target = this._rdf.GetLiteral(value);
+
+    return this._ds.hasArcIn(target, property);
+  },
+
+  /**
+   * Get the source for a property set to a given literal value.
+   *
+   * @param propertyURI {string} the URI of the property
+   * @param value       {string} the literal value
+   */
+  _getSourceForLiteral: function HS__getSourceForLiteral(propertyURI, value) {
+    var property = this._rdf.GetResource(propertyURI);
+    var target = this._rdf.GetLiteral(value);
+
+    var source = this._ds.GetSource(property, target, true);
+    if (source)
+      return source.ValueUTF8;
+
+    return null;
+  },
+
+  /**
    * Whether or not there is an RDF source that has the given property set to
    * the given resource target.
    * 
    * @param propertyURI {string} the URI of the property
    * @param targetURI   {string} the URI of the target
    *
    * @returns {boolean} whether or not there is a source
    */
@@ -774,69 +1131,18 @@ HandlerService.prototype = {
     var source = this._rdf.GetResource(sourceURI);
     var properties = this._ds.ArcLabelsOut(source);
  
     while (properties.hasMoreElements()) {
       var property = properties.getNext();
       var target = this._ds.GetTarget(source, property, true);
       this._ds.Unassert(source, property, target);
     }
-  },
-
-
-  //**************************************************************************//
-  // Utilities
-
-  // FIXME: given that I keep copying them from JS component to JS component,
-  // these utilities should all be in a JavaScript module or FUEL interface.
-
-  /**
-   * Get an app pref or a default value if the pref doesn't exist.
-   *
-   * @param   aPrefName
-   * @param   aDefaultValue
-   * @returns the pref's value or the default (if it is missing)
-   */
-  _getAppPref: function _getAppPref(aPrefName, aDefaultValue) {
-    try {
-      var prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                       getService(Ci.nsIPrefBranch);
-      switch (prefBranch.getPrefType(aPrefName)) {
-        case prefBranch.PREF_STRING:
-          return prefBranch.getCharPref(aPrefName);
-
-        case prefBranch.PREF_INT:
-          return prefBranch.getIntPref(aPrefName);
+  }
 
-        case prefBranch.PREF_BOOL:
-          return prefBranch.getBoolPref(aPrefName);
-      }
-    }
-    catch (ex) { /* return the default value */ }
-    
-    return aDefaultValue;
-  },
-
-  // Console Service
-  __consoleSvc: null,
-  get _consoleSvc() {
-    if (!this.__consoleSvc)
-      this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
-                          getService(Ci.nsIConsoleService);
-    return this.__consoleSvc;
-  },
-
-  _log: function _log(aMessage) {
-    if (!this._getAppPref("browser.contentHandling.log", false))
-      return;
-
-    aMessage = "*** HandlerService: " + aMessage;
-    dump(aMessage + "\n");
-    this._consoleSvc.logStringMessage(aMessage);
-  }
 };
 
 
 //****************************************************************************//
 // More XPCOM Plumbing
 
 function NSGetModule(compMgr, fileSpec) {
   return XPCOMUtils.generateModule([HandlerService]);
deleted file mode 100644
--- a/uriloader/exthandler/nsHelperAppRDF.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * 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
- * 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 ***** */
-
-/** @file
- * This file holds our vocabulary definition for the helper
- * app data source. The helper app data source contains user specified helper
- * application override information.
- *
- * @see profile/defaults/mimeTypes.rdf for documentation on these properties
- */
-
-
-#define NC_RDF_MIMETYPES				NC_NAMESPACE_URI"MIME-Types"
-// a mime type has the following properties...
-#define NC_RDF_MIMETYPE 				NC_NAMESPACE_URI"MIME-Type"
-#define NC_RDF_DESCRIPTION			NC_NAMESPACE_URI"description"
-#define NC_RDF_VALUE					  NC_NAMESPACE_URI"value"
-#define NC_RDF_EDITABLE   			NC_NAMESPACE_URI"editable"
-#define NC_RDF_LARGEICON				NC_NAMESPACE_URI"largeIcon"
-#define NC_RDF_SMALLICON				NC_NAMESPACE_URI"smallIcon"
-#define NC_RDF_HANDLER				  NC_NAMESPACE_URI"handler"
-#define NC_RDF_FILEEXTENSIONS	  NC_NAMESPACE_URI"fileExtensions"
-#define NC_RDF_CHILD            NC_NAMESPACE_URI"child"
-#define NC_RDF_ROOT 			      "NC:HelperAppRoot"
-#define NC_CONTENT_NODE_PREFIX  "urn:mimetype:"
-#define NC_HANDLER_SUFFIX "handler:"
-// This should be called preferredApplication to distinguish it from the larger
-// set of possible external applications, but it's too late to change this now.
-#define NC_EXTERNALAPP_SUFFIX "externalApplication:"
-
-// for URI schemes.  We re-use NC_RDF_HANDLER as an arc from these nodes.
-#define NC_SCHEME_NODE_PREFIX "urn:scheme:"
-#define NS_RDF_PROTOCOLSCHEMES NC_NAMESPACE_URI"Protocol-Schemes"
-
-// File Extensions have file extension properties....
-#define NC_RDF_FILEEXTENSION    NC_NAMESPACE_URI"fileExtension"
-
-// handler properties
-#define NC_RDF_SAVETODISK           NC_NAMESPACE_URI"saveToDisk"
-#define NC_RDF_USESYSTEMDEFAULT     NC_NAMESPACE_URI"useSystemDefault"
-#define NC_RDF_HANDLEINTERNAL       NC_NAMESPACE_URI"handleInternal"
-#define NC_RDF_ALWAYSASK            NC_NAMESPACE_URI"alwaysAsk"
-#define NC_RDF_POSSIBLEAPPLICATION  NC_NAMESPACE_URI"possibleApplication"
-
-// external applications properties....
-#define NC_RDF_PRETTYNAME 			    NC_NAMESPACE_URI"prettyName"
-// for local apps, we'll have the path (but not uriTemplate)
-#define NC_RDF_PATH 			          NC_NAMESPACE_URI"path"
-// for web apps, we'll have the uriTemplate (but not path)
-#define NC_RDF_URITEMPLATE NC_NAMESPACE_URI"uriTemplate"  
--- a/uriloader/exthandler/nsIHandlerService.idl
+++ b/uriloader/exthandler/nsIHandlerService.idl
@@ -34,45 +34,113 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIHandlerInfo;
 interface nsISimpleEnumerator;
 
-[scriptable, uuid(18bd7cc7-04b5-46d1-ad95-386e51191eb2)]
+[scriptable, uuid(53f0ad17-ec62-46a1-adbc-efccc06babcd)]
 interface nsIHandlerService : nsISupports
 {
   /**
    * Retrieve a list of all handlers in the datastore.  This list is not
    * guaranteed to be in any particular order, and callers should not assume
    * it will remain in the same order in the future.
    *
    * @returns a list of all handlers in the datastore
    */
   nsISimpleEnumerator enumerate();
 
   /**
-   * Save the preferred action, preferred handler, and always ask properties
-   * of the given handler info object to the datastore.  Updates an existing
-   * record or creates a new one if necessary.
+   * Fill a handler info object with information from the datastore.
+   *
+   * Note: because of the way the external helper app service currently mixes
+   * OS and user handler info in the same handler info object, this method
+   * takes an existing handler info object (probably retrieved from the OS)
+   * and "fills it in" with information from the datastore, overriding any
+   * existing properties on the object with properties from the datastore.
+   *
+   * Ultimately, however, we're going to separate OS and user handler info
+   * into separate objects, at which point this method should be renamed to
+   * something like "get" or "retrieve", take a class and type (or perhaps
+   * a type whose class can be determined by querying the type, for example
+   * an nsIContentType which is also an nsIMIMEType or an nsIProtocolScheme),
+   * and return a handler info object representing only the user info.
+   *
+   * Note: if you specify an override type, then the service will fill in
+   * the handler info object with information about that type instead of
+   * the type specified by the object's nsIHandlerInfo::type attribute.
+   *
+   * This is useful when you are trying to retrieve information about a MIME
+   * type that doesn't exist in the datastore, but you have a file extension
+   * for that type, and nsIHandlerService::getTypeFromExtension returns another
+   * MIME type that does exist in the datastore and can handle that extension.
+   *
+   * For example, the user clicks on a link, and the content has a MIME type
+   * that isn't in the datastore, but the link has a file extension, and that
+   * extension is associated with another MIME type in the datastore (perhaps
+   * an unofficial MIME type preceded an official one, like with image/x-png
+   * and image/png).
+   *
+   * In that situation, you can call this method to fill in the handler info
+   * object with information about that other type by passing the other type
+   * as the aOverrideType parameter.
+   *
+   * @param aHandlerInfo   the handler info object
+   * @param aOverrideType  a type to use instead of aHandlerInfo::type
+   *
+   * Note: if there is no information in the datastore about this type,
+   * this method throws NS_ERROR_NOT_AVAILABLE.
+   */
+  void fillHandlerInfo(in nsIHandlerInfo aHandlerInfo,
+                       in ACString aOverrideType);
+
+  /**
+   * Save the preferred action, preferred handler, possible handlers, and
+   * always ask properties of the given handler info object to the datastore.
+   * Updates an existing record or creates a new one if necessary.
    *
    * Note: if preferred action is undefined or invalid, then we assume
    * the default value nsIHandlerInfo::useHelperApp.
    *
-   * FIXME: also store any changes to the list of possible handlers
-   * (once we support possible handlers).
-   *
    * @param aHandlerInfo  the handler info object
    */
   void store(in nsIHandlerInfo aHandlerInfo);
 
   /**
+   * Whether or not a record for the given handler info object exists
+   * in the datastore.
+   *
+   * @param aHandlerInfo  a handler info object
+   *
+   * @returns whether or not a record exists
+   */
+  boolean exists(in nsIHandlerInfo aHandlerInfo);
+
+  /**
    * Remove the given handler info object from the datastore.  Deletes all
    * records associated with the object, including the preferred handler, info,
    * and type records plus the entry in the list of types, if they exist.
    * Otherwise, it does nothing and does not return an error.
    *
    * @param aHandlerInfo  the handler info object
    */
   void remove(in nsIHandlerInfo aHandlerInfo);
+
+  /**
+   * Get the MIME type mapped to the given file extension in the datastore.
+   *
+   * XXX If we ever support extension -> protocol scheme mappings, then this
+   * method should work for those as well.
+   *
+   * Note: in general, you should use nsIMIMEService::getTypeFromExtension
+   * to get a MIME type from a file extension, as that method checks a variety
+   * of other sources besides just the datastore.  Use this only when you want
+   * to specifically get only the mapping available in the datastore.
+   *
+   * @param aFileExtension  the file extension
+   *
+   * @returns the MIME type, if any; otherwise throws NS_ERROR_NOT_AVAILABLE
+   */
+  ACString getTypeFromExtension(in ACString aFileExtension);
 };
--- a/uriloader/exthandler/nsLocalHandlerApp.cpp
+++ b/uriloader/exthandler/nsLocalHandlerApp.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is 
  * the Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mosedale <dmose@mozilla.org>
+ *   Myk Melez <myk@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
@@ -61,16 +62,41 @@ NS_IMETHODIMP nsLocalHandlerApp::GetName
 
 NS_IMETHODIMP nsLocalHandlerApp::SetName(const nsAString & aName)
 {
   mName.Assign(aName);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsLocalHandlerApp::Equals(nsIHandlerApp *aHandlerApp, PRBool *_retval)
+{
+  NS_ENSURE_ARG_POINTER(aHandlerApp);
+
+  // If the handler app isn't a local handler app, then it's not the same app.
+  nsCOMPtr <nsILocalHandlerApp> localHandlerApp = do_QueryInterface(aHandlerApp);
+  if (!localHandlerApp) {
+    *_retval = PR_FALSE;
+    return NS_OK;
+  }
+
+  // If either handler app doesn't have an executable, then they aren't
+  // the same app.
+  nsCOMPtr<nsIFile> executable;
+  nsresult rv = localHandlerApp->GetExecutable(getter_AddRefs(executable));
+  if (NS_FAILED(rv) || !executable || !mExecutable) {
+    *_retval = PR_FALSE;
+    return NS_OK;
+  }
+
+  return executable->Equals(mExecutable, _retval);
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////
 //// nsILocalHandlerApp
 
 NS_IMETHODIMP nsLocalHandlerApp::GetExecutable(nsIFile **aExecutable)
 {
   NS_IF_ADDREF(*aExecutable = mExecutable);
   return NS_OK;
 }
--- a/uriloader/exthandler/nsWebHandlerApp.js
+++ b/uriloader/exthandler/nsWebHandlerApp.js
@@ -15,16 +15,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com>
+ *   Myk Melez <myk@mozilla.org>
  *
  * 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
@@ -36,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const Ci = Components.interfaces;
+const Cr = Components.results;
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsWebHandler class
 
 function nsWebHandlerApp() {}
 
 nsWebHandlerApp.prototype = {
   //////////////////////////////////////////////////////////////////////////////
@@ -64,16 +66,29 @@ nsWebHandlerApp.prototype = {
   get name() {
     return this._name;
   },
 
   set name(aName) {
     this._name = aName;
   },
 
+  equals: function(aHandlerApp) {
+    if (!aHandlerApp)
+      throw Cr.NS_ERROR_NULL_POINTER;
+
+    if (aHandlerApp instanceof Ci.nsIWebHandlerApp &&
+        aHandlerApp.uriTemplate &&
+        this.uriTemplate &&
+        aHandlerApp.uriTemplate == this.uriTemplate)
+      return true;
+
+    return false;
+  },
+
   //////////////////////////////////////////////////////////////////////////////
   //// nsIWebHandlerApp
 
   get uriTemplate() {
     return this._uriTemplate;
   },
 
   set uriTemplate(aURITemplate) {
--- a/uriloader/exthandler/tests/unit/test_handlerService.js
+++ b/uriloader/exthandler/tests/unit/test_handlerService.js
@@ -70,30 +70,16 @@ function run_test() {
     }
   };
   
   var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
                    createInstance(Ci.nsIWebHandlerApp);
   webHandler.name = "Web Handler";
   webHandler.uriTemplate = "http://www.example.com/?%s";
 
-
-  //**************************************************************************//
-  // Helper Functions
-
-  function checkLocalHandlersAreEquivalent(handler1, handler2) {
-    do_check_eq(handler1.name, handler2.name);
-    do_check_eq(handler1.executable.path, handler2.executable.path);
-  }
-
-  function checkWebHandlersAreEquivalent(handler1, handler2) {
-    do_check_eq(handler1.name, handler2.name);
-    do_check_eq(handler1.uriTemplate, handler2.uriTemplate);
-  }
-
   // FIXME: these tests create and manipulate enough variables that it would
   // make sense to move each test into its own scope so we don't run the risk
   // of one test stomping on another's data.
 
 
   //**************************************************************************//
   // Test Default Properties
 
@@ -106,19 +92,20 @@ function run_test() {
   // Make sure it's also an nsIHandlerInfo.
   do_check_true(handlerInfo instanceof Ci.nsIHandlerInfo);
 
   do_check_eq(handlerInfo.type, "nonexistent/type");
 
   // Deprecated property, but we should still make sure it's set correctly.
   do_check_eq(handlerInfo.MIMEType, "nonexistent/type");
 
-  // These three properties are the ones the handler service knows how to store.
+  // These properties are the ones the handler service knows how to store.
   do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
   do_check_eq(handlerInfo.preferredApplicationHandler, null);
+  do_check_eq(handlerInfo.possibleApplicationHandlers.length, 0);
   do_check_true(handlerInfo.alwaysAskBeforeHandling);
 
   // These properties are initialized to default values by the service,
   // so we might as well make sure they're initialized to the right defaults.
   do_check_eq(handlerInfo.description, "");
   do_check_eq(handlerInfo.hasDefaultHandler, false);
   do_check_eq(handlerInfo.defaultDescription, "");
 
@@ -238,29 +225,30 @@ function run_test() {
   else
     [localPossibleHandler, webPossibleHandler, localIndex] = [handler2,
                                                               handler1,
                                                               1];
   localPossibleHandler.QueryInterface(Ci.nsILocalHandlerApp);
   webPossibleHandler.QueryInterface(Ci.nsIWebHandlerApp);
 
   // Make sure the two handlers are the ones we stored.
-  checkLocalHandlersAreEquivalent(localPossibleHandler, localHandler);
-  checkWebHandlersAreEquivalent(webPossibleHandler, webHandler);
+  do_check_eq(localPossibleHandler.name, localHandler.name);
+  do_check_true(localPossibleHandler.equals(localHandler));
+  do_check_eq(webPossibleHandler.name, webHandler.name);
+  do_check_true(webPossibleHandler.equals(webHandler));
 
   // Remove a handler, store the object, re-retrieve it, and make sure
   // it only has one handler.
   possibleHandlersInfo.possibleApplicationHandlers.removeElementAt(localIndex);
   handlerSvc.store(possibleHandlersInfo);
   possibleHandlersInfo =
     mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
   do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 1);
 
   // Make sure the handler is the one we didn't remove.
-  checkWebHandlersAreEquivalent(possibleHandlersInfo.
-                                  possibleApplicationHandlers.
-                                  queryElementAt(0, Ci.nsIHandlerApp).
-                                  QueryInterface(Ci.nsIWebHandlerApp),
-                                webHandler);
+  webPossibleHandler = possibleHandlersInfo.possibleApplicationHandlers.
+                       queryElementAt(0, Ci.nsIWebHandlerApp);
+  do_check_eq(webPossibleHandler.name, webHandler.name);
+  do_check_true(webPossibleHandler.equals(webHandler));
 
   // FIXME: test round trip integrity for a protocol.
   // FIXME: test round trip integrity for a handler info with a web handler.
 }