Bug 1014185 - Remove about:customizing and use about:blank for customize mode instead. r=jaws
authorDão Gottwald <dao@mozilla.com>
Sat, 20 Feb 2016 14:03:25 +0100
changeset 284968 48a7a7615da812d4adca2d9aa4e9a10ac259bebc
parent 284967 62cc4b503870cf7be4ae0c530a3d7ab99c8585b9
child 284969 ca83a163091aff314159011e639520f25daa3a99
push id30016
push userphilringnalda@gmail.com
push dateSun, 21 Feb 2016 01:25:37 +0000
treeherdermozilla-central@889096db9c1b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1014185
milestone47.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 1014185 - Remove about:customizing and use about:blank for customize mode instead. r=jaws
browser/base/content/browser-social.js
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
browser/components/about/AboutRedirector.cpp
browser/components/build/nsModule.cpp
browser/components/customizableui/CustomizeMode.jsm
browser/components/customizableui/content/aboutCustomizing.xul
browser/components/customizableui/content/jar.mn
browser/components/customizableui/content/panelUI.js
browser/components/customizableui/test/browser.ini
browser/components/customizableui/test/browser_889120_customize_tab_merging.js
browser/components/customizableui/test/head.js
browser/components/nsBrowserGlue.js
browser/components/sessionstore/SessionStore.jsm
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/CustomizationTabPreloader.jsm
browser/modules/moz.build
dom/plugins/test/mochitest/browser.ini
dom/plugins/test/mochitest/browser_bug1163570.js
testing/mochitest/browser-test.js
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -254,18 +254,16 @@ SocialUI = {
         node.setAttribute("disabled", "true")
       }
     }
   },
 
   // called on tab/urlbar/location changes and after customization. Update
   // anything that is tab specific.
   updateState: function() {
-    if (location == "about:customizing")
-      return;
     goSetCommandEnabled("Social:PageShareOrMark", this.canShareOrMarkPage(gBrowser.currentURI));
     if (!SocialUI.enabled)
       return;
     // larger update that may change button icons
     SocialMarks.update();
   }
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -178,19 +178,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor",
   "resource:///modules/ProcessHangMonitor.jsm");
 
 if (AppConstants.MOZ_SAFE_BROWSING) {
   XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
     "resource://gre/modules/SafeBrowsing.jsm");
 }
 
-XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader",
-  "resource:///modules/CustomizationTabPreloader.jsm", "CustomizationTabPreloader");
-
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Translation",
   "resource:///modules/translation/Translation.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions",
   "resource:///modules/SitePermissions.jsm");
@@ -4336,17 +4333,17 @@ var XULBrowserWindow = {
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         BookmarkingUI.onLocationChange();
-        SocialUI.updateState(location);
+        SocialUI.updateState();
         UITour.onLocationChange(location);
         gTabletModePageCounter.inc();
       }
 
       // Utility functions for disabling find
       var shouldDisableFind = function shouldDisableFind(aDocument) {
         let docElt = aDocument.documentElement;
         return docElt && docElt.getAttribute("disablefastfind") == "true";
@@ -4384,22 +4381,21 @@ var XULBrowserWindow = {
             content.document.addEventListener("readystatechange", onContentRSChange);
           }
         }
       } else
         disableFindCommands(false);
 
       // Try not to instantiate gCustomizeMode as much as possible,
       // so don't use CustomizeMode.jsm to check for URI or customizing.
-      let customizingURI = "about:customizing";
-      if (location == customizingURI) {
+      if (location == "about:blank" &&
+          gBrowser.selectedTab.hasAttribute("customizemode")) {
         gCustomizeMode.enter();
-      } else if (location != customizingURI &&
-                 (CustomizationHandler.isEnteringCustomizeMode ||
-                  CustomizationHandler.isCustomizing())) {
+      } else if (CustomizationHandler.isEnteringCustomizeMode ||
+                 CustomizationHandler.isCustomizing()) {
         gCustomizeMode.exit();
       }
     }
     UpdateBackForwardCommands(gBrowser.webNavigation);
     ReaderParent.updateReaderButton(gBrowser.selectedBrowser);
 
     gGestureSupport.restoreRotationState();
 
@@ -6387,16 +6383,19 @@ function undoCloseWindow(aIndex) {
 /*
  * Determines if a tab is "empty", usually used in the context of determining
  * if it's ok to close the tab.
  */
 function isTabEmpty(aTab) {
   if (aTab.hasAttribute("busy"))
     return false;
 
+  if (aTab.hasAttribute("customizemode"))
+    return false;
+
   let browser = aTab.linkedBrowser;
   if (!isBlankPageURL(browser.currentURI.spec))
     return false;
 
   if (!checkEmptyPageOrigin(browser))
     return false;
 
   if (browser.canGoForward || browser.canGoBack)
@@ -7339,17 +7338,16 @@ var gRemoteTabsUI = {
  *        the one from the new URI.
  * @return True if an existing tab was found, false otherwise
  */
 function switchToTabHavingURI(aURI, aOpenNew, aOpenParams={}) {
   // Certain URLs can be switched to irrespective of the source or destination
   // window being in private browsing mode:
   const kPrivateBrowsingWhitelist = new Set([
     "about:addons",
-    "about:customizing",
   ]);
 
   let ignoreFragment = aOpenParams.ignoreFragment;
   let ignoreQueryString = aOpenParams.ignoreQueryString;
   let replaceQueryString = aOpenParams.replaceQueryString;
 
   // These properties are only used by switchToTabHavingURI and should
   // not be used as a parameter for the new load.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1393,16 +1393,21 @@
                   var characterSet = browser.characterSet;
                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
                                                  .getService(Components.interfaces.nsITextToSubURI);
                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
                 } catch(ex) { /* Do nothing. */ }
 
                 crop = "center";
 
+              } else if (aTab.hasAttribute("customizemode")) {
+                let brandBundle = document.getElementById("bundle_brand");
+                let brandShortName = brandBundle.getString("brandShortName");
+                title = gNavigatorBundle.getFormattedString("customizeMode.tabTitle",
+                                                            [ brandShortName ]);
               } else // Still no title?  Fall back to our untitled string.
                 title = this.mStringBundle.getString("tabs.emptyTabTitle");
             }
 
             if (aTab.label == title &&
                 aTab.crop == crop)
               return false;
 
@@ -1921,21 +1926,16 @@
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this._tabListeners.set(t, tabListener);
             this._tabFilters.set(t, filter);
 
             b.droppedLinkHandler = handleDroppedLink;
 
-            // Swap in a preloaded customize tab, if available.
-            if (aURI == "about:customizing") {
-              usingPreloadedContent = gCustomizationTabPreloader.newTab(t);
-            }
-
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
             // If we didn't swap docShells with a preloaded browser
@@ -2469,43 +2469,16 @@
               } while (tab && remainingTabs.indexOf(tab) == -1);
             }
 
             this.selectedTab = tab;
           ]]>
         </body>
       </method>
 
-      <method name="swapNewTabWithBrowser">
-        <parameter name="aNewTab"/>
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            // The browser must be standalone.
-            if (aBrowser.getTabBrowser())
-              throw Cr.NS_ERROR_INVALID_ARG;
-
-            // The tab is definitely not loading.
-            aNewTab.removeAttribute("busy");
-            if (aNewTab.selected) {
-              this.mIsBusy = false;
-            }
-
-            this._swapBrowserDocShells(aNewTab, aBrowser);
-
-            // Update the new tab's title.
-            this.setTabTitle(aNewTab);
-
-            if (aNewTab.selected) {
-              this.updateCurrentBrowser(true);
-            }
-          ]]>
-        </body>
-      </method>
-
       <method name="swapBrowsersAndCloseOther">
         <parameter name="aOurTab"/>
         <parameter name="aOtherTab"/>
         <body>
           <![CDATA[
             // Do not allow transfering a private tab to a non-private window
             // and vice versa.
             if (PrivateBrowsingUtils.isWindowPrivate(window) !=
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -94,18 +94,16 @@ static RedirEntry kRedirMap[] = {
   { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #ifdef MOZ_SERVICES_HEALTHREPORT
   { "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
-  { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
-    nsIAboutModule::ALLOW_SCRIPT },
   { "loopconversation", "chrome://loop/content/panels/conversation.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::ENABLE_INDEXED_DB },
   { "looppanel", "chrome://loop/content/panels/panel.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -102,17 +102,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -6,17 +6,16 @@
 
 this.EXPORTED_SYMBOLS = ["CustomizeMode"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 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 kMaxTransitionDurationMs = 2000;
 
 const kPanelItemContextMenu = "customizationPanelItemContextMenu";
@@ -31,16 +30,18 @@ Cu.import("resource://gre/modules/AddonM
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DragPositionManager",
                                   "resource:///modules/DragPositionManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
                                   "resource:///modules/BrowserUITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
+                                  "resource:///modules/sessionstore/SessionStore.jsm");
 
 let gDebug;
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
   let ConsoleAPI = scope.ConsoleAPI;
   try {
     gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
@@ -51,16 +52,33 @@ XPCOMUtils.defineLazyGetter(this, "log",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
 var gDisableAnimation = null;
 
 var gDraggingInToolbars;
 
+var gTab;
+
+function closeGlobalTab() {
+  let win = gTab.ownerGlobal;
+  if (win.gBrowser.browsers.length == 1) {
+    win.BrowserOpenTab();
+  }
+  win.gBrowser.removeTab(gTab);
+}
+
+function unregisterGlobalTab() {
+  gTab.removeEventListener("TabClose", unregisterGlobalTab);
+  gTab.ownerGlobal.removeEventListener("unload", unregisterGlobalTab);
+  gTab.removeAttribute("customizemode");
+  gTab = null;
+}
+
 function CustomizeMode(aWindow) {
   if (gDisableAnimation === null) {
     gDisableAnimation = Services.prefs.getPrefType(kPrefCustomizationAnimation) == Ci.nsIPrefBranch.PREF_BOOL &&
                         Services.prefs.getBoolPref(kPrefCustomizationAnimation);
   }
   this.window = aWindow;
   this.document = aWindow.document;
   this.browser = aWindow.gBrowser;
@@ -135,37 +153,72 @@ CustomizeMode.prototype = {
    let lwthemeIcon = aDocument.getAnonymousElementByAttribute(lwthemeButton,
           "class", "button-icon");
    let imageURL = LightweightThemeManager.currentTheme === null ?
           "chrome://browser/skin/theme-switcher-icon.png" :
           LightweightThemeManager.currentTheme.iconURL;
     lwthemeIcon.style.backgroundImage = "url(" + imageURL + ")";
   },
 
+  setTab: function(aTab) {
+    if (gTab == aTab) {
+      return;
+    }
+
+    if (gTab) {
+      closeGlobalTab();
+    }
+
+    gTab = aTab;
+
+    gTab.setAttribute("customizemode", "true");
+    SessionStore.persistTabAttribute("customizemode");
+
+    gTab.linkedBrowser.stop();
+
+    let win = gTab.ownerGlobal;
+
+    win.gBrowser.setTabTitle(gTab);
+    win.gBrowser.setIcon(gTab,
+                         "chrome://browser/skin/customizableui/customizeFavicon.ico");
+
+    gTab.addEventListener("TabClose", unregisterGlobalTab);
+    win.addEventListener("unload", unregisterGlobalTab);
+
+    if (gTab.selected) {
+      win.gCustomizeMode.enter();
+    }
+  },
+
   enter: function() {
     this._wantToBeInCustomizeMode = true;
 
     if (this._customizing || this._handler.isEnteringCustomizeMode) {
       return;
     }
 
     // Exiting; want to re-enter once we've done that.
     if (this._handler.isExitingCustomizeMode) {
       log.debug("Attempted to enter while we're in the middle of exiting. " +
                 "We'll exit after we've entered");
       return;
     }
 
-
-    // We don't need to switch to kAboutURI, or open a new tab at
-    // kAboutURI if we're already on it.
-    if (this.browser.selectedBrowser.currentURI.spec != kAboutURI) {
-      this.window.switchToTabHavingURI(kAboutURI, true, {
-        skipTabAnimation: true,
-      });
+    if (!gTab) {
+      this.setTab(this.browser.loadOneTab("about:blank",
+                                          { inBackground: false,
+                                            forceNotRemote: true,
+                                            skipAnimation: true }));
+      return;
+    }
+    if (!gTab.selected) {
+      gTab.ownerGlobal.gBrowser.selectedTab = gTab;
+    }
+    gTab.ownerGlobal.focus();
+    if (gTab.ownerDocument != this.document) {
       return;
     }
 
     let window = this.window;
     let document = this.document;
 
     this._handler.isEnteringCustomizeMode = true;
 
@@ -308,20 +361,16 @@ CustomizeMode.prototype = {
 
       CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
       this._enableOutlinesTimeout = window.setTimeout(() => {
         this.document.getElementById("nav-bar").setAttribute("showoutline", "true");
         this.panelUIContents.setAttribute("showoutline", "true");
         delete this._enableOutlinesTimeout;
       }, 0);
 
-      // It's possible that we didn't enter customize mode via the menu panel,
-      // meaning we didn't kick off about:customizing preloading. If that's
-      // the case, let's kick it off for the next time we load this mode.
-      window.gCustomizationTabPreloader.ensurePreloading();
       if (!this._wantToBeInCustomizeMode) {
         this.exit();
       }
     }.bind(this)).then(null, function(e) {
       log.error("Error entering customize mode", e);
       // We should ensure this has been called, and calling it again doesn't hurt:
       window.PanelUI.endBatchUpdate();
       this._handler.isEnteringCustomizeMode = false;
@@ -397,41 +446,24 @@ CustomizeMode.prototype = {
     Task.spawn(function*() {
       yield this.depopulatePalette();
 
       yield this._doTransition(false);
       this.removeLWTStyling();
 
       Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
 
-      let browser = document.getElementById("browser");
-      if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
-        let custBrowser = this.browser.selectedBrowser;
-        if (custBrowser.canGoBack) {
-          // If there's history to this tab, just go back.
-          // Note that this throws an exception if the previous document has a
-          // problematic URL (e.g. about:idontexist)
-          try {
-            custBrowser.goBack();
-          } catch (ex) {
-            log.error(ex);
-          }
+      if (this.browser.selectedTab == gTab) {
+        if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
+          closeGlobalTab();
         } else {
-          // If we can't go back, we're removing the about:customization tab.
-          // We only do this if we're the top window for this window (so not
-          // a dialog window, for example).
-          if (window.getTopWin(true) == window) {
-            let customizationTab = this.browser.selectedTab;
-            if (this.browser.browsers.length == 1) {
-              window.BrowserOpenTab();
-            }
-            this.browser.removeTab(customizationTab);
-          }
+          unregisterGlobalTab();
         }
       }
+      let browser = document.getElementById("browser");
       browser.parentNode.selectedPanel = browser;
       let customizer = document.getElementById("customization-container");
       customizer.hidden = true;
 
       window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
 
       DragPositionManager.stop();
       this._removeDragHandlers(this.visiblePalette);
deleted file mode 100644
--- a/browser/components/customizableui/content/aboutCustomizing.xul
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- 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/. -->
-
-<!DOCTYPE window [
-  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
-  %brandDTD;
-  <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
-  %browserDTD;
-]>
-
-<window id="aboutCustomizingWindow"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        xmlns:html="http://www.w3.org/1999/xhtml"
-        title="&customizeMode.tabTitle;">
-  <html:head>
-    <html:link rel="icon" type="image/x-icon"
-               href="chrome://browser/skin/customizableui/customizeFavicon.ico"/>
-  </html:head>
-</window>
--- a/browser/components/customizableui/content/jar.mn
+++ b/browser/components/customizableui/content/jar.mn
@@ -1,11 +1,10 @@
 # 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/.
 
 browser.jar:
-  content/browser/customizableui/aboutCustomizing.xul
   content/browser/customizableui/panelUI.css
   content/browser/customizableui/panelUI.js
   content/browser/customizableui/panelUI.xml
   content/browser/customizableui/toolbar.xml
 
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -152,20 +152,16 @@ const PanelUI = {
           aEvent.type == "command") {
         anchor = this.menuButton;
       } else {
         anchor = aEvent.target;
       }
 
       this.panel.addEventListener("popupshown", function onPopupShown() {
         this.removeEventListener("popupshown", onPopupShown);
-        // As an optimization for the customize mode transition, we preload
-        // about:customizing in the background once the menu panel is first
-        // shown.
-        gCustomizationTabPreloader.ensurePreloading();
         deferred.resolve();
       });
 
       let iconAnchor =
         document.getAnonymousElementByAttribute(anchor, "class",
                                                 "toolbarbutton-icon");
       this.panel.openPopup(iconAnchor || anchor);
     }, (reason) => {
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -20,17 +20,16 @@ skip-if = os == "linux"
 [browser_885052_customize_mode_observers_disabed.js]
 # Bug 951403 - Disabled on OSX for frequent failures
 skip-if = os == "mac"
 
 [browser_885530_showInPrivateBrowsing.js]
 [browser_886323_buildArea_removable_nodes.js]
 [browser_887438_currentset_shim.js]
 [browser_888817_currentset_updating.js]
-[browser_889120_customize_tab_merging.js]
 [browser_890140_orphaned_placeholders.js]
 [browser_890262_destroyWidget_after_add_to_panel.js]
 [browser_892955_isWidgetRemovable_for_removed_widgets.js]
 [browser_892956_destroyWidget_defaultPlacements.js]
 [browser_909779_overflow_toolbars_new_window.js]
 skip-if = os == "linux"
 
 [browser_901207_searchbar_in_panel.js]
deleted file mode 100644
--- a/browser/components/customizableui/test/browser_889120_customize_tab_merging.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* 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 kTestToolbarId = "test-empty-drag";
-
-// Attempting to switch quickly from one tab to another to see whether the state changes
-// correctly.
-add_task(function* CheckBasicCustomizeMode() {
-  yield startCustomizing();
-  ok(CustomizationHandler.isCustomizing(), "We should be in customize mode");
-  yield endCustomizing();
-  ok(!CustomizationHandler.isCustomizing(), "We should not be in customize mode");
-});
-add_task(function* CheckQuickCustomizeModeSwitch() {
-  let tab1 = gBrowser.addTab("about:newtab");
-  gBrowser.selectedTab = tab1;
-  let tab2 = gBrowser.addTab("about:customizing");
-  let tab3 = gBrowser.addTab("about:newtab");
-  gBrowser.selectedTab = tab2;
-  try {
-    yield waitForCondition(() => CustomizationHandler.isEnteringCustomizeMode);
-  } catch (ex) {
-    Cu.reportError(ex);
-  }
-  ok(CustomizationHandler.isEnteringCustomizeMode, "Should be entering customize mode");
-  gBrowser.selectedTab = tab3;
-  try {
-    yield waitForCondition(() => !CustomizationHandler.isEnteringCustomizeMode && !CustomizationHandler.isCustomizing());
-  } catch (ex) {
-    Cu.reportError(ex);
-  }
-  ok(!CustomizationHandler.isCustomizing(), "Should not be entering customize mode");
-  gBrowser.removeTab(tab1);
-  gBrowser.removeTab(tab2);
-  gBrowser.removeTab(tab3);
-});
-
-add_task(function* asyncCleanup() {
-  yield endCustomizing();
-});
-
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -194,27 +194,27 @@ function endCustomizing(aWindow=window) 
   return deferredEndCustomizing.promise.then(function() {
     let deferredLoadNewTab = Promise.defer();
 
     //XXXgijs so some tests depend on this tab being about:blank. Make it so.
     let newTabBrowser = aWindow.gBrowser.selectedBrowser;
     newTabBrowser.stop();
 
     // If we stop early enough, this might actually be about:blank.
-    if (newTabBrowser.contentDocument.location.href == "about:blank") {
+    if (newTabBrowser.currentURI.spec == "about:blank") {
       return null;
     }
 
     // Otherwise, make it be about:blank, and wait for that to be done.
     function onNewTabLoaded(e) {
       newTabBrowser.removeEventListener("load", onNewTabLoaded, true);
       deferredLoadNewTab.resolve();
     }
     newTabBrowser.addEventListener("load", onNewTabLoaded, true);
-    newTabBrowser.contentDocument.location.replace("about:blank");
+    newTabBrowser.loadURI("about:blank");
     return deferredLoadNewTab.promise;
   });
 }
 
 function startCustomizing(aWindow=window) {
   if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
     return null;
   }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -53,19 +53,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/BookmarkJSONUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
                                   "resource:///modules/WebappManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
                                   "resource://gre/modules/PageThumbs.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
-                                  "resource:///modules/CustomizationTabPreloader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
                                   "resource://pdf.js/PdfJs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor",
                                   "resource:///modules/ProcessHangMonitor.jsm");
 
 if (AppConstants.NIGHTLY_BUILD) {
   XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
@@ -1067,17 +1064,16 @@ BrowserGlue.prototype = {
                          .getService(Ci.nsIAppStartup);
       appStartup.trackStartupCrashEnd();
     } catch (e) {
       Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
     }
 
     SelfSupportBackend.uninit();
 
-    CustomizationTabPreloader.uninit();
     WebappManager.uninit();
 
     NewTabPrefsProvider.prefs.uninit();
     AboutNewTab.uninit();
     webrtcUI.uninit();
     FormValidationHandler.uninit();
     if (AppConstants.NIGHTLY_BUILD) {
       AddonWatcher.uninit();
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -796,16 +796,18 @@ var SessionStoreInternal = {
         if (activePageData) {
           if (activePageData.title) {
             tab.label = activePageData.title;
             tab.crop = "end";
           } else if (activePageData.url != "about:blank") {
             tab.label = activePageData.url;
             tab.crop = "center";
           }
+        } else if (tab.hasAttribute("customizemode")) {
+          win.gCustomizeMode.setTab(tab);
         }
 
         // Restore the tab icon.
         if ("image" in tabData) {
           // Using null as the loadingPrincipal because serializing
           // the principal would be overkill. Within SetIcon we
           // default to the systemPrincipal if aLoadingPrincipal is
           // null which will allow the favicon to load.
@@ -3294,16 +3296,20 @@ var SessionStoreInternal = {
    * Kicks off restoring the given tab.
    *
    * @param aTab
    *        the tab to restore
    * @param aLoadArguments
    *        optional load arguments used for loadURI()
    */
   restoreTabContent: function (aTab, aLoadArguments = null) {
+    if (aTab.hasAttribute("customizemode")) {
+      return;
+    }
+
     let browser = aTab.linkedBrowser;
     let window = aTab.ownerDocument.defaultView;
     let tabbrowser = window.gBrowser;
     let tabData = TabState.clone(aTab);
     let activeIndex = tabData.index - 1;
     let activePageData = tabData.entries[activeIndex] || null;
     let uri = activePageData ? activePageData.url || null : null;
     if (aLoadArguments) {
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -839,17 +839,16 @@ you can use these alternative items. Oth
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
 <!ENTITY social.directory.label "Activations Directory">
 <!ENTITY social.directory.text "You can activate Share services from the directory.">
 <!ENTITY social.directory.button "Take me there!">
 <!ENTITY social.directory.introText "Click on a service to add it to &brandShortName;.">
 <!ENTITY social.directory.viewmore.text "View More">
 
-<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
 <!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
 <!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
 <!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
 <!ENTITY customizeMode.restoreDefaults "Restore Defaults">
 <!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
 <!ENTITY customizeMode.titlebar "Title Bar">
 <!ENTITY customizeMode.lwthemes "Themes">
 <!ENTITY customizeMode.lwthemes.myThemes "My Themes">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -684,16 +684,19 @@ flashHang.helpButton.accesskey = L
 
 # LOCALIZATION NOTE(customizeTips.tip0): %1$S will be replaced with the text defined
 # in customizeTips.tip0.hint, %2$S will be replaced with brandShortName, %3$S will
 # be replaced with a hyperlink containing the text defined in customizeTips.tip0.learnMore.
 customizeTips.tip0 = %1$S: You can customize %2$S to work the way you do. Simply drag any of the above to the menu or toolbar. %3$S about customizing %2$S.
 customizeTips.tip0.hint = Hint
 customizeTips.tip0.learnMore = Learn more
 
+# LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
+customizeMode.tabTitle = Customize %S
+
 # LOCALIZATION NOTE(appmenu.*.description, appmenu.*.label): these are used for
 # the appmenu labels and buttons that appear when an update is staged for
 # installation or a background update has failed and a manual download is required.
 # %S is brandShortName
 appmenu.restartNeeded.description = Restart %S to apply updates
 appmenu.updateFailed.description = Background update failed, please download update
 appmenu.restartBrowserButton.label = Restart %S
 appmenu.downloadUpdateButton.label = Download Update
deleted file mode 100644
--- a/browser/modules/CustomizationTabPreloader.jsm
+++ /dev/null
@@ -1,173 +0,0 @@
-/* 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 = ["CustomizationTabPreloader"];
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "HiddenFrame",
-  "resource:///modules/HiddenFrame.jsm");
-
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const XUL_PAGE = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='win'/>";
-const CUSTOMIZATION_URL = "about:customizing";
-
-// The interval between swapping in a preload docShell and kicking off the
-// next preload in the background.
-const PRELOADER_INTERVAL_MS = 600;
-
-function createTimer(obj, delay) {
-  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  timer.init(obj, delay, Ci.nsITimer.TYPE_ONE_SHOT);
-  return timer;
-}
-
-function clearTimer(timer) {
-  if (timer) {
-    timer.cancel();
-  }
-  return null;
-}
-
-this.CustomizationTabPreloader = {
-  uninit: function () {
-    CustomizationTabPreloaderInternal.uninit();
-  },
-
-  newTab: function (aTab) {
-    return CustomizationTabPreloaderInternal.newTab(aTab);
-  },
-
-  /**
-   * ensurePreloading starts the preloading of the about:customizing
-   * content page. This function is idempotent (until a call to uninit),
-   * so multiple calls to it are fine.
-   */
-  ensurePreloading: function() {
-    CustomizationTabPreloaderInternal.ensurePreloading();
-  },
-};
-
-Object.freeze(CustomizationTabPreloader);
-
-this.CustomizationTabPreloaderInternal = {
-  _browser: null,
-
-  uninit: function () {
-    if (this._browser) {
-      this._browser.destroy();
-      this._browser = null;
-    }
-  },
-
-  newTab: function (aTab) {
-    let win = aTab.ownerDocument.defaultView;
-    if (win.gBrowser && this._browser) {
-      return this._browser.swapWithNewTab(aTab);
-    }
-
-    return false;
-  },
-
-  ensurePreloading: function () {
-    if (!this._browser) {
-      this._browser = new HiddenBrowser();
-    }
-  }
-};
-
-function HiddenBrowser() {
-  this._createBrowser();
-}
-
-HiddenBrowser.prototype = {
-  _timer: null,
-  _hiddenFrame: null,
-
-  get isPreloaded() {
-    return this._browser &&
-           this._browser.contentDocument &&
-           this._browser.contentDocument.readyState === "complete" &&
-           this._browser.currentURI.spec === CUSTOMIZATION_URL;
-  },
-
-  swapWithNewTab: function (aTab) {
-    if (!this.isPreloaded || this._timer) {
-      return false;
-    }
-
-    let win = aTab.ownerDocument.defaultView;
-    let tabbrowser = win.gBrowser;
-
-    if (!tabbrowser) {
-      return false;
-    }
-
-    // Swap docShells.
-    tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
-
-    // Load all default frame scripts attached to the target window.
-    let mm = aTab.linkedBrowser.messageManager;
-    let scripts = win.getGroupMessageManager("browsers").getDelayedFrameScripts();
-    Array.forEach(scripts, ([script, runGlobal]) => mm.loadFrameScript(script, true, runGlobal));
-
-    // Remove the browser, it will be recreated by a timer.
-    this._removeBrowser();
-
-    // Start a timer that will kick off preloading the next page.
-    this._timer = createTimer(this, PRELOADER_INTERVAL_MS);
-
-    // Signal that we swapped docShells.
-    return true;
-  },
-
-  observe: function () {
-    this._timer = null;
-
-    // Start pre-loading the customization page.
-    this._createBrowser();
-  },
-
-  destroy: function () {
-    this._removeBrowser();
-    if (this._hiddenFrame) {
-      this._hiddenFrame.destroy();
-      this._hiddenFrame = null;
-    }
-    this._timer = clearTimer(this._timer);
-  },
-
-  _createBrowser: function () {
-    if (!this._hiddenFrame) {
-      this._hiddenFrame = new HiddenFrame();
-    }
-
-    this._hiddenFrame.get().then(aFrame => {
-      let doc = aFrame.document;
-      this._browser = doc.createElementNS(XUL_NS, "browser");
-      this._browser.permanentKey = {};
-      this._browser.setAttribute("type", "content");
-      this._browser.setAttribute("src", CUSTOMIZATION_URL);
-      this._browser.style.width = "400px";
-      this._browser.style.height = "400px";
-      doc.getElementById("win").appendChild(this._browser);
-    });
-  },
-
-  _removeBrowser: function () {
-    if (this._browser) {
-      this._browser.remove();
-      this._browser = null;
-    }
-  }
-};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -17,17 +17,16 @@ EXTRA_JS_MODULES += [
     'CastingApps.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentObservers.jsm',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
-    'CustomizationTabPreloader.jsm',
     'DirectoryLinksProvider.jsm',
     'E10SUtils.jsm',
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'HiddenFrame.jsm',
     'LaterRun.jsm',
     'NetworkPrioritizer.jsm',
--- a/dom/plugins/test/mochitest/browser.ini
+++ b/dom/plugins/test/mochitest/browser.ini
@@ -1,15 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
   plugin_test.html
   plugin_subframe_test.html
   plugin_no_scroll_div.html
 
 [browser_bug1163570.js]
-skip-if = (!e10s || os != "win")
+skip-if = true # Bug 1249878
 [browser_bug1196539.js]
 skip-if = (!e10s || os != "win")
 [browser_tabswitchbetweenplugins.js]
 skip-if = (!e10s || os != "win")
 [browser_pluginscroll.js]
 skip-if = (true || !e10s || os != "win") # Bug 1213631
--- a/dom/plugins/test/mochitest/browser_bug1163570.js
+++ b/dom/plugins/test/mochitest/browser_bug1163570.js
@@ -34,85 +34,72 @@ function promiseWaitForEvent(object, eve
       resolve(event);
     }
     object.addEventListener(eventName, listener, capturing, chrome);
   });
 }
 
 add_task(function* () {
   registerCleanupFunction(function () {
-    Services.prefs.clearUserPref("browser.uiCustomization.disableAnimation");
     window.focus();
   });
 });
 
 add_task(function* () {
-  Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
 
   let pluginTab = gBrowser.selectedTab = gBrowser.addTab();
-  let customizeTab = gBrowser.addTab();
+  let prefTab = gBrowser.addTab();
 
   yield promiseTabLoad(pluginTab, gTestRoot + "plugin_test.html");
-  yield promiseTabLoad(customizeTab, "about:customizing");
+  yield promiseTabLoad(prefTab, "about:preferences");
 
   let result = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
     let doc = content.document;
     let plugin = doc.getElementById("testplugin");
     return !!plugin;
   });
 
   is(result, true, "plugin is loaded");
 
-  let cpromise = promiseWaitForEvent(window.gNavToolbox, "customizationready");
   let ppromise = promiseWaitForEvent(window, "MozAfterPaint");
-  gBrowser.selectedTab = customizeTab;
-  yield cpromise;
+  gBrowser.selectedTab = prefTab;
   yield ppromise;
 
   // We're going to switch tabs using actual mouse clicks, which helps
   // reproduce this bug.
   let tabStripContainer = document.getElementById("tabbrowser-tabs");
 
   // diagnosis if front end layout changes
   info("-> " + tabStripContainer.tagName); // tabs
   info("-> " + tabStripContainer.firstChild.tagName); // tab
   info("-> " + tabStripContainer.childNodes[0].label); // test harness tab
   info("-> " + tabStripContainer.childNodes[1].label); // plugin tab
-  info("-> " + tabStripContainer.childNodes[2].label); // customize tab
+  info("-> " + tabStripContainer.childNodes[2].label); // preferences tab
 
   for (let iteration = 0; iteration < 5; iteration++) {
-    cpromise = promiseWaitForEvent(window.gNavToolbox, "aftercustomization");
     ppromise = promiseWaitForEvent(window, "MozAfterPaint");
     EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[1], {}, window);
-    yield cpromise;
     yield ppromise;
 
     result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
       let doc = content.document;
       let plugin = doc.getElementById("testplugin");
       return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
     });
 
     is(result, true, "plugin is visible");
 
-    cpromise = promiseWaitForEvent(window.gNavToolbox, "customizationready");
     ppromise = promiseWaitForEvent(window, "MozAfterPaint");
     EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[2], {}, window);
-    yield cpromise;
     yield ppromise;
 
     result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
       let doc = content.document;
       let plugin = doc.getElementById("testplugin");
       return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
     });
     is(result, false, "plugin is hidden");
   }
 
-  // wait for customize view to shutdown cleanly otherwise we get
-  // a ton of error spew on shutdown.
-  cpromise = promiseWaitForEvent(window.gNavToolbox, "aftercustomization");
-  gBrowser.removeTab(customizeTab);
-  yield cpromise;
-
+  gBrowser.removeTab(prefTab);
   gBrowser.removeTab(pluginTab);
 });
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -3,19 +3,16 @@
 var gTimeoutSeconds = 45;
 var gConfig;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
-  "resource:///modules/CustomizationTabPreloader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
   "resource:///modules/ContentSearch.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SelfSupportBackend",
   "resource:///modules/SelfSupportBackend.jsm");
 
 const SIMPLETEST_OVERRIDES =
   ["ok", "is", "isnot", "todo", "todo_is", "todo_isnot", "info", "expectAssertions", "requestCompleteLog"];
@@ -614,17 +611,16 @@ Tester.prototype = {
 
             // Do the same for the social sidebar.
             let socialSidebar = document.getElementById("social-sidebar-browser");
             socialSidebar.setAttribute("src", "data:text/html;charset=utf-8,");
             socialSidebar.docShell.createAboutBlankContentViewer(null);
             socialSidebar.setAttribute("src", "about:blank");
 
             SelfSupportBackend.uninit();
-            CustomizationTabPreloader.uninit();
             SocialFlyout.unload();
             SocialShare.uninit();
           }
 
           // Destroy BackgroundPageThumbs resources.
           let {BackgroundPageThumbs} =
             Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", {});
           BackgroundPageThumbs._destroy();