Bug 931801 - Make shared pref sync code 2-way. r=jimm
authorBrian R. Bondy <netzen@gmail.com>
Tue, 19 Nov 2013 15:54:03 -0500
changeset 172698 5257899d8b21d416137402dfa3bb485437c6574a
parent 172697 fb3ae66def36f3781fed5d4d819f07c279912ca0
child 172699 074a7f87397c8ba70de62d9d2edeb31c86e460b3
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs931801
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 931801 - Make shared pref sync code 2-way. r=jimm
browser/base/content/browser.js
browser/metro/base/content/browser-ui.js
toolkit/modules/WindowsPrefSync.jsm
toolkit/modules/moz.build
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4,16 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NotificationDB.jsm");
 Cu.import("resource:///modules/RecentWindow.jsm");
+Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource:///modules/CharsetMenu.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
@@ -1146,24 +1147,20 @@ var gBrowserInit = {
     // tabs and to postpone saving the pref to disk.
     try {
       const startupCrashEndDelay = 30 * 1000;
       setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
     } catch (ex) {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
-#ifdef XP_WIN
-#ifdef MOZ_METRO
-    gMetroPrefs.prefDomain.forEach(function(prefName) {
-      gMetroPrefs.pushDesktopControlledPrefToMetro(prefName);
-      Services.prefs.addObserver(prefName, gMetroPrefs, false);
-    }, this);
-#endif
-#endif
+    if (typeof WindowsPrefSync !== 'undefined') {
+      // Pulls in Metro controlled prefs and pushes out Desktop controlled prefs
+      WindowsPrefSync.init();
+    }
 
     if (gMultiProcessBrowser) {
       // Bug 862519 - Backspace doesn't work in electrolysis builds.
       // We bypass the problem by disabling the backspace-to-go-back command.
       document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
       document.getElementById("key_delete").setAttribute("disabled", true);
     }
 
@@ -1275,23 +1272,19 @@ var gBrowserInit = {
       Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
 
       try {
         gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
       } catch (ex) {
         Cu.reportError(ex);
       }
 
-#ifdef XP_WIN
-#ifdef MOZ_METRO
-      gMetroPrefs.prefDomain.forEach(function(prefName) {
-        Services.prefs.removeObserver(prefName, gMetroPrefs);
-      });
-#endif
-#endif
+      if (typeof WindowsPrefSync !== 'undefined') {
+        WindowsPrefSync.uninit();
+      }
 
       BrowserOffline.uninit();
       OfflineApps.uninit();
       IndexedDBPromptHelper.uninit();
       SocialUI.uninit();
       LightweightThemeListener.uninit();
       PanelUI.uninit();
     }
@@ -4678,71 +4671,16 @@ function sidebarOnLoad(event) {
  */
 function fireSidebarFocusedEvent() {
   var sidebar = document.getElementById("sidebar");
   var event = document.createEvent("Events");
   event.initEvent("SidebarFocused", true, false);
   sidebar.contentWindow.dispatchEvent(event);
 }
 
-#ifdef XP_WIN
-#ifdef MOZ_METRO
-/**
- * Some prefs that have consequences in both Metro and Desktop such as
- * app-update prefs, are automatically pushed from Desktop here for use
- * in Metro.
- */
-var gMetroPrefs = {
-  prefDomain: ["app.update.auto", "app.update.enabled",
-               "app.update.service.enabled",
-               "app.update.metro.enabled",
-               "browser.sessionstore.resume_session_once"],
-  observe: function (aSubject, aTopic, aPrefName)
-  {
-    if (aTopic != "nsPref:changed")
-      return;
-
-    this.pushDesktopControlledPrefToMetro(aPrefName);
-  },
-
-  /**
-   * Writes the pref to HKCU in the registry and adds a pref-observer to keep
-   * the registry in sync with changes to the value.
-   */
-  pushDesktopControlledPrefToMetro: function(aPrefName) {
-    let registry = Cc["@mozilla.org/windows-registry-key;1"].
-                    createInstance(Ci.nsIWindowsRegKey);
-    try {
-      var prefType = Services.prefs.getPrefType(aPrefName);
-      let prefFunc;
-      if (prefType == Components.interfaces.nsIPrefBranch.PREF_INT)
-        prefFunc = "getIntPref";
-      else if (prefType == Components.interfaces.nsIPrefBranch.PREF_BOOL)
-        prefFunc = "getBoolPref";
-      else if (prefType == Components.interfaces.nsIPrefBranch.PREF_STRING)
-        prefFunc = "getCharPref";
-      else
-        throw "Unsupported pref type";
-
-      let prefValue = Services.prefs[prefFunc](aPrefName);
-      registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                    "Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
-                    Ci.nsIWindowsRegKey.ACCESS_WRITE);
-      // Always write as string, but the registry subfolder will determine
-      // how Metro interprets that string value.
-      registry.writeStringValue(aPrefName, prefValue);
-    } catch (ex) {
-      Components.utils.reportError("Couldn't push pref " + aPrefName + ": " + ex);
-    } finally {
-      registry.close();
-    }
-  }
-};
-#endif
-#endif
 
 var gHomeButton = {
   prefDomain: "browser.startup.homepage",
   observe: function (aSubject, aTopic, aPrefName)
   {
     if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
       return;
 
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -1,15 +1,16 @@
 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* 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/. */
 "use strict";
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm")
+Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
 /**
  * Constants
  */
 
 // Devtools Messages
 const debugServerStateChanged = "devtools.debugger.remote-enabled";
 const debugServerPortChanged = "devtools.debugger.remote-port";
@@ -148,17 +149,20 @@ var BrowserUI = {
         MetroDownloadsView.init();
         DialogUI.init();
         FormHelperUI.init();
         FindHelperUI.init();
       } catch(ex) {
         Util.dumpLn("Exception in delay load module:", ex.message);
       }
 
-      BrowserUI._pullDesktopControlledPrefs();
+      if (WindowsPrefSync) {
+        // Pulls in Desktop controlled prefs and pushes out Metro controlled prefs
+        WindowsPrefSync.init();
+      }
 
       // check for left over crash reports and submit them if found.
       BrowserUI.startupCrashCheck();
 
       Util.dumpLn("* delay load complete.");
     }, false);
 
 #ifndef MOZ_OFFICIAL_BRANDING
@@ -184,16 +188,19 @@ var BrowserUI = {
     messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
     Services.obs.removeObserver(this, "handle-xul-text-link");
 
     PanelUI.uninit();
     FlyoutPanelsUI.uninit();
     MetroDownloadsView.uninit();
     SettingsCharm.uninit();
     PageThumbs.uninit();
+    if (WindowsPrefSync) {
+      WindowsPrefSync.uninit();
+    }
     this.stopDebugServer();
   },
 
   /************************************
    * Devtools Debugger
    */
   runDebugServer: function runDebugServer(aPort) {
     let port = aPort || Services.prefs.getIntPref(debugServerPortChanged);
@@ -633,47 +640,16 @@ var BrowserUI = {
         break;
     }
   },
 
   /*********************************
    * Internal utils
    */
 
-  /**
-  * Some prefs that have consequences in both Metro and Desktop such as
-  * app-update prefs, are automatically pulled from Desktop here.
-  */
-  _pullDesktopControlledPrefs: function() {
-    function pullDesktopControlledPrefType(prefType, prefFunc) {
-      try {
-        registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      "Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
-                      Ci.nsIWindowsRegKey.ACCESS_ALL);
-        for (let i = 0; i < registry.valueCount; i++) {
-          let prefName = registry.getValueName(i);
-          let prefValue = registry.readStringValue(prefName);
-          if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
-            prefValue = prefValue == "true";
-          }
-          Services.prefs[prefFunc](prefName, prefValue);
-        }
-      } catch (ex) {
-        Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
-      } finally {
-        registry.close();
-      }
-    }
-    let registry = Cc["@mozilla.org/windows-registry-key;1"].
-                   createInstance(Ci.nsIWindowsRegKey);
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
-  },
-
   _titleChanged: function(aBrowser) {
     let url = this.getDisplayURI(aBrowser);
 
     let tabCaption;
     if (aBrowser.contentTitle) {
       tabCaption = aBrowser.contentTitle;
     } else if (!Util.isURLEmpty(aBrowser.userTypedValue)) {
       tabCaption = aBrowser.userTypedValue;
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/WindowsPrefSync.jsm
@@ -0,0 +1,161 @@
+/* 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/. */
+
+'use strict';
+
+this.EXPORTED_SYMBOLS = [];
+
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} =
+  Components;
+const PREF_BASE_KEY = "Software\\Mozilla\\Firefox\\Metro\\Prefs\\";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = [ "WindowsPrefSync" ];
+
+/**
+ * Manages preferences that need to be pulled and pushed between Metro
+ * and desktop.
+ */
+this.WindowsPrefSync = {
+  init: function() {
+    this.pullSharedPrefs();
+    this.prefListToPush.forEach(function(prefName) {
+      this.pushSharedPref(prefName);
+      Services.prefs.addObserver(prefName, this, false);
+    }, this);
+  },
+
+  uninit: function() {
+    this.prefListToPush.forEach(function(prefName) {
+        Services.prefs.removeObserver(prefName, this);
+    }, this);
+  },
+
+  /**
+   * Returns the list of prefs that should be pushed for the current
+   * environment.
+   */
+  get prefListToPush() {
+    return !Services.metro.immersive ? this.desktopControlledPrefs :
+      this.metroControlledPrefs;
+  },
+
+  /**
+   * Returns the list of prefs that should be pulled for the current
+   * environment.
+   */
+  get prefListToPull() {
+    return Services.metro.immersive ? this.desktopControlledPrefs :
+      this.metroControlledPrefs;
+  },
+
+  /**
+   * The following preferences will be pushed to registry from Desktop
+   * Firefox and pulled in from Metro Firefox.
+   *
+   * app.update.* prefs are because Metro shares an installation directory with
+   * Firefox, and the options for these are only present in the Desktop options.
+   *
+   * browser.sessionstore.resume_session_once is mainly for the switch to Metro
+   * and switch to Desktop feature.
+   */
+  desktopControlledPrefs: ["app.update.auto",
+    "app.update.enabled",
+    "app.update.service.enabled",
+    "app.update.metro.enabled",
+    "browser.sessionstore.resume_session_once"],
+
+  /**
+   * The following preferences will be pushed to registry from Metro
+   * Firefox and pulled in from Desktop Firefox.
+   *
+   * browser.sessionstore.resume_session_once is mainly for the switch to Metro
+   * and switch to Desktop feature.
+   */
+  metroControlledPrefs: ["browser.sessionstore.resume_session_once"],
+
+  /**
+   * Observes preference changes and writes them to the registry, only
+   * the list of preferences initialized will be observed
+   */
+  observe: function (aSubject, aTopic, aPrefName) {
+    if (aTopic != "nsPref:changed")
+      return;
+
+    this.pushSharedPref(aPrefName);
+  },
+
+  /**
+   * Writes the pref to HKCU in the registry and adds a pref-observer to keep
+   * the registry in sync with changes to the value.
+   */
+  pushSharedPref : function(aPrefName) {
+    let registry = Cc["@mozilla.org/windows-registry-key;1"].
+      createInstance(Ci.nsIWindowsRegKey);
+    try {
+      var prefType = Services.prefs.getPrefType(aPrefName);
+      let prefFunc;
+      if (prefType == Ci.nsIPrefBranch.PREF_INT)
+        prefFunc = "getIntPref";
+      else if (prefType == Ci.nsIPrefBranch.PREF_BOOL)
+        prefFunc = "getBoolPref";
+      else if (prefType == Ci.nsIPrefBranch.PREF_STRING)
+        prefFunc = "getCharPref";
+      else
+        throw "Unsupported pref type";
+
+      let prefValue = Services.prefs[prefFunc](aPrefName);
+      registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+        PREF_BASE_KEY + prefType,
+        Ci.nsIWindowsRegKey.ACCESS_WRITE);
+      // Always write as string, but the registry subfolder will determine
+      // how Metro interprets that string value.
+      registry.writeStringValue(aPrefName, prefValue);
+    } catch (ex) {
+      Cu.reportError("Couldn't push pref " + aPrefName + ": " + ex);
+    } finally {
+      registry.close();
+    }
+  },
+
+  /**
+   * Pulls in all shared prefs from the registry
+   */
+  pullSharedPrefs: function() {
+    function pullSharedPrefType(prefType, prefFunc) {
+      try {
+        registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+          PREF_BASE_KEY + prefType,
+          Ci.nsIWindowsRegKey.ACCESS_ALL);
+        for (let i = 0; i < registry.valueCount; i++) {
+          let prefName = registry.getValueName(i);
+          let prefValue = registry.readStringValue(prefName);
+          if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
+            prefValue = prefValue == "true";
+          }
+          if (self.prefListToPull.indexOf(prefName) != -1) {
+            Services.prefs[prefFunc](prefName, prefValue);
+          }
+        }
+      } catch (ex) {
+        dump("Could not pull for prefType " + prefType + ": " + ex + "\n");
+      } finally {
+        registry.close();
+      }
+    }
+    let self = this;
+    let registry = Cc["@mozilla.org/windows-registry-key;1"].
+      createInstance(Ci.nsIWindowsRegKey);
+    pullSharedPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
+    pullSharedPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
+    pullSharedPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
+  }
+};
+#endif
+#endif
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -47,16 +47,17 @@ EXTRA_JS_MODULES += [
 
 EXTRA_PP_JS_MODULES += [
     'CertUtils.jsm',
     'ResetProfile.jsm',
     'Services.jsm',
     'Troubleshoot.jsm',
     'UpdateChannel.jsm',
     'WindowDraggingUtils.jsm',
+    'WindowsPrefSync.jsm',
 ]
 
 if 'Android' != CONFIG['OS_TARGET']:
     EXTRA_PP_JS_MODULES += [
         'LightweightThemeConsumer.jsm',
     ]
 
 DEFINES['MOZ_APP_NAME'] = CONFIG['MOZ_APP_NAME']