merge autoland to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 02 Dec 2016 09:22:46 +0100
changeset 325064 b1e2d1fe6b070b71661047f94129a1ccd0a7f48f
parent 325035 6f39c69810f258b4108f8ee88048c5b690a503a2 (current diff)
parent 325063 cf2d377f4a5fc4f7b2402406908abd0ca571e3f8 (diff)
child 325092 f65ad27efe839ce9df0283840a1a40b4bbc9ead0
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
merge autoland to mozilla-central a=merge
devtools/client/styleeditor/utils.js
--- a/browser/components/customizableui/test/browser_967000_button_sync.js
+++ b/browser/components/customizableui/test/browser_967000_button_sync.js
@@ -37,18 +37,28 @@ let mockedInternal = {
   hasSyncedThisSession: false,
 };
 
 
 add_task(function* setup() {
   let oldInternal = SyncedTabs._internal;
   SyncedTabs._internal = mockedInternal;
 
+  // This test hacks some observer states to simulate a user being signed
+  // in to Sync - restore them when the test completes.
+  let initialObserverStates = {};
+  for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) {
+    initialObserverStates[id] = document.getElementById(id).hidden;
+  }
+
   registerCleanupFunction(() => {
     SyncedTabs._internal = oldInternal;
+    for (let [id, initial] of Object.entries(initialObserverStates)) {
+      document.getElementById(id).hidden = initial;
+    }
   });
 });
 
 // The test expects the about:preferences#sync page to open in the current tab
 function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
   info("Check Sync button functionality");
   Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
 
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -80,16 +80,17 @@ def rust_triple_alias(host_or_target):
     assert host_or_target in (host, target)
 
     @depends(rustc, host_or_target, when=rust_compiler)
     @imports('os')
     @imports('subprocess')
     @imports(_from='mozbuild.configure.util', _import='LineIO')
     @imports(_from='mozbuild.shellutil', _import='quote')
     @imports(_from='tempfile', _import='mkstemp')
+    @imports(_from='textwrap', _import='dedent')
     def rust_target(rustc, host_or_target):
         # Rust's --target options are similar to, but not exactly the same
         # as, the autoconf-derived targets we use.  An example would be that
         # Rust uses distinct target triples for targetting the GNU C++ ABI
         # and the MSVC C++ ABI on Win32, whereas autoconf has a single
         # triple and relies on the user to ensure that everything is
         # compiled for the appropriate ABI.  We need to perform appropriate
         # munging to get the correct option to rustc.
@@ -150,17 +151,23 @@ def rust_triple_alias(host_or_target):
             cmd = [
                 rustc,
                 '--crate-type', 'staticlib',
                 target_arg,
                 '-o', out_path,
                 in_path,
             ]
             def failed():
-                die('Cannot compile for {} with {}'.format(host_or_target.alias, rustc))
+                die(dedent('''\
+                Cannot compile for {} with {}
+                The target may be unsupported, or you may not have
+                a rust std library for that target installed. Try:
+
+                  rustup target add {}
+                '''.format(host_or_target.alias, rustc, rustc_target)))
             check_cmd_output(*cmd, onerror=failed)
             if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
                 failed()
         finally:
             os.remove(in_path)
             os.remove(out_path)
 
         # This target is usable.
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -313,24 +313,16 @@ DevTools.prototype = {
     // Reset the theme if an extension theme that's currently applied
     // is being removed.
     // Ignore shutdown since addons get disabled during that time.
     if (!Services.startup.shuttingDown &&
         !isCoreTheme &&
         theme.id == currTheme) {
       Services.prefs.setCharPref("devtools.theme", "light");
 
-      let data = {
-        pref: "devtools.theme",
-        newValue: "light",
-        oldValue: currTheme
-      };
-
-      this.emit("pref-changed", data);
-
       this.emit("theme-unregistered", theme);
     }
 
     this._themes.delete(themeId);
   },
 
   /**
    * Get a theme definition if it exists.
--- a/devtools/client/framework/test/browser_toolbox_options.js
+++ b/devtools/client/framework/test/browser_toolbox_options.js
@@ -7,16 +7,17 @@
 "use strict";
 
 // Tests that changing preferences in the options panel updates the prefs
 // and toggles appropriate things in the toolbox.
 
 var doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 
 add_task(function* () {
   const URL = "data:text/html;charset=utf8,test for dynamically registering " +
               "and unregistering tools";
   registerNewTool();
   let tab = yield addTab(URL);
   let target = TargetFactory.forTab(tab);
   toolbox = yield gDevTools.showToolbox(target);
@@ -146,60 +147,63 @@ function* testSelect(select) {
   is(select.options[select.selectedIndex].value, GetPref(pref),
     "select starts out selected");
 
   for (let option of options) {
     if (options.indexOf(option) === select.selectedIndex) {
       continue;
     }
 
+    let observer = new PrefObserver("devtools.");
+
     let deferred = defer();
-    gDevTools.once("pref-changed", (event, data) => {
-      if (data.pref == pref) {
-        ok(true, "Correct pref was changed");
-        is(GetPref(pref), option.value, "Preference been switched for " + pref);
-      } else {
-        ok(false, "Pref " + pref + " was not changed correctly");
-      }
+    let changeSeen = false;
+    observer.once(pref, () => {
+      changeSeen = true;
+      is(GetPref(pref), option.value, "Preference been switched for " + pref);
       deferred.resolve();
     });
 
     select.selectedIndex = options.indexOf(option);
     let changeEvent = new Event("change");
     select.dispatchEvent(changeEvent);
 
     yield deferred.promise;
+
+    ok(changeSeen, "Correct pref was changed");
+    observer.destroy();
   }
 }
 
 function* testMouseClick(node, prefValue) {
   let deferred = defer();
 
+  let observer = new PrefObserver("devtools.");
+
   let pref = node.getAttribute("data-pref");
-  gDevTools.once("pref-changed", (event, data) => {
-    if (data.pref == pref) {
-      ok(true, "Correct pref was changed");
-      is(data.oldValue, prefValue, "Previous value is correct for " + pref);
-      is(data.newValue, !prefValue, "New value is correct for " + pref);
-    } else {
-      ok(false, "Pref " + pref + " was not changed correctly");
-    }
+  let changeSeen = false;
+  observer.once(pref, () => {
+    changeSeen = true;
+    is(GetPref(pref), !prefValue, "New value is correct for " + pref);
     deferred.resolve();
   });
 
   node.scrollIntoView();
 
   // We use executeSoon here to ensure that the element is in view and
   // clickable.
   executeSoon(function () {
     info("Click event synthesized for pref " + pref);
     EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
   });
 
   yield deferred.promise;
+
+  ok(changeSeen, "Correct pref was changed");
+  observer.destroy();
 }
 
 function* testToggleTools() {
   let toolNodes = panelWin.document.querySelectorAll(
     "#default-tools-box input[type=checkbox]:not([data-unsupported])," +
     "#additional-tools-box input[type=checkbox]:not([data-unsupported])");
   let enabledTools = [...toolNodes].filter(node => node.checked);
 
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -270,17 +270,17 @@ OptionsPanel.prototype = {
 
     let createThemeOption = theme => {
       let inputLabel = this.panelDoc.createElement("label");
       let inputRadio = this.panelDoc.createElement("input");
       inputRadio.setAttribute("type", "radio");
       inputRadio.setAttribute("value", theme.id);
       inputRadio.setAttribute("name", "devtools-theme-item");
       inputRadio.addEventListener("change", function (e) {
-        setPrefAndEmit(themeBox.getAttribute("data-pref"),
+        SetPref(themeBox.getAttribute("data-pref"),
           e.target.value);
       });
 
       let inputSpanLabel = this.panelDoc.createElement("span");
       inputSpanLabel.textContent = theme.label;
       inputLabel.appendChild(inputRadio);
       inputLabel.appendChild(inputSpanLabel);
 
@@ -300,32 +300,32 @@ OptionsPanel.prototype = {
     let prefCheckboxes = this.panelDoc.querySelectorAll(
       "input[type=checkbox][data-pref]");
     for (let prefCheckbox of prefCheckboxes) {
       if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
         prefCheckbox.setAttribute("checked", true);
       }
       prefCheckbox.addEventListener("change", function (e) {
         let checkbox = e.target;
-        setPrefAndEmit(checkbox.getAttribute("data-pref"), checkbox.checked);
+        SetPref(checkbox.getAttribute("data-pref"), checkbox.checked);
       });
     }
     // Themes radio inputs are handled in setupThemeList
     let prefRadiogroups = this.panelDoc.querySelectorAll(
       ".radiogroup[data-pref]:not(#devtools-theme-box)");
     for (let radioGroup of prefRadiogroups) {
       let selectedValue = GetPref(radioGroup.getAttribute("data-pref"));
 
       for (let radioInput of radioGroup.querySelectorAll("input[type=radio]")) {
         if (radioInput.getAttribute("value") == selectedValue) {
           radioInput.setAttribute("checked", true);
         }
 
         radioInput.addEventListener("change", function (e) {
-          setPrefAndEmit(radioGroup.getAttribute("data-pref"),
+          SetPref(radioGroup.getAttribute("data-pref"),
             e.target.value);
         });
       }
     }
     let prefSelects = this.panelDoc.querySelectorAll("select[data-pref]");
     for (let prefSelect of prefSelects) {
       let pref = GetPref(prefSelect.getAttribute("data-pref"));
       let options = [...prefSelect.options];
@@ -335,17 +335,17 @@ OptionsPanel.prototype = {
         if (value == pref) {
           prefSelect.selectedIndex = options.indexOf(option);
           return true;
         }
       });
 
       prefSelect.addEventListener("change", function (e) {
         let select = e.target;
-        setPrefAndEmit(select.getAttribute("data-pref"),
+        SetPref(select.getAttribute("data-pref"),
           select.options[select.selectedIndex].value);
       });
     }
 
     if (this.target.activeTab) {
       return this.target.client.attachTab(this.target.activeTab._actor)
         .then(([response, client]) => {
           this._origJavascriptEnabled = !response.javascriptEnabled;
@@ -417,22 +417,8 @@ OptionsPanel.prototype = {
       deferred.resolve();
     }
 
     this.panelWin = this.panelDoc = this.disableJSNode = this.toolbox = null;
 
     return this.destroyPromise;
   }
 };
-
-/* Set a pref and emit the pref-changed event if needed. */
-function setPrefAndEmit(prefName, newValue) {
-  let data = {
-    pref: prefName,
-    newValue: newValue
-  };
-  data.oldValue = GetPref(data.pref);
-  SetPref(data.pref, data.newValue);
-
-  if (data.newValue != data.oldValue) {
-    gDevTools.emit("pref-changed", data);
-  }
-}
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -108,17 +108,19 @@ function Toolbox(target, selectedTool, h
   this._toggleAutohide = this._toggleAutohide.bind(this);
   this.showFramesMenu = this.showFramesMenu.bind(this);
   this._updateFrames = this._updateFrames.bind(this);
   this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
-  this._prefChanged = this._prefChanged.bind(this);
+  this._applyCacheSettings = this._applyCacheSettings.bind(this);
+  this._applyServiceWorkersTestingSettings =
+    this._applyServiceWorkersTestingSettings.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
   this._onFocus = this._onFocus.bind(this);
   this._onBrowserMessage = this._onBrowserMessage.bind(this);
   this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
   this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this);
   this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this);
   this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
   this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
@@ -362,17 +364,20 @@ Toolbox.prototype = {
       yield domReady.promise;
 
       this.isReady = true;
       let framesPromise = this._listFrames();
 
       this.closeButton = this.doc.getElementById("toolbox-close");
       this.closeButton.addEventListener("click", this.destroy, true);
 
-      gDevTools.on("pref-changed", this._prefChanged);
+      Services.prefs.addObserver("devtools.cache.disabled", this._applyCacheSettings,
+                                false);
+      Services.prefs.addObserver("devtools.serviceWorkers.testing.enabled",
+                                 this._applyServiceWorkersTestingSettings, false);
 
       let framesMenu = this.doc.getElementById("command-button-frames");
       framesMenu.addEventListener("click", this.showFramesMenu, false);
 
       let noautohideMenu = this.doc.getElementById("command-button-noautohide");
       noautohideMenu.addEventListener("click", this._toggleAutohide, true);
 
       this.textBoxContextMenuPopup =
@@ -480,39 +485,16 @@ Toolbox.prototype = {
     this._telemetry.logOncePerBrowserVersion(OS_HISTOGRAM, system.getOSCPU());
     this._telemetry.logOncePerBrowserVersion(OS_IS_64_BITS,
                                              Services.appinfo.is64Bit ? 1 : 0);
     this._telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM,
                                              system.getScreenDimensions());
     this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());
   },
 
-  /**
-   * Because our panels are lazy loaded this is a good place to watch for
-   * "pref-changed" events.
-   * @param  {String} event
-   *         The event type, "pref-changed".
-   * @param  {Object} data
-   *         {
-   *           newValue: The new value
-   *           oldValue:  The old value
-   *           pref: The name of the preference that has changed
-   *         }
-   */
-  _prefChanged: function (event, data) {
-    switch (data.pref) {
-      case "devtools.cache.disabled":
-        this._applyCacheSettings();
-        break;
-      case "devtools.serviceWorkers.testing.enabled":
-        this._applyServiceWorkersTestingSettings();
-        break;
-    }
-  },
-
   _buildOptions: function () {
     let selectOptions = (name, event) => {
       // Flip back to the last used panel if we are already
       // on the options panel.
       if (this.currentToolId === "options" &&
           gDevTools.getToolDefinition(this.lastUsedToolId)) {
         this.selectTool(this.lastUsedToolId);
       } else {
@@ -2209,17 +2191,19 @@ Toolbox.prototype = {
     this._target.off("frame-update", this._updateFrames);
     this.off("select", this._refreshHostTitle);
     this.off("host-changed", this._refreshHostTitle);
     this.off("ready", this._showDevEditionPromo);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
-    gDevTools.off("pref-changed", this._prefChanged);
+    Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings);
+    Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",
+                                  this._applyServiceWorkersTestingSettings);
 
     this._lastFocusedElement = null;
     if (this._sourceMapService) {
       this._sourceMapService.destroy();
       this._sourceMapService = null;
     }
 
     if (this.webconsolePanel) {
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -8,17 +8,17 @@
 
 const ToolDefinitions = require("devtools/client/definitions").Tools;
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {OutputParser} = require("devtools/client/shared/output-parser");
-const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
@@ -28,16 +28,18 @@ const TooltipsOverlay = require("devtool
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {BoxModelView} = require("devtools/client/inspector/components/box-model");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
+const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
+
 const FILTER_CHANGED_TIMEOUT = 150;
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 /**
  * Helper for long-running processes that should yield occasionally to
  * the mainloop.
  *
  * @param {Window} win
@@ -191,24 +193,23 @@ function CssComputedView(inspector, docu
   this.includeBrowserStylesCheckbox.addEventListener("input",
     this._onIncludeBrowserStyles);
 
   this.searchClearButton.hidden = true;
 
   // No results text.
   this.noResults = this.styleDocument.getElementById("computedview-no-results");
 
-  // Refresh panel when color unit changed.
+  // Refresh panel when color unit changed or pref for showing
+  // original sources changes.
   this._handlePrefChange = this._handlePrefChange.bind(this);
-  gDevTools.on("pref-changed", this._handlePrefChange);
-
-  // Refresh panel when pref for showing original sources changes
   this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
+  this._prefObserver.on("devtools.defaultColorUnit", this._handlePrefChange);
 
   // The element that we're inspecting, and the document that it comes from.
   this._viewedElement = null;
 
   this.createStyleViews();
 
   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
 
@@ -255,18 +256,17 @@ CssComputedView.prototype = {
     this.pageStyle = pageStyle;
   },
 
   get includeBrowserStyles() {
     return this.includeBrowserStylesCheckbox.checked;
   },
 
   _handlePrefChange: function (event, data) {
-    if (this._computed && (data.pref === "devtools.defaultColorUnit" ||
-        data.pref === PREF_ORIG_SOURCES)) {
+    if (this._computed) {
       this.refreshPanel();
     }
   },
 
   /**
    * Update the view with a new selected element. The CssComputedView panel
    * will show the style information for the given element.
    *
@@ -593,16 +593,17 @@ CssComputedView.prototype = {
   refreshSourceFilter: function () {
     this._matchedProperties = null;
     this._sourceFilter = this.includeBrowserStyles ?
                                  CssLogic.FILTER.UA :
                                  CssLogic.FILTER.USER;
   },
 
   _onSourcePrefChanged: function () {
+    this._handlePrefChange();
     for (let propView of this.propertyViews) {
       propView.updateSourceLinks();
     }
     this.inspector.emit("computed-view-sourcelinks-updated");
   },
 
   /**
    * The CSS as displayed by the UI.
@@ -727,19 +728,18 @@ CssComputedView.prototype = {
 
   /**
    * Destructor for CssComputedView.
    */
   destroy: function () {
     this._viewedElement = null;
     this._outputParser = null;
 
-    gDevTools.off("pref-changed", this._handlePrefChange);
-
     this._prefObserver.off(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
+    this._prefObserver.off("devtools.defaultColorUnit", this._handlePrefChange);
     this._prefObserver.destroy();
 
     // Cancel tree construction
     if (this._createViewsProcess) {
       this._createViewsProcess.cancel();
     }
     if (this._refreshProcess) {
       this._refreshProcess.cancel();
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -14,17 +14,17 @@ const EventEmitter = require("devtools/s
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const {PluralForm} = require("devtools/shared/plural-form");
 const {template} = require("devtools/shared/gcli/templater");
 const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
 const {UndoStack} = require("devtools/client/shared/undo");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
-const {PrefObserver} = require("devtools/client/styleeditor/utils");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 const HTMLEditor = require("devtools/client/inspector/markup/views/html-editor");
 const MarkupElementContainer = require("devtools/client/inspector/markup/views/element-container");
 const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/read-only-container");
 const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
 const RootContainer = require("devtools/client/inspector/markup/views/root-container");
 
 const INSPECTOR_L10N =
       new LocalizationHelper("devtools/client/locales/inspector.properties");
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -8,17 +8,17 @@
 
 const promise = require("promise");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {Tools} = require("devtools/client/definitions");
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {OutputParser} = require("devtools/client/shared/output-parser");
-const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 const {ElementStyle} = require("devtools/client/inspector/rules/models/element-style");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {RuleEditor} = require("devtools/client/inspector/rules/views/rule-editor");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
@@ -35,16 +35,17 @@ const clipboardHelper = require("devtool
 const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
 const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
       "devtools.inspector.mdnDocsTooltip.enabled";
 const FILTER_CHANGED_TIMEOUT = 150;
+const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
 // This is used to parse user input when filtering.
 const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/;
 // This is used to parse the filter search value to see if the filter
 // should be strict or not
 const FILTER_STRICT_RE = /\s*`(.*?)`\s*$/;
 
 /**
--- a/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-03.js
@@ -9,17 +9,17 @@
  * The desired behavior is:
  * - if the preference is true, show the "Show MDN Docs" context menu item
  * - if the preference is false, don't show the item
  * - listen for changes to the pref, so we can show/hide the item dynamically
  */
 
 "use strict";
 
-const { PrefObserver } = require("devtools/client/styleeditor/utils");
+const { PrefObserver } = require("devtools/client/shared/prefs");
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
   "devtools.inspector.mdnDocsTooltip.enabled";
 const PROPERTY_NAME_CLASS = "ruleview-propertyname";
 
 const TEST_DOC = `
   <html>
     <body>
       <div style="color: red">
--- a/devtools/client/inspector/rules/test/browser_rules_user-agent-styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_user-agent-styles.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Check that user agent styles are inspectable via rule view if
 // it is preffed on.
 
 var PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
-const { PrefObserver } = require("devtools/client/styleeditor/utils");
+const { PrefObserver } = require("devtools/client/shared/prefs");
 
 const TEST_URI = URL_ROOT + "doc_author-sheet.html";
 
 const TEST_DATA = [
   {
     selector: "blockquote",
     numUserRules: 1,
     numUARules: 0
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -1,17 +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/. */
 
 "use strict";
 
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const {PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {InplaceEditor, editableField, editableItem} =
       require("devtools/client/shared/inplace-editor");
 const {TextPropertyEditor} =
       require("devtools/client/inspector/rules/views/text-property-editor");
 const {
   createChild,
   blurOnMultipleProperties,
@@ -28,16 +27,18 @@ const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {Task} = require("devtools/shared/task");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
+const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
+
 /**
  * RuleEditor is responsible for the following:
  *   Owns a Rule object and creates a list of TextPropertyEditors
  *     for its TextProperties.
  *   Manages creation of new text properties.
  *
  * One step of a RuleEditor's instantiation is figuring out what's the original
  * source link to the parent stylesheet (in case of source maps). This step is
--- a/devtools/client/inspector/shared/style-inspector-menu.js
+++ b/devtools/client/inspector/shared/style-inspector-menu.js
@@ -1,17 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 {PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 const {
   VIEW_NODE_SELECTOR_TYPE,
@@ -23,16 +22,17 @@ const {
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
   "devtools.inspector.mdnDocsTooltip.enabled";
+const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
 /**
  * Style inspector context menu
  *
  * @param {RuleView|ComputedView} view
  *        RuleView or ComputedView instance controlling this menu
  * @param {Object} options
  *        Option menu configuration
--- a/devtools/client/performance/performance-controller.js
+++ b/devtools/client/performance/performance-controller.js
@@ -11,17 +11,17 @@ var BrowserLoaderModule = {};
 Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
 var { loader, require } = BrowserLoaderModule.BrowserLoader({
   baseURI: "resource://devtools/client/performance/",
   window
 });
 var { Task } = require("devtools/shared/task");
 /* exported Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
 var { Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
-var { gDevTools } = require("devtools/client/framework/devtools");
+var { PrefObserver } = require("devtools/client/shared/prefs");
 
 // Events emitted by various objects in the panel.
 var EVENTS = require("devtools/client/performance/events");
 Object.defineProperty(this, "EVENTS", {
   value: EVENTS,
   enumerable: true,
   writable: false
 });
@@ -138,17 +138,18 @@ var PerformanceController = {
     PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
     PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
     RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.on(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
     DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);
 
-    gDevTools.on("pref-changed", this._onThemeChanged);
+    this._prefObserver = new PrefObserver("devtools.");
+    this._prefObserver.on("devtools.theme", this._onThemeChanged);
   }),
 
   /**
    * Remove events handled by the PerformanceController
    */
   destroy: function () {
     this._telemetry.destroy();
     this._prefs.off("pref-changed", this._onPrefChanged);
@@ -158,17 +159,18 @@ var PerformanceController = {
     PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
     PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
     RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.off(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
     DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);
 
-    gDevTools.off("pref-changed", this._onThemeChanged);
+    this._prefObserver.off("devtools.theme", this._onThemeChanged);
+    this._prefObserver.destroy();
   },
 
   /**
    * Enables front event listeners.
    *
    * The rationale behind this is given by the async intialization of all the
    * frontend components. Even though the panel is considered "open" only after
    * both the controller and the view are created, and even though their
@@ -397,24 +399,19 @@ var PerformanceController = {
    */
   _onPrefChanged: function (_, prefName, prefValue) {
     this.emit(EVENTS.PREF_CHANGED, prefName, prefValue);
   },
 
   /*
    * Called when the developer tools theme changes.
    */
-  _onThemeChanged: function (_, data) {
-    // Right now, gDevTools only emits `pref-changed` for the theme,
-    // but this could change in the future.
-    if (data.pref !== "devtools.theme") {
-      return;
-    }
-
-    this.emit(EVENTS.THEME_CHANGED, data.newValue);
+  _onThemeChanged: function () {
+    let newValue = Services.prefs.getCharPref("devtools.theme");
+    this.emit(EVENTS.THEME_CHANGED, newValue);
   },
 
   /**
    * Fired from the front on any event. Propagates to other handlers from here.
    */
   _onFrontEvent: function (eventName, ...data) {
     switch (eventName) {
       case "profiler-status":
--- a/devtools/client/responsive.html/actions/screenshot.js
+++ b/devtools/client/responsive.html/actions/screenshot.js
@@ -11,17 +11,17 @@ const {
   TAKE_SCREENSHOT_END,
 } = require("./index");
 
 const { getFormatStr } = require("../utils/l10n");
 const { getToplevelWindow } = require("sdk/window/utils");
 const { Task: { spawn } } = require("devtools/shared/task");
 const e10s = require("../utils/e10s");
 
-const audioCamera = new window.Audio("resource://devtools/client/themes/audio/shutter.wav");
+const CAMERA_AUDIO_URL = "resource://devtools/client/themes/audio/shutter.wav";
 
 const animationFrame = () => new Promise(resolve => {
   window.requestAnimationFrame(resolve);
 });
 
 function getFileName() {
   let date = new Date();
   let month = ("0" + (date.getMonth() + 1)).substr(-2);
@@ -49,17 +49,18 @@ function saveToFile(data, filename) {
 
     chromeWindow.saveURL(data, filename, null,
                          true, true,
                          chromeDocument.documentURIObject, chromeDocument);
   });
 }
 
 function simulateCameraEffects(node) {
-  audioCamera.play();
+  let cameraAudio = new window.Audio(CAMERA_AUDIO_URL);
+  cameraAudio.play();
   node.animate({ opacity: [ 0, 1 ] }, 500);
 }
 
 module.exports = {
 
   takeScreenshot() {
     return function* (dispatch, getState) {
       yield dispatch({ type: TAKE_SCREENSHOT_START });
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -102,16 +102,27 @@ function swapToInnerBrowser({ tab, conta
       gBrowser.swapBrowsersAndCloseOther(tab, containerTab);
 
       // 7. Start a tunnel from the tool tab's browser to the viewport browser
       //    so that some browser UI functions, like navigation, are connected to
       //    the content in the viewport, instead of the tool page.
       tunnel = tunnelToInnerBrowser(tab.linkedBrowser, innerBrowser);
       yield tunnel.start();
 
+      // Swapping browsers disconnects the find bar UI from the browser.
+      // If the find bar has been initialized, reconnect it.
+      if (gBrowser.isFindBarInitialized(tab)) {
+        let findBar = gBrowser.getFindBar(tab);
+        findBar.browser = tab.linkedBrowser;
+        if (!findBar.hidden) {
+          // Force the find bar to activate again, restoring the search string.
+          findBar.onFindCommand();
+        }
+      }
+
       // Force the browser UI to match the new state of the tab and browser.
       thawNavigationState(tab);
       gBrowser.setTabTitle(tab);
       gBrowser.updateCurrentBrowser(true);
     }),
 
     stop() {
       // 1. Stop the tunnel between outer and inner browsers.
@@ -142,16 +153,28 @@ function swapToInnerBrowser({ tab, conta
       gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true,
                                        contentBrowser.remoteType);
 
       // 6. Swap the content into the original browser tab and close the
       //    temporary tab used to hold the content via
       //    `swapBrowsersAndCloseOther`.
       dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
       gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
+
+      // Swapping browsers disconnects the find bar UI from the browser.
+      // If the find bar has been initialized, reconnect it.
+      if (gBrowser.isFindBarInitialized(tab)) {
+        let findBar = gBrowser.getFindBar(tab);
+        findBar.browser = tab.linkedBrowser;
+        if (!findBar.hidden) {
+          // Force the find bar to activate again, restoring the search string.
+          findBar.onFindCommand();
+        }
+      }
+
       gBrowser = null;
 
       // The focus manager seems to get a little dizzy after all this swapping.  If a
       // content element had been focused inside the viewport before stopping, it will
       // have lost focus.  Activate the frame to restore expected focus.
       tab.linkedBrowser.frameLoader.activateRemoteFrame();
 
       delete tab.isResponsiveDesignMode;
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -18,16 +18,17 @@ function debug(msg) {
   // console.log(msg);
 }
 
 /**
  * Properties swapped between browsers by browser.xml's `swapDocShells`.  See also the
  * list at /devtools/client/responsive.html/docs/browser-swap.md.
  */
 const SWAPPED_BROWSER_STATE = [
+  "_remoteFinder",
   "_securityUI",
   "_documentURI",
   "_documentContentType",
   "_contentTitle",
   "_characterSet",
   "_contentPrincipal",
   "_imageDocument",
   "_fullZoom",
--- a/devtools/client/shared/autocomplete-popup.js
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -2,19 +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/. */
 
 "use strict";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const Services = require("Services");
-const {gDevTools} = require("devtools/client/framework/devtools");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 
 let itemIdCounter = 0;
 /**
  * Autocomplete popup UI implementation.
  *
  * @constructor
  * @param {Document} toolboxDoc
  *        The toolbox document to attach the autocomplete popup panel.
@@ -42,17 +42,19 @@ function AutocompletePopup(toolboxDoc, o
   this.onClickCallback = options.onClick;
 
   // If theme is auto, use the devtools.theme pref
   if (theme === "auto") {
     theme = Services.prefs.getCharPref("devtools.theme");
     this.autoThemeEnabled = true;
     // Setup theme change listener.
     this._handleThemeChange = this._handleThemeChange.bind(this);
-    gDevTools.on("pref-changed", this._handleThemeChange);
+    this._prefObserver = new PrefObserver("devtools.");
+    this._prefObserver.on("devtools.theme", this._handleThemeChange);
+    this._currentTheme = theme;
   }
 
   // Create HTMLTooltip instance
   this._tooltip = new HTMLTooltip(this._document);
   this._tooltip.panel.classList.add(
     "devtools-autocomplete-popup",
     "devtools-monospace",
     theme + "-theme");
@@ -189,17 +191,18 @@ AutocompletePopup.prototype = {
   destroy: function () {
     if (this.isOpen) {
       this.hidePopup();
     }
 
     this._list.removeEventListener("click", this.onClick, false);
 
     if (this.autoThemeEnabled) {
-      gDevTools.off("pref-changed", this._handleThemeChange);
+      this._prefObserver.off("devtools.theme", this._handleThemeChange);
+      this._prefObserver.destroy();
     }
 
     this._list.remove();
     this._listClone.remove();
     this._tooltip.destroy();
     this._document = null;
     this._list = null;
     this._tooltip = null;
@@ -557,35 +560,27 @@ AutocompletePopup.prototype = {
   selectPreviousPageItem: function () {
     let prevPageIndex = this.selectedIndex - this._itemsPerPane - 1;
     this.selectedIndex = Math.max(prevPageIndex, 0);
     return this.selectedItem;
   },
 
   /**
    * Manages theme switching for the popup based on the devtools.theme pref.
-   *
-   * @private
-   *
-   * @param {String} event
-   *        The name of the event. In this case, "pref-changed".
-   * @param {Object} data
-   *        An object passed by the emitter of the event. In this case, the
-   *        object consists of three properties:
-   *        - pref {String} The name of the preference that was modified.
-   *        - newValue {Object} The new value of the preference.
-   *        - oldValue {Object} The old value of the preference.
    */
-  _handleThemeChange: function (event, data) {
-    if (data.pref === "devtools.theme") {
-      this._tooltip.panel.classList.toggle(data.oldValue + "-theme", false);
-      this._tooltip.panel.classList.toggle(data.newValue + "-theme", true);
-      this._list.classList.toggle(data.oldValue + "-theme", false);
-      this._list.classList.toggle(data.newValue + "-theme", true);
-    }
+  _handleThemeChange: function () {
+    const oldValue = this._currentTheme;
+    const newValue = Services.prefs.getCharPref("devtools.theme");
+
+    this._tooltip.panel.classList.toggle(oldValue + "-theme", false);
+    this._tooltip.panel.classList.toggle(newValue + "-theme", true);
+    this._list.classList.toggle(oldValue + "-theme", false);
+    this._list.classList.toggle(newValue + "-theme", true);
+
+    this._currentTheme = newValue;
   },
 
   /**
    * Used by tests.
    */
   get _panel() {
     return this._tooltip.panel;
   },
--- a/devtools/client/shared/prefs.js
+++ b/devtools/client/shared/prefs.js
@@ -171,8 +171,36 @@ function makeObserver(self, cache, prefs
       }
       cache.delete(prefName);
       self.emit("pref-changed", accessorName, self[accessorName]);
     }
   };
 }
 
 exports.PrefsHelper = PrefsHelper;
+
+/**
+ * A PreferenceObserver observes a pref branch for pref changes.
+ * It emits an event for each preference change.
+ */
+function PrefObserver(branchName) {
+  this.branchName = branchName;
+  this.branch = Services.prefs.getBranch(branchName);
+  this.branch.addObserver("", this, false);
+
+  EventEmitter.decorate(this);
+}
+
+exports.PrefObserver = PrefObserver;
+
+PrefObserver.prototype = {
+  observe: function (subject, topic, data) {
+    if (topic == "nsPref:changed") {
+      this.emit(this.branchName + data);
+    }
+  },
+
+  destroy: function () {
+    if (this.branch) {
+      this.branch.removeObserver("", this);
+    }
+  },
+};
--- a/devtools/client/shared/test/browser_theme.js
+++ b/devtools/client/shared/test/browser_theme.js
@@ -2,16 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that theme utilities work
 
 const {getColor, getTheme, setTheme} = require("devtools/client/shared/theme");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 
 add_task(function* () {
   testGetTheme();
   testSetTheme();
   testGetColor();
   testColorExistence();
 });
 
@@ -26,37 +27,40 @@ function testGetTheme() {
   is(getTheme(), "firebug", "getTheme() correctly returns firebug theme");
   Services.prefs.setCharPref("devtools.theme", "unknown");
   is(getTheme(), "unknown", "getTheme() correctly returns an unknown theme");
   Services.prefs.setCharPref("devtools.theme", originalTheme);
 }
 
 function testSetTheme() {
   let originalTheme = getTheme();
-  gDevTools.once("pref-changed", (_, { pref, oldValue, newValue }) => {
+
+  let prefObserver = new PrefObserver("devtools.");
+  prefObserver.once("devtools.theme", pref => {
     is(pref, "devtools.theme",
-      "The 'pref-changed' event triggered by setTheme has correct pref.");
-    is(oldValue, originalTheme,
-      "The 'pref-changed' event triggered by setTheme has correct oldValue.");
+      "A preference event triggered by setTheme has correct pref.");
+    let newValue = Services.prefs.getCharPref("devtools.theme");
     is(newValue, "dark",
-      "The 'pref-changed' event triggered by setTheme has correct newValue.");
+      "A preference event triggered by setTheme comes after the value is set.");
   });
   setTheme("dark");
   is(Services.prefs.getCharPref("devtools.theme"), "dark",
      "setTheme() correctly sets dark theme.");
   setTheme("light");
   is(Services.prefs.getCharPref("devtools.theme"), "light",
      "setTheme() correctly sets light theme.");
   setTheme("firebug");
   is(Services.prefs.getCharPref("devtools.theme"), "firebug",
      "setTheme() correctly sets firebug theme.");
   setTheme("unknown");
   is(Services.prefs.getCharPref("devtools.theme"), "unknown",
      "setTheme() correctly sets an unknown theme.");
   Services.prefs.setCharPref("devtools.theme", originalTheme);
+
+  prefObserver.destroy();
 }
 
 function testGetColor() {
   let BLUE_DARK = "#46afe3";
   let BLUE_LIGHT = "#0088cc";
   let BLUE_FIREBUG = "#3455db";
   let originalTheme = getTheme();
 
--- a/devtools/client/shared/theme.js
+++ b/devtools/client/shared/theme.js
@@ -5,17 +5,16 @@
 "use strict";
 
 /**
  * Colors for themes taken from:
  * https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
  */
 
 const Services = require("Services");
-const { gDevTools } = require("devtools/client/framework/devtools");
 
 const variableFileContents = require("raw!devtools/client/themes/variables.css");
 
 const THEME_SELECTOR_STRINGS = {
   light: ":root.theme-light {",
   dark: ":root.theme-dark {",
   firebug: ":root.theme-firebug {"
 };
@@ -62,23 +61,14 @@ const getColor = exports.getColor = (typ
   let themeFile = getThemeFile(themeName);
   let match = themeFile.match(new RegExp("--theme-" + type + ": (.*);"));
 
   // Return the appropriate variable in the theme, or otherwise, null.
   return match ? match[1] : null;
 };
 
 /**
- * Mimics selecting the theme selector in the toolbox;
- * sets the preference and emits an event on gDevTools to trigger
- * the themeing.
+ * Set the theme preference.
  */
 const setTheme = exports.setTheme = (newTheme) => {
-  let oldTheme = getTheme();
-
   Services.prefs.setCharPref("devtools.theme", newTheme);
-  gDevTools.emit("pref-changed", {
-    pref: "devtools.theme",
-    newValue: newTheme,
-    oldValue: oldTheme
-  });
 };
 /* eslint-enable */
--- a/devtools/client/shared/vendor/REACT_UPGRADING
+++ b/devtools/client/shared/vendor/REACT_UPGRADING
@@ -46,14 +46,16 @@ Unfortunately, you need to manually repl
 calls in this version again. We know this is not ideal but WE NEED TO
 MOVE OFF XUL and we don't need to do this anymore once that happens.
 
 After patching `build/react-with-addons.js` again, copy the production
 version over:
 
 * cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react.js
 
-You also need to copy the ReactDOM package. It requires React, so
+You also need to copy the ReactDOM and ReactDOMServer package. It requires React, so
 right now we are just manually changing the path from `react` to
 `devtools/client/shared/vendor/react`.
 
 * cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom.js
 * (change `require('react')` at the top of the file to the right path)
+* cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server.js
+* (change `require('react')` at the top of the file to the right path)
--- a/devtools/client/shared/vendor/moz.build
+++ b/devtools/client/shared/vendor/moz.build
@@ -11,16 +11,17 @@ modules += [
 ]
 
 # react-dev is used if either debug mode is enabled,
 # so include it for both
 if CONFIG['DEBUG_JS_MODULES'] or CONFIG['MOZ_DEBUG']:
     modules += ['react-dev.js']
 
 modules += [
+    'react-dom-server.js',
     'react-dom.js',
     'react-proxy.js',
     'react-redux.js',
     'react-virtualized.js',
     'react.js',
     'redux.js',
     'reselect.js',
     'seamless-immutable.js',
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-server.js
@@ -0,0 +1,42 @@
+/**
+ * ReactDOMServer v15.3.2
+ *
+ * Copyright 2013-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
+;(function(f) {
+  // CommonJS
+  if (typeof exports === "object" && typeof module !== "undefined") {
+    module.exports = f(require('react'));
+
+  // RequireJS
+  } else if (typeof define === "function" && define.amd) {
+    define(['react'], f);
+
+  // <script>
+  } else {
+    var g;
+    if (typeof window !== "undefined") {
+      g = window;
+    } else if (typeof global !== "undefined") {
+      g = global;
+    } else if (typeof self !== "undefined") {
+      g = self;
+    } else {
+      // works providing we're not in "use strict";
+      // needed for Java 8 Nashorn
+      // see https://github.com/facebook/react/issues/3037
+      g = this;
+    }
+    g.ReactDOMServer = f(g.React);
+  }
+
+})(function(React) {
+  return React.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+});
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -27,17 +27,17 @@ const MAX_VERTICAL_OFFSET = 3;
 // Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
 // line in text selection.
 const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
 const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
 
 const Services = require("Services");
 const promise = require("promise");
 const events = require("devtools/shared/event-emitter");
-const { PrefObserver } = require("devtools/client/styleeditor/utils");
+const { PrefObserver } = require("devtools/client/shared/prefs");
 const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/sourceeditor.properties");
 
 const { OS } = Services.appinfo;
 
--- a/devtools/client/styleeditor/StyleEditorUI.jsm
+++ b/devtools/client/styleeditor/StyleEditorUI.jsm
@@ -21,31 +21,32 @@ const {
   getString,
   text,
   wire,
   showFilePicker,
 } = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
 const {SplitView} = require("resource://devtools/client/shared/SplitView.jsm");
 const {StyleSheetEditor} = require("resource://devtools/client/styleeditor/StyleSheetEditor.jsm");
 const {PluralForm} = require("devtools/shared/plural-form");
-const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 const csscoverage = require("devtools/shared/fronts/csscoverage");
 const {console} = require("resource://gre/modules/Console.jsm");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const {ResponsiveUIManager} =
   require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const LOAD_ERROR = "error-load";
 const STYLE_EDITOR_TEMPLATE = "stylesheet";
 const SELECTOR_HIGHLIGHTER_TYPE = "SelectorHighlighter";
 const PREF_MEDIA_SIDEBAR = "devtools.styleeditor.showMediaSidebar";
 const PREF_SIDEBAR_WIDTH = "devtools.styleeditor.mediaSidebarWidth";
 const PREF_NAV_WIDTH = "devtools.styleeditor.navSidebarWidth";
+const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
 /**
  * StyleEditorUI is controls and builds the UI of the Style Editor, including
  * maintaining a list of editors for each stylesheet on a debuggee.
  *
  * Emits events:
  *   'editor-added': A new editor was added to the UI
  *   'editor-selected': An editor was selected
--- a/devtools/client/styleeditor/moz.build
+++ b/devtools/client/styleeditor/moz.build
@@ -7,10 +7,9 @@
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 DevToolsModules(
     'styleeditor-commands.js',
     'styleeditor-panel.js',
     'StyleEditorUI.jsm',
     'StyleEditorUtil.jsm',
     'StyleSheetEditor.jsm',
-    'utils.js',
 )
deleted file mode 100644
--- a/devtools/client/styleeditor/utils.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* 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 Services = require("Services");
-const EventEmitter = require("devtools/shared/event-emitter");
-
-exports.PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
-
-/**
- * A PreferenceObserver observes a pref branch for pref changes.
- * It emits an event for each preference change.
- */
-function PrefObserver(branchName) {
-  this.branchName = branchName;
-  this.branch = Services.prefs.getBranch(branchName);
-  this.branch.addObserver("", this, false);
-
-  EventEmitter.decorate(this);
-}
-
-exports.PrefObserver = PrefObserver;
-
-PrefObserver.prototype = {
-  observe: function (subject, topic, data) {
-    if (topic == "nsPref:changed") {
-      this.emit(this.branchName + data);
-    }
-  },
-
-  destroy: function () {
-    if (this.branch) {
-      this.branch.removeObserver("", this);
-    }
-  }
-};
--- a/devtools/client/webaudioeditor/controller.js
+++ b/devtools/client/webaudioeditor/controller.js
@@ -1,12 +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/. */
 
+const {PrefObserver} = require("devtools/client/shared/prefs");
+
 /**
  * A collection of `AudioNodeModel`s used throughout the editor
  * to keep track of audio nodes within the audio context.
  */
 var gAudioNodes = new AudioNodesCollection();
 
 /**
  * Initializes the web audio editor views
@@ -53,17 +55,19 @@ var WebAudioEditorController = {
     gFront.on("connect-param", this._onConnectParam);
     gFront.on("disconnect-node", this._onDisconnectNode);
     gFront.on("change-param", this._onChangeParam);
     gFront.on("destroy-node", this._onDestroyNode);
 
     // Hook into theme change so we can change
     // the graph's marker styling, since we can't do this
     // with CSS
-    gDevTools.on("pref-changed", this._onThemeChange);
+
+    this._prefObserver = new PrefObserver("");
+    this._prefObserver.on("devtools.theme", this._onThemeChange);
 
     // Store the AudioNode definitions from the WebAudioFront, if the method exists.
     // If not, get the JSON directly. Using the actor method is preferable so the client
     // knows exactly what methods are supported on the server.
     let actorHasDefinition = yield gTarget.actorHasMethod("webaudio", "getDefinition");
     if (actorHasDefinition) {
       AUDIO_NODE_DEFINITION = yield gFront.getDefinition();
     } else {
@@ -85,17 +89,18 @@ var WebAudioEditorController = {
     gTarget.off("navigate", this._onTabNavigated);
     gFront.off("start-context", this._onStartContext);
     gFront.off("create-node", this._onCreateNode);
     gFront.off("connect-node", this._onConnectNode);
     gFront.off("connect-param", this._onConnectParam);
     gFront.off("disconnect-node", this._onDisconnectNode);
     gFront.off("change-param", this._onChangeParam);
     gFront.off("destroy-node", this._onDestroyNode);
-    gDevTools.off("pref-changed", this._onThemeChange);
+    this._prefObserver.off("devtools.theme", this._onThemeChange);
+    this._prefObserver.destroy();
   },
 
   /**
    * Called when page is reloaded to show the reload notice and waiting
    * for an audio context notice.
    */
   reset: function () {
     $("#content").hidden = true;
@@ -124,18 +129,19 @@ var WebAudioEditorController = {
     return node;
   },
 
   /**
    * Fired when the devtools theme changes (light, dark, etc.)
    * so that the graph can update marker styling, as that
    * cannot currently be done with CSS.
    */
-  _onThemeChange: function (event, data) {
-    window.emit(EVENTS.THEME_CHANGE, data.newValue);
+  _onThemeChange: function () {
+    let newValue = Services.prefs.getCharPref("devtools.theme");
+    window.emit(EVENTS.THEME_CHANGE, newValue);
   },
 
   /**
    * Called for each location change in the debugged tab.
    */
   _onTabNavigated: Task.async(function* (event, {isFrameSwitching}) {
     switch (event) {
       case "will-navigate": {
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_timestamps.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_timestamps.js
@@ -3,50 +3,52 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test for the message timestamps option: check if the preference toggles the
 // display of messages in the console output. See bug 722267.
 
 "use strict";
 
+const {PrefObserver} = require("devtools/client/shared/prefs");
+
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
                  "bug 1307871 - preference for toggling timestamps in messages";
 const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
 
 add_task(function* () {
   let hud = yield openNewTabAndConsole(TEST_URI);
   let outputNode = hud.ui.experimentalOutputNode;
   let outputEl = outputNode.querySelector(".webconsole-output");
 
   testPrefDefaults(outputEl);
 
+  let observer = new PrefObserver("");
   let toolbox = gDevTools.getToolbox(hud.target);
   let optionsPanel = yield toolbox.selectTool("options");
-  yield togglePref(optionsPanel);
+  yield togglePref(optionsPanel, observer);
+  observer.destroy();
 
   yield testChangedPref(outputEl);
 
   Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
 });
 
 function testPrefDefaults(outputEl) {
   let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
   ok(!prefValue, "Messages should have no timestamp by default (pref check)");
   ok(outputEl.classList.contains("hideTimestamps"),
      "Messages should have no timestamp (class name check)");
 }
 
-function* togglePref(panel) {
+function* togglePref(panel, observer) {
   info("Options panel opened");
 
   info("Changing pref");
-  let prefChanged = new Promise(resolve => {
-    gDevTools.once("pref-changed", resolve);
-  });
+  let prefChanged = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
   let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages");
   checkbox.click();
 
   yield prefChanged;
 }
 
 function* testChangedPref(outputEl) {
   let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
--- a/devtools/client/webconsole/new-console-output/test/require-helper.js
+++ b/devtools/client/webconsole/new-console-output/test/require-helper.js
@@ -3,18 +3,19 @@
 "use strict";
 
 const requireHacker = require("require-hacker");
 
 requireHacker.global_hook("default", path => {
   switch (path) {
     // For Enzyme
     case "react-dom":
+      return `const ReactDOM = require('devtools/client/shared/vendor/react-dom'); module.exports = ReactDOM`;
     case "react-dom/server":
-      return `const React = require('devtools/client/shared/vendor/react-dev'); module.exports = React`;
+      return `const ReactDOMServer = require('devtools/client/shared/vendor/react-dom-server'); module.exports = ReactDOMServer`;
     case "react-addons-test-utils":
       return `const React = require('devtools/client/shared/vendor/react-dev'); module.exports = React.addons.TestUtils`;
     case "react-redux":
       return `const ReactRedux = require('devtools/client/shared/vendor/react-redux'); module.exports = ReactRedux`;
     // Use react-dev. This would be handled by browserLoader in Firefox.
     case "react":
     case "devtools/client/shared/vendor/react":
       return `const React = require('devtools/client/shared/vendor/react-dev'); module.exports = React`;
--- a/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js
+++ b/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js
@@ -3,49 +3,53 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test for the message timestamps option: check if the preference toggles the
 // display of messages in the console output. See bug 722267.
 
 "use strict";
 
+const {PrefObserver} = require("devtools/client/shared/prefs");
+
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
                  "bug 722267 - preference for toggling timestamps in messages";
 const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
 var hud;
 
 add_task(function* () {
   yield loadTab(TEST_URI);
 
   hud = yield openConsole();
   let panel = yield consoleOpened();
 
-  yield onOptionsPanelSelected(panel);
+  let observer = new PrefObserver("");
+  yield onOptionsPanelSelected(panel, observer);
   onPrefChanged();
+  observer.destroy();
 
   Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
   hud = null;
 });
 
 function consoleOpened() {
   info("console opened");
   let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
   ok(!prefValue, "messages have no timestamp by default (pref check)");
   ok(hud.outputNode.classList.contains("hideTimestamps"),
      "messages have no timestamp (class name check)");
 
   let toolbox = gDevTools.getToolbox(hud.target);
   return toolbox.selectTool("options");
 }
 
-function onOptionsPanelSelected(panel) {
+function onOptionsPanelSelected(panel, observer) {
   info("options panel opened");
 
-  let prefChanged = gDevTools.once("pref-changed", onPrefChanged);
+  let prefChanged = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
 
   let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages");
   checkbox.click();
 
   return prefChanged;
 }
 
 function onPrefChanged() {
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -12,16 +12,17 @@ const {Utils: WebConsoleUtils, CONSOLE_W
   require("devtools/client/webconsole/utils");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const BrowserLoaderModule = {};
 Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
 
 const promise = require("promise");
 const Services = require("Services");
 const Telemetry = require("devtools/client/shared/telemetry");
+const {PrefObserver} = require("devtools/client/shared/prefs");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
@@ -631,21 +632,19 @@ WebConsoleFrame.prototype = {
           event.target.getAttribute("type").toLowerCase() === "search") {
         return;
       }
 
       this.jsterm.focus();
     });
 
     // Toggle the timestamp on preference change
-    gDevTools.on("pref-changed", this._onToolboxPrefChanged);
-    this._onToolboxPrefChanged("pref-changed", {
-      pref: PREF_MESSAGE_TIMESTAMP,
-      newValue: Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP),
-    });
+    this._prefObserver = new PrefObserver("");
+    this._prefObserver.on(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
+    this._onToolboxPrefChanged();
 
     this._initShortcuts();
 
     // focus input node
     this.jsterm.focus();
   },
 
   /**
@@ -2686,35 +2685,26 @@ WebConsoleFrame.prototype = {
 
       this._startX = this._startY = undefined;
 
       callback.call(this, event);
     }, false);
   },
 
   /**
-   * Handler for the pref-changed event coming from the toolbox.
-   * Currently this function only handles the timestamps preferences.
-   *
-   * @private
-   * @param object event
-   *        This parameter is a string that holds the event name
-   *        pref-changed in this case.
-   * @param object data
-   *        This is the pref-changed data object.
-  */
-  _onToolboxPrefChanged: function (event, data) {
-    if (data.pref == PREF_MESSAGE_TIMESTAMP) {
-      if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
-        this.newConsoleOutput.dispatchTimestampsToggle(data.newValue);
-      } else if (data.newValue) {
-        this.outputNode.classList.remove("hideTimestamps");
-      } else {
-        this.outputNode.classList.add("hideTimestamps");
-      }
+   * Called when the message timestamp pref changes.
+   */
+  _onToolboxPrefChanged: function () {
+    let newValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
+    if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
+      this.newConsoleOutput.dispatchTimestampsToggle(newValue);
+    } else if (newValue) {
+      this.outputNode.classList.remove("hideTimestamps");
+    } else {
+      this.outputNode.classList.add("hideTimestamps");
     }
   },
 
   /**
    * Copies the selected items to the system clipboard.
    *
    * @param object options
    *        - linkOnly:
@@ -2813,17 +2803,18 @@ WebConsoleFrame.prototype = {
 
     this._destroyer = promise.defer();
 
     let toolbox = gDevTools.getToolbox(this.owner.target);
     if (toolbox) {
       toolbox.off("webconsole-selected", this._onPanelSelected);
     }
 
-    gDevTools.off("pref-changed", this._onToolboxPrefChanged);
+    this._prefObserver.off(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
+    this._prefObserver.destroy();
     this.window.removeEventListener("resize", this.resize, true);
 
     this._repeatNodes = {};
     this._outputQueue.forEach(this._destroyItem, this);
     this._outputQueue = [];
     this._itemDestroyQueue.forEach(this._destroyItem, this);
     this._itemDestroyQueue = [];
     this._pruneCategoriesQueue = {};
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -335,17 +335,17 @@ VRManager::GetVRControllerInfo(nsTArray<
   }
 }
 
 void
 VRManager::RefreshVRControllers()
 {
   nsTArray<RefPtr<gfx::VRControllerHost>> controllers;
 
-  ScanForDevices();
+  ScanForControllers();
 
   for (uint32_t i = 0; i < mControllerManagers.Length()
       && controllers.Length() == 0; ++i) {
     mControllerManagers[i]->GetControllers(controllers);
   }
 
   bool controllerInfoChanged = false;
 
@@ -367,23 +367,32 @@ VRManager::RefreshVRControllers()
     for (const auto& controller : controllers) {
       mVRControllers.Put(controller->GetControllerInfo().GetControllerID(),
                          controller);
     }
   }
 }
 
 void
-VRManager::ScanForDevices()
+VRManager::ScanForControllers()
 {
   for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
     mControllerManagers[i]->ScanForDevices();
   }
 }
 
+void
+VRManager::RemoveControllers()
+{
+  for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+    mControllerManagers[i]->RemoveDevices();
+  }
+  mVRControllers.Clear();
+}
+
 template<class T>
 void
 VRManager::NotifyGamepadChange(const T& aInfo)
 {
   dom::GamepadChangeEvent e(aInfo);
 
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
     Unused << iter.Get()->GetKey()->SendGamepadUpdate(e);
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -33,17 +33,18 @@ public:
   static VRManager* Get();
 
   void AddVRManagerParent(VRManagerParent* aVRManagerParent);
   void RemoveVRManagerParent(VRManagerParent* aVRManagerParent);
 
   void NotifyVsync(const TimeStamp& aVsyncTimestamp);
   void NotifyVRVsync(const uint32_t& aDisplayID);
   void RefreshVRDisplays(bool aMustDispatch = false);
-  void ScanForDevices();
+  void ScanForControllers();
+  void RemoveControllers();
   template<class T> void NotifyGamepadChange(const T& aInfo);
   RefPtr<gfx::VRDisplayHost> GetDisplay(const uint32_t& aDisplayID);
   void GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo);
 
   void SubmitFrame(VRLayerParent* aLayer, layers::PTextureParent* aTexture,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect);
   RefPtr<gfx::VRControllerHost> GetController(const uint32_t& aControllerID);
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -249,16 +249,17 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRControllerManager)
 
   static uint32_t AllocateControllerID();
   virtual bool Init() = 0;
   virtual void Destroy() = 0;
   virtual void HandleInput() = 0;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;
   virtual void ScanForDevices() = 0;
+  virtual void RemoveDevices() = 0;
   void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
   void NewAxisMove(uint32_t aIndex, uint32_t aAxis, double aValue);
   void NewPoseState(uint32_t aIndex, const dom::GamepadPoseState& aPose);
   void AddGamepad(const char* aID, uint32_t aMapping,
                   uint32_t aNumButtons, uint32_t aNumAxes);
   void RemoveGamepad(uint32_t aIndex);
 
 protected:
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -562,17 +562,17 @@ VRControllerManagerOpenVR::Init()
 
   mOpenVRInstalled = true;
   return true;
 }
 
 void
 VRControllerManagerOpenVR::Destroy()
 {
-  mOpenVRController.Clear();
+  RemoveDevices();
   mOpenVRInstalled = false;
 }
 
 void
 VRControllerManagerOpenVR::HandleInput()
 {
   RefPtr<impl::VRControllerOpenVR> controller;
   vr::VRControllerState_t state;
@@ -755,9 +755,16 @@ VRControllerManagerOpenVR::ScanForDevice
   #ifdef MOZ_GAMEPAD
       // Not already present, add it.
       AddGamepad("OpenVR Gamepad", static_cast<uint32_t>(GamepadMappingType::_empty),
                  gNumOpenVRButtonMask, gNumOpenVRAxis);
       ++mControllerCount;
   #endif
     }
   }
+}
+
+void
+VRControllerManagerOpenVR::RemoveDevices()
+{
+  mOpenVRController.Clear();
+  mControllerCount = 0;
 }
\ No newline at end of file
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -110,16 +110,17 @@ public:
   static already_AddRefed<VRControllerManagerOpenVR> Create();
 
   virtual bool Init() override;
   virtual void Destroy() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForDevices() override;
+  virtual void RemoveDevices() override;
 
 private:
   VRControllerManagerOpenVR();
   ~VRControllerManagerOpenVR();
 
   virtual void HandleButtonPress(uint32_t aControllerIdx,
                                  uint64_t aButtonPressed) override;
   virtual void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -292,24 +292,25 @@ VRManagerParent::RecvSetHaveEventListene
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvControllerListenerAdded()
 {
   VRManager* vm = VRManager::Get();
   // Ask the connected gamepads to be added to GamepadManager
-  vm->ScanForDevices();
-
+  vm->ScanForControllers();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvControllerListenerRemoved()
 {
+  VRManager* vm = VRManager::Get();
+  vm->RemoveControllers();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvGetControllers(nsTArray<VRControllerInfo> *aControllers)
 {
   VRManager* vm = VRManager::Get();
   vm->GetVRControllerInfo(*aControllers);
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -764,21 +764,20 @@ nsRubyBaseContainerFrame::PullOneColumn(
       if (textFrame && !textFrame->IsIntraLevelWhitespace()) {
         textFrame = nullptr;
       }
     }
   } else {
     // We are not pulling an intra-level whitespace, which means all
     // elements we are going to pull can have non-whitespace content,
     // which may contain float which we need to reparent.
-    nsBlockFrame* oldFloatCB = nullptr;
-    for (nsIFrame* frame : aColumn) {
-      oldFloatCB = nsLayoutUtils::GetFloatContainingBlock(frame);
-      break;
-    }
+    MOZ_ASSERT(aColumn.begin() != aColumn.end(),
+               "Ruby column shouldn't be empty");
+    nsBlockFrame* oldFloatCB =
+      nsLayoutUtils::GetFloatContainingBlock(*aColumn.begin());
 #ifdef DEBUG
     MOZ_ASSERT(oldFloatCB, "Must have found a float containing block");
     for (nsIFrame* frame : aColumn) {
       MOZ_ASSERT(nsLayoutUtils::GetFloatContainingBlock(frame) == oldFloatCB,
                  "All frames in the same ruby column should share "
                  "the same old float containing block");
     }
 #endif
--- a/media/ffvpx/ffvpxcommon.mozbuild
+++ b/media/ffvpx/ffvpxcommon.mozbuild
@@ -82,8 +82,23 @@ elif CONFIG['_MSC_VER']:
 DEFINES['HAVE_AV_CONFIG_H'] = True
 
 if CONFIG['MOZ_DEBUG']:
     # Enable all assertions in debug builds.
     DEFINES['ASSERT_LEVEL'] = 2
 elif not CONFIG['RELEASE_OR_BETA']:
     # Enable fast assertions in opt builds of Nightly and Aurora.
     DEFINES['ASSERT_LEVEL'] = 1
+
+# clang-cl's <intrin.h> doesn't work the same as MSVC's.  For details, see:
+#
+# http://lists.llvm.org/pipermail/cfe-dev/2016-September/050943.html
+#
+# As a temporary workaround while upstream decides how to address this,
+# we enable modules to make <intrin.h> more MSVC-compatible.
+if CONFIG['CLANG_CL']:
+    CFLAGS += [
+        '-Xclang',
+        '-fmodules',
+        '-Xclang',
+        '-fmodules-cache-path=' + TOPOBJDIR + '/media/ffpvx',
+        '-fbuiltin-module-map',
+    ]
--- a/mobile/android/components/NSSDialogService.js
+++ b/mobile/android/components/NSSDialogService.js
@@ -224,17 +224,17 @@ NSSDialogs.prototype = {
       this.formatString("clientAuthAsk.issuer", [issuerOrg]),
     ].join("<br/>");
 
     let certNickList = [];
     let certDetailsList = [];
     for (let i = 0; i < certList.length; i++) {
       let cert = certList.queryElementAt(i, Ci.nsIX509Cert);
       certNickList.push(this.formatString("clientAuthAsk.nickAndSerial",
-                                          [cert.nickname, cert.serialNumber]));
+                                          [cert.displayName, cert.serialNumber]));
       certDetailsList.push(this.getCertDetails(cert));
     }
 
     selectedIndex.value = 0;
     while (true) {
       let buttons = [
         this.getString("nssdialogs.ok.label"),
         this.getString("clientAuthAsk.viewCert.label"),
--- a/netwerk/test/unit/test_altsvc.js
+++ b/netwerk/test/unit/test_altsvc.js
@@ -123,17 +123,17 @@ function readFile(file) {
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
   let der = readFile(certFile);
-  certdb.addCert(der, trustString, null);
+  certdb.addCert(der, trustString);
 }
 
 function makeChan(origin) {
   return NetUtil.newChannel({
     uri: origin + "altsvc-test",
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -1106,10 +1106,10 @@ function readFile(file) {
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
   let der = readFile(certFile);
-  certdb.addCert(der, trustString, null);
+  certdb.addCert(der, trustString);
 }
--- a/netwerk/test/unit/test_immutable.js
+++ b/netwerk/test/unit/test_immutable.js
@@ -47,17 +47,17 @@ function readFile(file) {
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
   let der = readFile(certFile);
-  certdb.addCert(der, trustString, null);
+  certdb.addCert(der, trustString);
 }
 
 function makeChan(origin, path) {
   return NetUtil.newChannel({
     uri: origin + path,
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -279,17 +279,16 @@ certErrorExpiredNow=The certificate expi
 # LOCALIZATION NOTE (certErrorNotYetValidNow): Do not translate %1$S (date+time certificate will become valid) or %2$S (current date+time)
 certErrorNotYetValidNow=The certificate will not be valid until %1$S. The current time is %2$S.
 
 # LOCALIZATION NOTE (certErrorCodePrefix2): Do not translate <a id="errorCode" title="%1$S">%1$S</a>
 certErrorCodePrefix2=Error code: <a id="errorCode" title="%1$S">%1$S</a>
 
 P12DefaultNickname=Imported Certificate
 CertUnknown=Unknown
-CertNoNickname=(no nickname)
 CertNoEmailAddress=(no email address)
 CaCertExists=This certificate is already installed as a certificate authority.
 NotACACert=This is not a certificate authority certificate, so it can’t be imported into the certificate authority list.
 NotImportingUnverifiedCert=This certificate can’t be verified and will not be imported. The certificate issuer might be unknown or untrusted, the certificate might have expired or been revoked, or the certificate might not have been approved.
 UserCertIgnoredNoPrivateKey=This personal certificate can’t be installed because you do not own the corresponding private key which was created when the certificate was requested.
 UserCertImported=Your personal certificate has been installed. You should keep a backup copy of this certificate.
 CertOrgUnknown=(Unknown)
 CertNotStored=(Not Stored)
--- a/security/manager/pki/resources/content/certViewer.js
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -39,28 +39,22 @@ function doPrompt(msg)
  *
  * @param {tree} node
  *        Parent tree node to append to.
  * @param {nsIArray<nsIX509Cert>} chain
  *        Chain where cert element n is issued by cert element n + 1.
  */
 function AddCertChain(node, chain)
 {
-  var child = document.getElementById(node);
-  var currCert;
-  var displayVal;
+  let child = document.getElementById(node);
   for (let i = chain.length - 1; i >= 0; i--) {
-    currCert = chain.queryElementAt(i, nsIX509Cert);
-    if (currCert.commonName) {
-      displayVal = currCert.commonName;
-    } else {
-      displayVal = currCert.windowTitle;
-    }
+    let currCert = chain.queryElementAt(i, nsIX509Cert);
+    let displayValue = currCert.displayName;
     let addTwistie = i != 0;
-    child = addChildrenToTree(child, displayVal, currCert.dbKey, addTwistie);
+    child = addChildrenToTree(child, displayValue, currCert.dbKey, addTwistie);
   }
 }
 
 /**
  * Adds a "verified usage" of a cert to the "General" tab of the cert viewer.
  *
  * @param {String} usage
  *        Verified usage to add.
@@ -77,17 +71,17 @@ function AddUsage(usage)
 }
 
 function setWindowName()
 {
   bundle = document.getElementById("pippki_bundle");
 
   let cert = window.arguments[0].QueryInterface(Ci.nsIX509Cert);
   document.title = bundle.getFormattedString("certViewerTitle",
-                                             [cert.windowTitle]);
+                                             [cert.displayName]);
 
   //
   //  Set the cert attributes for viewing
   //
 
   //  The chain of trust
   AddCertChain("treesetDump", cert.getChain());
   DisplayGeneralDataFromCert(cert);
--- a/security/manager/pki/resources/content/clientauthask.js
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -84,17 +84,17 @@ function onLoad() {
 
   let selectElement = document.getElementById("nicknames");
   certArray = window.arguments[4].QueryInterface(Ci.nsIArray);
   for (let i = 0; i < certArray.length; i++) {
     let menuItemNode = document.createElement("menuitem");
     let cert = certArray.queryElementAt(i, Ci.nsIX509Cert);
     let nickAndSerial =
       bundle.getFormattedString("clientAuthNickAndSerial",
-                                [cert.nickname, cert.serialNumber]);
+                                [cert.displayName, cert.serialNumber]);
     menuItemNode.setAttribute("value", i);
     menuItemNode.setAttribute("label", nickAndSerial); // This is displayed.
     selectElement.firstChild.appendChild(menuItemNode);
     if (i == 0) {
       selectElement.selectedItem = menuItemNode;
     }
   }
 
--- a/security/manager/pki/resources/content/pippki.js
+++ b/security/manager/pki/resources/content/pippki.js
@@ -80,20 +80,17 @@ const DEFAULT_CERT_EXTENSION = "crt";
  * attribute on an nsIFilePicker.
  *
  * @param {nsIX509Cert} cert
  *        The cert to generate a filename for.
  * @returns {String}
  *          Generated filename.
  */
 function certToFilename(cert) {
-  let filename = cert.commonName;
-  if (!filename) {
-    filename = cert.windowTitle;
-  }
+  let filename = cert.displayName;
 
   // Remove unneeded and/or unsafe characters.
   filename = filename.replace(/\s/g, "")
                      .replace(/\./g, "")
                      .replace(/\\/g, "")
                      .replace(/\//g, "");
 
   // nsIFilePicker.defaultExtension is more of a suggestion to some
--- a/security/manager/ssl/LocalCertService.cpp
+++ b/security/manager/ssl/LocalCertService.cpp
@@ -8,63 +8,87 @@
 #include "ScopedNSSTypes.h"
 #include "cert.h"
 #include "mozilla/Casting.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsIPK11Token.h"
 #include "nsIPK11TokenDB.h"
 #include "nsIX509Cert.h"
-#include "nsIX509CertDB.h"
 #include "nsIX509CertValidity.h"
 #include "nsLiteralString.h"
 #include "nsProxyRelease.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "pk11pub.h"
 
 namespace mozilla {
 
+// Given a name, searches the internal certificate/key database for a
+// self-signed certificate with subject and issuer distinguished name equal to
+// "CN={name}". This assumes that the user has already authenticated to the
+// internal DB if necessary.
+static nsresult
+FindLocalCertByName(const nsACString& aName,
+            /*out*/ UniqueCERTCertificate& aResult)
+{
+  aResult.reset(nullptr);
+  NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
+  nsAutoCString expectedDistinguishedName(commonNamePrefix + aName);
+  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (!slot) {
+    return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
+  }
+  UniqueCERTCertList certList(PK11_ListCertsInSlot(slot.get()));
+  if (!certList) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  for (const CERTCertListNode* node = CERT_LIST_HEAD(certList);
+       !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) {
+    // If this isn't a self-signed cert, it's not what we're interested in.
+    if (!node->cert->isRoot) {
+      continue;
+    }
+    if (!expectedDistinguishedName.Equals(node->cert->subjectName)) {
+      continue; // Subject should match nickname
+    }
+    if (!expectedDistinguishedName.Equals(node->cert->issuerName)) {
+      continue; // Issuer should match nickname
+    }
+    // We found a match.
+    aResult.reset(CERT_DupCertificate(node->cert));
+    return NS_OK;
+  }
+  return NS_OK;
+}
+
 class LocalCertTask : public CryptoTask
 {
 protected:
   explicit LocalCertTask(const nsACString& aNickname)
     : mNickname(aNickname)
   {
   }
 
   nsresult RemoveExisting()
   {
-    // Search for any existing certs with this name and remove them
-    nsresult rv;
-
+    // Search for any existing self-signed certs with this name and remove them
     for (;;) {
-      UniqueCERTCertificate cert(
-        PK11_FindCertFromNickname(mNickname.get(), nullptr));
-      if (!cert) {
-        return NS_OK; // All done
+      UniqueCERTCertificate cert;
+      nsresult rv = FindLocalCertByName(mNickname, cert);
+      if (NS_FAILED(rv)) {
+        return rv;
       }
-
-      // Found a cert, check if generated by this service
-      if (!cert->isRoot) {
-        return NS_ERROR_UNEXPECTED; // Should be self-signed
+      // If we didn't find a match, we're done.
+      if (!cert) {
+        return NS_OK;
       }
-
-      NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
-      nsAutoCString subjectNameFromNickname(commonNamePrefix + mNickname);
-      if (!subjectNameFromNickname.Equals(cert->subjectName)) {
-        return NS_ERROR_UNEXPECTED; // Subject should match nickname
-      }
-      if (!subjectNameFromNickname.Equals(cert->issuerName)) {
-        return NS_ERROR_UNEXPECTED; // Issuer should match nickname
-      }
-
       rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert.get(), nullptr));
       if (NS_FAILED(rv)) {
-        return rv; // Some error, abort the loop
+        return rv;
       }
     }
   }
 
   nsCString mNickname;
 };
 
 class LocalCertGetTask final : public LocalCertTask
@@ -248,29 +272,25 @@ private:
     }
 
     // We should now have cert in the DB, read it back in nsIX509Cert form
     return GetFromDB();
   }
 
   nsresult GetFromDB()
   {
-    nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
-    if (!certDB) {
-      return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIX509Cert> certFromDB;
-    nsresult rv;
-    rv = certDB->FindCertByNickname(NS_ConvertASCIItoUTF16(mNickname),
-                                    getter_AddRefs(certFromDB));
+    UniqueCERTCertificate cert;
+    nsresult rv = FindLocalCertByName(mNickname, cert);
     if (NS_FAILED(rv)) {
       return rv;
     }
-    mCert = certFromDB;
+    if (!cert) {
+      return NS_ERROR_FAILURE;
+    }
+    mCert = nsNSSCertificate::Create(cert.get());
     return NS_OK;
   }
 
   nsresult Validate()
   {
     // Verify cert is self-signed
     bool selfSigned;
     nsresult rv = mCert->GetIsSelfSigned(&selfSigned);
--- a/security/manager/ssl/nsCertTree.cpp
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -1070,40 +1070,19 @@ nsCertTree::GetCellText(int32_t row, nsI
     if (myString) {
       myString->GetData(_retval);
       return NS_OK;
     }
   }
 
   if (NS_LITERAL_STRING("certcol").Equals(colID)) {
     if (!cert) {
-      mNSSComponent->GetPIPNSSBundleString("CertNotStored", _retval);
-    }
-    else {
-      rv = cert->GetCommonName(_retval);
-      if (NS_FAILED(rv) || _retval.IsEmpty()) {
-        // kaie: I didn't invent the idea to cut off anything before 
-        //       the first colon. :-)
-        nsAutoString nick;
-        rv = cert->GetNickname(nick);
-        
-        nsAString::const_iterator start, end, end2;
-        nick.BeginReading(start);
-        nick.EndReading(end);
-        end2 = end;
-  
-        if (FindInReadable(NS_LITERAL_STRING(":"), start, end)) {
-          // found. end points to the first char after the colon,
-          // that's what we want.
-          _retval = Substring(end, end2);
-        }
-        else {
-          _retval = nick;
-        }
-      }
+      rv = mNSSComponent->GetPIPNSSBundleString("CertNotStored", _retval);
+    } else {
+      rv = cert->GetDisplayName(_retval);
     }
   } else if (NS_LITERAL_STRING("tokencol").Equals(colID) && cert) {
     rv = cert->GetTokenName(_retval);
   } else if (NS_LITERAL_STRING("emailcol").Equals(colID) && cert) {
     rv = cert->GetEmailAddress(_retval);
   } else if (NS_LITERAL_STRING("issuedcol").Equals(colID) && cert) {
     nsCOMPtr<nsIX509CertValidity> validity;
 
--- a/security/manager/ssl/nsIX509Cert.idl
+++ b/security/manager/ssl/nsIX509Cert.idl
@@ -24,21 +24,16 @@ interface nsICertVerificationListener;
  *       change this uuid you probably need a hack in nsBinaryInputStream to
  *       read the old uuid.  If you change the format of the object
  *       serialization then more complex changes will be needed.
  */
 [scriptable, uuid(bdc3979a-5422-4cd5-8589-696b6e96ea83)]
 interface nsIX509Cert : nsISupports {
 
   /**
-   *  A nickname for the certificate.
-   */
-  readonly attribute AString nickname;
-
-  /**
    *  The primary email address of the certificate, if present.
    */
   readonly attribute AString emailAddress;
 
   /**
    * Did this certificate ship with the platform as a built-in root?
    */
   readonly attribute bool isBuiltInRoot;
@@ -140,17 +135,17 @@ interface nsIX509Cert : nsISupports {
   /**
    *  A unique identifier of this certificate within the local storage.
    */
   readonly attribute ACString dbKey;
 
   /**
    *  A human readable identifier to label this certificate.
    */
-  readonly attribute AString windowTitle;
+  readonly attribute AString displayName;
 
   /**
    *  Constants to classify the type of a certificate.
    */
   const unsigned long UNKNOWN_CERT =      0;
   const unsigned long CA_CERT      = 1 << 0;
   const unsigned long USER_CERT    = 1 << 1;
   const unsigned long EMAIL_CERT   = 1 << 2;
--- a/security/manager/ssl/nsIX509CertDB.idl
+++ b/security/manager/ssl/nsIX509CertDB.idl
@@ -70,57 +70,26 @@ interface nsIX509CertDB : nsISupports {
    *  is trusted for.
    */
   const unsigned long UNTRUSTED       =      0;
   const unsigned long TRUSTED_SSL     = 1 << 0;
   const unsigned long TRUSTED_EMAIL   = 1 << 1;
   const unsigned long TRUSTED_OBJSIGN = 1 << 2;
 
   /**
-   *  Given a nickname,
-   *  locate the matching certificate.
-   *
-   *  @param aNickname The nickname to be used as the key
-   *                   to find a certificate.
-   *
-   *  @return The matching certificate if found.
-   */
-  nsIX509Cert findCertByNickname(in AString aNickname);
-
-  /**
    *  Will find a certificate based on its dbkey
    *  retrieved by getting the dbKey attribute of
    *  the certificate.
    *
    *  @param aDBkey Database internal key, as obtained using
    *                attribute dbkey in nsIX509Cert.
    */
   nsIX509Cert findCertByDBKey(in string aDBkey);
 
   /**
-   *  Find user's own email encryption certificate by nickname.
-   *
-   *  @param aNickname The nickname to be used as the key
-   *                   to find the certificate.
-   *
-   *  @return The matching certificate if found.
-   */
-  nsIX509Cert findEmailEncryptionCert(in AString aNickname);
-
-  /**
-   *  Find user's own email signing certificate by nickname.
-   *
-   *  @param aNickname The nickname to be used as the key
-   *                   to find the certificate.
-   *
-   *  @return The matching certificate if found.
-   */
-  nsIX509Cert findEmailSigningCert(in AString aNickname);
-
-  /**
    *  Find a certificate by email address.
    *
    *  @param aEmailAddress The email address to be used as the key
    *                       to find the certificate.
    *
    *  @return The matching certificate if found.
    */
   nsIX509Cert findCertByEmailAddress(in string aEmailAddress);
@@ -333,23 +302,23 @@ interface nsIX509CertDB : nsISupports {
                                  in nsIInputStream aManifestStream,
                                  in nsIInputStream aSignatureStream,
                                  in nsIVerifySignedManifestCallback callback);
 
   /*
    * Add a cert to a cert DB from a binary string.
    *
    * @param certDER The raw DER encoding of a certificate.
-   * @param aTrust decoded by CERT_DecodeTrustString. 3 comma separated characters,
-   *                indicating SSL, Email, and Obj signing trust
-   * @param aName name of the cert for display purposes.
-   *              TODO(bug 857627): aName is currently ignored. It should either
-   *              not be ignored, or be removed.
+   * @param trust String describing the trust settings to assign the
+   *              certificate. Decoded by CERT_DecodeTrustString. Consists of 3
+   *              comma separated sets of characters, indicating SSL, Email, and
+   *              Object signing trust.
+   * @return nsIX509Cert the resulting certificate
    */
-  void addCert(in ACString certDER, in ACString aTrust, in AUTF8String aName);
+  nsIX509Cert addCert(in ACString certDER, in ACString trust);
 
   // Flags for verifyCertNow (these must match the values in CertVerifier.cpp):
   // Prevent network traffic. Doesn't work with classic verification.
   const uint32_t FLAG_LOCAL_ONLY = 1 << 0;
   // Do not fall back to DV verification after attempting EV validation.
   // Actually does prevent network traffic, but can cause a valid EV
   // certificate to not be considered valid.
   const uint32_t FLAG_MUST_BE_EV = 1 << 1;
@@ -402,26 +371,24 @@ interface nsIX509CertDB : nsISupports {
 
   // Clears the OCSP cache for the current certificate verification
   // implementation.
   void clearOCSPCache();
 
   /*
    * Add a cert to a cert DB from a base64 encoded string.
    *
-   * @param base64 The raw representation of a certificate,
-   *                encoded as Base 64.
-   * @param aTrust decoded by CERT_DecodeTrustString. 3 comma separated characters,
-   *                indicating SSL, Email, and Obj signing trust
-   * @param aName name of the cert for display purposes.
-   *              TODO(bug 857627): aName is currently ignored. It should either
-   *              not be ignored, or be removed.
+   * @param base64 The raw representation of a certificate, encoded as Base 64.
+   * @param trust String describing the trust settings to assign the
+   *              certificate. Decoded by CERT_DecodeTrustString. Consists of 3
+   *              comma separated sets of characters, indicating SSL, Email, and
+   *              Object signing trust.
+   * @return nsIX509Cert the resulting certificate
    */
-  void addCertFromBase64(in ACString base64, in ACString aTrust,
-                         in AUTF8String aName);
+  nsIX509Cert addCertFromBase64(in ACString base64, in ACString trust);
 
   /*
    * Get all the known certs in the database
    */
   nsIX509CertList getCerts();
 
   /*
    * Get a list of imported enterprise root certificates (currently only
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -1962,23 +1962,26 @@ nsNSSCertificate::CreateASN1Struct(nsIAS
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   nsCOMPtr<nsIASN1Sequence> sequence = new nsNSSASN1Sequence();
 
   nsCOMPtr<nsIMutableArray> asn1Objects;
   sequence->GetASN1Objects(getter_AddRefs(asn1Objects));
 
-  nsAutoString title;
-  nsresult rv = GetWindowTitle(title);
+  nsAutoString displayName;
+  nsresult rv = GetDisplayName(displayName);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  sequence->SetDisplayName(title);
+  rv = sequence->SetDisplayName(displayName);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
   sequence.forget(aRetVal);
 
   // This sequence will be contain the tbsCertificate, signatureAlgorithm,
   // and signatureValue.
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
   if (NS_FAILED(rv))
     return rv;
 
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNSSCertificate.h"
 
 #include "CertVerifier.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "certdb.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Unused.h"
 #include "nsArray.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsICertificateDialogs.h"
@@ -361,72 +362,85 @@ nsNSSCertificate::GetDbKey(const UniqueC
              cert->serialNumber.len);
   buf.Append(BitwiseCast<char*, unsigned char*>(cert->derIssuer.data),
              cert->derIssuer.len);
 
   return Base64Encode(buf, aDbKey);
 }
 
 NS_IMETHODIMP
-nsNSSCertificate::GetWindowTitle(nsAString& aWindowTitle)
+nsNSSCertificate::GetDisplayName(nsAString& aDisplayName)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  aWindowTitle.Truncate();
+  aDisplayName.Truncate();
 
+  MOZ_ASSERT(mCert, "mCert should not be null in GetDisplayName");
   if (!mCert) {
-    NS_ERROR("Somehow got nullptr for mCert in nsNSSCertificate.");
     return NS_ERROR_FAILURE;
   }
 
   UniquePORTString commonName(CERT_GetCommonName(&mCert->subject));
+  UniquePORTString organizationalUnitName(CERT_GetOrgUnitName(&mCert->subject));
+  UniquePORTString organizationName(CERT_GetOrgName(&mCert->subject));
 
-  const char* titleOptions[] = {
-    mCert->nickname,
+  bool isBuiltInRoot;
+  nsresult rv = GetIsBuiltInRoot(&isBuiltInRoot);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // Only use the nickname for built-in roots where we already have a hard-coded
+  // reasonable display name (unfortunately we have to strip off the leading
+  // slot identifier followed by a ':'). Otherwise, attempt to use the following
+  // in order:
+  //  - the common name, if present
+  //  - an organizational unit name, if present
+  //  - an organization name, if present
+  //  - the entire subject distinguished name, if non-empty
+  //  - an email address, if one can be found
+  // In the unlikely event that none of these fields are present and non-empty
+  // (the subject really shouldn't be empty), an empty string is returned.
+  nsAutoCString builtInRootNickname;
+  if (isBuiltInRoot) {
+    nsAutoCString fullNickname(mCert->nickname);
+    int32_t index = fullNickname.Find(":");
+    if (index != kNotFound) {
+      // Substring will gracefully handle the case where index is the last
+      // character in the string (that is, if the nickname is just
+      // "Builtin Object Token:"). In that case, we'll get an empty string.
+      builtInRootNickname = Substring(fullNickname,
+                                      AssertedCast<uint32_t>(index + 1));
+    }
+  }
+  const char* nameOptions[] = {
+    builtInRootNickname.get(),
     commonName.get(),
+    organizationalUnitName.get(),
+    organizationName.get(),
     mCert->subjectName,
     mCert->emailAddr
   };
 
-  nsAutoCString titleOption;
-  for (size_t i = 0; i < ArrayLength(titleOptions); i++) {
-    titleOption = titleOptions[i];
-    if (titleOption.Length() > 0 && IsUTF8(titleOption)) {
-      CopyUTF8toUTF16(titleOption, aWindowTitle);
+  nsAutoCString nameOption;
+  for (auto nameOptionPtr : nameOptions) {
+    nameOption.Assign(nameOptionPtr);
+    if (nameOption.Length() > 0 && IsUTF8(nameOption)) {
+      CopyUTF8toUTF16(nameOption, aDisplayName);
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSCertificate::GetNickname(nsAString& aNickname)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  if (mCert->nickname) {
-    CopyUTF8toUTF16(mCert->nickname, aNickname);
-  } else {
-    nsresult rv;
-    nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
-    if (NS_FAILED(rv) || !nssComponent) {
-      return NS_ERROR_FAILURE;
-    }
-    nssComponent->GetPIPNSSBundleString("CertNoNickname", aNickname);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   if (mCert->emailAddr) {
     CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress);
@@ -650,17 +664,16 @@ nsNSSCertificate::GetOrganizationalUnit(
 NS_IMETHODIMP
 nsNSSCertificate::GetChain(nsIArray** _rvChain)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(_rvChain);
-  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting chain for \"%s\"\n", mCert->nickname));
 
   mozilla::pkix::Time now(mozilla::pkix::Now());
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   UniqueCERTCertList nssChain;
   // We want to test all usages, but we start with server because most of the
@@ -684,55 +697,47 @@ nsNSSCertificate::GetChain(nsIArray** _r
                                 certificateUsageObjectSigner |
                                 certificateUsageStatusResponder;
   for (int usage = certificateUsageSSLClient;
        usage < certificateUsageAnyCA && !nssChain;
        usage = usage << 1) {
     if ((usage & otherUsagesToTest) == 0) {
       continue;
     }
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-           ("pipnss: PKIX attempting chain(%d) for '%s'\n",
-            usage, mCert->nickname));
     if (certVerifier->VerifyCert(mCert.get(), usage, now,
                                  nullptr, /*XXX fixme*/
                                  nullptr, /*hostname*/
                                  nssChain,
                                  CertVerifier::FLAG_LOCAL_ONLY)
           != mozilla::pkix::Success) {
       nssChain = nullptr;
       // keep going
     }
   }
 
   if (!nssChain) {
     // There is not verified path for the chain, however we still want to
     // present to the user as much of a possible chain as possible, in the case
     // where there was a problem with the cert or the issuers.
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-           ("pipnss: getchain :CertVerify failed to get chain for '%s'\n",
-            mCert->nickname));
     nssChain = UniqueCERTCertList(
       CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient));
   }
   if (!nssChain) {
     return NS_ERROR_FAILURE;
   }
 
   // enumerate the chain for scripting purposes
   nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
   if (!array) {
     return NS_ERROR_FAILURE;
   }
   CERTCertListNode* node;
   for (node = CERT_LIST_HEAD(nssChain.get());
        !CERT_LIST_END(node, nssChain.get());
        node = CERT_LIST_NEXT(node)) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-           ("adding %s to chain\n", node->cert->nickname));
     nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
     array->AppendElement(cert, false);
   }
   *_rvChain = array;
   NS_IF_ADDREF(*_rvChain);
   return NS_OK;
 }
 
@@ -744,17 +749,16 @@ nsNSSCertificate::GetAllTokenNames(uint3
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(aLength);
   NS_ENSURE_ARG(aTokenNames);
   *aLength = 0;
   *aTokenNames = nullptr;
 
   // Get the slots from NSS
-  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting slots for \"%s\"\n", mCert->nickname));
   UniquePK11SlotList slots(PK11_GetAllSlotsForCert(mCert.get(), nullptr));
   if (!slots) {
     if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
       return NS_OK; // List of slots is empty, return empty array
     }
     return NS_ERROR_FAILURE;
   }
 
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -6,16 +6,17 @@
 
 #include "CertVerifier.h"
 #include "CryptoTask.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "SharedSSLState.h"
 #include "certdb.h"
 #include "mozilla/Base64.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
 #include "nsArray.h"
 #include "nsArrayUtils.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsComponentManagerUtils.h"
 #include "nsICertificateDialogs.h"
@@ -89,46 +90,16 @@ nsNSSCertificateDB::~nsNSSCertificateDB(
   if (isAlreadyShutDown()) {
     return;
   }
 
   shutdown(ShutdownCalledFrom::Object);
 }
 
 NS_IMETHODIMP
-nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname,
-                                       nsIX509Cert** _rvCert)
-{
-  NS_ENSURE_ARG_POINTER(_rvCert);
-  *_rvCert = nullptr;
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  char *asciiname = nullptr;
-  NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname);
-  asciiname = const_cast<char*>(aUtf8Nickname.get());
-  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting \"%s\"\n", asciiname));
-  UniqueCERTCertificate cert(PK11_FindCertFromNickname(asciiname, nullptr));
-  if (!cert) {
-    cert.reset(CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname));
-  }
-  if (cert) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("got it\n"));
-    nsCOMPtr<nsIX509Cert> pCert = nsNSSCertificate::Create(cert.get());
-    if (pCert) {
-      pCert.forget(_rvCert);
-      return NS_OK;
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
 nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert)
 {
   NS_ENSURE_ARG_POINTER(aDBKey);
   NS_ENSURE_ARG(aDBKey[0]);
   NS_ENSURE_ARG_POINTER(_cert);
   *_cert = nullptr;
 
   nsNSSShutDownPreventionLock locker;
@@ -1033,90 +1004,16 @@ nsNSSCertificateDB::ExportPKCS12File(nsI
   } else {
     localRef = do_QueryInterface(aToken);
   }
   blob.SetToken(localRef);
   return blob.ExportToFile(aFile, certs, count);
 }
 
 NS_IMETHODIMP
-nsNSSCertificateDB::FindEmailEncryptionCert(const nsAString& aNickname,
-                                            nsIX509Cert** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-  *_retval = nullptr;
-
-  if (aNickname.IsEmpty())
-    return NS_OK;
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
-  char *asciiname = nullptr;
-  NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
-  asciiname = const_cast<char*>(aUtf8Nickname.get());
-
-  /* Find a good cert in the user's database */
-  UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(),
-                                                      asciiname,
-                                                      certUsageEmailRecipient,
-                                                      true, ctx));
-  if (!cert) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
-  if (!nssCert) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  nssCert.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSCertificateDB::FindEmailSigningCert(const nsAString& aNickname,
-                                         nsIX509Cert** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-  *_retval = nullptr;
-
-  if (aNickname.IsEmpty())
-    return NS_OK;
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
-  char *asciiname = nullptr;
-  NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
-  asciiname = const_cast<char*>(aUtf8Nickname.get());
-
-  /* Find a good cert in the user's database */
-  UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(),
-                                                      asciiname,
-                                                      certUsageEmailSigner,
-                                                      true, ctx));
-  if (!cert) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
-  if (!nssCert) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  nssCert.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress,
                                            nsIX509Cert** _retval)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -1327,18 +1224,24 @@ nsNSSCertificateDB::get_default_nickname
     }
     count++;
   }
 }
 
 NS_IMETHODIMP
 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
                                       const nsACString& aTrust,
-                                      const nsACString& /*aName*/)
+                                      nsIX509Cert** addedCertificate)
 {
+  MOZ_ASSERT(addedCertificate);
+  if (!addedCertificate) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *addedCertificate = nullptr;
+
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsNSSCertTrust trust;
   if (CERT_DecodeTrustString(trust.GetTrust(), PromiseFlatCString(aTrust).get())
         != SECSuccess) {
@@ -1354,41 +1257,51 @@ nsNSSCertificateDB::AddCertFromBase64(co
   UniqueCERTCertificate tmpCert(newCert->GetCert());
   if (!tmpCert) {
     return NS_ERROR_FAILURE;
   }
 
   // If there's already a certificate that matches this one in the database, we
   // still want to set its trust to the given value.
   if (tmpCert->isperm) {
-    return SetCertTrustFromString(newCert, aTrust);
+    rv = SetCertTrustFromString(newCert, aTrust);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    newCert.forget(addedCertificate);
+    return NS_OK;
   }
 
   UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
 
   rv = attemptToLogInWithDefaultPassword();
   if (NS_WARN_IF(rv != NS_OK)) {
     return rv;
   }
 
   SECStatus srv = CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(),
                                          trust.GetTrust());
-  return MapSECStatus(srv);
+  if (srv != SECSuccess) {
+    return MapSECStatus(srv);
+  }
+  newCert.forget(addedCertificate);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSCertificateDB::AddCert(const nsACString& aCertDER, const nsACString& aTrust,
-                            const nsACString& aName)
+nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
+                            const nsACString& aTrust,
+                            nsIX509Cert** addedCertificate)
 {
   nsCString base64;
   nsresult rv = Base64Encode(aCertDER, base64);
   NS_ENSURE_SUCCESS(rv, rv);
-  return AddCertFromBase64(base64, aTrust, aName);
+  return AddCertFromBase64(base64, aTrust, addedCertificate);
 }
 
 NS_IMETHODIMP
 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
                                            const nsACString& trustString)
 {
   NS_ENSURE_ARG(cert);
 
--- a/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
+++ b/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
@@ -31,24 +31,17 @@ nsNSSCertificateFakeTransport::~nsNSSCer
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetDbKey(nsACString&)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsNSSCertificateFakeTransport::GetWindowTitle(nsAString&)
-{
-  NS_NOTREACHED("Unimplemented on content process");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsNSSCertificateFakeTransport::GetNickname(nsAString&)
+nsNSSCertificateFakeTransport::GetDisplayName(nsAString&)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetEmailAddress(nsAString&)
 {
--- a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
@@ -65,17 +65,17 @@ function checkDialogContents(win, notBef
   Assert.equal(win.document.getElementById("organization").textContent,
                `Organization: \u201C${TEST_ORG}\u201D`,
                "Actual and expected organization should be equal");
   Assert.equal(win.document.getElementById("issuer").textContent,
                `Issued Under: \u201C${TEST_ISSUER_ORG}\u201D`,
                "Actual and expected issuer organization should be equal");
 
   Assert.equal(win.document.getElementById("nicknames").label,
-               "test client certificate [03]",
+               "Mochitest client [03]",
                "Actual and expected selected cert nickname and serial should " +
                "be equal");
 
   let [subject, serialNum, validity, issuer, tokenName] =
     win.document.getElementById("details").value.split("\n");
   Assert.equal(subject, "Issued to: CN=Mochitest client",
                "Actual and expected subject should be equal");
   Assert.equal(serialNum, "Serial number: 03",
@@ -85,18 +85,29 @@ function checkDialogContents(win, notBef
   Assert.equal(issuer,
                "Issued by: CN=Temporary Certificate Authority,O=Mozilla " +
                "Testing,OU=Profile Guided Optimization",
                "Actual and expected issuer should be equal");
   Assert.equal(tokenName, "Stored on: Software Security Device",
                "Actual and expected token name should be equal");
 }
 
+function findCertByCommonName(commonName) {
+  let certEnumerator = certDB.getCerts().getEnumerator();
+  while (certEnumerator.hasMoreElements()) {
+    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+    if (cert.commonName == commonName) {
+      return cert;
+    }
+  }
+  return null;
+}
+
 add_task(function* setup() {
-  cert = certDB.findCertByNickname("test client certificate");
+  cert = findCertByCommonName("Mochitest client");
   Assert.notEqual(cert, null, "Should be able to find the test client cert");
 });
 
 // Test that the contents of the dialog correspond to the details of the
 // provided cert.
 add_task(function* testContents() {
   let [win, retVals] = yield openClientAuthDialog(cert);
   checkDialogContents(win, cert.validity.notBeforeLocalTime,
--- a/security/manager/ssl/tests/mochitest/browser/head.js
+++ b/security/manager/ssl/tests/mochitest/browser/head.js
@@ -46,14 +46,14 @@ function pemToBase64(pem) {
  */
 function readCertificate(filename, trustString) {
   return OS.File.read(getTestFilePath(filename)).then(data => {
     let decoder = new TextDecoder();
     let pem = decoder.decode(data);
     let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                    .getService(Ci.nsIX509CertDB);
     let base64 = pemToBase64(pem);
-    certdb.addCertFromBase64(base64, trustString, "unused");
+    certdb.addCertFromBase64(base64, trustString);
     let cert = certdb.constructX509FromBase64(base64);
     gImportedCerts.push(cert);
     return cert;
   }, error => { throw error; });
 }
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -134,25 +134,21 @@ function readFile(file) {
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
   let certBytes = readFile(certFile);
-  let successful = false;
   try {
-    certdb.addCert(certBytes, trustString, null);
-    successful = true;
+    return certdb.addCert(certBytes, trustString);
   } catch (e) {}
-  if (!successful) {
-    // It might be PEM instead of DER.
-    certdb.addCertFromBase64(pemToBase64(certBytes), trustString, null);
-  }
+  // It might be PEM instead of DER.
+  return certdb.addCertFromBase64(pemToBase64(certBytes), trustString);
 }
 
 function constructCertFromFile(filename) {
   let certFile = do_get_file(filename, false);
   let certBytes = readFile(certFile);
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
   try {
--- a/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
+++ b/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
@@ -9,37 +9,36 @@
 // in the new trust bits being ignored.
 
 do_get_profile();
 var certDB = Cc["@mozilla.org/security/x509certdb;1"]
                .getService(Ci.nsIX509CertDB);
 
 function load_cert(cert, trust) {
   let file = "test_intermediate_basic_usage_constraints/" + cert + ".pem";
-  addCertFromFile(certDB, file, trust);
+  return addCertFromFile(certDB, file, trust);
 }
 
 function getDERString(cert) {
   let derString = "";
   for (let rawByte of cert.getRawDER({})) {
     derString += String.fromCharCode(rawByte);
   }
   return derString;
 }
 
 function run_test() {
   load_cert("ca", "CTu,CTu,CTu");
-  load_cert("int-limited-depth", "CTu,CTu,CTu");
+  let int_cert = load_cert("int-limited-depth", "CTu,CTu,CTu");
   let file = "test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem";
   let cert_pem = readFile(do_get_file(file));
   let ee = certDB.constructX509FromBase64(pemToBase64(cert_pem));
   checkCertErrorGeneric(certDB, ee, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   // Change the already existing intermediate certificate's trust using
-  // addCertFromBase64(). We use findCertByNickname first to ensure that the
-  // certificate already exists.
-  let int_cert = certDB.findCertByNickname("int-limited-depth");
+  // addCertFromBase64().
   notEqual(int_cert, null, "Intermediate cert should be in the cert DB");
   let base64_cert = btoa(getDERString(int_cert));
-  certDB.addCertFromBase64(base64_cert, "p,p,p", "ignored_argument");
+  let returnedEE = certDB.addCertFromBase64(base64_cert, "p,p,p");
+  notEqual(returnedEE, null, "addCertFromBase64 should return a certificate");
   checkCertErrorGeneric(certDB, ee, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLServer);
 }
--- a/security/manager/ssl/tests/unit/test_certDB_import.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import.js
@@ -63,30 +63,40 @@ function getCertAsByteArray(certPath) {
   let byteArray = [];
   for (let i = 0; i < certBytes.length; i++) {
     byteArray.push(certBytes.charCodeAt(i));
   }
 
   return byteArray;
 }
 
+function findCertByCommonName(commonName) {
+  let certEnumerator = gCertDB.getCerts().getEnumerator();
+  while (certEnumerator.hasMoreElements()) {
+    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+    if (cert.commonName == commonName) {
+      return cert;
+    }
+  }
+  return null;
+}
+
 function testImportCACert() {
   // Sanity check the CA cert is missing.
-  throws(() => gCertDB.findCertByNickname(CA_CERT_COMMON_NAME),
-         /NS_ERROR_FAILURE/,
-         "CA cert should not be in the database before import");
+  equal(findCertByCommonName(CA_CERT_COMMON_NAME), null,
+        "CA cert should not be in the database before import");
 
   // Import and check for success.
   let caArray = getCertAsByteArray("test_certDB_import/importedCA.pem");
   gCertDB.importCertificates(caArray, caArray.length, Ci.nsIX509Cert.CA_CERT,
                              gInterfaceRequestor);
   equal(gCACertImportDialogCount, 1,
         "Confirmation dialog for the CA cert should only be shown once");
 
-  let caCert = gCertDB.findCertByNickname(CA_CERT_COMMON_NAME);
+  let caCert = findCertByCommonName(CA_CERT_COMMON_NAME);
   notEqual(caCert, null, "CA cert should now be found in the database");
   ok(gCertDB.isCertTrusted(caCert, Ci.nsIX509Cert.CA_CERT,
                            Ci.nsIX509CertDB.TRUSTED_EMAIL),
      "CA cert should be trusted for e-mail");
 }
 
 function run_test() {
   // We have to set a password and login before we attempt to import anything.
--- a/security/manager/ssl/tests/unit/test_cert_signatures.js
+++ b/security/manager/ssl/tests/unit/test_cert_signatures.js
@@ -34,17 +34,17 @@ function readAndTamperWithNthByte(certif
 }
 
 // The signature on certificates appears last. This should modify the contents
 // of the signature such that it no longer validates correctly while still
 // resulting in a structurally valid certificate.
 const BYTE_IN_SIGNATURE = -8;
 function addSignatureTamperedCertificate(certificatePath) {
   let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
-  certdb.addCertFromBase64(base64, ",,", null);
+  certdb.addCertFromBase64(base64, ",,");
 }
 
 function ensureSignatureVerificationFailure(certificatePath) {
   let cert = constructCertFromFile(certificatePath);
   checkCertErrorGeneric(certdb, cert, SEC_ERROR_BAD_SIGNATURE,
                         certificateUsageSSLServer);
 }
 
@@ -69,17 +69,17 @@ function tamperWithSignatureAndEnsureVer
 // something from byte 15 to byte 30 will still get us what we want. Since the
 // serial number is a DER INTEGER and because it must be positive, it's best to
 // skip the first two bytes of the serial number so as to not run into any
 // issues there. Thus byte 17 is a good byte to modify.
 const BYTE_IN_SERIAL_NUMBER = 17;
 function addSerialNumberTamperedCertificate(certificatePath) {
   let base64 = readAndTamperWithNthByte(certificatePath,
                                         BYTE_IN_SERIAL_NUMBER);
-  certdb.addCertFromBase64(base64, ",,", null);
+  certdb.addCertFromBase64(base64, ",,");
 }
 
 function tamperWithSerialNumberAndEnsureVerificationFailure(certificatePath) {
   let base64 = readAndTamperWithNthByte(certificatePath,
                                         BYTE_IN_SERIAL_NUMBER);
   let cert = certdb.constructX509FromBase64(base64);
   checkCertErrorGeneric(certdb, cert, SEC_ERROR_BAD_SIGNATURE,
                         certificateUsageSSLServer);
--- a/security/manager/ssl/tests/unit/test_cert_trust.js
+++ b/security/manager/ssl/tests/unit/test_cert_trust.js
@@ -4,25 +4,20 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 "use strict";
 
 do_get_profile(); // must be called before getting nsIX509CertDB
 const certdb  = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
 
-var certList = [
-  'ee',
-  'int',
-  'ca',
-];
-
 function load_cert(cert_name, trust_string) {
   let cert_filename = cert_name + ".pem";
-  addCertFromFile(certdb, "test_cert_trust/" + cert_filename, trust_string);
+  return addCertFromFile(certdb, "test_cert_trust/" + cert_filename,
+                         trust_string);
 }
 
 function setup_basic_trusts(ca_cert, int_cert) {
   certdb.setCertTrust(ca_cert, Ci.nsIX509Cert.CA_CERT,
                       Ci.nsIX509CertDB.TRUSTED_SSL |
                       Ci.nsIX509CertDB.TRUSTED_EMAIL |
                       Ci.nsIX509CertDB.TRUSTED_OBJSIGN);
 
@@ -45,17 +40,17 @@ function test_ca_distrust(ee_cert, cert_
                         certificateUsageObjectSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_INADEQUATE_CERT_TYPE,
                         certificateUsageStatusResponder);
 
 
   // Test of active distrust. No usage should pass.
-  setCertTrust(cert_to_modify_trust, 'p,p,p');
+  setCertTrust(cert_to_modify_trust, "p,p,p");
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailSigner);
@@ -65,17 +60,17 @@ function test_ca_distrust(ee_cert, cert_
                         certificateUsageObjectSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageStatusResponder);
 
   // Trust set to T  -  trusted CA to issue client certs, where client cert is
   // usageSSLClient.
-  setCertTrust(cert_to_modify_trust, 'T,T,T');
+  setCertTrust(cert_to_modify_trust, "T,T,T");
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLServer);
 
   // XXX(Bug 982340)
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLClient);
@@ -96,17 +91,17 @@ function test_ca_distrust(ee_cert, cert_
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert,
                         isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                  : SEC_ERROR_INADEQUATE_CERT_TYPE,
                         certificateUsageStatusResponder);
 
 
   // Now tests on the SSL trust bit
-  setCertTrust(cert_to_modify_trust, 'p,C,C');
+  setCertTrust(cert_to_modify_trust, "p,C,C");
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLServer);
 
   //XXX(Bug 982340)
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
@@ -117,17 +112,17 @@ function test_ca_distrust(ee_cert, cert_
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageObjectSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageStatusResponder);
 
   // Inherited trust SSL
-  setCertTrust(cert_to_modify_trust, ',C,C');
+  setCertTrust(cert_to_modify_trust, ",C,C");
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   // XXX(Bug 982340)
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
@@ -138,17 +133,17 @@ function test_ca_distrust(ee_cert, cert_
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageObjectSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_INADEQUATE_CERT_TYPE,
                         certificateUsageStatusResponder);
 
   // Now tests on the EMAIL trust bit
-  setCertTrust(cert_to_modify_trust, 'C,p,C');
+  setCertTrust(cert_to_modify_trust, "C,p,C");
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailSigner);
@@ -158,17 +153,17 @@ function test_ca_distrust(ee_cert, cert_
                         certificateUsageObjectSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_INADEQUATE_CERT_TYPE,
                         certificateUsageStatusResponder);
 
 
   //inherited EMAIL Trust
-  setCertTrust(cert_to_modify_trust, 'C,,C');
+  setCertTrust(cert_to_modify_trust, "C,,C");
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
@@ -182,25 +177,31 @@ function test_ca_distrust(ee_cert, cert_
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageVerifyCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_INADEQUATE_CERT_TYPE,
                         certificateUsageStatusResponder);
 }
 
 
 function run_test() {
+  let certList = [
+    "ca",
+    "int",
+    "ee",
+  ];
+  let loadedCerts = [];
   for (let i = 0 ; i < certList.length; i++) {
-    load_cert(certList[i], ',,');
+    loadedCerts.push(load_cert(certList[i], ",,"));
   }
 
-  let ca_cert = certdb.findCertByNickname('ca');
-  notEqual(ca_cert, null, "CA cert should be in the cert DB");
-  let int_cert = certdb.findCertByNickname('int');
-  notEqual(int_cert, null, "Intermediate cert should be in the cert DB");
-  let ee_cert = certdb.findCertByNickname('ee');
-  notEqual(ee_cert, null, "EE cert should be in the cert DB");
+  let ca_cert = loadedCerts[0];
+  notEqual(ca_cert, null, "CA cert should have successfully loaded");
+  let int_cert = loadedCerts[1];
+  notEqual(int_cert, null, "Intermediate cert should have successfully loaded");
+  let ee_cert = loadedCerts[2];
+  notEqual(ee_cert, null, "EE cert should have successfully loaded");
 
   setup_basic_trusts(ca_cert, int_cert);
   test_ca_distrust(ee_cert, ca_cert, true);
 
   setup_basic_trusts(ca_cert, int_cert);
   test_ca_distrust(ee_cert, int_cert, false);
 }
--- a/security/manager/ssl/tests/unit/test_ev_certs.js
+++ b/security/manager/ssl/tests/unit/test_ev_certs.js
@@ -31,17 +31,17 @@ const certdb = Cc["@mozilla.org/security
 
 do_register_cleanup(() => {
   Services.prefs.clearUserPref("network.dns.localDomains");
   Services.prefs.clearUserPref("security.OCSP.enabled");
 });
 
 Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
 Services.prefs.setIntPref("security.OCSP.enabled", 1);
-addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,");
+const evroot = addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,");
 addCertFromFile(certdb, "test_ev_certs/non-evroot-ca.pem", "CTu,,");
 
 const SERVER_PORT = 8888;
 
 function failingOCSPResponder() {
   return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
 }
 
@@ -209,17 +209,16 @@ add_task(function* expectDVFallbackTests
   yield ensureVerifiesAsDV("test-and-cabforum-oid-ee-cabforum-oid-int-path");
 });
 
 // Test that removing the trust bits from an EV root causes verifications
 // relying on that root to fail (and then test that adding back the trust bits
 // causes the verifications to succeed again).
 add_task(function* evRootTrustTests() {
   clearOCSPCache();
-  let evroot = certdb.findCertByNickname("evroot");
   do_print("untrusting evroot");
   certdb.setCertTrust(evroot, Ci.nsIX509Cert.CA_CERT,
                       Ci.nsIX509CertDB.UNTRUSTED);
   yield ensureVerificationFails("test-oid-path", SEC_ERROR_UNKNOWN_ISSUER);
   do_print("re-trusting evroot");
   certdb.setCertTrust(evroot, Ci.nsIX509Cert.CA_CERT,
                       Ci.nsIX509CertDB.TRUSTED_SSL);
   yield ensureVerifiesAsEV("test-oid-path");
--- a/security/manager/ssl/tests/unit/test_getchain.js
+++ b/security/manager/ssl/tests/unit/test_getchain.js
@@ -62,21 +62,24 @@ function check_getchain(ee_cert, ssl_ca,
   // be consistent (the actual value is non-deterministic).
   check_matching_issuer_and_getchain(ee_cert.issuer.serialNumber, ee_cert);
 }
 
 function run_test() {
   clearOCSPCache();
   clearSessionCache();
 
+  let ee_cert = null;
   for (let cert of certList) {
-    addCertFromFile(certdb, `test_getchain/${cert}.pem`, ",,");
+    let result = addCertFromFile(certdb, `test_getchain/${cert}.pem`, ",,");
+    if (cert == "ee") {
+      ee_cert = result;
+    }
   }
 
-  let ee_cert = certdb.findCertByNickname('ee');
-  notEqual(ee_cert, null, "EE cert should be in the cert DB");
+  notEqual(ee_cert, null, "EE cert should have successfully loaded");
 
   let ca = get_ca_array();
 
   check_getchain(ee_cert, ca[1], ca[2]);
   // Swap ca certs to deal alternate trust settings.
   check_getchain(ee_cert, ca[2], ca[1]);
 }
--- a/security/manager/ssl/tests/unit/test_local_cert.js
+++ b/security/manager/ssl/tests/unit/test_local_cert.js
@@ -44,21 +44,25 @@ function removeCert(nickname) {
   });
 }
 
 add_task(function* () {
   // No master password, so no prompt required here
   ok(!certService.loginPromptRequired);
 
   let certA = yield getOrCreateCert(gNickname);
-  equal(certA.nickname, gNickname);
+  // The local cert service implementation takes the given nickname and uses it
+  // as the common name for the certificate it creates. nsIX509Cert.displayName
+  // uses the common name if it is present, so these should match. Should either
+  // implementation change to do something else, this won't necessarily work.
+  equal(certA.displayName, gNickname);
 
   // Getting again should give the same cert
   let certB = yield getOrCreateCert(gNickname);
-  equal(certB.nickname, gNickname);
+  equal(certB.displayName, gNickname);
 
   // Should be matching instances
   ok(certA.equals(certB));
 
   // Check a few expected attributes
   ok(certA.isSelfSigned);
   equal(certA.certType, Ci.nsIX509Cert.USER_CERT);
 
--- a/security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js
+++ b/security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js
@@ -40,30 +40,28 @@ function run_test() {
     "MAowCAYGBACORgEBMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQUFAAOCAQEA" +
     "v2V+nnYYMIgabmmgHx49CtlZIHdGS3TuWKXw130xFhbXDnNhEbx3alaskNsvjQQR" +
     "Lqs1ZwKy58yynse+eJYHqenmHDACpAfVpCF9PXC/mDarVsoQw7NTcUpsAFhSd/zT" +
     "v9jIf3twECyxx/RVzONVcob7nPePESHiKoG4FbtcuUh0wSHvCmTwRIQqPDCIuHcF" +
     "StSt3Jr9iXcbXEhe4mSccOZ8N+r7Rv3ncKcevlRl7uFfDKDTyd43SZeRS/7J8KRf" +
     "hD/h2nawrCFwc5gJW10aLJGFL/mcS7ViAIT9HCVk23j4TuBjsVmnZ0VKxB5edux+" +
     "LIEqtU428UVHZWU/I5ngLw==");
 
-  equal(cert.nickname, "(no nickname)",
-        "Actual and expected nickname should match");
   equal(cert.emailAddress, "ludek.rasek@centrum.cz",
         "Actual and expected emailAddress should match");
   equal(cert.subjectName, "serialNumber=ICA - 10003769,SN=Rašek,name=Luděk Rašek,initials=LR,givenName=Luděk,E=ludek.rasek@centrum.cz,L=\"Pacov, Nádražní 769\",ST=Vysočina,CN=Luděk Rašek,C=CZ",
         "Actual and expected subjectName should match");
   equal(cert.commonName, "Luděk Rašek",
         "Actual and expected commonName should match");
   equal(cert.organization, "",
         "Actual and expected organization should match");
   equal(cert.organizationalUnit, "",
         "Actual and expected organizationalUnit should match");
-  equal(cert.windowTitle, "Luděk Rašek",
-        "Actual and expected windowTitle should match");
+  equal(cert.displayName, "Luděk Rašek",
+        "Actual and expected displayName should match");
   equal(cert.issuerName, "OU=Akreditovaný poskytovatel certifikačních služeb,O=První certifikační autorita a.s.,L=\"Podvinný mlýn 2178/6, 190 00 Praha 9\",C=CZ,CN=I.CA - Qualified root certificate (kvalifikovaný certifikát poskytovatele) - PSEUDONYM",
         "Actual and expected issuerName should match");
   equal(cert.issuerCommonName, "I.CA - Qualified root certificate (kvalifikovaný certifikát poskytovatele) - PSEUDONYM",
         "Actual and expected issuerCommonName should match");
   equal(cert.issuerOrganization, "První certifikační autorita a.s.",
         "Actual and expected issuerOrganization should match");
   equal(cert.issuerOrganizationUnit, "Akreditovaný poskytovatel certifikačních služeb",
         "Actual and expected issuerOrganizationUnit should match");
--- a/security/manager/tools/dumpGoogleRoots.js
+++ b/security/manager/tools/dumpGoogleRoots.js
@@ -53,30 +53,21 @@ function downloadRoots() {
     if (line == "-----BEGIN CERTIFICATE-----") {
       readingRoot = true;
     }
   }
   return roots;
 }
 
 function makeFormattedNickname(cert) {
-  if (cert.nickname.startsWith("Builtin Object Token:")) {
-    return `"${cert.nickname.substring("Builtin Object Token:".length)}"`;
+  if (cert.isBuiltInRoot) {
+    return `"${cert.displayName}"`;
   }
   // Otherwise, this isn't a built-in and we have to comment it out.
-  if (cert.commonName) {
-    return `// "${cert.commonName}"`;
-  }
-  if (cert.organizationalUnit) {
-    return `// "${cert.organizationalUnit}"`;
-  }
-  if (cert.organization) {
-    return `// "${cert.organization}"`;
-  }
-  throw new Error(`couldn't make nickname for ${cert.subjectName}`);
+  return `// "${cert.displayName}"`;
 }
 
 var roots = downloadRoots();
 var rootNicknames = [];
 for (var root of roots) {
   rootNicknames.push(makeFormattedNickname(root));
 }
 rootNicknames.sort(function(rootA, rootB) {
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -23,17 +23,16 @@ var { 'classes': Cc, 'interfaces': Ci, '
 
 var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 var { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 var gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
                 .getService(Ci.nsIX509CertDB);
 
-const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
 const SHA256_PREFIX = "sha256/";
 const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_";
 
 // Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable)
 const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14;
 
 const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
 " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
@@ -397,17 +396,17 @@ function loadNSSCertinfo(extraCertificat
   let enumerator = allCerts.getEnumerator();
   let certNameToSKD = {};
   let certSKDToName = {};
   while (enumerator.hasMoreElements()) {
     let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
     if (!isCertBuiltIn(cert)) {
       continue;
     }
-    let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length);
+    let name = cert.displayName;
     let SKD = cert.sha256SubjectPublicKeyInfoDigest;
     certNameToSKD[name] = SKD;
     certSKDToName[SKD] = name;
   }
 
   for (let cert of extraCertificates) {
     let name = cert.commonName;
     let SKD = cert.sha256SubjectPublicKeyInfoDigest;
--- a/services/sync/modules/engines.js
+++ b/services/sync/modules/engines.js
@@ -648,17 +648,17 @@ Engine.prototype = {
   // Local 'constant'.
   // Signal to the engine that processing further records is pointless.
   eEngineAbortApplyIncoming: "error.engine.abort.applyincoming",
 
   // Should we keep syncing if we find a record that cannot be uploaded (ever)?
   // If this is false, we'll throw, otherwise, we'll ignore the record and
   // continue. This currently can only happen due to the record being larger
   // than the record upload limit.
-  allowSkippedRecord: false,
+  allowSkippedRecord: true,
 
   get prefName() {
     return this.name;
   },
 
   get enabled() {
     return Svc.Prefs.get("engine." + this.prefName, false);
   },
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -270,16 +270,17 @@ BookmarksEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _recordObj: PlacesItem,
   _storeObj: BookmarksStore,
   _trackerObj: BookmarksTracker,
   version: 2,
   _defaultSort: "index",
 
   syncPriority: 4,
+  allowSkippedRecord: false,
 
   // A diagnostic helper to get the string value for a bookmark's URL given
   // its ID. Always returns a string - on error will return a string in the
   // form of "<description of error>" as this is purely for, eg, logging.
   // (This means hitting the DB directly and we don't bother using a cached
   // statement - we should rarely hit this.)
   _getStringUrlForId(id) {
     let url;
--- a/services/sync/modules/engines/clients.js
+++ b/services/sync/modules/engines/clients.js
@@ -76,16 +76,17 @@ this.ClientEngine = function ClientEngin
   // Reset the last sync timestamp on every startup so that we fetch all clients
   this.resetLastSync();
 }
 ClientEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: ClientStore,
   _recordObj: ClientsRec,
   _trackerObj: ClientsTracker,
+  allowSkippedRecord: false,
 
   // Always sync client data as it controls other sync behavior
   get enabled() {
     return true;
   },
 
   get lastRecordUpload() {
     return Svc.Prefs.get(this.name + ".lastRecordUpload", 0);
--- a/services/sync/modules/engines/extension-storage.js
+++ b/services/sync/modules/engines/extension-storage.js
@@ -36,16 +36,17 @@ this.ExtensionStorageEngine = function E
 ExtensionStorageEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _trackerObj: ExtensionStorageTracker,
   // we don't need these since we implement our own sync logic
   _storeObj: undefined,
   _recordObj: undefined,
 
   syncPriority: 10,
+  allowSkippedRecord: false,
 
   _sync: function () {
     return Async.promiseSpinningly(ExtensionStorageSync.syncAll());
   },
 
   get enabled() {
     // By default, we sync extension storage if we sync addons. This
     // lets us simplify the UX since users probably don't consider
--- a/services/sync/modules/engines/forms.js
+++ b/services/sync/modules/engines/forms.js
@@ -106,17 +106,16 @@ this.FormEngine = function FormEngine(se
   SyncEngine.call(this, "Forms", service);
 }
 FormEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: FormStore,
   _trackerObj: FormTracker,
   _recordObj: FormRec,
   applyIncomingBatchSize: FORMS_STORE_BATCH_SIZE,
-  allowSkippedRecord: true,
 
   syncPriority: 6,
 
   get prefName() {
     return "history";
   },
 
   _findDupe: function _findDupe(item) {
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -37,17 +37,16 @@ this.HistoryEngine = function HistoryEng
 }
 HistoryEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _recordObj: HistoryRec,
   _storeObj: HistoryStore,
   _trackerObj: HistoryTracker,
   downloadLimit: MAX_HISTORY_DOWNLOAD,
   applyIncomingBatchSize: HISTORY_STORE_BATCH_SIZE,
-  allowSkippedRecord: true,
 
   syncPriority: 7,
 
   _processIncoming: function (newitems) {
     // We want to notify history observers that a batch operation is underway
     // so they don't do lots of work for each incoming record.
     let observers = PlacesUtils.history.getObservers();
     function notifyHistoryObservers(notification) {
--- a/services/sync/modules/engines/prefs.js
+++ b/services/sync/modules/engines/prefs.js
@@ -37,16 +37,17 @@ this.PrefsEngine = function PrefsEngine(
 PrefsEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: PrefStore,
   _trackerObj: PrefTracker,
   _recordObj: PrefRec,
   version: 2,
 
   syncPriority: 1,
+  allowSkippedRecord: false,
 
   getChangedIDs: function () {
     // No need for a proper timestamp (no conflict resolution needed).
     let changedIDs = {};
     if (this._tracker.modified)
       changedIDs[PREFS_GUID] = 0;
     return changedIDs;
   },
--- a/services/sync/tests/unit/test_syncengine_sync.js
+++ b/services/sync/tests/unit/test_syncengine_sync.js
@@ -1547,17 +1547,17 @@ add_task(async function test_uploadOutgo
 
 add_task(async function test_uploadOutgoing_largeRecords() {
   _("SyncEngine._uploadOutgoing throws on records larger than MAX_UPLOAD_BYTES");
 
   Service.identity.username = "foo";
   let collection = new ServerCollection();
 
   let engine = makeRotaryEngine();
-
+  engine.allowSkippedRecord = false;
   engine._store.items["large-item"] = "Y".repeat(MAX_UPLOAD_BYTES*2);
   engine._tracker.addChangedID("large-item", 0);
   collection.insert("large-item");
 
 
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
--- a/testing/firefox-ui/tests/functional/private_browsing/test_about_private_browsing.py
+++ b/testing/firefox-ui/tests/functional/private_browsing/test_about_private_browsing.py
@@ -48,13 +48,13 @@ class TestAboutPrivateBrowsing(Puppeteer
             self.assertTrue(pb_window.is_private)
 
             def tab_opener(tab):
                 with tab.marionette.using_context('content'):
                     link = tab.marionette.find_element(By.ID, 'learnMore')
                     link.click()
 
             tab = pb_window.tabbar.open_tab(trigger=tab_opener)
-            Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
                 lambda _: tab.location == self.pb_url)
 
         finally:
             pb_window.close()
--- a/testing/firefox-ui/tests/functional/security/test_mixed_script_content_blocking.py
+++ b/testing/firefox-ui/tests/functional/security/test_mixed_script_content_blocking.py
@@ -40,17 +40,17 @@ class TestMixedScriptContentBlocking(Pup
         else:
             color, identity, state = (
                 'rgb(255, 0, 0)',
                 'unknownIdentity mixedActiveContent',
                 'unblocked'
             )
 
         # First call to Wait() needs a longer timeout due to the reload of the web page.
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda _: self.locationbar.identity_box.get_attribute('className') == identity,
             message='Expected identity "{}" not found'.format(identity)
         )
 
         with self.marionette.using_context('content'):
             for identifier, description in self.test_elements:
                 el = self.marionette.find_element(By.ID, identifier)
                 Wait(self.marionette).until(
--- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_notification.py
+++ b/testing/firefox-ui/tests/functional/security/test_safe_browsing_notification.py
@@ -89,17 +89,17 @@ class TestSafeBrowsingNotificationBar(Pu
                 time.sleep(1)
                 self.check_ignore_warning_button(unsafe_page)
                 self.check_x_button()
 
     def check_ignore_warning_button(self, unsafe_page):
         button = self.marionette.find_element(By.ID, 'ignoreWarningButton')
         button.click()
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             expected.element_present(By.ID, 'main-feature'),
             message='Expected target element "#main-feature" has not been found',
         )
         self.assertEquals(self.marionette.get_url(), self.browser.get_final_url(unsafe_page))
 
         # Clean up here since the permission gets set in this function
         self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing')
 
@@ -108,42 +108,42 @@ class TestSafeBrowsingNotificationBar(Pu
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
             label = self.browser.get_property(button_property)
             button = (self.marionette.find_element(By.ID, 'content')
                       .find_element('anon attribute', {'label': label}))
 
             self.browser.tabbar.open_tab(lambda _: button.click())
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: report_page in mn.get_url(),
             message='The expected safe-browsing report page has not been opened',
         )
 
         with self.marionette.using_context('chrome'):
             self.browser.tabbar.close_tab()
 
     def check_get_me_out_of_here_button(self):
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
             label = self.browser.get_property('safebrowsing.getMeOutOfHereButton.label')
             button = (self.marionette.find_element(By.ID, 'content')
                       .find_element('anon attribute', {'label': label}))
             button.click()
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: self.browser.default_homepage in mn.get_url(),
             message='The default home page has not been loaded',
         )
 
     def check_x_button(self):
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
             button = (self.marionette.find_element(By.ID, 'content')
                       .find_element('anon attribute', {'value': 'blocked-badware-page'})
                       .find_element('anon attribute',
                                     {'class': 'messageCloseButton close-icon tabbable'}))
             button.click()
 
-            Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
                 expected.element_stale(button),
                 message='The notification bar has not been closed',
             )
--- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_warning_pages.py
+++ b/testing/firefox-ui/tests/functional/security/test_safe_browsing_warning_pages.py
@@ -64,48 +64,48 @@ class TestSafeBrowsingWarningPages(Puppe
                 # Wait for the DOM to receive events for about:blocked
                 time.sleep(1)
                 self.check_ignore_warning_button(unsafe_page)
 
     def check_get_me_out_of_here_button(self, unsafe_page):
         button = self.marionette.find_element(By.ID, "getMeOutButton")
         button.click()
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: self.browser.default_homepage in mn.get_url())
 
     def check_report_button(self, unsafe_page):
         # Get the URL of the support site for phishing and malware. This may result in a redirect.
         with self.marionette.using_context('chrome'):
             url = self.marionette.execute_script("""
               Components.utils.import("resource://gre/modules/Services.jsm");
               return Services.urlFormatter.formatURLPref("app.support.baseURL")
                                                          + "phishing-malware";
             """)
 
         button = self.marionette.find_element(By.ID, "reportButton")
         button.click()
 
         # Wait for the button to become stale, whereby a longer timeout is needed
         # here to not fail in case of slow connections.
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             expected.element_stale(button))
 
         # Wait for page load to be completed, so we can verify the URL even if a redirect happens.
         # TODO: Bug 1140470: use replacement for mozmill's waitforPageLoad
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: mn.execute_script('return document.readyState == "DOMContentLoaded" ||'
                                          '       document.readyState == "complete";')
         )
 
         # check that our current url matches the final url we expect
         self.assertEquals(self.marionette.get_url(), self.browser.get_final_url(url))
 
     def check_ignore_warning_button(self, unsafe_page):
         button = self.marionette.find_element(By.ID, 'ignoreWarningButton')
         button.click()
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             expected.element_present(By.ID, 'main-feature'))
         self.assertEquals(self.marionette.get_url(), self.browser.get_final_url(unsafe_page))
 
         # Clean up by removing safe browsing permission for unsafe page
         self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing')
--- a/testing/firefox-ui/tests/functional/security/test_ssl_disabled_error_page.py
+++ b/testing/firefox-ui/tests/functional/security/test_ssl_disabled_error_page.py
@@ -51,10 +51,10 @@ class TestSSLDisabledErrorPage(Puppeteer
                           short_description.get_property('textContent'))
             self.assertIn('mozqa.com', short_description.get_property('textContent'))
 
             # Verify that the "Restore" button appears and works
             reset_button = self.marionette.find_element(By.ID, 'prefResetButton')
             reset_button.click()
 
             # With the preferences reset, the page has to load correctly
-            Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
                 expected.element_present(By.LINK_TEXT, 'http://quality.mozilla.org'))
--- a/testing/firefox-ui/tests/functional/security/test_submit_unencrypted_info_warning.py
+++ b/testing/firefox-ui/tests/functional/security/test_submit_unencrypted_info_warning.py
@@ -41,25 +41,25 @@ class TestSubmitUnencryptedInfoWarning(P
             message = self.browser.get_property('formPostSecureToInsecureWarning.message')
             message = message.replace('##', '\n\n')
 
             # Wait for the warning, verify the expected text matches warning, accept the warning
             warning = Alert(self.marionette)
             try:
                 Wait(self.marionette,
                      ignored_exceptions=NoAlertPresentException,
-                     timeout=self.browser.timeout_page_load).until(
+                     timeout=self.marionette.timeout.page_load).until(
                     lambda _: warning.text == message)
             finally:
                 warning.accept()
 
             # Wait for the search box to become stale, then wait for the page to be reloaded.
             Wait(self.marionette).until(expected.element_stale(searchbox))
 
             # TODO: Bug 1140470: use replacement for mozmill's waitforPageLoad
-            Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
                 lambda mn: mn.execute_script('return document.readyState == "DOMContentLoaded" ||'
                                              '       document.readyState == "complete";')
             )
 
             # Check that search_term contains the test string.
             search_term = self.marionette.find_element(By.ID, 'search-term')
             self.assertEqual(search_term.get_property('textContent'), self.test_string)
--- a/testing/firefox-ui/tests/functional/security/test_untrusted_connection_error_page.py
+++ b/testing/firefox-ui/tests/functional/security/test_untrusted_connection_error_page.py
@@ -26,10 +26,10 @@ class TestUntrustedConnectionErrorPage(P
         self.assertRaises(MarionetteException, self.marionette.navigate, self.url)
 
         # Wait for the DOM to receive events
         time.sleep(1)
 
         button = self.marionette.find_element(By.ID, "returnButton")
         button.click()
 
-        Wait(self.marionette, timeout=self.browser.timeout_page_load).until(
+        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: target_url == self.marionette.get_url())
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
@@ -40,19 +40,16 @@ class BrowserWindow(BaseWindow):
     ]
 
     def __init__(self, *args, **kwargs):
         super(BrowserWindow, self).__init__(*args, **kwargs)
 
         self._navbar = None
         self._tabbar = None
 
-        # Timeout for loading remote web pages
-        self.timeout_page_load = 30
-
     @property
     def default_homepage(self):
         """The default homepage as used by the current locale.
 
         :returns: The default homepage for the current locale.
         """
         return self._prefs.get_pref('browser.startup.homepage', interface='nsIPrefLocalizedString')
 
--- a/testing/mozharness/configs/android/androidarm_4_3.py
+++ b/testing/mozharness/configs/android/androidarm_4_3.py
@@ -352,17 +352,17 @@ config = {
                 "--xre-path=%(xre_path)s",
                 "--dm_trans=adb",
                 "--localBinDir=../bin",
                 "--apk=%(installer_path)s",
                 ".",
             ],
         },
         "marionette": {
-            "run_filename": os.path.join("marionette","runtests.py"),
+            "run_filename": os.path.join("harness", "marionette", "runtests.py"),
             "testsdir": "marionette",
             "options": [
                 "--emulator",
                 "--app=fennec",
                 "--package=%(app)s",
                 "--address=%(address)s",
                 "%(test_manifest)s",
                 "--disable-e10s",
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -321,30 +321,34 @@ class Talos(TestingMixin, MercurialScrip
                     self.validate_suite()
                 else:
                     self.fatal("Suite name not provided")
         else:
             # talos initiated in production via mozharness
             self.suite = self.config['suite']
 
         # now that have the suite name, check if pageset is required, if so download it
+        # the --no-download option will override this
         if self.query_pagesets_name():
-            self.info("Downloading pageset with tooltool...")
-            self.src_talos_webdir = os.path.join(self.talos_path, 'talos')
-            src_talos_pageset = os.path.join(self.src_talos_webdir, 'tests')
-            manifest_file = os.path.join(self.talos_path, 'tp5n-pageset.manifest')
-            self.tooltool_fetch(
-                manifest_file,
-                output_dir=src_talos_pageset,
-                cache=self.config.get('tooltool_cache')
-            )
-            archive = os.path.join(src_talos_pageset, self.pagesets_name)
-            unzip = self.query_exe('unzip')
-            unzip_cmd = [unzip, '-q', '-o', archive, '-d', src_talos_pageset]
-            self.run_command(unzip_cmd, halt_on_failure=True)
+            if '--no-download' not in self.config['talos_extra_options']:
+                self.info("Downloading pageset with tooltool...")
+                self.src_talos_webdir = os.path.join(self.talos_path, 'talos')
+                src_talos_pageset = os.path.join(self.src_talos_webdir, 'tests')
+                manifest_file = os.path.join(self.talos_path, 'tp5n-pageset.manifest')
+                self.tooltool_fetch(
+                    manifest_file,
+                    output_dir=src_talos_pageset,
+                    cache=self.config.get('tooltool_cache')
+                )
+                archive = os.path.join(src_talos_pageset, self.pagesets_name)
+                unzip = self.query_exe('unzip')
+                unzip_cmd = [unzip, '-q', '-o', archive, '-d', src_talos_pageset]
+                self.run_command(unzip_cmd, halt_on_failure=True)
+            else:
+                self.info("Not downloading pageset because the no-download option was specified")
 
     # Action methods. {{{1
     # clobber defined in BaseScript
     # read_buildbot_config defined in BuildbotMixin
 
     def download_and_extract(self, extract_dirs=None, suite_categories=None):
         return super(Talos, self).download_and_extract(
             suite_categories=['common', 'talos']
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -150,17 +150,17 @@ class AndroidEmulatorTest(BlobUploadMixi
         dirs['abs_modules_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'modules')
         dirs['abs_blob_upload_dir'] = os.path.join(
             abs_dirs['abs_work_dir'], 'blobber_upload_dir')
         dirs['abs_emulator_dir'] = abs_dirs['abs_work_dir']
         dirs['abs_mochitest_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'mochitest')
         dirs['abs_marionette_dir'] = os.path.join(
-            dirs['abs_test_install_dir'], 'marionette', 'marionette')
+            dirs['abs_test_install_dir'], 'marionette', 'harness', 'marionette')
         dirs['abs_marionette_tests_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'marionette', 'tests', 'testing',
             'marionette', 'harness', 'marionette', 'tests')
         dirs['abs_avds_dir'] = self.config.get("avds_dir", "/home/cltbld/.android")
 
         for key in dirs.keys():
             if key not in abs_dirs:
                 abs_dirs[key] = dirs[key]
--- a/testing/talos/talos/cmdline.py
+++ b/testing/talos/talos/cmdline.py
@@ -122,16 +122,18 @@ def create_parser(mach_interface=False):
     add_arg('--tptimeout', type=int,
             help='number of milliseconds to wait for a load event after'
                  ' calling loadURI before timing out')
     add_arg('--tppagecycles', type=int,
             help='number of pageloader cycles to run for each page in'
                  ' the manifest')
     add_arg('--tpdelay', type=int,
             help="length of the pageloader delay")
+    add_arg('--no-download', action="store_true", dest="no_download",
+            help="Do not download the talos test pagesets")
     add_arg('--sourcestamp',
             help='Specify the hg revision or sourcestamp for the changeset'
                  ' we are testing.  This will use the value found in'
                  ' application.ini if it is not specified.')
     add_arg('--repository',
             help='Specify the url for the repository we are testing. '
                  'This will use the value found in application.ini if'
                  ' it is not specified.')