Bug 1287660 - New implementation of nsIHandlerService for the JSON backend; r?paolo draft
authorAlphan Chen <alchen@mozilla.com>
Thu, 01 Dec 2016 16:43:50 +0800
changeset 446925 986a06f64331324ada6a9e53fe1f1157a33ed632
parent 443220 34fce7c12173bdd6dda54c2ebf6d344252f1ac48
child 538910 26521d53c1eea1229b3799b3bc0c950518f91232
push id37926
push userbmo:alchen@mozilla.com
push dateFri, 02 Dec 2016 06:47:54 +0000
reviewerspaolo
bugs1287660
milestone53.0a1
Bug 1287660 - New implementation of nsIHandlerService for the JSON backend; r?paolo
browser/components/feeds/WebContentConverter.js
browser/components/preferences/in-content/applications.js
browser/extensions/pdfjs/content/PdfJs.jsm
browser/installer/package-manifest.in
docshell/build/nsDocShellModule.cpp
modules/libpref/init/all.js
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/handling/content/dialog.js
uriloader/exthandler/moz.build
uriloader/exthandler/nsCExternalHandlerService.idl
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsHandlerService-json.js
uriloader/exthandler/nsHandlerService-json.manifest
uriloader/exthandler/tests/unit/mimeTypes.json
uriloader/exthandler/tests/unit/test_handlerService-json.js
uriloader/exthandler/tests/unit/test_handlerService.js
uriloader/exthandler/tests/unit/xpcshell.ini
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -26,16 +26,17 @@ const TYPE_ANY = "*/*";
 
 const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
 const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
 const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
 const PREF_SELECTED_ACTION = "browser.feeds.handler";
 const PREF_SELECTED_READER = "browser.feeds.handler.default";
 const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external";
 const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost";
+const PREF_HANDLERSVC_JSON = "browser.handlerSvcJson.enabled";
 
 const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";
 
 const NS_ERROR_MODULE_DOM = 2152923136;
 const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12;
 
 function WebContentConverter() {
 }
@@ -321,18 +322,24 @@ WebContentConverterRegistrar.prototype =
               getService(Ci.nsIExternalProtocolService);
     let handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
     let handlers =  handlerInfo.possibleApplicationHandlers;
     for (let i = 0; i < handlers.length; i++) {
       try { // We only want to test web handlers
         let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
         if (handler.uriTemplate == aURITemplate) {
           handlers.removeElementAt(i);
-          let hs = Cc["@mozilla.org/uriloader/handler-service;1"].
-                   getService(Ci.nsIHandlerService);
+          var hs;
+          if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON)) {
+            hs = Cc["@mozilla.org/uriloader/handler-service;1"].
+                     getService(Ci.nsIHandlerService);
+          } else {
+            hs = Cc["@mozilla.org/uriloader/handler-service-json;1"].
+                     getService(Ci.nsIHandlerService);
+          }
           hs.store(handlerInfo);
           return;
         }
       } catch (e) { /* it wasn't a web handler */ }
     }
   },
 
   /**
@@ -448,18 +455,24 @@ WebContentConverterRegistrar.prototype =
           handlerInfo.possibleApplicationHandlers.appendElement(handler, false);
 
           // Since the user has agreed to add a new handler, chances are good
           // that the next time they see a handler of this type, they're going
           // to want to use it.  Reset the handlerInfo to ask before the next
           // use.
           handlerInfo.alwaysAskBeforeHandling = true;
 
-          let hs = Cc["@mozilla.org/uriloader/handler-service;1"].
-                   getService(Ci.nsIHandlerService);
+          var hs;
+          if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON)) {
+            hs = Cc["@mozilla.org/uriloader/handler-service;1"].
+                 getService(Ci.nsIHandlerService);
+          } else {
+            hs = Cc["@mozilla.org/uriloader/handler-service-json;1"].
+                 getService(Ci.nsIHandlerService);
+          }
           hs.store(handlerInfo);
         }
     };
     let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
     notificationBox.appendNotification(message,
                                        notificationValue,
                                        notificationIcon,
                                        notificationBox.PRIORITY_INFO_LOW,
--- a/browser/components/preferences/in-content/applications.js
+++ b/browser/components/preferences/in-content/applications.js
@@ -18,16 +18,18 @@ const TOPIC_PDFJS_HANDLER_CHANGED = "pdf
 
 const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";
 
 // Preferences that affect which entries to show in the list.
 const PREF_SHOW_PLUGINS_IN_LIST = "browser.download.show_plugins_in_list";
 const PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS =
   "browser.download.hide_plugins_without_extensions";
 
+const PREF_HANDLERSVC_JSON = "browser.handlerSvcJson.enabled";
+
 /*
  * Preferences where we store handling information about the feed type.
  *
  * browser.feeds.handler
  * - "bookmarks", "reader" (clarified further using the .default preference),
  *   or "ask" -- indicates the default handler being used to process feeds;
  *   "bookmarks" is obsolete; to specify that the handler is bookmarks,
  *   set browser.feeds.handler.default to "bookmarks";
@@ -160,16 +162,18 @@ HandlerInfoWrapper.prototype = {
   // we haven't (yet?) implemented, so we make it a public property.
   wrappedHandlerInfo: null,
 
 
   // Convenience Utils
 
   _handlerSvc: Cc["@mozilla.org/uriloader/handler-service;1"].
                getService(Ci.nsIHandlerService),
+  _handlerSvcJson   : Cc["@mozilla.org/uriloader/handler-service-json;1"].
+                      getService(Ci.nsIHandlerService),
 
   _prefSvc: Cc["@mozilla.org/preferences-service;1"].
             getService(Ci.nsIPrefBranch),
 
   _categoryMgr: Cc["@mozilla.org/categorymanager;1"].
                 getService(Ci.nsICategoryManager),
 
   element: function(aID) {
@@ -408,17 +412,20 @@ HandlerInfoWrapper.prototype = {
                        false,
                        true);
   },
 
 
   // Storage
 
   store: function() {
-    this._handlerSvc.store(this.wrappedHandlerInfo);
+    if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON))
+      this._handlerSvc.store(this.wrappedHandlerInfo);
+    else
+      this._handlerSvcJson.store(this.wrappedHandlerInfo);
   },
 
 
   // Icons
 
   get smallIcon() {
     return this._getIcon(16);
   },
@@ -861,16 +868,18 @@ var gApplicationsPane = {
   _mimeSvc      : Cc["@mozilla.org/mime;1"].
                   getService(Ci.nsIMIMEService),
 
   _helperAppSvc : Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
                   getService(Ci.nsIExternalHelperAppService),
 
   _handlerSvc   : Cc["@mozilla.org/uriloader/handler-service;1"].
                   getService(Ci.nsIHandlerService),
+  _handlerSvcJson   : Cc["@mozilla.org/uriloader/handler-service-json;1"].
+                  getService(Ci.nsIHandlerService),
 
   _ioSvc        : Cc["@mozilla.org/network/io-service;1"].
                   getService(Ci.nsIIOService),
 
 
   // Initialization & Destruction
 
   init: function() {
@@ -1086,16 +1095,20 @@ var gApplicationsPane = {
     }
   },
 
   /**
    * Load the set of handlers defined by the application datastore.
    */
   _loadApplicationHandlers: function() {
     var wrappedHandlerInfos = this._handlerSvc.enumerate();
+    if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON))
+      wrappedHandlerInfos = this._handlerSvc.enumerate();
+    else
+      wrappedHandlerInfos = this._handlerSvcJson.enumerate();
     while (wrappedHandlerInfos.hasMoreElements()) {
       let wrappedHandlerInfo =
         wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
       let type = wrappedHandlerInfo.type;
 
       let handlerInfoWrapper;
       if (type in this._handledTypes)
         handlerInfoWrapper = this._handledTypes[type];
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -28,16 +28,17 @@ const Cu = Components.utils;
 
 const PREF_PREFIX = 'pdfjs';
 const PREF_DISABLED = PREF_PREFIX + '.disabled';
 const PREF_MIGRATION_VERSION = PREF_PREFIX + '.migrationVersion';
 const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousHandler.preferredAction';
 const PREF_PREVIOUS_ASK = PREF_PREFIX +
                           '.previousHandler.alwaysAskBeforeHandling';
 const PREF_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types';
+const PREF_HANDLERSVC_JSON = "browser.handlerSvcJson.enabled";
 const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
 const TOPIC_PLUGINS_LIST_UPDATED = 'plugins-list-updated';
 const TOPIC_PLUGIN_INFO_UPDATED = 'plugin-info-updated';
 const PDF_CONTENT_TYPE = 'application/pdf';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
@@ -232,18 +233,24 @@ var PdfJs = {
         handlerInfo.preferredAction !== false) {
       // Store the previous settings of preferredAction and
       // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
       // would turn pdf.js off.
       prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
       prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
     }
 
-    let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
-                         getService(Ci.nsIHandlerService);
+    var handlerService;
+    if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON)) {
+      handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
+                           getService(Ci.nsIHandlerService);
+    } else {
+      handlerService = Cc['@mozilla.org/uriloader/handler-service-json;1'].
+                           getService(Ci.nsIHandlerService);
+    }
 
     // Change and save mime handler settings.
     handlerInfo.alwaysAskBeforeHandling = false;
     handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
     handlerService.store(handlerInfo);
 
     // Also disable any plugins for pdfs.
     var stringTypes = '';
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -446,16 +446,18 @@
 @RESPATH@/components/ColorAnalyzer.js
 @RESPATH@/components/PageThumbsProtocol.js
 @RESPATH@/components/nsDefaultCLH.manifest
 @RESPATH@/components/nsDefaultCLH.js
 @RESPATH@/components/nsContentPrefService.manifest
 @RESPATH@/components/nsContentPrefService.js
 @RESPATH@/components/nsContentDispatchChooser.manifest
 @RESPATH@/components/nsContentDispatchChooser.js
+@RESPATH@/components/nsHandlerService-json.manifest
+@RESPATH@/components/nsHandlerService-json.js
 @RESPATH@/components/nsHandlerService.manifest
 @RESPATH@/components/nsHandlerService.js
 @RESPATH@/components/nsWebHandlerApp.manifest
 @RESPATH@/components/nsWebHandlerApp.js
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
 @RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -187,16 +187,17 @@ const mozilla::Module::ContractIDEntry k
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
   { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
   { NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY },
+  { NS_HANDLERSERVICEJSON_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY },
   { NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_MIMESERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default", &kNS_EXTERNALPROTOCOLHANDLER_CID },
   { NS_PREFETCHSERVICE_CONTRACTID, &kNS_PREFETCHSERVICE_CID },
   { NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &kNS_OFFLINECACHEUPDATESERVICE_CID },
   { NS_OFFLINECACHEUPDATE_CONTRACTID, &kNS_OFFLINECACHEUPDATE_CID },
   { NS_LOCALHANDLERAPP_CONTRACTID, &kNS_LOCALHANDLERAPP_CID },
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5533,8 +5533,10 @@ pref("dom.storageManager.enabled", true)
 #else
 pref("dom.storageManager.enabled", false);
 #endif
 
 // Enable the Storage management in about:preferences and persistent-storage permission request
 // To enable the DOM implementation, turn on "dom.storageManager.enabled"
 pref("browser.storageManager.enabled", false);
 
+// Swich the backend of handler service (RDF/JSON)
+pref("browser.handlerSvcJson.enabled", false);
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -867,17 +867,25 @@ nsUnknownContentTypeDialog.prototype = {
 
     return needUpdate && !discardUpdate;
   },
 
   // See if the user changed things, and if so, update the
   // mimeTypes.rdf entry for this mime type.
   updateHelperAppPref: function() {
     var handlerInfo = this.mLauncher.MIMEInfo;
-    var hs = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
+    var hs;
+    var jsonEnabled = Services.prefs.getBoolPref(browser.handlerSvcJson.enabled);
+    if (jsonEnabled) {
+      hs = Cc["@mozilla.org/uriloader/handler-service;1"].
+           getService(Ci.nsIHandlerService);
+    } else {
+      hs = Cc["@mozilla.org/uriloader/handler-service-json;1"].
+           getService(Ci.nsIHandlerService);
+    }
     hs.store(handlerInfo);
   },
 
   // onOK:
   onOK: function() {
     // Verify typed app path, if necessary.
     if (this.useOtherHandler) {
       var helperApp = this.helperAppChoice();
--- a/toolkit/mozapps/handling/content/dialog.js
+++ b/toolkit/mozapps/handling/content/dialog.js
@@ -216,18 +216,24 @@ var dialog = {
         this._handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
         this._handlerInfo.preferredApplicationHandler = this.selectedItem.obj;
       }
       else
         this._handlerInfo.preferredAction = Ci.nsIHandlerInfo.useSystemDefault;
     }
     this._handlerInfo.alwaysAskBeforeHandling = !checkbox.checked;
 
-    var hs = Cc["@mozilla.org/uriloader/handler-service;1"].
-             getService(Ci.nsIHandlerService);
+    var hs;
+    if (!Services.prefs.getBoolPref(PREF_HANDLERSVC_JSON)) {
+      hs = Cc["@mozilla.org/uriloader/handler-service;1"].
+           getService(Ci.nsIHandlerService);
+    } else {
+      hs = Cc["@mozilla.org/uriloader/handler-service-json;1"].
+           getService(Ci.nsIHandlerService);
+    }
     hs.store(this._handlerInfo);
 
     this._handlerInfo.launchWithURI(this._URI, this._windowCtxt);
 
     return true;
   },
 
  /**
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -103,16 +103,18 @@ if CONFIG['MOZ_ENABLE_DBUS']:
     ]
 
 if CONFIG['MOZ_ENABLE_CONTENTACTION']:
     UNIFIED_SOURCES += [
         'nsContentHandlerApp.cpp',
     ]
 
 EXTRA_COMPONENTS += [
+    'nsHandlerService-json.js',
+    'nsHandlerService-json.manifest',
     'nsHandlerService.js',
     'nsHandlerService.manifest',
     'nsWebHandlerApp.js',
     'nsWebHandlerApp.manifest',
 ]
 
 IPDL_SOURCES += [
     'PExternalHelperApp.ipdl',
--- a/uriloader/exthandler/nsCExternalHandlerService.idl
+++ b/uriloader/exthandler/nsCExternalHandlerService.idl
@@ -19,16 +19,19 @@ nsIExternalHelperAppService
  { 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_HANDLERSERVICEJSON_CONTRACTID \
+"@mozilla.org/uriloader/handler-service-json;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
@@ -111,16 +111,18 @@
 #endif
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 // Download Folder location constants
 #define NS_PREF_DOWNLOAD_DIR        "browser.download.dir"
 #define NS_PREF_DOWNLOAD_FOLDERLIST "browser.download.folderList"
+#define NS_PREF_HANDLERSVC_JSON "browser.handlerSvcJson.enabled"
+
 enum {
   NS_FOLDER_VALUE_DESKTOP = 0
 , NS_FOLDER_VALUE_DOWNLOADS = 1
 , NS_FOLDER_VALUE_CUSTOM = 2
 };
 
 LazyLogModule nsExternalHelperAppService::mLog("HelperAppService");
 
@@ -130,16 +132,17 @@ LazyLogModule nsExternalHelperAppService
 #undef LOG
 #define LOG(args) MOZ_LOG(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info)
 
 static const char NEVER_ASK_FOR_SAVE_TO_DISK_PREF[] =
   "browser.helperApps.neverAsk.saveToDisk";
 static const char NEVER_ASK_FOR_OPEN_FILE_PREF[] =
   "browser.helperApps.neverAsk.openFile";
+static bool handlerSvcWithJSON = false;
 
 // Helper functions for Content-Disposition headers
 
 /**
  * Given a URI fragment, unescape it
  * @param aFragment The string to unescape
  * @param aURI The URI from which this fragment is taken. Only its character set
  *             will be used.
@@ -654,33 +657,51 @@ NS_IMPL_ISUPPORTS(
   nsIExternalProtocolService,
   nsIMIMEService,
   nsIObserver,
   nsISupportsWeakReference)
 
 nsExternalHelperAppService::nsExternalHelperAppService()
 {
 }
+
+void
+HandlerSvcPrefChangedCallback(const char* aPrefName,
+                           void* aClosure)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aPrefName, NS_PREF_HANDLERSVC_JSON));
+  MOZ_ASSERT(!aClosure);
+
+  handlerSvcWithJSON = Preferences::GetBool(NS_PREF_HANDLERSVC_JSON, false);
+}
+
+
+
 nsresult nsExternalHelperAppService::Init()
 {
   // Add an observer for profile change
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs)
     return NS_ERROR_FAILURE;
 
   nsresult rv = obs->AddObserver(this, "profile-before-change", true);
   NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = Preferences::RegisterCallbackAndCall(HandlerSvcPrefChangedCallback, NS_PREF_HANDLERSVC_JSON);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return obs->AddObserver(this, "last-pb-context-exited", true);
 }
 
 nsExternalHelperAppService::~nsExternalHelperAppService()
 {
+  Preferences::UnregisterCallback(HandlerSvcPrefChangedCallback, NS_PREF_HANDLERSVC_JSON);
 }
 
-
 nsresult
 nsExternalHelperAppService::DoContentContentProcessHelper(const nsACString& aMimeContentType,
                                                           nsIRequest *aRequest,
                                                           nsIInterfaceRequestor *aContentContext,
                                                           bool aForceSave,
                                                           nsIInterfaceRequestor *aWindowContext,
                                                           nsIStreamListener ** aStreamListener)
 {
@@ -1160,17 +1181,23 @@ nsExternalHelperAppService::GetProtocolH
 
   bool exists;
   nsresult rv = GetProtocolHandlerInfoFromOS(aScheme, &exists, aHandlerInfo);
   if (NS_FAILED(rv)) {
     // Either it knows nothing, or we ran out of memory
     return NS_ERROR_FAILURE;
   }
   
-  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  nsCOMPtr<nsIHandlerService> handlerSvc;
+  if (!handlerSvcWithJSON) {
+    handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  } else {
+    handlerSvc = do_GetService(NS_HANDLERSERVICEJSON_CONTRACTID);
+  }
+
   if (handlerSvc) {
     bool hasHandler = false;
     (void) handlerSvc->Exists(*aHandlerInfo, &hasHandler);
     if (hasHandler) {
       rv = handlerSvc->FillHandlerInfo(*aHandlerInfo, EmptyCString());
       if (NS_SUCCEEDED(rv))
         return NS_OK;
     }
@@ -1730,17 +1757,24 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
   if (alwaysAsk) {
     // But we *don't* ask if this mimeInfo didn't come from
     // 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.
 
     bool mimeTypeIsInDatastore = false;
-    nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+
+    nsCOMPtr<nsIHandlerService> handlerSvc;
+    if (!handlerSvcWithJSON) {
+      handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+    } else {
+      handlerSvc = do_GetService(NS_HANDLERSERVICEJSON_CONTRACTID);
+    }
+
     if (handlerSvc) {
       handlerSvc->Exists(mMimeInfo, &mimeTypeIsInDatastore);
     }
     if (!handlerSvc || !mimeTypeIsInDatastore) {
       nsAutoCString MIMEType;
       mMimeInfo->GetMIMEType(MIMEType);
       if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_SAVE_TO_DISK_PREF, MIMEType.get())) {
         // Don't need to ask after all.
@@ -2638,17 +2672,22 @@ NS_IMETHODIMP nsExternalHelperAppService
   // 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 datastore
   // This will not overwrite the OS information that interests us
   // (i.e. default application, default app. description)
   nsresult rv;
-  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  nsCOMPtr<nsIHandlerService> handlerSvc;
+  if (!handlerSvcWithJSON) {
+    handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  } else {
+    handlerSvc = do_GetService(NS_HANDLERSERVICEJSON_CONTRACTID);
+  }
   if (handlerSvc) {
     bool hasHandler = false;
     (void) handlerSvc->Exists(*_retval, &hasHandler);
     if (hasHandler) {
       rv = handlerSvc->FillHandlerInfo(*_retval, EmptyCString());
       LOG(("Data source: Via type: retval 0x%08x\n", rv));
     } else {
       rv = NS_ERROR_NOT_AVAILABLE;
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsHandlerService-json.js
@@ -0,0 +1,553 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
+                                  "resource://gre/modules/JSONFile.jsm");
+
+// Observer Service
+XPCOMUtils.defineLazyServiceGetter(this, "observerSvc",
+                                   "@mozilla.org/observer-service;1",
+                                   "nsIObserverService");
+// MIME Service
+XPCOMUtils.defineLazyServiceGetter(this, "mimeSvc",
+                                   "@mozilla.org/mime;1",
+                                   "nsIMIMEService");
+// Protocol Service
+XPCOMUtils.defineLazyServiceGetter(this, "protoSvc",
+                                   "@mozilla.org/uriloader/external-protocol-service;1",
+                                   "nsIExternalProtocolService");
+
+function HandlerService() {
+  this._init();
+}
+
+const HandlerServiceFactory = {
+  _instance: null,
+  createInstance: function (outer, iid) {
+    if (this._instance)
+      return this._instance;
+
+    let processType = Cc["@mozilla.org/xre/runtime;1"].
+      getService(Ci.nsIXULRuntime).processType;
+    if (processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
+      return Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+    return (this._instance = new HandlerService());
+  }
+};
+
+HandlerService.prototype = {
+  //**************************************************************************//
+  // XPCOM Plumbing
+
+  classID:          Components.ID("{220cc253-b60f-41f6-b9cf-fdcb325f970f}"),
+  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIHandlerService]),
+  _xpcom_factory: HandlerServiceFactory,
+
+  //**************************************************************************//
+  // Initialization & Destruction
+
+  _init() {
+    // Observe profile-before-change so we can switch to the datasource
+    // in the new profile when the user changes profiles.
+    observerSvc.addObserver(this, "profile-before-change", false);
+
+    // Observe xpcom-shutdown so we can remove these observers
+    // when the application shuts down.
+    observerSvc.addObserver(this, "xpcom-shutdown", false);
+
+    // Observe profile-do-change so that non-default profiles get upgraded too
+    observerSvc.addObserver(this, "profile-do-change", false);
+
+     let jsonPath = OS.Path.join(OS.Constants.Path.profileDir,
+                              "mimeTypes.json");
+
+    // LOAD DATA
+    this._store = new JSONFile({
+      path: jsonPath,
+      dataPostProcessor: this._dataPostProcessor.bind(this),
+    });
+    this._store.load();
+    this._store.saveSoon();
+
+    // Update DB
+    // do any necessary updating of the datastore
+    this._updateDB();
+  },
+
+  _dataPostProcessor(data) {
+    /* we can do import here if there is no mimeTypes.json */
+    if (!data || !data.mimetypes || !data.schemes) {
+      data = {"version":{},"mimetypes":{},"schemes":{}};
+      // TODO : Do import here
+    }
+    return data;
+  },
+
+  _updateDB() {
+    this._store.ensureDataReady();
+
+    try {
+      if (!(this._currentLocale in this._store.data.version)) {
+        this._store.data.version[this._currentLocale] = -1;
+      }
+      var defaultHandlersVersion = this._store.data.version[this._currentLocale];
+
+      if (defaultHandlersVersion < this._prefsDefaultHandlersVersion ) {
+        // set the new version first so that if we recurse we don't
+        // call _injectNewDefaults several times
+        this._store.data.version[this._currentLocale] = this._prefsDefaultHandlersVersion;
+        this._injectNewDefaults();
+      }
+    }  catch (ex) {
+      // if injecting the defaults failed, set the version back to the
+      // previous value
+      this._store.data.version[this._currentLocale] = defaultHandlersVersion;
+    }
+  },
+
+  get _prefsDefaultHandlersVersion() {
+    // get handler service pref branch
+    var handlerSvcBranch = Services.prefs.getBranch("gecko.handlerService.");
+
+    // get the version of the preferences for this locale
+    return Number(handlerSvcBranch.
+                  getComplexValue("defaultHandlersVersion",
+                                  Ci.nsIPrefLocalizedString).data);
+  },
+
+  get _currentLocale() {
+    var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].
+                         getService(Ci.nsIXULChromeRegistry);
+    var currentLocale = chromeRegistry.getSelectedLocale("global");
+    return currentLocale;
+  },
+
+  _injectNewDefaults() {
+    // get handler service pref branch
+
+    let schemesPrefBranch = Services.prefs.getBranch("gecko.handlerService.schemes.");
+    let schemePrefList = schemesPrefBranch.getChildList("");
+
+    var schemes = {};
+
+    // read all the scheme prefs into a hash
+    for (var schemePrefName of schemePrefList) {
+
+      let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
+
+      try {
+        var attrData =
+          schemesPrefBranch.getComplexValue(schemePrefName,
+                                            Ci.nsIPrefLocalizedString).data;
+        if (!(scheme in schemes))
+          schemes[scheme] = {};
+
+        if (!(handlerNumber in schemes[scheme]))
+          schemes[scheme][handlerNumber] = {};
+
+        schemes[scheme][handlerNumber][attribute] = attrData;
+      } catch (ex) {}
+    }
+
+    for (var scheme in schemes) {
+
+      // This clause is essentially a reimplementation of
+      // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
+      // Necessary because calling that from here would make XPConnect barf
+      // when getService tried to re-enter the constructor for this
+      // service.
+      let osDefaultHandlerFound = {};
+      let protoInfo = protoSvc.getProtocolHandlerInfoFromOS(scheme,
+                               osDefaultHandlerFound);
+
+      if (this.exists(protoInfo))
+        this.fillHandlerInfo(protoInfo, null);
+      else
+        protoSvc.setProtocolHandlerDefaults(protoInfo,
+                                            osDefaultHandlerFound.value);
+
+      // cache the possible handlers to avoid extra xpconnect traversals.
+      let possibleHandlers = protoInfo.possibleApplicationHandlers;
+
+      for (let handlerNumber in schemes[scheme]) {
+        let handlerPrefs = schemes[scheme][handlerNumber];
+        let handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+                         createInstance(Ci.nsIWebHandlerApp);
+
+        handlerApp.uriTemplate = handlerPrefs.uriTemplate;
+        handlerApp.name = handlerPrefs.name;
+
+        if (!this._isInHandlerArray(possibleHandlers, handlerApp)) {
+          possibleHandlers.appendElement(handlerApp, false);
+        }
+      }
+
+      this.store(protoInfo);
+    }
+  },
+
+  _destroy() {
+    observerSvc.removeObserver(this, "profile-before-change");
+    observerSvc.removeObserver(this, "xpcom-shutdown");
+    observerSvc.removeObserver(this, "profile-do-change");
+
+    // XXX Should we also null references to all the services that get stored
+    // by our memoizing getters in the Convenience Getters section?
+  },
+
+  _onProfileChange() {
+    // Lose our reference to the datasource so we reacquire it
+    // from the new profile the next time we need it.
+    this.__ds = null;
+  },
+
+  _isInHandlerArray(aArray, aHandler) {
+    var enumerator = aArray.enumerate();
+    while (enumerator.hasMoreElements()) {
+      let handler = enumerator.getNext();
+      handler.QueryInterface(Ci.nsIHandlerApp);
+      if (handler.equals(aHandler))
+        return true;
+    }
+
+    return false;
+  },
+
+
+  //**************************************************************************//
+  // nsIObserver
+
+  observe(subject, topic, data) {
+    switch(topic) {
+      case "profile-before-change":
+        this._onProfileChange();
+        break;
+      case "xpcom-shutdown":
+        this._destroy();
+        break;
+      case "profile-do-change":
+        this._updateDB();
+        break;
+    }
+  },
+
+
+  //**************************************************************************//
+  // nsIHandlerService
+
+  enumerate() {
+    var handlers = Cc["@mozilla.org/array;1"].
+                   createInstance(Ci.nsIMutableArray);
+    this._appendHandlers(handlers, "mimetypes");
+    this._appendHandlers(handlers, "schemes");
+    return handlers.enumerate();
+  },
+
+  fillHandlerInfo(aHandlerInfo, aOverrideType) {
+    var type = aOverrideType || aHandlerInfo.type;
+
+    // Determine whether or not information about this handler is available
+    // in the datastore.
+    var overrideObj;
+    this._store.ensureDataReady();
+    if ((type in this._store.data.mimetypes)) {
+      overrideObj = this._store.data.mimetypes[type];
+    }  else if ((aHandlerInfo.type in this._store.data.schemes)) {
+      overrideObj = this._store.data.schemes[type];
+    } else {
+      return Cr.NS_ERROR_NOT_AVAILABLE;
+    }
+
+    aHandlerInfo.description = overrideObj.description;
+    aHandlerInfo.preferredAction = overrideObj.action;
+
+    aHandlerInfo.preferredApplicationHandler =
+      this._retrieveHandlerApp(overrideObj.preferredHandler);
+    if (aHandlerInfo.preferredApplicationHandler != null)
+      aHandlerInfo.possibleApplicationHandlers.appendElement(aHandlerInfo.preferredApplicationHandler, false);
+
+    for (let handler of overrideObj.possibleHandlers) {
+      var possibleHandler = this._retrieveHandlerApp(handler);
+      aHandlerInfo.possibleApplicationHandlers.appendElement(possibleHandler, false);
+    }
+
+    // If we have an "always ask" flag stored in the RDF, always use its
+    // value. Otherwise, use the default value stored in the pref service.
+    var alwaysAsk;
+    if (overrideObj.askBeforeHandle != null) {
+      alwaysAsk = overrideObj.askBeforeHandle;
+    } else {
+      var prefBranch = Services.prefs.getBranch("network.protocol-handler.");
+      try {
+        alwaysAsk = prefBranch.getBoolPref("warn-external." + type);
+      } catch (e) {
+        // will throw if pref didn't exist.
+        try {
+          alwaysAsk = prefBranch.getBoolPref("warn-external-default");
+        } catch (e) {
+          // Nothing to tell us what to do, so be paranoid and prompt.
+          alwaysAsk = true;
+        }
+      }
+    }
+    aHandlerInfo.alwaysAskBeforeHandling = alwaysAsk;
+
+    // extensions
+    if(aHandlerInfo instanceof Ci.nsIMIMEInfo) {
+      for (let extension of overrideObj.fileExtensions)
+        aHandlerInfo.appendExtension(extension);
+    }
+  },
+
+  store(aHandlerInfo) {
+    var type = aHandlerInfo.type;
+    var description = aHandlerInfo.description;
+    var action = aHandlerInfo.preferredAction;
+    var askBeforeHandle = aHandlerInfo.alwaysAskBeforeHandling;
+
+    var handlerObj = {
+      "description": description,
+      "action": action,
+      "askBeforeHandle": askBeforeHandle,
+      "fileExtensions": [],
+      "preferredHandler": {},
+      "possibleHandlers": [],
+    };
+
+    var HandlerType = {
+      LocalHandlerApp: 0,
+      WebHandlerApp: 1,
+      DBusHandlerApp: 2,
+    };
+
+    var preferredHandler = aHandlerInfo.preferredApplicationHandler;
+    if (preferredHandler) {
+
+      var preferredHandlerRecord;
+      if (preferredHandler instanceof Ci.nsILocalHandlerApp) {
+        preferredHandlerRecord = {
+          "name": preferredHandler.name,
+          "description": preferredHandler.detailedDescription,
+          "type": HandlerType.LocalHandlerApp,
+          "path": preferredHandler.executable.path,
+        };
+      } else if(preferredHandler instanceof Ci.nsIWebHandlerApp) {
+        preferredHandlerRecord = {
+          "name": preferredHandler.name,
+          "description": preferredHandler.detailedDescription,
+          "type": HandlerType.WebHandlerApp,
+          "uriTemplate": preferredHandler.uriTemplate,
+        };
+      } else if(preferredHandler instanceof Ci.nsIDBusHandlerApp) {
+        preferredHandlerRecord = {
+          "name": preferredHandler.name,
+          "description": preferredHandler.detailedDescription,
+          "type": HandlerType.DBusHandlerApp,
+          "service": preferredHandler.service,
+          "method": preferredHandler.method,
+          "objectPath": preferredHandler.objectPath,
+          "dBusInterface": preferredHandler.dBusInterface,
+        };
+      }
+      handlerObj.preferredHandler = preferredHandlerRecord;
+    }
+
+    var handlers = aHandlerInfo.possibleApplicationHandlers;
+    var apps = handlers.enumerate();
+    while (apps.hasMoreElements()) {
+      var handler = apps.getNext();
+      handler.QueryInterface(Ci.nsIHandlerApp);
+
+      if (handler instanceof Ci.nsILocalHandlerApp) {
+        handlerObj.possibleHandlers.push( {
+          "name": handler.name,
+          "description": handler.detailedDescription,
+          "type": HandlerType.LocalHandlerApp,
+          "path": handler.executable.path,
+        });
+      } else if(handler instanceof Ci.nsIWebHandlerApp) {
+        handlerObj.possibleHandlers.push( {
+          "name": handler.name,
+          "description": handler.detailedDescription,
+          "type": HandlerType.WebHandlerApp,
+          "uriTemplate": handler.uriTemplate,
+        });
+      } else if(handler instanceof Ci.nsIDBusHandlerApp) {
+        handlerObj.possibleHandlers.push( {
+            "name": handler.name,
+            "description": handler.detailedDescription,
+            "type": HandlerType.DBusHandlerApp,
+            "service": handler.service,
+            "method": handler.method,
+            "objectPath": handler.objectPath,
+            "dBusInterface": handler.dBusInterface,
+        });
+      } else {
+        continue;
+      }
+    } // end_while(apps.hasMoreElements())
+
+    this._store.ensureDataReady();
+    if(aHandlerInfo instanceof Ci.nsIMIMEInfo) {
+      var extEnumerator = aHandlerInfo.getFileExtensions();
+      while (extEnumerator.hasMore()) {
+        var extension = extEnumerator.getNext();
+        if (handlerObj.fileExtensions.indexOf(extension) == -1)
+          handlerObj.fileExtensions.push(extension);
+      }
+      this._store.data.mimetypes[type] = handlerObj;
+    } else {
+      this._store.data.schemes[type] = handlerObj;
+    }
+    this._store.saveSoon();
+  },
+
+  exists(aHandlerInfo) {
+    this._store.ensureDataReady();
+    if ((aHandlerInfo.type in this._store.data.mimetypes))
+      return true;
+    if ((aHandlerInfo.type in this._store.data.schemes))
+      return true;
+    return false;
+  },
+
+  remove(aHandlerInfo) {
+    this._store.ensureDataReady();
+    if ((aHandlerInfo.type in this._store.data.mimetypes)) {
+      delete this._store.data.mimetypes[aHandlerInfo.type];
+    } else if ((aHandlerInfo.type in this._store.data.schemes)) {
+      delete this._store.data.schemes[aHandlerInfo.type];
+    }
+    this._store.saveSoon();
+  },
+
+  getTypeFromExtension(aFileExtension) {
+    var fileExtension = aFileExtension.toLowerCase();
+    this._store.ensureDataReady();
+    var mimeTypes = this._store.data.mimetypes;
+    for (var type in mimeTypes) {
+      if (mimeTypes[type].fileExtensions.includes(fileExtension))
+        return type;
+    }
+    return "";
+  },
+
+  /**
+   * 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(aHandlerObj) {
+    if (!("type" in aHandlerObj))
+      return null;
+
+    var handlerApp;
+    var HandlerType = {
+      LocalHandlerApp: 0,
+      WebHandlerApp: 1,
+      DBusHandlerApp: 2,
+    };
+
+    if (aHandlerObj.type == HandlerType.LocalHandlerApp) {
+      if (aHandlerObj.path) {
+        let executable = this._getFileWithPath(aHandlerObj.path);
+        if (!executable)
+          return null;
+        handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+                   createInstance(Ci.nsILocalHandlerApp);
+        handlerApp.executable = executable;
+      }
+    } else if(aHandlerObj.type == HandlerType.WebHandlerApp) {
+      handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+                   createInstance(Ci.nsIWebHandlerApp);
+      handlerApp.uriTemplate = aHandlerObj.uriTemplate;
+    } else if(aHandlerObj.type == HandlerType.WebHandlerApp) {
+      handlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"].
+                   createInstance(Ci.nsIDBusHandlerApp);
+      handlerApp.service   = aHandlerObj.service;
+      handlerApp.method    = aHandlerObj.method;
+      handlerApp.objectPath   = aHandlerObj.objpath;
+      handlerApp.dBusInterface = aHandlerObj.iface;
+    } else
+      return null;
+
+    handlerApp.name = aHandlerObj.name;
+    return handlerApp;
+  },
+
+  /**
+   * 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(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 = Service.dirSvc.get("XCurProcD", Ci.nsIFile);
+
+      try {
+        file.append(aPath);
+        if (file.exists())
+          return file;
+      }
+      catch(ex) {}
+    }
+
+    return null;
+  },
+
+  /**
+   * Append known handlers of the given class to the given array.
+   *
+   * @param aHandlers   {array} the array of handlers to append to
+   * @param aClass      {string} the class for which to append handlers
+   */
+  _appendHandlers(aHandlers, aClass) {
+    this._store.ensureDataReady();
+    var typesObj = this._store.data[aClass]
+    var handler;
+    for (var type in typesObj) {
+      if (aClass.localeCompare("mimetypes") == 0) {
+        handler = mimeSvc.getFromTypeAndExtension(type, null);
+      } else if (aClass.localeCompare("schemes") == 0){
+        handler = protoSvc.getProtocolHandlerInfo(type);
+      } else {
+        continue;
+      }
+      aHandlers.appendElement(handler, false);
+    }
+  },
+};
+
+//****************************************************************************//
+// More XPCOM Plumbing
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HandlerService]);
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsHandlerService-json.manifest
@@ -0,0 +1,2 @@
+component {220cc253-b60f-41f6-b9cf-fdcb325f970f} nsHandlerService-json.js
+contract @mozilla.org/uriloader/handler-service-json;1 {220cc253-b60f-41f6-b9cf-fdcb325f970f} process=main
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/mimeTypes.json
@@ -0,0 +1,1 @@
+{"version":{"en-US":4},"mimetypes":{"application/pdf":{"description":"PDF document","action":3,"askBeforeHandle":false,"fileExtensions":["pdf"],"preferredHandler":{},"possibleHandlers":[]},"application/x-php":{"description":"PHP script","action":2,"askBeforeHandle":false,"fileExtensions":[],"preferredHandler":{"name":"Firefox browser","description":"","type":0,"path":"/usr/bin/firefox"},"possibleHandlers":[{"name":"Firefox browser","description":"","type":0,"path":"/usr/bin/firefox"}]}},"schemes":{"webcal":{"description":"","action":2,"askBeforeHandle":false,"fileExtensions":[],"preferredHandler":{},"possibleHandlers":[{"name":"30 Boxes","description":null,"type":1,"uriTemplate":"http://30boxes.com/external/widget?refer=ff&url=%s"},{"name":"30 Boxes","description":null,"type":1,"uriTemplate":"https://30boxes.com/external/widget?refer=ff&url=%s"}]},"ircs":{"description":"","action":2,"askBeforeHandle":false,"fileExtensions":[],"preferredHandler":{},"possibleHandlers":[{"name":"Mibbit","description":null,"type":1,"uriTemplate":"https://www.mibbit.com/?url=%s"},{"name":"IRCCloud","description":null,"type":1,"uriTemplate":"https://www.irccloud.com/#?/irc_url=%s"},{"name":"IRCCloud","description":null,"type":1,"uriTemplate":"https://irccloud.mozilla.com/#?/irc_url=%s"}]}}}
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_handlerService-json.js
@@ -0,0 +1,125 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests new implementation of handler service by reloading mimeType.json
+ */
+
+"use strict"
+
+Cu.import("resource://gre/modules/osfile.jsm")
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyServiceGetter(this, "mimeSvc",
+                                   "@mozilla.org/mime;1",
+                                   "nsIMIMEService");
+XPCOMUtils.defineLazyServiceGetter(this, "handlerJsonSvc",
+                                   "@mozilla.org/uriloader/handler-service-json;1",
+                                   "nsIHandlerService");
+
+const pdfHandlerInfo = mimeSvc.getFromTypeAndExtension("application/pdf", null);
+const gzipHandlerInfo = mimeSvc.getFromTypeAndExtension("application/x-gzip", null);
+
+function run_test() {
+  do_get_profile();
+  run_next_test();
+}
+
+function test_exists(handlerJsonSvc) {
+  // test "exists()" only
+  do_check_true(handlerJsonSvc.exists(pdfHandlerInfo));
+  do_check_false(handlerJsonSvc.exists(gzipHandlerInfo));
+}
+
+function test_getTypeFromExtension(handlerJsonSvc) {
+  // test "getTypeFromExtension()" only
+  var type = handlerJsonSvc.getTypeFromExtension("doc");
+  do_check_eq(type, "");
+  type = handlerJsonSvc.getTypeFromExtension("pdf");
+  do_check_eq(type, "application/pdf");
+}
+
+function test_remove(handlerJsonSvc) {
+  // test "remove()" combined with "exist()" and "getTypeFromExtension()"
+  handlerJsonSvc.remove(pdfHandlerInfo);
+  do_check_false(handlerJsonSvc.exists(pdfHandlerInfo));
+  var type = handlerJsonSvc.getTypeFromExtension("pdf");
+  do_check_eq(type, "");
+}
+
+function test_store(handlerJsonSvc) {
+  // test "store()" combined with "exist()" and "getTypeFromExtension()"
+  handlerJsonSvc.store(pdfHandlerInfo);
+  do_check_true(handlerJsonSvc.exists(pdfHandlerInfo));
+  var type = handlerJsonSvc.getTypeFromExtension("pdf");
+  do_check_eq(type, "application/pdf");
+}
+
+function test_enumerate(handlerJsonSvc) {
+  // test "enumerate()"
+  var handlerTypes = ["application/x-php", "application/pdf", "webcal", "ircs"];
+  var handlerInfos = handlerJsonSvc.enumerate();
+  while (handlerInfos.hasMoreElements()) {
+    var handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+    do_check_neq(handlerTypes.indexOf(handlerInfo.type), -1);
+  }
+
+  // test "enumerate()" combined with "store()"
+  handlerJsonSvc.store(gzipHandlerInfo);
+  handlerTypes.push("application/x-gzip");
+  handlerInfos = handlerJsonSvc.enumerate();
+  while (handlerInfos.hasMoreElements()) {
+    handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+    do_check_neq(handlerTypes.indexOf(handlerInfo.type), -1);
+    handlerTypes.splice(handlerTypes.indexOf(handlerInfo.type), 1);
+  }
+  do_check_eq(handlerTypes.length, 0);
+
+  // test "enumerate()" combined with "remove()"
+  handlerJsonSvc.remove(pdfHandlerInfo);
+  handlerTypes = ["application/x-php", "application/x-gzip", "webcal", "ircs"];
+  handlerInfos = handlerJsonSvc.enumerate();
+  while (handlerInfos.hasMoreElements()) {
+    handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+    do_check_neq(handlerTypes.indexOf(handlerInfo.type), -1);
+    handlerTypes.splice(handlerTypes.indexOf(handlerInfo.type), 1);
+  }
+  do_check_eq(handlerTypes.length, 0);
+}
+
+// test "fillHandlerInfo()"
+function test_fillHandlerInfo(handlerJsonSvc) {
+  // Get a handler info for a MIME type that neither the application nor
+  // the OS knows about and make sure its properties are set to the proper
+  // default values.
+  var handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
+  do_check_eq(handlerInfo.description, "");
+  do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
+  do_check_eq(handlerInfo.possibleApplicationHandlers.length, 0);
+
+  handlerJsonSvc.fillHandlerInfo(handlerInfo, "application/x-php");
+  var phpHandlerInfo = mimeSvc.getFromTypeAndExtension("application/x-php", null);
+  do_check_eq(handlerInfo.description, phpHandlerInfo.description);
+  do_check_eq(handlerInfo.preferredAction, phpHandlerInfo.preferredAction);
+  do_check_eq(handlerInfo.possibleApplicationHandlers.length, phpHandlerInfo.possibleApplicationHandlers.length);
+}
+
+add_task(function* testHandlerServiceJson() {
+  let src = "mimeTypes.json";
+  let dst = OS.Path.join(OS.Constants.Path.profileDir, src);
+
+  yield OS.File.copy(src, dst);
+  Assert.ok((yield OS.File.exists(dst)), "should have a DB now");
+  Services.prefs.setBoolPref("browser.handlerSvcJson.enabled", true);
+
+  test_exists(handlerJsonSvc);
+  test_getTypeFromExtension(handlerJsonSvc);
+  test_remove(handlerJsonSvc);
+  test_store(handlerJsonSvc);
+  test_enumerate(handlerJsonSvc);
+  test_fillHandlerInfo(handlerJsonSvc);
+});
+
--- a/uriloader/exthandler/tests/unit/test_handlerService.js
+++ b/uriloader/exthandler/tests/unit/test_handlerService.js
@@ -1,104 +1,101 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests two different backend of handler service
+ */
+
+"use strict"
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+
+XPCOMUtils.defineLazyServiceGetter(this, "mimeSvc",
+                                   "@mozilla.org/mime;1",
+                                   "nsIMIMEService");
+XPCOMUtils.defineLazyServiceGetter(this, "protoSvc",
+                                   "@mozilla.org/uriloader/external-protocol-service;1",
+                                   "nsIExternalProtocolService");
+XPCOMUtils.defineLazyServiceGetter(this, "env",
+                                   "@mozilla.org/process/environment;1",
+                                   "nsIEnvironment");
+XPCOMUtils.defineLazyServiceGetter(this, "handlerSvcRdf",
+                                   "@mozilla.org/uriloader/handler-service;1",
+                                   "nsIHandlerService");
+XPCOMUtils.defineLazyServiceGetter(this, "handlerSvcJson",
+                                   "@mozilla.org/uriloader/handler-service-json;1",
+                                   "nsIHandlerService");
+
+//**************************************************************************//
+// Constants
+const rootPrefBranch = Services.prefs.getBranch("");
 
 function run_test() {
-  //**************************************************************************//
-  // Constants
-
-  const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].
-                     getService(Ci.nsIHandlerService);
-
-  const mimeSvc = Cc["@mozilla.org/mime;1"].
-                  getService(Ci.nsIMIMEService);
+  do_get_profile();
+  run_next_test();
+}
 
-  const protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
-                   getService(Ci.nsIExternalProtocolService);
-  
-  const prefSvc = Cc["@mozilla.org/preferences-service;1"].
-                  getService(Ci.nsIPrefService);
-                  
-  const ioService = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-
-  const env = Cc["@mozilla.org/process/environment;1"].
-              getService(Components.interfaces.nsIEnvironment);
-
-  const rootPrefBranch = prefSvc.getBranch("");
-  
+function checkMailto() {
   let noMailto = false;
   if (mozinfo.os == "win") {
     // Check mailto handler from registry.
     // If registry entry is nothing, no mailto handler
     let regSvc = Cc["@mozilla.org/windows-registry-key;1"].
                  createInstance(Ci.nsIWindowsRegKey);
     try {
       regSvc.open(regSvc.ROOT_KEY_CLASSES_ROOT,
                   "mailto",
                   regSvc.ACCESS_READ);
       noMailto = false;
     } catch (ex) {
       noMailto = true;
     }
     regSvc.close();
-  }
-
-  if (mozinfo.os == "linux") {
+  }else  if (mozinfo.os == "linux") {
     // Check mailto handler from GIO
     // If there isn't one, then we have no mailto handler
     let gIOSvc = Cc["@mozilla.org/gio-service;1"].
                  createInstance(Ci.nsIGIOService);
     try {
       gIOSvc.getAppForURIScheme("mailto");
       noMailto = false;
     } catch (ex) {
       noMailto = true;
     }
   }
-
-  //**************************************************************************//
-  // Sample Data
+  return noMailto;
+}
 
-  // It doesn't matter whether or not this nsIFile is actually executable,
-  // only that it has a path and exists.  Since we don't know any executable
-  // that exists on all platforms (except possibly the application being
-  // tested, but there doesn't seem to be a way to get a reference to that
-  // from the directory service), we use the temporary directory itself.
-  var executable = HandlerServiceTest._dirSvc.get("TmpD", Ci.nsIFile);
-  // XXX We could, of course, create an actual executable in the directory:
-  //executable.append("localhandler");
-  //if (!executable.exists())
-  //  executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
-
-  var localHandler = {
+function configure_localHandler(executable) {
+  return {
     name: "Local Handler",
     executable: executable,
     interfaces: [Ci.nsIHandlerApp, Ci.nsILocalHandlerApp, Ci.nsISupports],
     QueryInterface: function(iid) {
       if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
         throw Cr.NS_ERROR_NO_INTERFACE;
       return this;
     }
   };
-  
+}
+
+function configure_webHandler() {
   var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
                    createInstance(Ci.nsIWebHandlerApp);
   webHandler.name = "Web Handler";
   webHandler.uriTemplate = "http://www.example.com/?%s";
-
-  // 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.
+  return webHandler;
+}
 
-
-  //**************************************************************************//
-  // Test Default Properties
-
+function configureAndTest_handlerInfo() {
   // Get a handler info for a MIME type that neither the application nor
   // the OS knows about and make sure its properties are set to the proper
   // default values.
 
   var handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
 
   // Make sure it's also an nsIHandlerInfo.
   do_check_true(handlerInfo instanceof Ci.nsIHandlerInfo);
@@ -115,73 +112,65 @@ function run_test() {
   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, "");
 
-  // test some default protocol info properties
-  var haveDefaultHandlersVersion = false;
-  try { 
-    // If we have a defaultHandlersVersion pref, then assume that we're in the
-    // firefox tree and that we'll also have default handlers.
-    // Bug 395131 has been filed to make this test work more generically
-    // by providing our own prefs for this test rather than this icky
-    // special casing.
-    rootPrefBranch.getCharPref("gecko.handlerService.defaultHandlersVersion");
-    haveDefaultHandlersVersion = true;
-  } catch (ex) {}
+  return handlerInfo;
+}
 
-  const kExternalWarningDefault = 
-    "network.protocol-handler.warn-external-default";
-  prefSvc.setBoolPref(kExternalWarningDefault, true);
+function SetExternalWarningPref(type, value) {
+  const kExternalWarningPrefPrefix = "network.protocol-handler.warn-external.";
+  Services.prefs.setBoolPref(kExternalWarningPrefPrefix + type, value);
+}
 
-  // XXX add more thorough protocol info property checking
-  
+
+function configureAndTest_protoInfo(noMailto, haveDefaultHandlersVersion) {
+
   // no OS default handler exists
   var protoInfo = protoSvc.getProtocolHandlerInfo("x-moz-rheet");
   do_check_eq(protoInfo.preferredAction, protoInfo.alwaysAsk);
   do_check_true(protoInfo.alwaysAskBeforeHandling);
   
   // OS default exists, injected default does not exist, 
   // explicit warning pref: false
-  const kExternalWarningPrefPrefix = "network.protocol-handler.warn-external.";
-  prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", false);
+  SetExternalWarningPref("http", false);
   protoInfo = protoSvc.getProtocolHandlerInfo("http");
   do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
   do_check_false(protoInfo.alwaysAskBeforeHandling);
   
   // OS default exists, injected default does not exist, 
   // explicit warning pref: true
-  prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", true);
+  SetExternalWarningPref("http", true);
   protoInfo = protoSvc.getProtocolHandlerInfo("http");
   // OS handler isn't included in possibleApplicationHandlers, so length is 0
   // Once they become instances of nsILocalHandlerApp, this number will need
   // to change.
   do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
   do_check_true(protoInfo.alwaysAskBeforeHandling);
 
   // OS default exists, injected default exists, explicit warning pref: false
-  prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
+  SetExternalWarningPref("mailto", false);
   protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
   if (haveDefaultHandlersVersion)
     do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
   else
     do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
 
   // Win7+ or Linux's GIO might not have a default mailto: handler
   if (noMailto)
     do_check_true(protoInfo.alwaysAskBeforeHandling);
   else
     do_check_false(protoInfo.alwaysAskBeforeHandling);
 
   // OS default exists, injected default exists, explicit warning pref: true
-  prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", true);
+  SetExternalWarningPref("mailto", true);
   protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
   if (haveDefaultHandlersVersion) {
     do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
     // Win7+ or Linux's GIO may have no default mailto: handler. Otherwise
     // alwaysAskBeforeHandling is expected to be false here, because although
     // the pref is true, the value in RDF is false. The injected mailto handler
     // carried over the default pref value, and so when we set the pref above
     // to true it's ignored.
@@ -190,82 +179,81 @@ function run_test() {
     else
       do_check_false(protoInfo.alwaysAskBeforeHandling);
 
   } else {
     do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
     do_check_true(protoInfo.alwaysAskBeforeHandling);
   }
 
-  if (haveDefaultHandlersVersion) {
-    // Now set the value stored in RDF to true, and the pref to false, to make
-    // sure we still get the right value. (Basically, same thing as above but
-    // with the values reversed.)
-    prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
-    protoInfo.alwaysAskBeforeHandling = true;
-    handlerSvc.store(protoInfo);
-    protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
-    do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
-    do_check_true(protoInfo.alwaysAskBeforeHandling);
-  }
+  return protoInfo;
+}
 
+function test_storeProtoInfoBySvc(protoInfo, handlerSvc) {
 
-  //**************************************************************************//
-  // Test Round-Trip Data Integrity
-
-  // Test round-trip data integrity by setting the properties of the handler
-  // info object to different values, telling the handler service to store the
-  // object, and then retrieving a new info object for the same type and making
-  // sure its properties are identical.
+  // Now set the value stored in DB to true, and the pref to false, to make
+  // sure we still get the right value. (Basically, same thing as above but
+  // with the values reversed.)
+  SetExternalWarningPref("mailto", false);
+  protoInfo.alwaysAskBeforeHandling = true;
+  handlerSvc.store(protoInfo);
+  protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
+  do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
+  do_check_true(protoInfo.alwaysAskBeforeHandling);
 
-  handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
-  handlerInfo.preferredApplicationHandler = localHandler;
-  handlerInfo.alwaysAskBeforeHandling = false;
+}
 
-  handlerSvc.store(handlerInfo);
-
-  handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
+function test_chekHandlerInfoFromMimeSvc(localHandler) {
+  var handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
 
   do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp);
 
   do_check_neq(handlerInfo.preferredApplicationHandler, null);
   var preferredHandler = handlerInfo.preferredApplicationHandler;
   do_check_eq(typeof preferredHandler, "object");
   do_check_eq(preferredHandler.name, "Local Handler");
   do_check_true(preferredHandler instanceof Ci.nsILocalHandlerApp);
   preferredHandler.QueryInterface(Ci.nsILocalHandlerApp);
   do_check_eq(preferredHandler.executable.path, localHandler.executable.path);
 
   do_check_false(handlerInfo.alwaysAskBeforeHandling);
+}
 
+function test_enumerate(handlerSvc, haveDefaultHandlersVersion) {
   // Make sure the handler service's enumerate method lists all known handlers.
   var handlerInfo2 = mimeSvc.getFromTypeAndExtension("nonexistent/type2", null);
   handlerSvc.store(handlerInfo2);
   var handlerTypes = ["nonexistent/type", "nonexistent/type2"];
   if (haveDefaultHandlersVersion) {
     handlerTypes.push("webcal");
     handlerTypes.push("mailto");
     handlerTypes.push("irc");
     handlerTypes.push("ircs");
   }
+
   var handlers = handlerSvc.enumerate();
   while (handlers.hasMoreElements()) {
     var handler = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
     do_check_neq(handlerTypes.indexOf(handler.type), -1);
     handlerTypes.splice(handlerTypes.indexOf(handler.type), 1);
   }
   do_check_eq(handlerTypes.length, 0);
+}
 
+function test_remove(handlerSvc) {
   // Make sure the handler service's remove method removes a handler record.
+  var handlerInfo2 = mimeSvc.getFromTypeAndExtension("nonexistent/type2", null)
   handlerSvc.remove(handlerInfo2);
-  handlers = handlerSvc.enumerate();
+  var handlers = handlerSvc.enumerate();
   while (handlers.hasMoreElements())
     do_check_neq(handlers.getNext().QueryInterface(Ci.nsIHandlerInfo).type,
                  handlerInfo2.type);
+}
 
+function test_storeAndReceive(handlerSvc, localHandler, webHandler) {
   // Make sure we can store and retrieve a handler info object with no preferred
   // handler.
   var noPreferredHandlerInfo =
     mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
   handlerSvc.store(noPreferredHandlerInfo);
   noPreferredHandlerInfo =
     mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
   do_check_eq(noPreferredHandlerInfo.preferredApplicationHandler, null);
@@ -344,19 +332,20 @@ function run_test() {
   do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 1);
 
   // Make sure the handler is the one we didn't remove.
   webPossibleHandler = possibleHandlersInfo.possibleApplicationHandlers.
                        queryElementAt(0, Ci.nsIWebHandlerApp);
   do_check_eq(webPossibleHandler.name, webHandler.name);
   do_check_true(webPossibleHandler.equals(webHandler));
 
-  //////////////////////////////////////////////////////
-  // handler info command line parameters and equality
-  var localApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+}
+
+function test_LocalApp(executable) {
+ var localApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
                  createInstance(Ci.nsILocalHandlerApp);
   var handlerApp = localApp.QueryInterface(Ci.nsIHandlerApp);
 
   do_check_true(handlerApp.equals(localApp));
 
   localApp.executable = executable;
 
   do_check_eq(0, localApp.parameterCount);
@@ -407,64 +396,122 @@ function run_test() {
 
   var str;
   str = localApp.getParameter(0)
   do_check_eq(str, "-test1");
   str = localApp.getParameter(1)
   do_check_eq(str, "-test2");
   str = localApp.getParameter(2)
   do_check_eq(str, "-test3");
-
-  // FIXME: test round trip integrity for a protocol.
-  // FIXME: test round trip integrity for a handler info with a web handler.
+}
 
-  //**************************************************************************//
-  // getTypeFromExtension tests
-
+function test_getTypeFromExtension(handlerSvc, localHandler) {
   // test nonexistent extension
   var lolType = handlerSvc.getTypeFromExtension("lolcat");
   do_check_eq(lolType, "");
 
-
   // add a handler for the extension
   var lolHandler = mimeSvc.getFromTypeAndExtension("application/lolcat", null);
 
   do_check_false(lolHandler.extensionExists("lolcat"));
+  // add "lolcat" as file extension
+  lolHandler.appendExtension("lolcat");
   lolHandler.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
   lolHandler.preferredApplicationHandler = localHandler;
   lolHandler.alwaysAskBeforeHandling = false;
 
   // store the handler
   do_check_false(handlerSvc.exists(lolHandler));
   handlerSvc.store(lolHandler);
   do_check_true(handlerSvc.exists(lolHandler));
 
-  // Get a file:// string pointing to mimeTypes.rdf
-  var rdfFile = HandlerServiceTest._dirSvc.get("UMimTyp", Ci.nsIFile);
-  var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
-  var rdfFileURI = fileHandler.getURLSpecFromFile(rdfFile);
-
-  // Assign a file extenstion to the handler. handlerSvc.store() doesn't
-  // actually store any file extensions added with setFileExtensions(), you
-  // have to wade into RDF muck to do so.
-
-  // Based on toolkit/mozapps/downloads/content/helperApps.js :: addExtension()
-  var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
-  var mimeSource    = gRDF.GetUnicodeResource("urn:mimetype:application/lolcat");
-  var valueProperty = gRDF.GetUnicodeResource("http://home.netscape.com/NC-rdf#fileExtensions");
-  var mimeLiteral   = gRDF.GetLiteral("lolcat");
-
-  var DS = gRDF.GetDataSourceBlocking(rdfFileURI);
-  DS.Assert(mimeSource, valueProperty, mimeLiteral, true);
-
-
   // test now-existent extension
   lolType = handlerSvc.getTypeFromExtension("lolcat");
   do_check_eq(lolType, "application/lolcat");
+}
+
+// Test Round-Trip Data Integrity
+function test_roundtripDataIntegrity(handlerSvc, haveDefaultHandlersVersion) {
+  var noMailto = checkMailto();
+
+  // It doesn't matter whether or not this nsIFile is actually executable,
+  // only that it has a path and exists.  Since we don't know any executable
+  // that exists on all platforms (except possibly the application being
+  // tested, but there doesn't seem to be a way to get a reference to that
+  // from the directory service), we use the temporary directory itself.
+  var executable = HandlerServiceTest._dirSvc.get("TmpD", Ci.nsIFile);
+  // XXX We could, of course, create an actual executable in the directory:
+  //executable.append("localhandler");
+  //if (!executable.exists())
+  //  executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
+  var localHandler = configure_localHandler(executable);
+  var webHandler = configure_webHandler();
+  var handlerInfo = configureAndTest_handlerInfo();
+
+  var protoInfo = configureAndTest_protoInfo(noMailto, haveDefaultHandlersVersion);
+  if (haveDefaultHandlersVersion) {
+    test_storeProtoInfoBySvc(protoInfo, handlerSvc);
+  }
+
+  //**************************************************************************//
+  // Test Round-Trip Data Integrity
+
+  // Test round-trip data integrity by setting the properties of the handler
+  // info object to different values, telling the handler service to store the
+  // object, and then retrieving a new info object for the same type and making
+  // sure its properties are identical.
+  handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+  handlerInfo.preferredApplicationHandler = localHandler;
+  handlerInfo.alwaysAskBeforeHandling = false;
+  handlerSvc.store(handlerInfo);
+
+  test_chekHandlerInfoFromMimeSvc(localHandler);
+
+  test_enumerate(handlerSvc, haveDefaultHandlersVersion);
+
+  test_remove(handlerSvc);
+
+  // Check the store from handlerSvc and receive from mimeSvc behavior
+  test_storeAndReceive(handlerSvc, localHandler, webHandler);
+
+  // handler info command line parameters and equality
+  test_LocalApp(executable);
+
+  //**************************************************************************//
+  // getTypeFromExtension tests and test the ability of saving file extension 
+  test_getTypeFromExtension(handlerSvc, localHandler);
 
   // test mailcap entries with needsterminal are ignored on non-Windows non-Mac.
   if (mozinfo.os != "win" && mozinfo.os != "mac") {
     env.set('PERSONAL_MAILCAP', do_get_file('mailcap').path);
-    handlerInfo = mimeSvc.getFromTypeAndExtension("text/plain", null);
+    var handlerInfo = mimeSvc.getFromTypeAndExtension("text/plain", null);
     do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useSystemDefault);
     do_check_eq(handlerInfo.defaultDescription, "sed");
   }
 }
+
+
+add_task(function* testHandlerService() {
+
+  // test some default protocol info properties
+  var haveDefaultHandlersVersion = false;
+  try {
+    // If we have a defaultHandlersVersion pref, then assume that we're in the
+    // firefox tree and that we'll also have default handlers.
+    // Bug 395131 has been filed to make this test work more generically
+    // by providing our own prefs for this test rather than this icky
+    // special casing.
+    rootPrefBranch.getCharPref("gecko.handlerService.defaultHandlersVersion");
+    haveDefaultHandlersVersion = true;
+  } catch (ex) {}
+
+  const kExternalWarningDefault =
+    "network.protocol-handler.warn-external-default";
+  Services.prefs.setBoolPref(kExternalWarningDefault, true);
+
+  // original version : RDF
+  Services.prefs.setBoolPref("browser.handlerSvcJson.enabled", false);
+  test_roundtripDataIntegrity(handlerSvcRdf, haveDefaultHandlersVersion);
+
+  // new version : JSON
+  Services.prefs.setBoolPref("browser.handlerSvcJson.enabled", true);
+  test_roundtripDataIntegrity(handlerSvcJson, haveDefaultHandlersVersion);
+});
--- a/uriloader/exthandler/tests/unit/xpcshell.ini
+++ b/uriloader/exthandler/tests/unit/xpcshell.ini
@@ -3,13 +3,17 @@ head = head_handlerService.js
 tail = tail_handlerService.js
 run-sequentially = Bug 912235 - Intermittent failures
 
 [test_getTypeFromExtension_ext_to_type_mapping.js]
 [test_getTypeFromExtension_with_empty_Content_Type.js]
 [test_badMIMEType.js]
 [test_handlerService.js]
 support-files = mailcap
+
 # Bug 676997: test consistently fails on Android
 fail-if = os == "android"
 [test_punycodeURIs.js]
 # Bug 676997: test consistently fails on Android
 fail-if = os == "android"
+
+[test_handlerService-json.js]
+support-files = mimeTypes.json