Merge m-c to b2ginbound a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 30 Mar 2015 19:41:43 -0700
changeset 236539 e19d1eac741814964894c45c94c053cb60313c79
parent 236538 9b61f0b00d9275afbd2f6b5ac113dd79dd284d77 (current diff)
parent 236530 8af276ab8636bbe330a95ce38bb223af17d44a1a (diff)
child 236540 eca50ab2c04c0f37d9823837de69a9f771e45130
push id28512
push usercbook@mozilla.com
push dateTue, 31 Mar 2015 12:20:03 +0000
treeherdermozilla-central@2c5bc2504228 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.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
Merge m-c to b2ginbound a=merge CLOSED TREE
browser/base/content/defaultthemes/devedition.header.png
browser/base/content/defaultthemes/devedition.icon.png
--- a/.hgtags
+++ b/.hgtags
@@ -111,8 +111,9 @@ ca89fe55717059e4e43040d16d260765ffa9dca7
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 b297a6727acfd21e757ddd38cd61894812666265 FIREFOX_AURORA_36_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 2c951493eef5b50b8085ef78ffe0d7902ff3d593 FIREFOX_AURORA_37_BASE
 98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
+1b6bf6612c0f4d4fee81d18bf18016e692f874e1 FIREFOX_AURORA_39_BASE
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1137470 NSS update required a clobber to fully rebuild
+Merge day clobber
\ No newline at end of file
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
 # 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/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=39.0a1
+MOZ_APP_VERSION=40.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=3.0.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1323,18 +1323,21 @@ pref("services.sync.prefs.sync.security.
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
 // Developer edition preferences
 #ifdef MOZ_DEV_EDITION
-pref("lightweightThemes.selectedThemeID", "firefox-devedition@mozilla.org");
 pref("browser.devedition.theme.enabled", true);
+pref("browser.devedition.theme.showCustomizeButton", true);
+#else
+pref("browser.devedition.theme.enabled", false);
+pref("browser.devedition.theme.showCustomizeButton", false);
 #endif
 
 // Developer edition promo preferences
 pref("devtools.devedition.promo.shown", false);
 pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer/?utm_source=firefox-dev-tools&utm_medium=firefox-browser&utm_content=betadoorhanger");
 
 // Only potentially show in beta release
 #if MOZ_UPDATE_CHANNEL == beta
@@ -1487,21 +1490,26 @@ pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
 // Scratchpad settings
 // - recentFileMax: The maximum number of recently-opened files
 //                  stored. Setting this preference to 0 will not
 //                  clear any recent files, but rather hide the
 //                  'Open Recent'-menu.
+// - lineNumbers: Whether to show line numbers or not.
+// - wrapText: Whether to wrap text or not.
 // - showTrailingSpace: Whether to highlight trailing space or not.
-// - enableCodeFolding: Whether to enable code folding or not.
+// - editorFontSize: Editor font size configuration.
 // - enableAutocompletion: Whether to enable JavaScript autocompletion.
 pref("devtools.scratchpad.recentFilesMax", 10);
+pref("devtools.scratchpad.lineNumbers", true);
+pref("devtools.scratchpad.wrapText", false);
 pref("devtools.scratchpad.showTrailingSpace", false);
+pref("devtools.scratchpad.editorFontSize", 12);
 pref("devtools.scratchpad.enableAutocompletion", true);
 
 // Enable the Storage Inspector
 pref("devtools.storage.enabled", false);
 
 // Enable the Style Editor.
 pref("devtools.styleeditor.enabled", true);
 pref("devtools.styleeditor.source-maps-enabled", true);
@@ -1604,16 +1612,17 @@ pref("devtools.hud.loglimit.console", 20
 pref("devtools.eyedropper.zoom", 6);
 
 // The developer tools editor configuration:
 // - tabsize: how many spaces to use when a Tab character is displayed.
 // - expandtab: expand Tab characters to spaces.
 // - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
 // - autoclosebrackets: whether to permit automatic bracket/quote closing.
 // - detectindentation: whether to detect the indentation from the file
+// - enableCodeFolding: Whether to enable code folding or not.
 pref("devtools.editor.tabsize", 2);
 pref("devtools.editor.expandtab", true);
 pref("devtools.editor.keymap", "default");
 pref("devtools.editor.autoclosebrackets", true);
 pref("devtools.editor.detectindentation", true);
 pref("devtools.editor.enableCodeFolding", true);
 pref("devtools.editor.autocomplete", true);
 
--- a/browser/base/content/browser-devedition.js
+++ b/browser/base/content/browser-devedition.js
@@ -2,47 +2,56 @@
 # 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/.
 
 /**
  * Listeners for the DevEdition theme.  This adds an extra stylesheet
  * to browser.xul if a pref is set and no other themes are applied.
  */
 let DevEdition = {
+  _prefName: "browser.devedition.theme.enabled",
+  _themePrefName: "general.skins.selectedSkin",
+  _lwThemePrefName: "lightweightThemes.selectedThemeID",
   _devtoolsThemePrefName: "devtools.theme",
+
   styleSheetLocation: "chrome://browser/skin/devedition.css",
   styleSheet: null,
 
-  get isThemeCurrentlyApplied() {
-    let theme = LightweightThemeManager.currentTheme;
-    return theme && theme.id == "firefox-devedition@mozilla.org";
-  },
+  init: function () {
+    this._updateDevtoolsThemeAttribute();
+    this._updateStyleSheetFromPrefs();
 
-  init: function () {
+    // Listen for changes to all prefs except for complete themes.
+    // No need for this since changing a complete theme requires a
+    // restart.
+    Services.prefs.addObserver(this._lwThemePrefName, this, false);
+    Services.prefs.addObserver(this._prefName, this, false);
     Services.prefs.addObserver(this._devtoolsThemePrefName, this, false);
     Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
-    this._updateDevtoolsThemeAttribute();
-
-    if (this.isThemeCurrentlyApplied) {
-      this._toggleStyleSheet(true);
-    }
   },
 
   observe: function (subject, topic, data) {
     if (topic == "lightweight-theme-styling-update") {
       let newTheme = JSON.parse(data);
-      if (newTheme && newTheme.id == "firefox-devedition@mozilla.org") {
-        this._toggleStyleSheet(true);
+      if (!newTheme) {
+        // A lightweight theme has been unapplied, so just re-read prefs.
+        this._updateStyleSheetFromPrefs();
       } else {
+        // A lightweight theme has been applied, but the pref may not be
+        // set yet if this happened from customize menu or addons page.
         this._toggleStyleSheet(false);
       }
     }
 
-    if (topic == "nsPref:changed" && data == this._devtoolsThemePrefName) {
-      this._updateDevtoolsThemeAttribute();
+    if (topic == "nsPref:changed") {
+      if (data == this._devtoolsThemePrefName) {
+        this._updateDevtoolsThemeAttribute();
+      } else {
+        this._updateStyleSheetFromPrefs();
+      }
     }
   },
 
   _inferBrightness: function() {
     ToolbarIconColor.inferFromText();
     // Get an inverted full screen button if the dark theme is applied.
     if (this.styleSheet &&
         document.documentElement.getAttribute("devtoolstheme") == "dark") {
@@ -56,23 +65,42 @@ let DevEdition = {
     // Set an attribute on root element to make it possible
     // to change colors based on the selected devtools theme.
     let devtoolsTheme = Services.prefs.getCharPref(this._devtoolsThemePrefName);
     if (devtoolsTheme != "dark") {
       devtoolsTheme = "light";
     }
     document.documentElement.setAttribute("devtoolstheme", devtoolsTheme);
     this._inferBrightness();
+    this._updateStyleSheetFromPrefs();
+  },
+
+  _updateStyleSheetFromPrefs: function() {
+    let lightweightThemeSelected = false;
+    try {
+      lightweightThemeSelected = !!Services.prefs.getCharPref(this._lwThemePrefName);
+    } catch(e) {}
+
+    let defaultThemeSelected = false;
+    try {
+       defaultThemeSelected = Services.prefs.getCharPref(this._themePrefName) == "classic/1.0";
+    } catch(e) {}
+
+    let deveditionThemeEnabled = Services.prefs.getBoolPref(this._prefName) &&
+      !lightweightThemeSelected && defaultThemeSelected;
+
+    this._toggleStyleSheet(deveditionThemeEnabled);
   },
 
   handleEvent: function(e) {
     if (e.type === "load") {
       this.styleSheet.removeEventListener("load", this);
       gBrowser.tabContainer._positionPinnedTabs();
       this._inferBrightness();
+      Services.obs.notifyObservers(window, "devedition-theme-state-changed", true);
     }
   },
 
   _toggleStyleSheet: function(deveditionThemeEnabled) {
     if (deveditionThemeEnabled && !this.styleSheet) {
       let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
       this.styleSheet = document.createProcessingInstruction(
         'xml-stylesheet', styleSheetAttr);
@@ -81,20 +109,23 @@ let DevEdition = {
       // NB: we'll notify observers once the stylesheet has fully loaded, see
       // handleEvent above.
     } else if (!deveditionThemeEnabled && this.styleSheet) {
       this.styleSheet.removeEventListener("load", this);
       this.styleSheet.remove();
       this.styleSheet = null;
       gBrowser.tabContainer._positionPinnedTabs();
       this._inferBrightness();
+      Services.obs.notifyObservers(window, "devedition-theme-state-changed", false);
     }
   },
 
   uninit: function () {
+    Services.prefs.removeObserver(this._lwThemePrefName, this);
+    Services.prefs.removeObserver(this._prefName, this);
     Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
     Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
     if (this.styleSheet) {
       this.styleSheet.removeEventListener("load", this);
     }
     this.styleSheet = null;
   }
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -40,18 +40,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
                                    "@mozilla.org/browser/favicon-service;1",
                                    "mozIAsyncFavicons");
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
-XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
-                                  "resource://gre/modules/LightweightThemeManager.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
 var gProxyFavIcon = null;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
deleted file mode 100644
index e4e8dcaa3b3cd01a1287359902561166d7f942ee..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 04cfba796dc6aa0bac6b3b368e929009bf01af52..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/base/content/test/general/browser_devedition.js
+++ b/browser/base/content/test/general/browser_devedition.js
@@ -1,65 +1,72 @@
 /*
  * Testing changes for Developer Edition theme.
  * A special stylesheet should be added to the browser.xul document
  * when browser.devedition.theme.enabled is set to true and no themes
  * are applied.
  */
 
+const PREF_DEVEDITION_THEME = "browser.devedition.theme.enabled";
+const PREF_LWTHEME = "lightweightThemes.selectedThemeID";
 const PREF_LWTHEME_USED_THEMES = "lightweightThemes.usedThemes";
 const PREF_DEVTOOLS_THEME = "devtools.theme";
 const {LightweightThemeManager} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
 
-LightweightThemeManager.clearBuiltInThemes();
-LightweightThemeManager.addBuiltInTheme(dummyLightweightTheme("firefox-devedition@mozilla.org"));
-
 registerCleanupFunction(() => {
   // Set preferences back to their original values
   LightweightThemeManager.currentTheme = null;
+  Services.prefs.clearUserPref(PREF_DEVEDITION_THEME);
+  Services.prefs.clearUserPref(PREF_LWTHEME);
   Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
   Services.prefs.clearUserPref(PREF_LWTHEME_USED_THEMES);
-
-  LightweightThemeManager.currentTheme = null;
-  LightweightThemeManager.clearBuiltInThemes();
 });
 
 add_task(function* startTests() {
   Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
 
-  info ("Setting the current theme to null");
-  LightweightThemeManager.currentTheme = null;
-  ok (!DevEdition.styleSheet, "There is no devedition style sheet when no lw theme is applied.");
+  info ("Setting browser.devedition.theme.enabled to false.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
+  ok (!DevEdition.styleSheet, "There is no devedition style sheet when the pref is false.");
+
+  info ("Setting browser.devedition.theme.enabled to true.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
+  ok (DevEdition.styleSheet, "There is a devedition stylesheet when no themes are applied and pref is set.");
 
   info ("Adding a lightweight theme.");
   LightweightThemeManager.currentTheme = dummyLightweightTheme("preview0");
   ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed when a lightweight theme is applied.");
 
-  info ("Applying the devedition lightweight theme.");
+  info ("Removing a lightweight theme.");
   let onAttributeAdded = waitForBrightTitlebarAttribute();
-  LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-devedition@mozilla.org");
-  ok (DevEdition.styleSheet, "The devedition stylesheet has been added when the devedition lightweight theme is applied");
+  LightweightThemeManager.currentTheme = null;
+  ok (DevEdition.styleSheet, "The devedition stylesheet has been added when a lightweight theme is removed.");
   yield onAttributeAdded;
+
   is (document.documentElement.getAttribute("brighttitlebarforeground"), "true",
      "The brighttitlebarforeground attribute is set on the window.");
 
-  info ("Unapplying all themes.");
-  LightweightThemeManager.currentTheme = null;
-  ok (!DevEdition.styleSheet, "There is no devedition style sheet when no lw theme is applied.");
+  info ("Setting browser.devedition.theme.enabled to false.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
+  ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed.");
 
-  info ("Applying the devedition lightweight theme.");
-  onAttributeAdded = waitForBrightTitlebarAttribute();
-  LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-devedition@mozilla.org");
-  ok (DevEdition.styleSheet, "The devedition stylesheet has been added when the devedition lightweight theme is applied");
+  ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
+     "The brighttitlebarforeground attribute is not set on the window after devedition.theme is false.");
+});
+
+add_task(function* testDevtoolsTheme() {
+  info ("Checking that Australis is shown when the light devtools theme is applied.");
+
+  let onAttributeAdded = waitForBrightTitlebarAttribute();
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
+  ok (DevEdition.styleSheet, "The devedition stylesheet exists.");
   yield onAttributeAdded;
   ok (document.documentElement.hasAttribute("brighttitlebarforeground"),
      "The brighttitlebarforeground attribute is set on the window with dark devtools theme.");
-});
 
-add_task(function* testDevtoolsTheme() {
   info ("Checking stylesheet and :root attributes based on devtools theme.");
   Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
   is (document.documentElement.getAttribute("devtoolstheme"), "light",
     "The documentElement has an attribute based on devtools theme.");
   ok (DevEdition.styleSheet, "The devedition stylesheet is still there with the light devtools theme.");
   ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
      "The brighttitlebarforeground attribute is not set on the window with light devtools theme.");
 
@@ -77,43 +84,52 @@ add_task(function* testDevtoolsTheme() {
   ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
      "The brighttitlebarforeground attribute is not set on the window with light devtools theme.");
 });
 
 function dummyLightweightTheme(id) {
   return {
     id: id,
     name: id,
-    headerURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.header.png",
-    iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
+    headerURL: "resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg",
+    iconURL: "resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg",
     textcolor: "red",
     accentcolor: "blue"
   };
 }
 
 add_task(function* testLightweightThemePreview() {
-  info ("Setting devedition to current and the previewing others");
-  LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-devedition@mozilla.org");
+  info ("Turning the pref on, then previewing lightweight themes");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
   ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
   LightweightThemeManager.previewTheme(dummyLightweightTheme("preview0"));
   ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a lightweight theme preview.");
   LightweightThemeManager.resetPreview();
   LightweightThemeManager.previewTheme(dummyLightweightTheme("preview1"));
   ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a second lightweight theme preview.");
   LightweightThemeManager.resetPreview();
   ok (DevEdition.styleSheet, "The devedition stylesheet is enabled again after resetting the preview.");
-  LightweightThemeManager.currentTheme = null;
-  ok (!DevEdition.styleSheet, "The devedition stylesheet is gone after removing the current theme.");
 
-  info ("Previewing the devedition theme");
-  LightweightThemeManager.previewTheme(LightweightThemeManager.getUsedTheme("firefox-devedition@mozilla.org"));
+  info ("Turning the pref on, then previewing a theme, turning it off and resetting the preview");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
   ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
   LightweightThemeManager.previewTheme(dummyLightweightTheme("preview2"));
+  ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a lightweight theme preview.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
+  ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after pref is turned off.");
   LightweightThemeManager.resetPreview();
-  ok (!DevEdition.styleSheet, "The devedition stylesheet is now disabled after resetting the preview.");
+  ok (!DevEdition.styleSheet, "The devedition stylesheet is still disabled after resetting the preview.");
+
+  info ("Turning the pref on, then previewing the default theme, turning it off and resetting the preview");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
+  ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
+  LightweightThemeManager.previewTheme(null);
+  ok (DevEdition.styleSheet, "The devedition stylesheet is still enabled after the default theme is applied.");
+  LightweightThemeManager.resetPreview();
+  ok (DevEdition.styleSheet, "The devedition stylesheet is still enabled after resetting the preview.");
 });
 
 // Use a mutation observer to wait for the brighttitlebarforeground
 // attribute to change.  Using this instead of waiting for the load
 // event on the DevEdition styleSheet.
 function waitForBrightTitlebarAttribute() {
   return new Promise((resolve, reject) => {
     let mutationObserver = new MutationObserver(function (mutations) {
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -93,18 +93,16 @@ browser.jar:
         content/browser/defaultthemes/4.footer.png    (content/defaultthemes/4.footer.png)
         content/browser/defaultthemes/4.header.png    (content/defaultthemes/4.header.png)
         content/browser/defaultthemes/4.icon.png      (content/defaultthemes/4.icon.png)
         content/browser/defaultthemes/4.preview.png   (content/defaultthemes/4.preview.png)
         content/browser/defaultthemes/5.footer.png    (content/defaultthemes/5.footer.png)
         content/browser/defaultthemes/5.header.png    (content/defaultthemes/5.header.png)
         content/browser/defaultthemes/5.icon.jpg      (content/defaultthemes/5.icon.jpg)
         content/browser/defaultthemes/5.preview.jpg   (content/defaultthemes/5.preview.jpg)
-        content/browser/defaultthemes/devedition.header.png   (content/defaultthemes/devedition.header.png)
-        content/browser/defaultthemes/devedition.icon.png     (content/defaultthemes/devedition.icon.png)
         content/browser/newtab/newTab.xul             (content/newtab/newTab.xul)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -32,16 +32,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const kSpecialWidgetPfx = "customizableui-special-";
 
 const kPrefCustomizationState        = "browser.uiCustomization.state";
 const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
 const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
 const kPrefDrawInTitlebar            = "browser.tabs.drawInTitlebar";
+const kPrefDeveditionTheme           = "browser.devedition.theme.enabled";
 const kPrefWebIDEInNavbar            = "devtools.webide.widget.inNavbarByDefault";
 
 /**
  * The keys are the handlers that are fired when the event type (the value)
  * is fired on the subview. A widget that provides a subview has the option
  * of providing onViewShowing and onViewHiding event handlers.
  */
 const kSubviewEvents = [
@@ -2326,23 +2327,25 @@ let CustomizableUIInternal = {
     }
 
     gResetting = false;
   },
 
   _resetUIState: function() {
     try {
       gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
+      gUIStateBeforeReset.deveditionTheme = Services.prefs.getBoolPref(kPrefDeveditionTheme);
       gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
     } catch(e) { }
 
     this._resetExtraToolbars();
 
     Services.prefs.clearUserPref(kPrefCustomizationState);
     Services.prefs.clearUserPref(kPrefDrawInTitlebar);
+    Services.prefs.clearUserPref(kPrefDeveditionTheme);
     LOG("State reset");
 
     // Reset placements to make restoring default placements possible.
     gPlacements = new Map();
     gDirtyAreaCache = new Set();
     gSeenWidgets = new Set();
     // Clear the saved state to ensure that defaults will be used.
     gSavedState = null;
@@ -2394,30 +2397,33 @@ let CustomizableUIInternal = {
     }
   },
 
   /**
    * Undoes a previous reset, restoring the state of the UI to the state prior to the reset.
    */
   undoReset: function() {
     if (gUIStateBeforeReset.uiCustomizationState == null ||
-        gUIStateBeforeReset.drawInTitlebar == null) {
+        gUIStateBeforeReset.drawInTitlebar == null ||
+        gUIStateBeforeReset.deveditionTheme == null) {
       return;
     }
     gUndoResetting = true;
 
     let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
     let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
+    let deveditionTheme = gUIStateBeforeReset.deveditionTheme;
 
     // Need to clear the previous state before setting the prefs
     // because pref observers may check if there is a previous UI state.
     this._clearPreviousUIState();
 
     Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
     Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
+    Services.prefs.setBoolPref(kPrefDeveditionTheme, deveditionTheme);
     this.loadSavedState();
     // If the user just customizes toolbar/titlebar visibility, gSavedState will be null
     // and we don't need to do anything else here:
     if (gSavedState) {
       for (let areaId of Object.keys(gSavedState.placements)) {
         let placements = gSavedState.placements[areaId];
         gPlacements.set(areaId, placements);
       }
@@ -2585,16 +2591,20 @@ let CustomizableUIInternal = {
         }
       }
     }
 
     if (Services.prefs.prefHasUserValue(kPrefDrawInTitlebar)) {
       LOG(kPrefDrawInTitlebar + " pref is non-default");
       return false;
     }
+    if (Services.prefs.prefHasUserValue(kPrefDeveditionTheme)) {
+      LOG(kPrefDeveditionTheme + " pref is non-default");
+      return false;
+    }
 
     return true;
   },
 
   setToolbarVisibility: function(aToolbarId, aIsVisible) {
     // We only persist the attribute the first time.
     let isFirstChangedToolbar = true;
     for (let window of CustomizableUI.windows) {
@@ -3285,17 +3295,18 @@ this.CustomizableUI = {
   /**
    * Can the last Restore Defaults operation be undone.
    *
    * @return A boolean stating whether an undo of the
    *         Restore Defaults can be performed.
    */
   get canUndoReset() {
     return gUIStateBeforeReset.uiCustomizationState != null ||
-           gUIStateBeforeReset.drawInTitlebar != null;
+           gUIStateBeforeReset.drawInTitlebar != null ||
+           gUIStateBeforeReset.deveditionTheme != null;
   },
 
   /**
    * Get the placement of a widget. This is by far the best way to obtain
    * information about what the state of your widget is. The internals of
    * this call are cheap (no DOM necessary) and you will know where the user
    * has put your widget.
    *
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -12,16 +12,19 @@ const kPrefCustomizationDebug = "browser
 const kPrefCustomizationAnimation = "browser.uiCustomization.disableAnimation";
 const kPaletteId = "customization-palette";
 const kAboutURI = "about:customizing";
 const kDragDataTypePrefix = "text/toolbarwrapper-id/";
 const kPlaceholderClass = "panel-customization-placeholder";
 const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
 const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
 const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
+const kDeveditionThemePref = "browser.devedition.theme.enabled";
+const kDeveditionButtonPref = "browser.devedition.theme.showCustomizeButton";
+const kDeveditionChangedNotification = "devedition-theme-state-changed";
 const kMaxTransitionDurationMs = 2000;
 
 const kPanelItemContextMenu = "customizationPanelItemContextMenu";
 const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/CustomizableUI.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -59,22 +62,27 @@ function CustomizeMode(aWindow) {
   // user. Then there's the visible palette, which gets populated and displayed
   // to the user when in customizing mode.
   this.visiblePalette = this.document.getElementById(kPaletteId);
   this.paletteEmptyNotice = this.document.getElementById("customization-empty");
   this.paletteSpacer = this.document.getElementById("customization-spacer");
   this.tipPanel = this.document.getElementById("customization-tipPanel");
   if (Services.prefs.getCharPref("general.skins.selectedSkin") != "classic/1.0") {
     let lwthemeButton = this.document.getElementById("customization-lwtheme-button");
+    let deveditionButton = this.document.getElementById("customization-devedition-theme-button");
     lwthemeButton.setAttribute("hidden", "true");
+    deveditionButton.setAttribute("hidden", "true");
   }
 #ifdef CAN_DRAW_IN_TITLEBAR
   this._updateTitlebarButton();
   Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
 #endif
+  this._updateDevEditionThemeButton();
+  Services.prefs.addObserver(kDeveditionButtonPref, this, false);
+  Services.obs.addObserver(this, kDeveditionChangedNotification, false);
   this.window.addEventListener("unload", this);
 };
 
 CustomizeMode.prototype = {
   _changed: false,
   _transitioning: false,
   window: null,
   document: null,
@@ -100,16 +108,18 @@ CustomizeMode.prototype = {
   get _handler() {
     return this.window.CustomizationHandler;
   },
 
   uninit: function() {
 #ifdef CAN_DRAW_IN_TITLEBAR
     Services.prefs.removeObserver(kDrawInTitlebarPref, this);
 #endif
+    Services.prefs.removeObserver(kDeveditionButtonPref, this);
+    Services.obs.removeObserver(this, kDeveditionChangedNotification);
   },
 
   toggle: function() {
     if (this._handler.isEnteringCustomizeMode || this._handler.isExitingCustomizeMode) {
       this._wantToBeInCustomizeMode = !this._wantToBeInCustomizeMode;
       return;
     }
     if (this._customizing) {
@@ -1492,16 +1502,23 @@ CustomizeMode.prototype = {
           aData = JSON.parse(aData);
           if (!aData) {
             this.removeLWTStyling();
           } else {
             this.updateLWTStyling(aData);
           }
         }
         break;
+      case kDeveditionChangedNotification:
+        if (aSubject == this.window) {
+          this._updateDevEditionThemeButton();
+          this._updateResetButton();
+          this._updateUndoResetButton();
+        }
+        break;
     }
   },
 
 #ifdef CAN_DRAW_IN_TITLEBAR
   _updateTitlebarButton: function() {
     let drawInTitlebar = true;
     try {
       drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
@@ -1516,16 +1533,54 @@ CustomizeMode.prototype = {
   },
 
   toggleTitlebar: function(aShouldShowTitlebar) {
     // Drawing in the titlebar means not showing the titlebar, hence the negation:
     Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
   },
 #endif
 
+  _updateDevEditionThemeButton: function() {
+    let button = this.document.getElementById("customization-devedition-theme-button");
+
+    let themeEnabled = !!this.window.DevEdition.styleSheet;
+    if (themeEnabled) {
+      button.setAttribute("checked", "true");
+    } else {
+      button.removeAttribute("checked");
+    }
+
+    let buttonVisible = Services.prefs.getBoolPref(kDeveditionButtonPref);
+    if (buttonVisible) {
+      button.removeAttribute("hidden");
+    } else {
+      button.setAttribute("hidden", "true");
+    }
+  },
+
+  toggleDevEditionTheme: function(shouldEnable) {
+    const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
+
+    Services.prefs.setBoolPref(kDeveditionThemePref, shouldEnable);
+
+    let currentLWT = LightweightThemeManager.currentTheme;
+    if (currentLWT && shouldEnable) {
+      this._lastLightweightTheme = currentLWT;
+      AddonManager.getAddonByID(DEFAULT_THEME_ID, function(aDefaultTheme) {
+        // Theoretically, this could race if people are /very/ quick in switching
+        // something else here, so doublecheck:
+        if (Services.prefs.getBoolPref(kDeveditionThemePref)) {
+          aDefaultTheme.userDisabled = false;
+        }
+      });
+    } else if (!currentLWT && !shouldEnable && this._lastLightweightTheme) {
+      LightweightThemeManager.currentTheme = this._lastLightweightTheme;
+    }
+  },
+
   _onDragStart: function(aEvent) {
     __dumpDragData(aEvent);
     let item = aEvent.target;
     while (item && item.localName != "toolbarpaletteitem") {
       if (item.localName == "toolbar") {
         return;
       }
       item = item.parentNode;
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -48,16 +48,23 @@
                            label="&customizeMode.lwthemes.menuGetMore;"
                            accesskey="&customizeMode.lwthemes.menuGetMore.accessKey;"
                            tabindex="0"
                            oncommand="gCustomizeMode.getMoreThemes(event);"/>
           </hbox>
         </panel>
       </button>
 
+      <button id="customization-devedition-theme-button"
+              class="customizationmode-button"
+              hidden="true"
+              label="&customizeMode.deveditionTheme.label2;"
+              oncommand="gCustomizeMode.toggleDevEditionTheme(this.hasAttribute('checked'))"
+              type="checkbox" />
+
       <spacer id="customization-footer-spacer"/>
       <button id="customization-undo-reset-button"
               class="customizationmode-button"
               hidden="true"
               oncommand="gCustomizeMode.undoReset();"
               label="&undoCmd.label;"/>
       <button id="customization-reset-button"
               oncommand="gCustomizeMode.reset();"
--- a/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
+++ b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
@@ -1,21 +1,19 @@
 /* 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";
 
 const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
-const {LightweightThemeManager} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
 
 add_task(function () {
   Services.prefs.clearUserPref("lightweightThemes.usedThemes");
   Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
-  LightweightThemeManager.clearBuiltInThemes();
 
   yield startCustomizing();
 
   let themesButton = document.getElementById("customization-lwtheme-button");
   let popup = document.getElementById("customization-lwtheme-menu");
 
   let popupShownPromise = popupShown(popup);
   EventUtils.synthesizeMouseAtCenter(themesButton, {});
--- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js
+++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
@@ -96,12 +96,54 @@ add_task(function() {
   is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
   is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
 
   Services.prefs.clearUserPref(prefName);
   ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
   is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
 });
 
+// Bug 1082108 - Restore Defaults should clear user pref for devedition theme
+add_task(function() {
+  let prefName = "browser.devedition.theme.enabled";
+  Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", true);
+  let defaultValue = Services.prefs.getBoolPref(prefName);
+  let restoreDefaultsButton = document.getElementById("customization-reset-button");
+  let deveditionThemeButton = document.getElementById("customization-devedition-theme-button");
+  let undoResetButton = document.getElementById("customization-undo-reset-button");
+  ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
+  ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
+  is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "Devedition theme button should reflect pref value");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
+  Services.prefs.setBoolPref(prefName, !defaultValue);
+
+  yield waitForCondition(() => !restoreDefaultsButton.disabled);
+  ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
+  is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "Devedition theme button should reflect changed pref value");
+  ok(!CustomizableUI.inDefaultState, "With devedition theme flipped, no longer default");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
+
+  yield gCustomizeMode.reset();
+  ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
+  is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "devedition theme button should reflect default value after reset");
+  is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset devedition.theme.enabled");
+  ok(CustomizableUI.inDefaultState, "In default state after devedition theme reset");
+  is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
+  ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
+
+  yield gCustomizeMode.undoReset();
+  ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
+  is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "devedition theme button should reflect undo-reset value");
+  ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
+  is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
+
+  //XXXgijs this line should be removed once bug 1094509 lands
+  Services.prefs.clearUserPref("devtools.theme");
+  Services.prefs.clearUserPref(prefName);
+  ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
+});
+
 add_task(function asyncCleanup() {
   yield gCustomizeMode.reset();
   yield endCustomizing();
 });
--- a/browser/components/loop/test/desktop-local/index.html
+++ b/browser/components/loop/test/desktop-local/index.html
@@ -30,17 +30,17 @@
 
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.1.js"></script>
   <script src="../shared/vendor/chai-2.1.0.js"></script>
   <script src="../shared/vendor/sinon-1.13.0.js"></script>
   <script>
     /*global chai,mocha */
     chai.config.includeStack = true;
-    mocha.setup('bdd');
+    mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
 
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
   <script src="../../content/shared/js/feedbackApiClient.js"></script>
   <script src="../../content/shared/js/models.js"></script>
   <script src="../../content/shared/js/mixins.js"></script>
   <script src="../../content/shared/js/websocket.js"></script>
--- a/browser/components/loop/test/shared/index.html
+++ b/browser/components/loop/test/shared/index.html
@@ -31,17 +31,17 @@
   <!-- test dependencies -->
   <script src="vendor/mocha-2.2.1.js"></script>
   <script src="vendor/chai-2.1.0.js"></script>
   <script src="vendor/chai-as-promised-4.3.0.js"></script>
   <script src="vendor/sinon-1.13.0.js"></script>
   <script>
     /*global chai, mocha */
     chai.config.includeStack = true;
-    mocha.setup('bdd');
+    mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
 
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
   <script src="../../content/shared/js/models.js"></script>
   <script src="../../content/shared/js/mixins.js"></script>
   <script src="../../content/shared/js/crypto.js"></script>
   <script src="../../content/shared/js/websocket.js"></script>
--- a/browser/components/loop/test/standalone/index.html
+++ b/browser/components/loop/test/standalone/index.html
@@ -30,17 +30,17 @@
   <script src="../../standalone/content/libs/l10n-gaia-02ca67948fe8.js"></script>
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.1.js"></script>
   <script src="../shared/vendor/chai-2.1.0.js"></script>
   <script src="../shared/vendor/sinon-1.13.0.js"></script>
   <script src="../shared/sdk_mock.js"></script>
   <script>
     chai.config.includeStack = true;
-    mocha.setup('bdd');
+    mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
   <script src="../../content/shared/js/models.js"></script>
   <script src="../../content/shared/js/mixins.js"></script>
   <script src="../../content/shared/js/websocket.js"></script>
   <script src="../../content/shared/js/feedbackApiClient.js"></script>
   <script src="../../content/shared/js/actions.js"></script>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -146,19 +146,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/WebChannel.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
                                   "resource:///modules/ReaderParent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
                                   "resource://gre/modules/AddonWatcher.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
-                                  "resource://gre/modules/LightweightThemeManager.jsm");
-
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
@@ -713,31 +710,16 @@ BrowserGlue.prototype = {
     Services.prefs.addObserver(POLARIS_ENABLED, this, false);
 #endif
 
 #ifdef MOZ_DEBUG_UA
     UserAgentOverrides.init();
     DebugUserAgent.init();
 #endif
 
-#ifndef RELEASE_BUILD
-    let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-    let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
-    let themeName = browserBundle.GetStringFromName("deveditionTheme.name");
-    let vendorShortName = brandBundle.GetStringFromName("vendorShortName");
-
-    LightweightThemeManager.addBuiltInTheme({
-      id: "firefox-devedition@mozilla.org",
-      name: themeName,
-      headerURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.header.png",
-      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
-      author: vendorShortName,
-    });
-#endif
-
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
 
     AddonWatcher.init(this._notifySlowAddon);
   },
 
   _checkForOldBuildUpdates: function () {
     // check for update if our build is old
     if (Services.prefs.getBoolPref("app.update.enabled") &&
@@ -1975,52 +1957,16 @@ BrowserGlue.prototype = {
       // Fix up document color use:
       const kOldColorPref = "browser.display.use_document_colors";
       if (Services.prefs.prefHasUserValue(kOldColorPref) &&
           !Services.prefs.getBoolPref(kOldColorPref)) {
         Services.prefs.setIntPref("browser.display.document_color_use", 2);
       }
     }
 
-    if (currentUIVersion < 28) {
-      // Convert old devedition theme pref to lightweight theme storage
-      let lightweightThemeSelected = false;
-      let selectedThemeID = null;
-      try {
-        lightweightThemeSelected = Services.prefs.prefHasUserValue("lightweightThemes.selectedThemeID");
-        selectedThemeID = Services.prefs.getCharPref("lightweightThemes.selectedThemeID");
-      } catch(e) {}
-
-      let defaultThemeSelected = false;
-      try {
-         defaultThemeSelected = Services.prefs.getCharPref("general.skins.selectedSkin") == "classic/1.0";
-      } catch(e) {}
-
-      let deveditionThemeEnabled = false;
-      try {
-         deveditionThemeEnabled = Services.prefs.getBoolPref("browser.devedition.theme.enabled");
-      } catch(e) {}
-
-      // If we are on the devedition channel, the devedition theme is on by
-      // default.  But we need to handle the case where they didn't want it
-      // applied, and unapply the theme.
-      let userChoseToNotUseDeveditionTheme =
-        !deveditionThemeEnabled ||
-        !defaultThemeSelected ||
-        (lightweightThemeSelected && selectedThemeID != "firefox-devedition@mozilla.org");
-
-      if (userChoseToNotUseDeveditionTheme && selectedThemeID == "firefox-devedition@mozilla.org") {
-        Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "");
-      }
-
-      // Not clearing browser.devedition.theme.enabled, to preserve user's pref
-      // if for some reason this function runs again (even though it shouldn't)
-      Services.prefs.clearUserPref("browser.devedition.showCustomizeButton");
-    }
-
     if (currentUIVersion < 29) {
       let group = null;
       try {
         group = Services.prefs.getComplexValue("font.language.group",
                                                Ci.nsIPrefLocalizedString);
       } catch (ex) {}
       if (group &&
           ["tr", "x-baltic", "x-central-euro"].some(g => g == group.data)) {
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-39.0a1
+40.0a1
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -21,16 +21,17 @@ support-files =
 [browser_target_support.js]
 [browser_two_tabs.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
 [browser_toolbox_highlight.js]
 [browser_toolbox_hosts.js]
 [browser_toolbox_hosts_size.js]
 [browser_toolbox_options.js]
+[browser_toolbox_options_devedition.js]
 [browser_toolbox_options_disable_buttons.js]
 [browser_toolbox_options_disable_cache-01.js]
 skip-if = e10s # Bug 1030318
 [browser_toolbox_options_disable_cache-02.js]
 skip-if = e10s # Bug 1030318
 [browser_toolbox_options_disable_js.js]
 skip-if = e10s # Bug 1030318
 # [browser_toolbox_raise.js] # Bug 962258
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_options_devedition.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that changing preferences in the options panel updates the prefs
+// and toggles appropriate things in the toolbox.
+
+let doc = null, toolbox = null, panelWin = null;
+
+const PREF_ENABLED = "browser.devedition.theme.enabled";
+const PREF_SHOW = "browser.devedition.theme.showCustomizeButton";
+
+const URL = "data:text/html;charset=utf8,test for toggling dev edition browser theme toggling";
+
+add_task(function*() {
+  // Set preference to false by default so this could
+  // run in Developer Edition which has it on by default.
+  Services.prefs.setBoolPref(PREF_ENABLED, false);
+  Services.prefs.setBoolPref(PREF_SHOW, true);
+
+  let tab = yield addTab(URL);
+  let target = TargetFactory.forTab(tab);
+  toolbox = yield gDevTools.showToolbox(target);
+  let tool = yield toolbox.selectTool("options");
+  panelWin = tool.panelWin;
+
+  let checkbox = tool.panelDoc.getElementById("devtools-browser-theme");
+
+  ise(Services.prefs.getBoolPref(PREF_ENABLED), false, "Dev Theme pref off on start");
+
+  let themeStatus = yield clickAndWaitForThemeChange(checkbox, panelWin);
+  ise(themeStatus, true, "Theme has been toggled on.");
+
+  themeStatus = yield clickAndWaitForThemeChange(checkbox, panelWin);
+  ise(themeStatus, false, "Theme has been toggled off.");
+
+  yield cleanup();
+});
+
+function clickAndWaitForThemeChange (el, win) {
+  let deferred = promise.defer();
+  gDevTools.on("pref-changed", function handler (event, {pref}) {
+    if (pref === PREF_ENABLED) {
+      gDevTools.off("pref-changed", handler);
+      deferred.resolve(Services.prefs.getBoolPref(PREF_ENABLED));
+    }
+  });
+
+  EventUtils.synthesizeMouseAtCenter(el, {}, win);
+
+  return deferred.promise;
+}
+
+function* cleanup() {
+  yield toolbox.destroy();
+  gBrowser.removeCurrentTab();
+  Services.prefs.clearUserPref(PREF_ENABLED);
+  Services.prefs.clearUserPref(PREF_SHOW);
+  toolbox = doc = panelWin = null;
+}
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -4,16 +4,19 @@
 
 "use strict";
 
 const {Cu, Cc, Ci} = require("chrome");
 const Services = require("Services");
 const promise = require("promise");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizeMode", "resource:///modules/CustomizeMode.jsm");
+const kDeveditionChangedNotification = "devedition-theme-state-changed";
+const DEVEDITION_THEME_PREF = "browser.devedition.theme.enabled";
 
 exports.OptionsPanel = OptionsPanel;
 
 XPCOMUtils.defineLazyGetter(this, "l10n", function() {
   let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
   let l10n = function(aName, ...aArgs) {
     try {
       if (aArgs.length == 0) {
@@ -79,16 +82,17 @@ function OptionsPanel(iframeWindow, tool
   this._themeRegistered = this._themeRegistered.bind(this);
   this._themeUnregistered = this._themeUnregistered.bind(this);
   this._disableJSClicked = this._disableJSClicked.bind(this);
 
   this.disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
 
   this._addListeners();
 
+  Services.obs.addObserver(this, kDeveditionChangedNotification, false);
   const EventEmitter = require("devtools/toolkit/event-emitter");
   EventEmitter.decorate(this);
 }
 
 OptionsPanel.prototype = {
 
   get target() {
     return this.toolbox.target;
@@ -103,16 +107,17 @@ OptionsPanel.prototype = {
     } else {
       targetPromise = promise.resolve(this.target);
     }
 
     return targetPromise.then(() => {
       this.setupToolsList();
       this.setupToolbarButtonsList();
       this.setupThemeList();
+      this.setupBrowserThemeButton();
       this.populatePreferences();
       this.updateDefaultTheme();
     }).then(() => {
       this.isReady = true;
       this.emit("ready");
       return this;
     }).then(null, function onError(aReason) {
       Cu.reportError("OptionsPanel open failed. " +
@@ -136,16 +141,18 @@ OptionsPanel.prototype = {
     if (data.pref === "devtools.cache.disabled") {
       let cacheDisabled = data.newValue;
       let cbx = this.panelDoc.getElementById("devtools-disable-cache");
 
       cbx.checked = cacheDisabled;
     }
     else if (data.pref === "devtools.theme") {
       this.updateCurrentTheme();
+    } else if (data.pref === "browser.devedition.theme.enabled") {
+      this.updateBrowserTheme();
     }
   },
 
   _themeRegistered: function(event, themeId) {
     this.setupThemeList();
   },
 
   _themeUnregistered: function(event, theme) {
@@ -271,16 +278,62 @@ OptionsPanel.prototype = {
     let themes = gDevTools.getThemeDefinitionArray();
     for (let theme of themes) {
       themeBox.appendChild(createThemeOption(theme));
     }
 
     this.updateCurrentTheme();
   },
 
+  /**
+   * Similar to `populatePrefs`, except we want more
+   * special rules for the browser theme button.
+   */
+  setupBrowserThemeButton: function() {
+    let checkbox = this.panelDoc.getElementById("devtools-browser-theme");
+
+    checkbox.addEventListener("command", function() {
+      setPrefAndEmit(DEVEDITION_THEME_PREF, this.checked);
+    }.bind(checkbox));
+
+    this.updateBrowserThemeButton();
+  },
+
+  /**
+   * Called on theme changed via observer of "devedition-theme-state-changed".
+   */
+  updateBrowserThemeButton: function() {
+    let checkbox = this.panelDoc.getElementById("devtools-browser-theme");
+
+    // Check if the dev edition style sheet is applied -- will not
+    // be applied when dev edition theme is disabled, or when there's
+    // a LWT applied.
+    if (this._isDevEditionThemeOn()) {
+      checkbox.setAttribute("checked", "true");
+    } else {
+      checkbox.removeAttribute("checked");
+    }
+
+    // Should the button be shown
+    if (GetPref("browser.devedition.theme.showCustomizeButton")) {
+      checkbox.removeAttribute("hidden");
+    } else {
+      checkbox.setAttribute("hidden", "true");
+    }
+  },
+
+  /**
+   * Called when clicking the browser theme button to enable/disable
+   * the dev edition browser theme.
+   */
+  updateBrowserTheme: function() {
+    let enabled = GetPref("browser.devedition.theme.enabled");
+    CustomizeMode.prototype.toggleDevEditionTheme.call(this, enabled);
+  },
+
   populatePreferences: function() {
     let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]");
     for (let checkbox of prefCheckboxes) {
       checkbox.checked = GetPref(checkbox.getAttribute("data-pref"));
       checkbox.addEventListener("command", function() {
         setPrefAndEmit(this.getAttribute("data-pref"), this.checked);
       }.bind(checkbox));
     }
@@ -359,16 +412,35 @@ OptionsPanel.prototype = {
 
     let options = {
       "javascriptEnabled": !checked
     };
 
     this.target.activeTab.reconfigure(options);
   },
 
+  /**
+   * Returns a boolean indicating whether or not the dev edition
+   * browser theme is applied.
+   */
+  _isDevEditionThemeOn: function() {
+    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    return !!(win && win.DevEdition.styleSheet);
+  },
+
+  /**
+   * Called on observer notification for "devedition-theme-state-changed"
+   * to possibly change the state of the dev edition button
+   */
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic === kDeveditionChangedNotification) {
+      this.updateBrowserThemeButton();
+    }
+  },
+
   destroy: function() {
     if (this.destroyPromise) {
       return this.destroyPromise;
     }
 
     let deferred = promise.defer();
 
     this.destroyPromise = deferred.promise;
@@ -383,16 +455,18 @@ OptionsPanel.prototype = {
       this.target.activeTab.reconfigure(options, () => {
         this.toolbox = null;
         deferred.resolve();
       }, true);
     }
 
     this.panelWin = this.panelDoc = this.disableJSNode = null;
 
+    Services.obs.removeObserver(this, kDeveditionChangedNotification);
+
     return deferred.promise;
   }
 };
 
 /* Set a pref and emit the pref-changed event if needed. */
 function setPrefAndEmit(prefName, newValue) {
   let data = {
     pref: prefName,
--- a/browser/devtools/framework/toolbox-options.xul
+++ b/browser/devtools/framework/toolbox-options.xul
@@ -30,16 +30,19 @@
       <vbox class="options-vertical-pane" flex="1">
         <label>&options.selectDevToolsTheme.label2;</label>
         <vbox id="theme-options" class="options-groupbox">
           <radiogroup id="devtools-theme-box"
                       class="options-groupbox"
                       data-pref="devtools.theme"
                       orient="horizontal">
           </radiogroup>
+          <checkbox id="devtools-browser-theme"
+                    label="&options.usedeveditiontheme.label;"
+                    tooltiptext="&options.usedeveditiontheme.tooltip;"/>
         </vbox>
         <label>&options.commonPrefs.label;</label>
         <vbox id="commonprefs-options" class="options-groupbox">
           <checkbox label="&options.enablePersistentLogs.label;"
                     tooltiptext="&options.enablePersistentLogs.tooltip;"
                     data-pref="devtools.webconsole.persistlog"/>
         </vbox>
         <label>&options.context.inspector;</label>
--- a/browser/devtools/framework/toolbox-process-window.js
+++ b/browser/devtools/framework/toolbox-process-window.js
@@ -47,16 +47,17 @@ let connect = Task.async(function*() {
     }
   });
 });
 
 // Certain options should be toggled since we can assume chrome debugging here
 function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
   Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
+  Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", false);
   Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
   Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 }
 
 window.addEventListener("load", function() {
   let cmdClose = document.getElementById("toolbox-cmd-close");
   cmdClose.addEventListener("command", onCloseCommand);
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -28,17 +28,20 @@ const EVAL_FUNCTION_TIMEOUT      = 1000;
 
 const MAXIMUM_FONT_SIZE = 96;
 const MINIMUM_FONT_SIZE = 6;
 const NORMAL_FONT_SIZE = 12;
 
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
+const SHOW_LINE_NUMBERS = "devtools.scratchpad.lineNumbers";
+const WRAP_TEXT = "devtools.scratchpad.wrapText";
 const SHOW_TRAILING_SPACE = "devtools.scratchpad.showTrailingSpace";
+const EDITOR_FONT_SIZE = "devtools.scratchpad.editorFontSize";
 const ENABLE_AUTOCOMPLETION = "devtools.scratchpad.enableAutocompletion";
 const TAB_SIZE = "devtools.editor.tabsize";
 const FALLBACK_CHARSET_LIST = "intl.fallbackCharsetList.ISO-8859-1";
 
 const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul";
 
 const require   = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 
@@ -215,23 +218,23 @@ var Scratchpad = {
       },
       "sp-cmd-documentationLink": () => {
         Scratchpad.openDocumentationPage();
       },
       "sp-cmd-hideSidebar": () => {
         Scratchpad.sidebar.hide();
       },
       "sp-cmd-line-numbers": () => {
-        Scratchpad.toggleEditorOption('lineNumbers');
+        Scratchpad.toggleEditorOption('lineNumbers', SHOW_LINE_NUMBERS);
       },
       "sp-cmd-wrap-text": () => {
-        Scratchpad.toggleEditorOption('lineWrapping');
+        Scratchpad.toggleEditorOption('lineWrapping', WRAP_TEXT);
       },
       "sp-cmd-highlight-trailing-space": () => {
-        Scratchpad.toggleEditorOption('showTrailingSpace');
+        Scratchpad.toggleEditorOption('showTrailingSpace', SHOW_TRAILING_SPACE);
       },
       "sp-cmd-larger-font": () => {
         Scratchpad.increaseFontSize();
       },
       "sp-cmd-smaller-font": () => {
         Scratchpad.decreaseFontSize();
       },
       "sp-cmd-normal-font": () => {
@@ -243,16 +246,34 @@ var Scratchpad = {
       let elem = document.getElementById(command);
       if (elem) {
         elem.addEventListener("command", commands[command]);
       }
     }
   },
 
   /**
+   * Check or uncheck view menu items according to stored preferences.
+   */
+  _updateViewMenuItems: function SP_updateViewMenuItems() {
+    this._updateViewMenuItem(SHOW_LINE_NUMBERS, "sp-menu-line-numbers");
+    this._updateViewMenuItem(WRAP_TEXT, "sp-menu-word-wrap");
+    this._updateViewMenuItem(SHOW_TRAILING_SPACE, "sp-menu-highlight-trailing-space");
+  },
+
+  _updateViewMenuItem: function SP_updateViewMenuItem(preferenceName, menuId) {
+    let checked = Services.prefs.getBoolPref(preferenceName);
+    if (checked) {
+        document.getElementById(menuId).setAttribute('checked', true);
+    } else {
+        document.getElementById(menuId).removeAttribute('checked');
+    }
+  },
+
+  /**
    * The script execution context. This tells Scratchpad in which context the
    * script shall execute.
    *
    * Possible values:
    *   - SCRATCHPAD_CONTEXT_CONTENT to execute code in the context of the current
    *   tab content window object.
    *   - SCRATCHPAD_CONTEXT_BROWSER to execute code in the context of the
    *   currently active chrome window object.
@@ -1689,27 +1710,30 @@ var Scratchpad = {
       }
     } else {
       this._instanceId = ScratchpadManager.createUid();
     }
 
     let config = {
       mode: Editor.modes.js,
       value: initialText,
-      lineNumbers: true,
+      lineNumbers: Services.prefs.getBoolPref(SHOW_LINE_NUMBERS),
       contextMenu: "scratchpad-text-popup",
       showTrailingSpace: Services.prefs.getBoolPref(SHOW_TRAILING_SPACE),
       autocomplete: Services.prefs.getBoolPref(ENABLE_AUTOCOMPLETION),
+      lineWrapping: Services.prefs.getBoolPref(WRAP_TEXT),
     };
 
     this.editor = new Editor(config);
     let editorElement = document.querySelector("#scratchpad-editor");
     this.editor.appendTo(editorElement).then(() => {
       var lines = initialText.split("\n");
 
+      this.editor.setFontSize(Services.prefs.getIntPref(EDITOR_FONT_SIZE));
+
       this.editor.on("change", this._onChanged);
       // Keep a reference to the bound version for use in onUnload.
       this.updateStatusBar = Scratchpad.updateStatusBar.bind(this);
       this.editor.on("cursorActivity", this.updateStatusBar);
       let okstring = this.strings.GetStringFromName("selfxss.okstring");
       let msg = this.strings.formatStringFromName("selfxss.msg", [okstring], 1);
       this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
                                                       document.querySelector('#scratchpad-notificationbox'),
@@ -1725,16 +1749,17 @@ var Scratchpad = {
 
       this.initialized = true;
       this._triggerObservers("Ready");
       this.populateRecentFilesMenu();
       PreferenceObserver.init();
       CloseObserver.init();
     }).then(null, (err) => console.error(err));
     this._setupCommandListeners();
+    this._updateViewMenuItems();
     this._setupPopupShowingListeners();
   },
 
   /**
    * The Source Editor "change" event handler. This function updates the
    * Scratchpad window title to show an asterisk when there are unsaved changes.
    *
    * @private
@@ -1906,52 +1931,58 @@ var Scratchpad = {
     });
 
     return shouldClose;
   },
 
   /**
    * Toggle a editor's boolean option.
    */
-  toggleEditorOption: function SP_toggleEditorOption(optionName)
+  toggleEditorOption: function SP_toggleEditorOption(optionName, optionPreference)
   {
     let newOptionValue = !this.editor.getOption(optionName);
     this.editor.setOption(optionName, newOptionValue);
+    Services.prefs.setBoolPref(optionPreference, newOptionValue);
   },
 
   /**
    * Increase the editor's font size by 1 px.
    */
   increaseFontSize: function SP_increaseFontSize()
   {
     let size = this.editor.getFontSize();
 
     if (size < MAXIMUM_FONT_SIZE) {
-      this.editor.setFontSize(size + 1);
+      let newFontSize = size + 1;
+      this.editor.setFontSize(newFontSize);
+      Services.prefs.setIntPref(EDITOR_FONT_SIZE, newFontSize);
     }
   },
 
   /**
    * Decrease the editor's font size by 1 px.
    */
   decreaseFontSize: function SP_decreaseFontSize()
   {
     let size = this.editor.getFontSize();
 
     if (size > MINIMUM_FONT_SIZE) {
-      this.editor.setFontSize(size - 1);
+      let newFontSize = size - 1;
+      this.editor.setFontSize(newFontSize);
+      Services.prefs.setIntPref(EDITOR_FONT_SIZE, newFontSize);
     }
   },
 
   /**
    * Restore the editor's original font size.
    */
   normalFontSize: function SP_normalFontSize()
   {
     this.editor.setFontSize(NORMAL_FONT_SIZE);
+    Services.prefs.setIntPref(EDITOR_FONT_SIZE, NORMAL_FONT_SIZE);
   },
 
   _observers: [],
 
   /**
    * Add an observer for Scratchpad events.
    *
    * The observer implements IScratchpadObserver := {
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -213,17 +213,16 @@
   </menu>
 
   <menu id="sp-view-menu" label="&viewMenu.label;" accesskey="&viewMenu.accesskey;">
     <menupopup id="sp-menu-viewpopup">
       <menuitem id="sp-menu-line-numbers"
                 label="&lineNumbers.label;"
                 accesskey="&lineNumbers.accesskey;"
                 type="checkbox"
-                checked="true"
                 command="sp-cmd-line-numbers"/>
       <menuitem id="sp-menu-word-wrap"
                 label="&wordWrap.label;"
                 accesskey="&wordWrap.accesskey;"
                 type="checkbox"
                 command="sp-cmd-wrap-text"/>
       <menuitem id="sp-menu-highlight-trailing-space"
                 label="&highlightTrailingSpace.label;"
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -37,8 +37,9 @@ support-files = NS_ERROR_ILLEGAL_INPUT.t
 [browser_scratchpad_throw_output.js]
 [browser_scratchpad_pprint-02.js]
 [browser_scratchpad_pprint.js]
 [browser_scratchpad_pprint_error_goto_line.js]
 [browser_scratchpad_restore.js]
 [browser_scratchpad_tab_switch.js]
 [browser_scratchpad_ui.js]
 [browser_scratchpad_close_toolbox.js]
+[browser_scratchpad_remember_view_options.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_remember_view_options.js
@@ -0,0 +1,65 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* Bug 1140839 */
+
+// Test that view menu items are remembered across scratchpad invocations.
+function test()
+{
+  waitForExplicitFinish();
+
+  // To test for this bug we open a Scratchpad window and change all
+  // view menu options. After each change we compare the correspondent
+  // preference value with the expected value.
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html,<title>Bug 1140839</title>" +
+    "<p>test Scratchpad should remember View options";
+}
+
+function runTests()
+{
+  let sp = gScratchpadWindow.Scratchpad;
+  let doc = gScratchpadWindow.document;
+
+  let testData = [
+    {itemMenuId: "sp-menu-line-numbers", prefId: "devtools.scratchpad.lineNumbers", expectedVal: false},
+    {itemMenuId: "sp-menu-word-wrap", prefId: "devtools.scratchpad.wrapText", expectedVal: true},
+    {itemMenuId: "sp-menu-highlight-trailing-space", prefId: "devtools.scratchpad.showTrailingSpace", expectedVal: true},
+    {itemMenuId: "sp-menu-larger-font", prefId: "devtools.scratchpad.editorFontSize", expectedVal: 13},
+    {itemMenuId: "sp-menu-normal-size-font", prefId: "devtools.scratchpad.editorFontSize", expectedVal: 12},
+    {itemMenuId: "sp-menu-smaller-font", prefId: "devtools.scratchpad.editorFontSize", expectedVal: 11},
+  ];
+
+  testData.forEach(function (data) {
+    let getPref = getPrefFunction(data.prefId);
+
+    try {
+      let menu = doc.getElementById(data.itemMenuId);
+      menu.doCommand();
+      let newPreferenceValue = getPref(data.prefId);
+      ok(newPreferenceValue === data.expectedVal, newPreferenceValue + " !== " + data.expectedVal);
+      Services.prefs.clearUserPref(data.prefId);
+    }
+    catch (exception) {
+      ok(false, "Exception thrown while executing the command of menuitem #" + data.itemMenuId);
+    }
+  });
+
+  finish();
+}
+
+function getPrefFunction(preferenceId)
+{
+  let preferenceType = Services.prefs.getPrefType(preferenceId);
+  if (preferenceType === Services.prefs.PREF_INT) {
+    return Services.prefs.getIntPref;
+  } else if (preferenceType === Services.prefs.PREF_BOOL) {
+    return Services.prefs.getBoolPref;
+  }
+}
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
 const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
 });
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -737,16 +737,17 @@ just addresses the organization to follo
 <!ENTITY customizeMode.titlebar "Title Bar">
 <!ENTITY customizeMode.lwthemes "Themes">
 <!ENTITY customizeMode.lwthemes.myThemes "My Themes">
 <!ENTITY customizeMode.lwthemes.recommended "Recommended">
 <!ENTITY customizeMode.lwthemes.menuManage "Manage">
 <!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
 <!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
 <!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
+<!ENTITY customizeMode.deveditionTheme.label2 "Use Developer Edition Theme">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
 <!ENTITY social.markpageMenu.accesskey "P">
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
--- a/browser/modules/ReaderParent.jsm
+++ b/browser/modules/ReaderParent.jsm
@@ -130,25 +130,26 @@ let ReaderParent = {
     let command = win.document.getElementById("View:ReaderView");
     if (browser.currentURI.spec.startsWith("about:reader")) {
       button.setAttribute("readeractive", true);
       button.hidden = false;
       let closeText = gStringBundle.GetStringFromName("readerView.close");
       button.setAttribute("tooltiptext", closeText);
       command.setAttribute("label", closeText);
       command.setAttribute("hidden", false);
+      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.close.accesskey"));
     } else {
       button.removeAttribute("readeractive");
       button.hidden = !browser.isArticle;
       let enterText = gStringBundle.GetStringFromName("readerView.enter");
       button.setAttribute("tooltiptext", enterText);
       command.setAttribute("label", enterText);
       command.setAttribute("hidden", !browser.isArticle);
+      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.enter.accesskey"));
     }
-    command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.accesskey"));
   },
 
   toggleReaderMode: function(event) {
     let win = event.target.ownerDocument.defaultView;
     let browser = win.gBrowser.selectedBrowser;
     let url = browser.currentURI.spec;
 
     if (url.startsWith("about:reader")) {
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.py.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-39.0a1
+40.0a1
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -907,17 +907,17 @@ public class BrowserApp extends GeckoApp
     }
 
     private void processTabQueue() {
         if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) {
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     if (TabQueueHelper.shouldOpenTabQueueUrls(BrowserApp.this)) {
-                        TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME);
+                        TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME, false);
                     }
                 }
             });
         }
     }
 
     @Override
     public void onResume() {
@@ -3423,17 +3423,17 @@ public class BrowserApp extends GeckoApp
         // Only solicit feedback when the app has been launched from the icon shortcut.
         if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
             GuestSession.handleIntent(this, intent);
         }
 
         // If the user has clicked the tab queue notification then load the tabs.
         if(AppConstants.NIGHTLY_BUILD  && AppConstants.MOZ_ANDROID_TAB_QUEUE && mInitialized && isTabQueueAction) {
             int queuedTabCount = TabQueueHelper.getTabQueueLength(this);
-            TabQueueHelper.openQueuedUrls(this, mProfile, TabQueueHelper.FILE_NAME);
+            TabQueueHelper.openQueuedUrls(this, mProfile, TabQueueHelper.FILE_NAME, false);
 
             // If there's more than one tab then also show the tabs panel.
             if (queuedTabCount > 1) {
                 showNormalTabs();
             }
         }
 
         if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -41,16 +41,17 @@ import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.menu.MenuPanel;
 import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.PromptService;
+import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.updater.UpdateServiceHelper;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeEventListener;
@@ -1822,20 +1823,48 @@ public abstract class GeckoApp
         }
 
         final String action = intent.getAction();
 
         if (ACTION_LOAD.equals(action)) {
             String uri = intent.getDataString();
             Tabs.getInstance().loadUrl(uri);
         } else if (Intent.ACTION_VIEW.equals(action)) {
-            String uri = intent.getDataString();
-            Tabs.getInstance().loadUrl(uri, Tabs.LOADURL_NEW_TAB |
-                                            Tabs.LOADURL_USER_ENTERED |
-                                            Tabs.LOADURL_EXTERNAL);
+            // We need to ensure that if we receive a VIEW action and there are tabs queued then the
+            // site loaded from the intent is op top (last loaded) and selected with all other tabs
+            // being opened behind it. We process the tab queue first and request a callback from the JS - the
+            // listener will open the url from the intent as normal when the tab queue has been processed.
+            ThreadUtils.postToBackgroundThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE
+                                && TabQueueHelper.shouldOpenTabQueueUrls(GeckoApp.this)) {
+
+                        EventDispatcher.getInstance().registerGeckoThreadListener(new NativeEventListener() {
+                            @Override
+                            public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
+                                if ("Tabs:TabsOpened".equals(event)) {
+                                    EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Tabs:TabsOpened");
+                                    String uri = intent.getDataString();
+                                    Tabs.getInstance().loadUrl(uri, Tabs.LOADURL_NEW_TAB |
+                                                                            Tabs.LOADURL_USER_ENTERED |
+                                                                            Tabs.LOADURL_EXTERNAL);
+                                }
+                            }
+                        }, "Tabs:TabsOpened");
+
+                        TabQueueHelper.openQueuedUrls(GeckoApp.this, mProfile, TabQueueHelper.FILE_NAME, true);
+                    } else {
+                        String uri = intent.getDataString();
+                        Tabs.getInstance().loadUrl(uri, Tabs.LOADURL_NEW_TAB |
+                                                                Tabs.LOADURL_USER_ENTERED |
+                                                                Tabs.LOADURL_EXTERNAL);
+                    }
+                }
+            });
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri));
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
         } else if (ACTION_ALERT_CALLBACK.equals(action)) {
             processAlertCallback(intent);
--- a/mobile/android/base/tabqueue/TabQueueDispatcher.java
+++ b/mobile/android/base/tabqueue/TabQueueDispatcher.java
@@ -2,17 +2,19 @@
  * 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/. */
 
 package org.mozilla.gecko.tabqueue;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.Locales;
+import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.sync.setup.activities.WebURLFinder;
 
 import android.content.Intent;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -46,17 +48,23 @@ public class TabQueueDispatcher extends 
 
         // TODO: This code is shared with ShareDialog - we should extract this to a helper class.
         final String pageUrl = new WebURLFinder(dataString).bestWebURL();
         if (TextUtils.isEmpty(pageUrl)) {
             abortDueToNoURL(dataString);
             return;
         }
 
-        showToast(intent);
+        boolean shouldShowOpenInBackgroundToast = GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_TAB_QUEUE, false);
+
+        if (shouldShowOpenInBackgroundToast) {
+            showToast(intent);
+        } else {
+            loadNormally(intent);
+        }
     }
 
     private void showToast(Intent intent) {
         intent.setClass(getApplicationContext(), TabQueueService.class);
         startService(intent);
         finish();
     }
 
--- a/mobile/android/base/tabqueue/TabQueueHelper.java
+++ b/mobile/android/base/tabqueue/TabQueueHelper.java
@@ -106,17 +106,17 @@ public class TabQueueHelper {
     public static int getTabQueueLength(final Context context) {
         ThreadUtils.assertNotOnUiThread();
 
         // TODO: Use profile shared prefs when bug 1147925 gets fixed.
         final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
         return prefs.getInt(PREF_TAB_QUEUE_COUNT, 0);
     }
 
-    public static void openQueuedUrls(final Context context, final GeckoProfile profile, final String filename) {
+    public static void openQueuedUrls(final Context context, final GeckoProfile profile, final String filename, boolean shouldPerformJavaScriptCallback) {
         ThreadUtils.assertNotOnUiThread();
 
         // Remove the notification.
         NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.cancel(TAB_QUEUE_NOTIFICATION_ID);
 
         // exit early if we don't have any tabs queued
         if (getTabQueueLength(context) < 1) {
@@ -124,16 +124,17 @@ public class TabQueueHelper {
         }
 
         JSONArray jsonArray = profile.readJSONArrayFromFile(filename);
 
         if (jsonArray.length() > 0) {
             JSONObject data = new JSONObject();
             try {
                 data.put("urls", jsonArray);
+                data.put("shouldNotifyTabsOpenedToJava", shouldPerformJavaScriptCallback);
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tabs:OpenMultiple", data.toString()));
             } catch (JSONException e) {
                 // Don't exit early as we perform cleanup at the end of this function.
                 Log.e(LOGTAG, "Error sending tab queue data", e);
             }
         }
 
         try {
--- a/mobile/android/components/PromptService.js
+++ b/mobile/android/components/PromptService.js
@@ -613,17 +613,17 @@ let PromptUtils = {
     return canSave;
   },
 
   getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) {
     let checkLabel = null;
     let check = { value: false };
     let selectedLogin;
 
-    checkLabel = this.getLocaleString("saveButton", "passwdmgr");
+    checkLabel = this.getLocaleString("rememberButton", "passwdmgr");
 
     // XXX Like the original code, we can't deal with multiple
     // account selection. (bug 227632)
     if (aFoundLogins.length > 0) {
       selectedLogin = aFoundLogins[0];
 
       // If the caller provided a username, try to use it. If they
       // provided only a password, this will try to find a password-only
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -155,17 +155,24 @@ SessionStore.prototype = {
         } else {
           // Not doing a restore; just send restore message
           Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
         }
         break;
       }
       case "Tabs:OpenMultiple": {
         let data = JSON.parse(aData);
+
         this._openTabs(data);
+
+        if (data.shouldNotifyTabsOpenedToJava) {
+          Messaging.sendRequest({
+            type: "Tabs:TabsOpened"
+          });
+        }
         break;
       }
       case "application-background":
         // We receive this notification when Android's onPause callback is
         // executed. After onPause, the application may be terminated at any
         // point without notice; therefore, we must synchronously write out any
         // pending save state to ensure that this data does not get lost.
         this.flushPendingState();
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -1,16 +1,16 @@
 # 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/.
 
 MOZ_APP_BASENAME=Fennec
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=39.0a1
+MOZ_APP_VERSION=40.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 # We support Android SDK version 9 and up by default.
 # See the --enable-android-min-sdk and --enable-android-max-sdk arguments in configure.in.
--- a/netwerk/protocol/about/nsIAboutModule.idl
+++ b/netwerk/protocol/about/nsIAboutModule.idl
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIChannel;
 interface nsILoadInfo;
 
-[scriptable, uuid(cdef9e4e-b731-450c-871a-8015a95fa54c)]
+[scriptable, uuid(c0c19db9-1b5a-4ac5-b656-ed6f8149fa48)]
 interface nsIAboutModule : nsISupports 
 {
 
     /**
      * Constructs a new channel for the about protocol module. 
      *
      * @param aURI the uri of the new channel
      * @param aLoadInfo the loadinfo of the new channel
--- a/services/sync/Makefile.in
+++ b/services/sync/Makefile.in
@@ -1,14 +1,14 @@
 # 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/.
 
 # Definitions used by constants.js.
-weave_version := 1.41.0
+weave_version := 1.42.0
 weave_id      := {340c2bbc-ce74-4362-90b5-7c26312808ef}
 
 # Preprocess files.
 SYNC_PP := modules/constants.js
 SYNC_PP_FLAGS := \
  -Dweave_version=$(weave_version) \
  -Dweave_id='$(weave_id)'
 SYNC_PP_PATH = $(FINAL_TARGET)/modules/services-sync
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -33,15 +33,17 @@ skip-if = android_version == "18"
 # Bug 1144395: crash on Android 4.3
 skip-if = android_version == "18"
 [test_TelemetryPing_idle.js]
 [test_TelemetryStopwatch.js]
 [test_TelemetryPingBuildID.js]
 # Bug 1144395: crash on Android 4.3
 skip-if = android_version == "18"
 [test_ThirdPartyCookieProbe.js]
+skip-if = true # Bug 1149284
 [test_TelemetrySendOldPings.js]
 skip-if = debug == true || os == "android" # Disabled due to intermittent orange on Android
 [test_TelemetrySession.js]
 # Bug 1144395: crash on Android 4.3
-skip-if = android_version == "18"
+#skip-if = android_version == "18"
+skip-if = true # Bug 1149284
 [test_ThreadHangStats.js]
 run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
--- a/toolkit/locales/en-US/chrome/global/aboutReader.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutReader.properties
@@ -26,10 +26,11 @@ aboutReader.toolbar.openReadingList=Open
 aboutReader.toolbar.closeReadingList=Close Reading List
 aboutReader.toolbar.share=Share
 
 aboutReader.footer.deleteThisArticle=Delete this article
 
 # These are used for the Reader View toolbar button and the menuitem within the
 # View menu.
 readerView.enter=Enter Reader View
+readerView.enter.accesskey=R
 readerView.close=Close Reader View
-readerView.accesskey=R
+readerView.close.accesskey=R
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -16,17 +16,17 @@ namespace mozilla {
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module
 {
-  static const unsigned int kVersion = 39;
+  static const unsigned int kVersion = 40;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
     const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
                                          const nsIID& aIID,