Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 09 Jul 2016 09:01:35 -0700
changeset 344430 489a2db1b1657f31916d33cac2862544a1c80de0
parent 344429 4ee47bdad6af2b140e9855d621b00e7cfd64b2e0 (current diff)
parent 344359 679118259e91f40d4a8f968f03ec4cff066cdb5b (diff)
child 344431 fd6e8d0e44befaf5a6bf38945f8529235a4ac0b9
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i
accessible/tests/mochitest/dumbfile.xpi
browser/base/content/browser-fullScreen.js
browser/themes/shared/glyphs.svg
dom/base/nsDocument.cpp
dom/media/tests/mochitest/test_zmedia_cleanup.html
--- a/accessible/tests/mochitest/a11y.ini
+++ b/accessible/tests/mochitest/a11y.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 support-files =
   ../../../dom/media/test/bug461281.ogg
-  dumbfile.xpi
+  dumbfile.zip
   formimage.png
   letters.gif
   moz.png
   longdesc_src.html
   *.js
   treeview.css
 
 [test_aria_token_attrs.html]
rename from accessible/tests/mochitest/dumbfile.xpi
rename to accessible/tests/mochitest/dumbfile.zip
--- a/accessible/tests/mochitest/states/a11y.ini
+++ b/accessible/tests/mochitest/states/a11y.ini
@@ -1,17 +1,17 @@
 [DEFAULT]
 support-files =
   z_frames.html
   z_frames_article.html
   z_frames_checkbox.html
   z_frames_textbox.html
   z_frames_update.html
   !/accessible/tests/mochitest/*.js
-  !/accessible/tests/mochitest/dumbfile.xpi
+  !/accessible/tests/mochitest/dumbfile.zip
   !/accessible/tests/mochitest/formimage.png
   !/accessible/tests/mochitest/treeview.css
 
 [test_aria.html]
 [test_aria.xul]
 [test_aria_imgmap.html]
 [test_aria_widgetitems.html]
 [test_buttons.html]
--- a/accessible/tests/mochitest/states/test_doc_busy.html
+++ b/accessible/tests/mochitest/states/test_doc_busy.html
@@ -69,11 +69,11 @@
      title="Missing busy state change event when downloading files"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=446469">Bug 446469</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <a id="link" href="http://example.com/a11y/accessible/tests/mochitest/dumbfile.xpi">a file</a>
+  <a id="link" href="http://example.com/a11y/accessible/tests/mochitest/dumbfile.zip">a file</a>
 </body>
 </html>
rename from browser/base/content/browser-fullScreen.js
rename to browser/base/content/browser-fullScreenAndPointerLock.js
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -1,13 +1,255 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * 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/. */
 
+var PointerlockFsWarning = {
+
+  _element: null,
+  _origin: null,
+
+  init: function() {
+    this.Timeout.prototype = {
+      start: function() {
+        this.cancel();
+        this._id = setTimeout(() => this._handle(), this._delay);
+      },
+      cancel: function() {
+        if (this._id) {
+          clearTimeout(this._id);
+          this._id = 0;
+        }
+      },
+      _handle: function() {
+        this._id = 0;
+        this._func();
+      },
+      get delay() {
+        return this._delay;
+      }
+    };
+  },
+
+  /**
+   * Timeout object for managing timeout request. If it is started when
+   * the previous call hasn't finished, it would automatically cancelled
+   * the previous one.
+   */
+  Timeout: function(func, delay) {
+    this._id = 0;
+    this._func = func;
+    this._delay = delay;
+  },
+
+  showPointerLock: function(aOrigin) {
+    if (!document.fullscreen) {
+      let timeout = gPrefService.getIntPref("pointer-lock-api.warning.timeout");
+      this.show(aOrigin, "pointerlock-warning", timeout, 0);
+    }
+  },
+
+  showFullScreen: function(aOrigin) {
+    let timeout = gPrefService.getIntPref("full-screen-api.warning.timeout");
+    let delay = gPrefService.getIntPref("full-screen-api.warning.delay");
+    this.show(aOrigin, "fullscreen-warning", timeout, delay);
+  },
+
+  // Shows a warning that the site has entered fullscreen or
+  // pointer lock for a short duration.
+  show: function(aOrigin, elementId, timeout, delay) {
+
+    if (!this._element) {
+      this._element = document.getElementById(elementId);
+      // Setup event listeners
+      this._element.addEventListener("transitionend", this);
+      window.addEventListener("mousemove", this, true);
+      // The timeout to hide the warning box after a while.
+      this._timeoutHide = new this.Timeout(() => {
+        this._state = "hidden";
+      }, timeout);
+      // The timeout to show the warning box when the pointer is at the top
+      this._timeoutShow = new this.Timeout(() => {
+        this._state = "ontop";
+        this._timeoutHide.start();
+      }, delay);
+    }
+
+    // Set the strings on the warning UI.
+    if (aOrigin) {
+      this._origin = aOrigin;
+    }
+    let uri = BrowserUtils.makeURI(this._origin);
+    let host = null;
+    try {
+      host = uri.host;
+    } catch (e) { }
+    let textElem = this._element.querySelector(".pointerlockfswarning-domain-text");
+    if (!host) {
+      textElem.setAttribute("hidden", true);
+    } else {
+      textElem.removeAttribute("hidden");
+      let hostElem = this._element.querySelector(".pointerlockfswarning-domain");
+      // Document's principal's URI has a host. Display a warning including it.
+      let utils = {};
+      Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
+      hostElem.textContent = utils.DownloadUtils.getURIHost(uri.spec)[0];
+    }
+
+    this._element.dataset.identity =
+      gIdentityHandler.pointerlockFsWarningClassName;
+
+    // User should be allowed to explicitly disable
+    // the prompt if they really want.
+    if (this._timeoutHide.delay <= 0) {
+      return;
+    }
+
+    // Explicitly set the last state to hidden to avoid the warning
+    // box being hidden immediately because of mousemove.
+    this._state = "onscreen";
+    this._lastState = "hidden";
+    this._timeoutHide.start();
+  },
+
+  close: function() {
+    if (!this._element) {
+      return;
+    }
+    // Cancel any pending timeout
+    this._timeoutHide.cancel();
+    this._timeoutShow.cancel();
+    // Reset state of the warning box
+    this._state = "hidden";
+    this._element.setAttribute("hidden", true);
+    // Remove all event listeners
+    this._element.removeEventListener("transitionend", this);
+    window.removeEventListener("mousemove", this, true);
+    // Clear fields
+    this._element = null;
+    this._timeoutHide = null;
+    this._timeoutShow = null;
+
+    // Ensure focus switches away from the (now hidden) warning box.
+    // If the user clicked buttons in the warning box, it would have
+    // been focused, and any key events would be directed at the (now
+    // hidden) chrome document instead of the target document.
+    gBrowser.selectedBrowser.focus();
+  },
+
+  // State could be one of "onscreen", "ontop", "hiding", and
+  // "hidden". Setting the state to "onscreen" and "ontop" takes
+  // effect immediately, while setting it to "hidden" actually
+  // turns the state to "hiding" before the transition finishes.
+  _lastState: null,
+  _STATES: ["hidden", "ontop", "onscreen"],
+  get _state() {
+    for (let state of this._STATES) {
+      if (this._element.hasAttribute(state)) {
+        return state;
+      }
+    }
+    return "hiding";
+  },
+  set _state(newState) {
+    let currentState = this._state;
+    if (currentState == newState) {
+      return;
+    }
+    if (currentState != "hiding") {
+      this._lastState = currentState;
+      this._element.removeAttribute(currentState);
+    }
+    if (newState != "hidden") {
+      if (currentState != "hidden") {
+        this._element.setAttribute(newState, true);
+      } else {
+        // When the previous state is hidden, the display was none,
+        // thus no box was constructed. We need to wait for the new
+        // display value taking effect first, otherwise, there won't
+        // be any transition. Since requestAnimationFrame callback is
+        // generally triggered before any style flush and layout, we
+        // should wait for the second animation frame.
+        requestAnimationFrame(() => {
+          requestAnimationFrame(() => {
+            if (this._element) {
+              this._element.setAttribute(newState, true);
+            }
+          });
+        });
+      }
+    }
+  },
+
+  handleEvent: function(event) {
+    switch (event.type) {
+    case "mousemove": {
+      let state = this._state;
+      if (state == "hidden") {
+        // If the warning box is currently hidden, show it after
+        // a short delay if the pointer is at the top.
+        if (event.clientY != 0) {
+          this._timeoutShow.cancel();
+        } else if (this._timeoutShow.delay >= 0) {
+          this._timeoutShow.start();
+        }
+      } else {
+        let elemRect = this._element.getBoundingClientRect();
+        if (state == "hiding" && this._lastState != "hidden") {
+          // If we are on the hiding transition, and the pointer
+          // moved near the box, restore to the previous state.
+          if (event.clientY <= elemRect.bottom + 50) {
+            this._state = this._lastState;
+            this._timeoutHide.start();
+          }
+        } else if (state == "ontop" || this._lastState != "hidden") {
+          // State being "ontop" or the previous state not being
+          // "hidden" indicates this current warning box is shown
+          // in response to user's action. Hide it immediately when
+          // the pointer leaves that area.
+          if (event.clientY > elemRect.bottom + 50) {
+            this._state = "hidden";
+            this._timeoutHide.cancel();
+          }
+        }
+      }
+      break;
+    }
+    case "transitionend": {
+      if (this._state == "hiding") {
+        this._element.setAttribute("hidden", true);
+      }
+      break;
+    }
+    }
+  }
+};
+
+var PointerLock = {
+
+  init: function() {
+    window.messageManager.addMessageListener("PointerLock:Entered", this);
+    window.messageManager.addMessageListener("PointerLock:Exited", this);
+  },
+
+  receiveMessage: function(aMessage) {
+    switch (aMessage.name) {
+      case "PointerLock:Entered": {
+        PointerlockFsWarning.showPointerLock(aMessage.data.originNoSuffix);
+        break;
+      }
+      case "PointerLock:Exited": {
+        PointerlockFsWarning.close();
+        break;
+      }
+    }
+  }
+};
+
 var FullScreen = {
   _MESSAGES: [
     "DOMFullscreen:Request",
     "DOMFullscreen:NewOrigin",
     "DOMFullscreen:Exit",
     "DOMFullscreen:Painted",
   ],
 
@@ -19,18 +261,16 @@ var FullScreen = {
                             /* wantsUntrusted */ false);
     window.addEventListener("MozDOMFullscreen:Exited", this,
                             /* useCapture */ true,
                             /* wantsUntrusted */ false);
     for (let type of this._MESSAGES) {
       window.messageManager.addMessageListener(type, this);
     }
 
-    this._WarningBox.init();
-
     if (window.fullScreen)
       this.toggle();
   },
 
   uninit: function() {
     for (let type of this._MESSAGES) {
       window.messageManager.removeMessageListener(type, this);
     }
@@ -103,21 +343,16 @@ var FullScreen = {
   },
 
   exitDomFullScreen : function() {
     document.exitFullscreen();
   },
 
   handleEvent: function (event) {
     switch (event.type) {
-      case "activate":
-        if (document.fullscreenElement) {
-          this._WarningBox.show();
-        }
-        break;
       case "fullscreen":
         this.toggle();
         break;
       case "MozDOMFullscreen:Entered": {
         // The event target is the element which requested the DOM
         // fullscreen. If we were entering DOM fullscreen for a remote
         // browser, the target would be `gBrowser` and the original
         // target would be the browser which was the parameter of
@@ -145,36 +380,41 @@ var FullScreen = {
   receiveMessage: function(aMessage) {
     let browser = aMessage.target;
     switch (aMessage.name) {
       case "DOMFullscreen:Request": {
         this._windowUtils.remoteFrameFullscreenChanged(browser);
         break;
       }
       case "DOMFullscreen:NewOrigin": {
-        this._WarningBox.show(aMessage.data.originNoSuffix);
+        PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
         break;
       }
       case "DOMFullscreen:Exit": {
         this._windowUtils.remoteFrameFullscreenReverted();
         break;
       }
       case "DOMFullscreen:Painted": {
         Services.obs.notifyObservers(window, "fullscreen-painted", "");
         TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
         break;
       }
     }
   },
 
   enterDomFullscreen : function(aBrowser) {
+
     if (!document.fullscreenElement) {
       return;
     }
 
+    // If we have a current pointerlock warning shown then hide it
+    // before transition.
+    PointerlockFsWarning.close();
+
     // If we've received a fullscreen notification, we have to ensure that the
     // element that's requesting fullscreen belongs to the browser that's currently
     // active. If not, we exit fullscreen since the "full-screen document" isn't
     // actually visible now.
     if (!aBrowser || gBrowser.selectedBrowser != aBrowser ||
         // The top-level window has lost focus since the request to enter
         // full-screen was made. Cancel full-screen.
         Services.focus.activeWindow != window) {
@@ -217,17 +457,17 @@ var FullScreen = {
       document.removeEventListener("popuphidden", this._setPopupOpen, false);
     }
   },
 
   cleanupDomFullscreen: function () {
     window.messageManager
           .broadcastAsyncMessage("DOMFullscreen:CleanUp");
 
-    this._WarningBox.close();
+    PointerlockFsWarning.close();
     gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
     gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
     gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
     window.removeEventListener("activate", this);
 
     document.documentElement.removeAttribute("inDOMFullscreen");
   },
 
@@ -315,223 +555,16 @@ var FullScreen = {
   },
   setAutohide: function()
   {
     gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
     // Try again to hide toolbar when we change the pref.
     FullScreen.hideNavToolbox(true);
   },
 
-  _WarningBox: {
-    _element: null,
-    _origin: null,
-
-    /**
-     * Timeout object for managing timeout request. If it is started when
-     * the previous call hasn't finished, it would automatically cancelled
-     * the previous one.
-     */
-    Timeout: function(func, delay) {
-      this._id = 0;
-      this._func = func;
-      this._delay = delay;
-    },
-
-    init: function() {
-      this.Timeout.prototype = {
-        start: function() {
-          this.cancel();
-          this._id = setTimeout(() => this._handle(), this._delay);
-        },
-        cancel: function() {
-          if (this._id) {
-            clearTimeout(this._id);
-            this._id = 0;
-          }
-        },
-        _handle: function() {
-          this._id = 0;
-          this._func();
-        },
-        get delay() {
-          return this._delay;
-        }
-      };
-    },
-
-    // Shows a warning that the site has entered fullscreen for a short duration.
-    show: function(aOrigin) {
-      if (!document.fullscreenElement) {
-        return;
-      }
-
-      if (!this._element) {
-        this._element = document.getElementById("fullscreen-warning");
-        // Setup event listeners
-        this._element.addEventListener("transitionend", this);
-        window.addEventListener("mousemove", this, true);
-        // The timeout to hide the warning box after a while.
-        this._timeoutHide = new this.Timeout(() => {
-          this._state = "hidden";
-        }, gPrefService.getIntPref("full-screen-api.warning.timeout"));
-        // The timeout to show the warning box when the pointer is at the top
-        this._timeoutShow = new this.Timeout(() => {
-          this._state = "ontop";
-          this._timeoutHide.start();
-        }, gPrefService.getIntPref("full-screen-api.warning.delay"));
-      }
-
-      // Set the strings on the fullscreen warning UI.
-      if (aOrigin) {
-        this._origin = aOrigin;
-      }
-      let uri = BrowserUtils.makeURI(this._origin);
-      let host = null;
-      try {
-        host = uri.host;
-      } catch (e) { }
-      let textElem = document.getElementById("fullscreen-domain-text");
-      if (!host) {
-        textElem.setAttribute("hidden", true);
-      } else {
-        textElem.removeAttribute("hidden");
-        let hostElem = document.getElementById("fullscreen-domain");
-        // Document's principal's URI has a host. Display a warning including it.
-        let utils = {};
-        Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
-        hostElem.textContent = utils.DownloadUtils.getURIHost(uri.spec)[0];
-      }
-      this._element.className = gIdentityHandler.fullscreenWarningClassName;
-
-      // User should be allowed to explicitly disable
-      // the prompt if they really want.
-      if (this._timeoutHide.delay <= 0) {
-        return;
-      }
-
-      // Explicitly set the last state to hidden to avoid the warning
-      // box being hidden immediately because of mousemove.
-      this._state = "onscreen";
-      this._lastState = "hidden";
-      this._timeoutHide.start();
-    },
-
-    close: function() {
-      if (!this._element) {
-        return;
-      }
-      // Cancel any pending timeout
-      this._timeoutHide.cancel();
-      this._timeoutShow.cancel();
-      // Reset state of the warning box
-      this._state = "hidden";
-      this._element.setAttribute("hidden", true);
-      // Remove all event listeners
-      this._element.removeEventListener("transitionend", this);
-      window.removeEventListener("mousemove", this, true);
-      // Clear fields
-      this._element = null;
-      this._timeoutHide = null;
-      this._timeoutShow = null;
-
-      // Ensure focus switches away from the (now hidden) warning box.
-      // If the user clicked buttons in the warning box, it would have
-      // been focused, and any key events would be directed at the (now
-      // hidden) chrome document instead of the target document.
-      gBrowser.selectedBrowser.focus();
-    },
-
-    // State could be one of "onscreen", "ontop", "hiding", and
-    // "hidden". Setting the state to "onscreen" and "ontop" takes
-    // effect immediately, while setting it to "hidden" actually
-    // turns the state to "hiding" before the transition finishes.
-    _lastState: null,
-    _STATES: ["hidden", "ontop", "onscreen"],
-    get _state() {
-      for (let state of this._STATES) {
-        if (this._element.hasAttribute(state)) {
-          return state;
-        }
-      }
-      return "hiding";
-    },
-    set _state(newState) {
-      let currentState = this._state;
-      if (currentState == newState) {
-        return;
-      }
-      if (currentState != "hiding") {
-        this._lastState = currentState;
-        this._element.removeAttribute(currentState);
-      }
-      if (newState != "hidden") {
-        if (currentState != "hidden") {
-          this._element.setAttribute(newState, true);
-        } else {
-          // When the previous state is hidden, the display was none,
-          // thus no box was constructed. We need to wait for the new
-          // display value taking effect first, otherwise, there won't
-          // be any transition. Since requestAnimationFrame callback is
-          // generally triggered before any style flush and layout, we
-          // should wait for the second animation frame.
-          requestAnimationFrame(() => {
-            requestAnimationFrame(() => {
-              if (this._element) {
-                this._element.setAttribute(newState, true);
-              }
-            });
-          });
-        }
-      }
-    },
-
-    handleEvent: function(event) {
-      switch (event.type) {
-        case "mousemove": {
-          let state = this._state;
-          if (state == "hidden") {
-            // If the warning box is currently hidden, show it after
-            // a short delay if the pointer is at the top.
-            if (event.clientY != 0) {
-              this._timeoutShow.cancel();
-            } else if (this._timeoutShow.delay >= 0) {
-              this._timeoutShow.start();
-            }
-          } else {
-            let elemRect = this._element.getBoundingClientRect();
-            if (state == "hiding" && this._lastState != "hidden") {
-              // If we are on the hiding transition, and the pointer
-              // moved near the box, restore to the previous state.
-              if (event.clientY <= elemRect.bottom + 50) {
-                this._state = this._lastState;
-                this._timeoutHide.start();
-              }
-            } else if (state == "ontop" || this._lastState != "hidden") {
-              // State being "ontop" or the previous state not being
-              // "hidden" indicates this current warning box is shown
-              // in response to user's action. Hide it immediately when
-              // the pointer leaves that area.
-              if (event.clientY > elemRect.bottom + 50) {
-                this._state = "hidden";
-                this._timeoutHide.cancel();
-              }
-            }
-          }
-          break;
-        }
-        case "transitionend": {
-          if (this._state == "hiding") {
-            this._element.setAttribute("hidden", true);
-          }
-          break;
-        }
-      }
-    }
-  },
-
   showNavToolbox: function(trackMouse = true) {
     this._fullScrToggler.hidden = true;
     gNavToolbox.removeAttribute("fullscreenShouldAnimate");
     gNavToolbox.style.marginTop = "";
 
     if (!this._isChromeCollapsed) {
       return;
     }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -655,52 +655,52 @@ window[chromehidden~="toolbar"] toolbar:
 
 /*  Full Screen UI */
 
 #fullscr-toggler {
   height: 1px;
   background: black;
 }
 
-html|*#fullscreen-warning {
+html|*.pointerlockfswarning {
   position: fixed;
   z-index: 2147483647 !important;
   visibility: visible;
   transition: transform 300ms ease-in;
   /* To center the warning box horizontally,
      we use left: 50% with translateX(-50%). */
   top: 0; left: 50%;
   transform: translate(-50%, -100%);
   box-sizing: border-box;
   width: -moz-max-content;
   max-width: 95%;
   pointer-events: none;
 }
-html|*#fullscreen-warning:not([hidden]) {
+html|*.pointerlockfswarning:not([hidden]) {
   display: flex;
   will-change: transform;
 }
-html|*#fullscreen-warning[onscreen] {
+html|*.pointerlockfswarning[onscreen] {
   transform: translate(-50%, 50px);
 }
-html|*#fullscreen-warning[ontop] {
+html|*.pointerlockfswarning[ontop] {
   /* Use -10px to hide the border and border-radius on the top */
   transform: translate(-50%, -10px);
 }
-#main-window[OSXLionFullscreen] html|*#fullscreen-warning[ontop] {
+#main-window[OSXLionFullscreen] html|*.pointerlockfswarning[ontop] {
   transform: translate(-50%, 80px);
 }
 
-html|*#fullscreen-domain-text,
-html|*#fullscreen-generic-text {
+html|*.pointerlockfswarning-domain-text,
+html|*.pointerlockfswarning-generic-text {
   word-wrap: break-word;
   /* We must specify a min-width, otherwise word-wrap:break-word doesn't work. Bug 630864. */
   min-width: 1px
 }
-html|*#fullscreen-domain-text:not([hidden]) + html|*#fullscreen-generic-text {
+html|*.pointerlockfswarning-domain-text:not([hidden]) + html|*.pointerlockfswarning-generic-text {
   display: none;
 }
 
 html|*#fullscreen-exit-button {
   pointer-events: auto;
 }
 
 /* ::::: Ctrl-Tab Panel ::::: */
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1292,17 +1292,19 @@ var gBrowserInit = {
       placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
     }
 
     LightWeightThemeWebInstaller.init();
 
     if (Win7Features)
       Win7Features.onOpenWindow();
 
+    PointerlockFsWarning.init();
     FullScreen.init();
+    PointerLock.init();
 
     // initialize the sync UI
     gSyncUI.init();
     gFxAccounts.init();
 
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
@@ -6815,17 +6817,17 @@ var gIdentityHandler = {
     }
   },
 
   /**
    * Return the CSS class name to set on the "fullscreen-warning" element to
    * display information about connection security in the notification shown
    * when a site enters the fullscreen mode.
    */
-  get fullscreenWarningClassName() {
+  get pointerlockFsWarningClassName() {
     // Note that the fullscreen warning does not handle _isSecureInternalUI.
     if (this._uriHasHost && this._isEV) {
       return "verifiedIdentity";
     }
     if (this._uriHasHost && this._isSecure) {
       return "verifiedDomain";
     }
     return "unknownIdentity";
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1137,35 +1137,46 @@
                  flex="1"
                  style="min-width: 14em; width: 18em; max-width: 36em;"/>
       </vbox>
       <vbox id="browser-border-end" hidden="true" layer="true"/>
     </hbox>
 #include ../../components/customizableui/content/customizeMode.inc.xul
   </deck>
 
-  <html:div id="fullscreen-warning" hidden="true">
-    <html:div id="fullscreen-domain-text">
+  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+    <html:div class="pointerlockfswarning-domain-text">
       &fullscreenWarning.beforeDomain.label;
-      <html:span id="fullscreen-domain"/>
+      <html:span class="pointerlockfswarning-domain"/>
       &fullscreenWarning.afterDomain.label;
     </html:div>
-    <html:div id="fullscreen-generic-text">
+    <html:div class="pointerlockfswarning-generic-text">
       &fullscreenWarning.generic.label;
     </html:div>
     <html:button id="fullscreen-exit-button"
                  onclick="FullScreen.exitDomFullScreen();">
 #ifdef XP_MACOSX
             &exitDOMFullscreenMac.button;
 #else
             &exitDOMFullscreen.button;
 #endif
     </html:button>
   </html:div>
 
+  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+    <html:div class="pointerlockfswarning-domain-text">
+      &pointerlockWarning.beforeDomain.label;
+      <html:span class="pointerlockfswarning-domain"/>
+      &pointerlockWarning.afterDomain.label;
+    </html:div>
+    <html:div class="pointerlockfswarning-generic-text">
+      &pointerlockWarning.generic.label;
+    </html:div>
+  </html:div>
+
   <vbox id="browser-bottombox" layer="true">
     <notificationbox id="global-notificationbox" notificationside="bottom"/>
   </vbox>
 
   <svg:svg height="0">
 #include tab-shape.inc.svg
     <svg:clipPath id="urlbar-back-button-clip-path">
 #ifndef XP_MACOSX
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -11,17 +11,17 @@
 <script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
 <script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
 
 <script type="application/javascript" src="chrome://browser/content/browser-addons.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-ctrlTab.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-customization.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-devedition.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-fullScreen.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser-fullScreenAndPointerLock.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-media.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/>
 #ifdef MOZ_SAFE_BROWSING
 <script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -36,16 +36,27 @@ XPCOMUtils.defineLazyGetter(this, "Simpl
   });
   return ssdp;
 });
 
 // TabChildGlobal
 var global = this;
 
 
+addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
+  sendAsyncMessage("PointerLock:Entered", {
+    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
+  });
+});
+
+addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
+  sendAsyncMessage("PointerLock:Exited");
+});
+
+
 addMessageListener("Browser:HideSessionRestoreButton", function (message) {
   // Hide session restore button on about:home
   let doc = content.document;
   let container;
   if (doc.documentURI.toLowerCase() == "about:home" &&
       (container = doc.getElementById("sessionRestoreContainer"))) {
     container.hidden = true;
   }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2725,16 +2725,23 @@
             if (remoteBrowser) {
               remoteBrowser._outerWindowIDBrowserMap.set(aOtherBrowser.outerWindowID, aOtherBrowser);
             }
 
             // Swap permanentKey properties.
             let ourPermanentKey = ourBrowser.permanentKey;
             ourBrowser.permanentKey = aOtherBrowser.permanentKey;
             aOtherBrowser.permanentKey = ourPermanentKey;
+            aOurTab.permanentKey = ourBrowser.permanentKey;
+            if (remoteBrowser) {
+              let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
+              if (otherTab) {
+                otherTab.permanentKey = aOtherBrowser.permanentKey;
+              }
+            }
 
             // Restore the progress listener
             tabListener = this.mTabProgressListener(aOurTab, ourBrowser, false, false);
             this._tabListeners.set(aOurTab, tabListener);
 
             const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
             filter.addProgressListener(tabListener, notifyAll);
             ourBrowser.webProgress.addProgressListener(filter, notifyAll);
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -74,17 +74,17 @@ browser.jar:
         content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
         content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
         content/browser/browser-devedition.js         (content/browser-devedition.js)
         content/browser/browser-feeds.js              (content/browser-feeds.js)
-        content/browser/browser-fullScreen.js         (content/browser-fullScreen.js)
+        content/browser/browser-fullScreenAndPointerLock.js  (content/browser-fullScreenAndPointerLock.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-fxaccounts.js         (content/browser-fxaccounts.js)
         content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
         content/browser/browser-media.js              (content/browser-media.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
         content/browser/browser-refreshblocker.js     (content/browser-refreshblocker.js)
 #ifdef MOZ_SAFE_BROWSING
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2735,84 +2735,27 @@ ContentPermissionPrompt.prototype = {
       },
     };
 
     this._showPrompt(aRequest, message, "desktop-notification", actions,
                      "web-notifications",
                      "web-notifications-notification-icon", options);
   },
 
-  _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) {
-    let message = gBrowserBundle.GetStringFromName(autoAllow ?
-                                  "pointerLock.autoLock.title3" : "pointerLock.title3");
-
-    // If this is an autoAllow info prompt, offer no actions.
-    // _showPrompt() will allow the request when it's dismissed.
-    let actions = [];
-    if (!autoAllow) {
-      actions = [
-        {
-          stringId: "pointerLock.allow2",
-          action: null,
-          expireType: null,
-          callback: function() {},
-        },
-        {
-          stringId: "pointerLock.alwaysAllow",
-          action: Ci.nsIPermissionManager.ALLOW_ACTION,
-          expireType: null,
-          callback: function() {},
-        },
-        {
-          stringId: "pointerLock.neverAllow",
-          action: Ci.nsIPermissionManager.DENY_ACTION,
-          expireType: null,
-          callback: function() {},
-        },
-      ];
-    }
-
-    function onFullScreen() {
-      notification.remove();
-    }
-
-    let options = {};
-    options.removeOnDismissal = autoAllow;
-    options.eventCallback = type => {
-      if (type == "removed") {
-        notification.browser.removeEventListener("fullscreenchange", onFullScreen, true);
-        if (autoAllow) {
-          aRequest.allow();
-        }
-      }
-    }
-
-    let notification =
-      this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock",
-                       "pointerLock-notification-icon", options);
-
-    // pointerLock is automatically allowed in fullscreen mode (and revoked
-    // upon exit), so if the page enters fullscreen mode after requesting
-    // pointerLock (but before the user has granted permission), we should
-    // remove the now-impotent notification.
-    notification.browser.addEventListener("fullscreenchange", onFullScreen, true);
-  },
-
   prompt: function CPP_prompt(request) {
     // Only allow exactly one permission request here.
     let types = request.types.QueryInterface(Ci.nsIArray);
     if (types.length != 1) {
       request.cancel();
       return;
     }
     let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
 
     const kFeatureKeys = { "geolocation" : "geo",
-                           "desktop-notification" : "desktop-notification",
-                           "pointerLock" : "pointerLock",
+                           "desktop-notification" : "desktop-notification"
                          };
 
     // Make sure that we support the request.
     if (!(perm.type in kFeatureKeys)) {
       return;
     }
 
     var requestingPrincipal = request.principal;
@@ -2828,21 +2771,16 @@ ContentPermissionPrompt.prototype = {
 
     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
       request.cancel();
       return;
     }
 
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
       autoAllow = true;
-      // For pointerLock, we still want to show a warning prompt.
-      if (perm.type != "pointerLock") {
-        request.allow();
-        return;
-      }
     }
 
     var browser = this._getBrowserForRequest(request);
     var chromeWin = browser.ownerDocument.defaultView;
     if (!chromeWin.PopupNotifications)
       // Ignore requests from browsers hosted in windows that don't support
       // PopupNotifications.
       return;
@@ -2850,19 +2788,16 @@ ContentPermissionPrompt.prototype = {
     // Show the prompt.
     switch (perm.type) {
     case "geolocation":
       this._promptGeo(request);
       break;
     case "desktop-notification":
       this._promptWebNotifications(request);
       break;
-    case "pointerLock":
-      this._promptPointerLock(request, autoAllow);
-      break;
     }
   },
 
 };
 
 var DefaultBrowserCheck = {
   get OPTIONPOPUP() { return "defaultBrowserNotificationPopup" },
   _setAsDefaultTimer: null,
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1483,32 +1483,43 @@ var SessionStoreInternal = {
    *        total (int):
    *          The total number of windows to be flushed.
    *        current (int):
    *          The current window that we're waiting for a flush on.
    *
    * @return Promise
    */
   flushAllWindowsAsync: Task.async(function*(progress={}) {
-    let windowPromises = [];
+    let windowPromises = new Map();
     // We collect flush promises and close each window immediately so that
     // the user can't start changing any window state while we're waiting
     // for the flushes to finish.
     this._forEachBrowserWindow((win) => {
-      windowPromises.push(TabStateFlusher.flushWindow(win));
-      win.close();
+      windowPromises.set(win, TabStateFlusher.flushWindow(win));
+
+      // We have to wait for these messages to come up from
+      // each window and each browser. In the meantime, hide
+      // the windows to improve perceived shutdown speed.
+      let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell)
+                       .QueryInterface(Ci.nsIDocShellTreeItem)
+                       .treeOwner
+                       .QueryInterface(Ci.nsIBaseWindow);
+      baseWin.visibility = false;
     });
 
-    progress.total = windowPromises.length;
+    progress.total = windowPromises.size;
+    progress.current = 0;
 
     // We'll iterate through the Promise array, yielding each one, so as to
     // provide useful progress information to AsyncShutdown.
-    for (let i = 0; i < windowPromises.length; ++i) {
-      progress.current = i;
-      yield windowPromises[i];
+    for (let [win, promise] of windowPromises) {
+      yield promise;
+      this._collectWindowData(win);
+      progress.current++;
     };
 
     // We must cache this because _getMostRecentBrowserWindow will always
     // return null by the time quit-application occurs.
     var activeWindow = this._getMostRecentBrowserWindow();
     if (activeWindow)
       this.activeWindowSSiCache = activeWindow.__SSi || "";
     DirtyWindows.clear();
--- a/browser/components/sessionstore/test/browser_456342_sample.xhtml
+++ b/browser/components/sessionstore/test/browser_456342_sample.xhtml
@@ -17,12 +17,20 @@
 <input type="HIDDEN" name="hideme2"/>
 <input type="submit" name="submit"/>
 <input type="reset"  name="reset"/>
 <input type="image"  name="image"/>
 <input type="button" name="button"/>
 <input type="password" name="password"/>
 <input type="PassWord" name="password2"/>
 <input type="PASSWORD" name="password3"/>
+<input autocomplete="off" name="auto1"/>
+<input type="text" autocomplete="OFF" name="auto2"/>
+<textarea autocomplete="off" name="auto3"/>
+<select autocomplete="off" name="auto4">
+  <option value="1" selected="true"/>
+  <option value="2"/>
+  <option value="3"/>
+</select>
 </form>
 
 </body>
 </html>
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -123,16 +123,21 @@ These should match what Safari and other
 <!-- LOCALIZATION NOTE (fullscreenWarning.beforeDomain.label,
      fullscreenWarning.afterDomain.label): these two strings are used
      respectively before and after the domain requiring fullscreen.
      Localizers can use one of them, or both, to better adapt this
      sentence to their language. -->
 <!ENTITY fullscreenWarning.beforeDomain.label "">
 <!ENTITY fullscreenWarning.afterDomain.label "is now full screen">
 <!ENTITY fullscreenWarning.generic.label "This document is now full screen">
+
+<!ENTITY pointerlockWarning.beforeDomain.label "">
+<!ENTITY pointerlockWarning.afterDomain.label "has control of your pointer. Press Esc to take back control.">
+<!ENTITY pointerlockWarning.generic.label "This document has control of your pointer. Press Esc to take back control">
+
 <!-- LOCALIZATION NOTE (exitDOMFullscreen.button,
      exitDOMFullscreenMac.button): the "escape" button on PC keyboards
      is uppercase, while on Mac keyboards it is lowercase -->
 <!ENTITY exitDOMFullscreen.button "Exit Full Screen (Esc)">
 <!ENTITY exitDOMFullscreenMac.button "Exit Full Screen (esc)">
 <!ENTITY leaveDOMFullScreen.label "Exit Full Screen">
 <!ENTITY leaveDOMFullScreen.accesskey "u">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -383,27 +383,16 @@ webNotifications.alwaysReceive.accesskey
 webNotifications.neverShow=Always Block Notifications
 webNotifications.neverShow.accesskey=N
 webNotifications.receiveFromSite=Would you like to receive notifications from this site?
 # LOCALIZATION NOTE (webNotifications.upgradeTitle): When using native notifications on OS X, the title may be truncated around 32 characters.
 webNotifications.upgradeTitle=Upgraded notifications
 # LOCALIZATION NOTE (webNotifications.upgradeBody): When using native notifications on OS X, the body may be truncated around 100 characters in some views.
 webNotifications.upgradeBody=You can now receive notifications from sites that are not currently loaded. Click to learn more.
 
-# Pointer lock UI
-
-pointerLock.allow2=Hide pointer
-pointerLock.allow2.accesskey=H
-pointerLock.alwaysAllow=Always allow hiding
-pointerLock.alwaysAllow.accesskey=A
-pointerLock.neverAllow=Never allow hiding
-pointerLock.neverAllow.accesskey=N
-pointerLock.title3=Would you like to allow the pointer to be hidden on this site?
-pointerLock.autoLock.title3=This site will hide the pointer.
-
 # Phishing/Malware Notification Bar.
 # LOCALIZATION NOTE (notADeceptiveSite, notAnAttack)
 # The two button strings will never be shown at the same time, so
 # it's okay for them to have the same access key
 safebrowsing.getMeOutOfHereButton.label=Get me out of here!
 safebrowsing.getMeOutOfHereButton.accessKey=G
 safebrowsing.deceptiveSite=Deceptive Site!
 safebrowsing.notADeceptiveSiteButton.label=This isn’t a deceptive site…
--- a/browser/locales/en-US/chrome/browser/sitePermissions.properties
+++ b/browser/locales/en-US/chrome/browser/sitePermissions.properties
@@ -11,9 +11,8 @@ permission.cookie.label = Set Cookies
 permission.desktop-notification2.label = Receive Notifications
 permission.image.label = Load Images
 permission.camera.label = Use the Camera
 permission.microphone.label = Use the Microphone
 permission.install.label = Install Add-ons
 permission.popup.label = Open Pop-up Windows
 permission.geo.label = Access Your Location
 permission.indexedDB.label = Maintain Offline Storage
-permission.pointerLock.label = Hide the Mouse Pointer
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -240,16 +240,12 @@ var gPermissionObject = {
                SitePermissions.BLOCK : SitePermissions.ALLOW;
     }
   },
 
   "geo": {
     exactHostMatch: true
   },
 
-  "indexedDB": {},
-
-  "pointerLock": {
-    exactHostMatch: true
-  }
+  "indexedDB": {}
 };
 
 const kPermissionIDs = Object.keys(gPermissionObject);
--- a/browser/modules/test/xpcshell/test_SitePermissions.js
+++ b/browser/modules/test/xpcshell/test_SitePermissions.js
@@ -4,17 +4,17 @@
 "use strict";
 
 Components.utils.import("resource:///modules/SitePermissions.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 add_task(function* testPermissionsListing() {
   Assert.deepEqual(SitePermissions.listPermissions().sort(),
     ["camera","cookie","desktop-notification","geo","image",
-     "indexedDB","install","microphone","pointerLock","popup"],
+     "indexedDB","install","microphone","popup"],
     "Correct list of all permissions");
 });
 
 add_task(function* testHasGrantedPermissions() {
   // check that it returns false on an invalid URI
   // like a file URI, which doesn't support site permissions
   let wrongURI = Services.io.newURI("file:///example.js", null, null)
   Assert.equal(SitePermissions.hasGrantedPermissions(wrongURI), false);
@@ -33,31 +33,28 @@ add_task(function* testHasGrantedPermiss
   // check that SESSION states return true
   SitePermissions.set(uri, "microphone", SitePermissions.SESSION);
   Assert.equal(SitePermissions.hasGrantedPermissions(uri), true);
 
   // removing the SESSION state should revert to false
   SitePermissions.remove(uri, "microphone");
   Assert.equal(SitePermissions.hasGrantedPermissions(uri), false);
 
-  SitePermissions.set(uri, "pointerLock", SitePermissions.BLOCK);
-
   // check that a combination of ALLOW and BLOCK states returns true
   SitePermissions.set(uri, "geo", SitePermissions.ALLOW);
   Assert.equal(SitePermissions.hasGrantedPermissions(uri), true);
 
   // check that a combination of SESSION and BLOCK states returns true
   SitePermissions.set(uri, "geo", SitePermissions.SESSION);
   Assert.equal(SitePermissions.hasGrantedPermissions(uri), true);
 
   // check that only BLOCK states will not return true
   SitePermissions.remove(uri, "geo");
   Assert.equal(SitePermissions.hasGrantedPermissions(uri), false);
 
-  SitePermissions.remove(uri, "pointerLock");
 });
 
 add_task(function* testGetPermissionsByURI() {
   // check that it returns an empty array on an invalid URI
   // like a file URI, which doesn't support site permissions
   let wrongURI = Services.io.newURI("file:///example.js", null, null)
   Assert.deepEqual(SitePermissions.getPermissionsByURI(wrongURI), []);
 
--- a/browser/themes/shared/fullscreen/warning.inc.css
+++ b/browser/themes/shared/fullscreen/warning.inc.css
@@ -1,51 +1,51 @@
 %if 0
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
-html|*#fullscreen-warning {
+html|*.pointerlockfswarning {
   align-items: center;
   background: rgba(45, 62, 72, 0.9);
   border: 2px solid #fafafa;
   box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.5);
   border-radius: 8px;
   padding: 24px 16px;
   font: message-box;
 }
 
-html|*#fullscreen-warning::before {
+html|*.pointerlockfswarning::before {
   margin: 0;
   width: 24px; height: 24px;
 }
 
-html|*#fullscreen-warning.verifiedIdentity::before,
-html|*#fullscreen-warning.verifiedDomain::before {
+html|*.pointerlockfswarning[data-identity="verifiedIdentity"]::before,
+html|*.pointerlockfswarning[data-identity="verifiedDomain"]::before {
   content: url("chrome://browser/skin/fullscreen/secure.svg");
 }
 
-html|*#fullscreen-warning.unknownIdentity::before {
+html|*.pointerlockfswarning[data-identity="unknownIdentity"]::before {
   content: url("chrome://browser/skin/fullscreen/insecure.svg");
 }
 
-html|*#fullscreen-domain-text,
-html|*#fullscreen-generic-text {
+html|*.pointerlockfswarning-domain-text,
+html|*.pointerlockfswarning-generic-text {
   font-size: 21px;
   font-weight: lighter;
   color: #fafafa;
   margin: 0 16px;
 }
 
-html|*#fullscreen-domain {
+html|*.pointerlockfswarning-domain {
   font-weight: bold;
   margin: 0;
 }
 
-html|*#fullscreen-exit-button {
+html|*.pointerlockfswarning-exit-button {
   padding: 5px 30px;
   font: message-box;
   font-size: 14px;
   font-weight: lighter;
   margin: 0;
   box-sizing: content-box;
-}
+}
\ No newline at end of file
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -52,26 +52,26 @@
   skin/classic/browser/customizableui/whimsy.png               (../shared/customizableui/whimsy.png)
   skin/classic/browser/customizableui/whimsy@2x.png            (../shared/customizableui/whimsy@2x.png)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (../shared/downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-blocked.svg          (../shared/downloads/download-blocked.svg)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/filters.svg                             (../shared/filters.svg)
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
-  skin/classic/browser/glyphs.svg                              (../shared/glyphs.svg)
   skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
   skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
   skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-not-secure.svg                 (../shared/identity-block/identity-not-secure.svg)
   skin/classic/browser/identity-secure.svg                     (../shared/identity-block/identity-secure.svg)
   skin/classic/browser/identity-mixed-passive-loaded.svg       (../shared/identity-block/identity-mixed-passive-loaded.svg)
   skin/classic/browser/identity-mixed-active-loaded.svg        (../shared/identity-block/identity-mixed-active-loaded.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
+  skin/classic/browser/permissions.svg                         (../shared/permissions.svg)
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/tracking-protection-disabled-16.svg     (../shared/identity-block/tracking-protection-disabled-16.svg)
   skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
   skin/classic/browser/newtab/whimsycorn.png                   (../shared/newtab/whimsycorn.png)
   skin/classic/browser/preferences/in-content/favicon.ico      (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs/search.css)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -76,124 +76,124 @@
 .webRTC-sharingDevices-notification-icon,
 .webRTC-sharingMicrophone-notification-icon,
 .in-use {
   fill: #fea01b;
 }
 
 .popup-notification-icon[popupid="web-notifications"],
 .desktop-notification-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#desktop-notification);
+  list-style-image: url(chrome://browser/skin/permissions.svg#desktop-notification);
 }
 
 .desktop-notification-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#desktop-notification-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#desktop-notification-blocked);
 }
 
 .geo-icon {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx);
 %elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-linux);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux);
 %else
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-windows);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows);
 %endif
 }
 
 .geo-icon.blocked {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-osx-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx-blocked);
 %elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-linux-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux-blocked);
 %else
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-windows-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows-blocked);
 %endif
 }
 
 .popup-notification-icon[popupid="geolocation"] {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx);
 %elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-linux-detailed);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux-detailed);
 %else
-  list-style-image: url(chrome://browser/skin/glyphs.svg#geo-windows-detailed);
+  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows-detailed);
 %endif
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#indexedDB);
+  list-style-image: url(chrome://browser/skin/permissions.svg#indexedDB);
 }
 
 .indexedDB-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#indexedDB-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#indexedDB-blocked);
 }
 
 .login-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#login);
+  list-style-image: url(chrome://browser/skin/permissions.svg#login);
 }
 
 .popup-notification-icon[popupid="password"] {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#login-detailed);
+  list-style-image: url(chrome://browser/skin/permissions.svg#login-detailed);
 }
 
 #login-fill-notification-icon {
   /* Temporary solution until the capture and fill doorhangers are unified. */
   transform: scaleX(-1);
 }
 
 /* The first selector is used by socialchat.xml (bug 1275558). */
 .webRTC-sharingDevices-notification-icon,
 .camera-icon,
 .popup-notification-icon[popupid="webRTC-shareDevices"],
 .popup-notification-icon[popupid="webRTC-sharingDevices"] {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#camera);
+  list-style-image: url(chrome://browser/skin/permissions.svg#camera);
 }
 
 .camera-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#camera-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#camera-blocked);
 }
 
 /* The first selector is used by socialchat.xml (bug 1275558). */
 .webRTC-sharingMicrophone-notification-icon,
 .microphone-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#microphone);
+  list-style-image: url(chrome://browser/skin/permissions.svg#microphone);
 }
 
 .microphone-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#microphone-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#microphone-blocked);
 }
 
 .popup-notification-icon[popupid="webRTC-shareMicrophone"],
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"] {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#microphone-detailed);
+  list-style-image: url(chrome://browser/skin/permissions.svg#microphone-detailed);
 }
 
 .popup-notification-icon[popupid="webRTC-shareScreen"],
 .popup-notification-icon[popupid="webRTC-sharingScreen"],
 .screen-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#screen);
+  list-style-image: url(chrome://browser/skin/permissions.svg#screen);
 }
 
 .screen-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#screen-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#screen-blocked);
 }
 
 .popup-notification-icon[popupid="pointerLock"],
 .pointerLock-icon {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#pointerLock);
+  list-style-image: url(chrome://browser/skin/permissions.svg#pointerLock);
 }
 
 .pointerLock-icon.blocked {
-  list-style-image: url(chrome://browser/skin/glyphs.svg#pointerLock-blocked);
+  list-style-image: url(chrome://browser/skin/permissions.svg#pointerLock-blocked);
 }
 
 /* This icon has a block sign in it, so we don't need a blocked version. */
 .popup-icon {
-  list-style-image: url("chrome://browser/skin/glyphs.svg#popup");
+  list-style-image: url("chrome://browser/skin/permissions.svg#popup");
 }
 
 /* EME */
 
 .popup-notification-icon[popupid="drmContentPlaying"],
 .drm-icon {
   list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
 }
rename from browser/themes/shared/glyphs.svg
rename to browser/themes/shared/permissions.svg
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -109,25 +109,25 @@
 
 .tab-sharing-icon-overlay {
   /* 16px of the icon + 6px of margin-inline-end of .tab-icon-image */
   margin-inline-start: -22px;
   position: relative;
 }
 
 .tab-sharing-icon-overlay[sharing="camera"] {
-  list-style-image: url("chrome://browser/skin/glyphs.svg#camera");
+  list-style-image: url("chrome://browser/skin/permissions.svg#camera");
 }
 
 .tab-sharing-icon-overlay[sharing="microphone"] {
-  list-style-image: url("chrome://browser/skin/glyphs.svg#microphone");
+  list-style-image: url("chrome://browser/skin/permissions.svg#microphone");
 }
 
 .tab-sharing-icon-overlay[sharing="screen"] {
-  list-style-image: url("chrome://browser/skin/glyphs.svg#screen");
+  list-style-image: url("chrome://browser/skin/permissions.svg#screen");
 }
 
 .tab-sharing-icon-overlay[sharing] {
   filter: url("chrome://browser/skin/filters.svg#fill");
   fill: rgb(224, 41, 29);
 }
 
 .tab-icon-overlay {
--- a/devtools/client/dom/content/components/search-box.css
+++ b/devtools/client/dom/content/components/search-box.css
@@ -1,46 +1,10 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
 /******************************************************************************/
 /* Search Box */
-
-.searchBox {
-  height: 18px;
-  font-size: 12px;
-  margin-top: 0;
-  border: 1px solid rgb(170, 188, 207);
-  width: 200px;
+.dom-searchbox {
   float: right;
-  background-image: url("./search.svg");
-  background-repeat: no-repeat;
-  background-position: 2px center;
-  padding-left: 20px;
-  margin-right: 5px;
-}
-
-/******************************************************************************/
-/* Light Theme & Dark Theme*/
-
-.theme-dark .searchBox,
-.theme-light .searchBox {
-  border: 1px solid rgb(170, 170, 170);
-  background-image: url("chrome://devtools/skin/images/magnifying-glass-light.png");
-  background-position: 8px center;
-  border-radius: 2px;
-  padding-left: 25px;
-  margin-top: 1px;
-  height: 16px;
-  font-style: italic;
-}
-
-/******************************************************************************/
-/* Dark Theme */
-
-.theme-dark .searchBox {
-  background-color: rgba(24, 29, 32, 1);
-  color: rgba(184, 200, 217, 1);
-  border-color: var(--theme-splitter-color);
-  background-image: url("chrome://devtools/skin/images/magnifying-glass.png");
-}
+}
\ No newline at end of file
--- a/devtools/client/dom/content/components/search-box.js
+++ b/devtools/client/dom/content/components/search-box.js
@@ -48,17 +48,17 @@ var SearchBox = React.createClass({
       this.searchTimeout = null;
       this.props.onSearch(searchBox.value);
     }, searchDelay);
   },
 
   render: function () {
     return (
       input({
-        className: "searchBox",
+        className: "dom-searchbox devtools-filterinput",
         placeholder: l10n.getStr("dom.filterDOMPanel"),
         onChange: this.onSearch
       })
     );
   }
 });
 
 // Exports from this module
--- a/devtools/client/framework/menu.js
+++ b/devtools/client/framework/menu.js
@@ -99,16 +99,25 @@ Menu.prototype._createMenuItems = functi
 
     if (item.submenu) {
       let menupopup = doc.createElement("menupopup");
       item.submenu._createMenuItems(menupopup);
 
       let menu = doc.createElement("menu");
       menu.appendChild(menupopup);
       menu.setAttribute("label", item.label);
+      if (item.disabled) {
+        menu.setAttribute("disabled", "true");
+      }
+      if (item.accesskey) {
+        menu.setAttribute("accesskey", item.accesskey);
+      }
+      if (item.id) {
+        menu.id = item.id;
+      }
       parent.appendChild(menu);
     } else if (item.type === "separator") {
       let menusep = doc.createElement("menuseparator");
       parent.appendChild(menusep);
     } else {
       let menuitem = doc.createElement("menuitem");
       menuitem.setAttribute("label", item.label);
       menuitem.addEventListener("command", () => {
--- a/devtools/client/framework/test/browser_menu_api.js
+++ b/devtools/client/framework/test/browser_menu_api.js
@@ -125,25 +125,37 @@ function* testSubmenu(toolbox) {
       info("Click callback has fired for submenu item");
       clickFired = true;
     },
   }));
   menu.append(new MenuItem({
     label: "Submenu parent",
     submenu: submenu,
   }));
+  menu.append(new MenuItem({
+    label: "Submenu parent with attributes",
+    id: "submenu-parent-with-attrs",
+    submenu: submenu,
+    accesskey: "A",
+    disabled: true,
+  }));
 
   menu.popup(0, 0, toolbox);
   ok(toolbox.doc.querySelector("#menu-popup"), "A popup is in the DOM");
   is(toolbox.doc.querySelectorAll("#menu-popup > menuitem").length, 0,
     "No menuitem children");
 
   let menus = toolbox.doc.querySelectorAll("#menu-popup > menu");
-  is(menus.length, 1, "Correct number of menus");
+  is(menus.length, 2, "Correct number of menus");
   is(menus[0].getAttribute("label"), "Submenu parent", "Correct label");
+  ok(!menus[0].hasAttribute("disabled"), "Correct disabled state");
+
+  is(menus[1].getAttribute("accesskey"), "A", "Correct accesskey");
+  ok(menus[1].hasAttribute("disabled"), "Correct disabled state");
+  ok(menus[1].id, "submenu-parent-with-attrs", "Correct id");
 
   let subMenuItems = menus[0].querySelectorAll("menupopup > menuitem");
   is(subMenuItems.length, 1, "Correct number of submenu items");
   is(subMenuItems[0].getAttribute("label"), "Submenu item", "Correct label");
 
   yield once(menu, "open");
   let closed = once(menu, "close");
 
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -64,38 +64,38 @@ InspectorSearch.prototype = {
         .catch(e => console.error(e));
   },
 
   doFullTextSearch: Task.async(function* (query, reverse) {
     let lastSearched = this._lastSearched;
     this._lastSearched = query;
 
     if (query.length === 0) {
-      this.searchBox.classList.remove("devtools-no-search-result");
+      this.searchBox.classList.remove("devtools-style-searchbox-no-match");
       if (!lastSearched || lastSearched.length > 0) {
         this.emit("search-cleared");
       }
       return;
     }
 
     let res = yield this.walker.search(query, { reverse });
 
     // Value has changed since we started this request, we're done.
     if (query != this.searchBox.value) {
       return;
     }
 
     if (res) {
       this.inspector.selection.setNodeFront(res.node, "inspectorsearch");
-      this.searchBox.classList.remove("devtools-no-search-result");
+      this.searchBox.classList.remove("devtools-style-searchbox-no-match");
 
       res.query = query;
       this.emit("search-result", res);
     } else {
-      this.searchBox.classList.add("devtools-no-search-result");
+      this.searchBox.classList.add("devtools-style-searchbox-no-match");
       this.emit("search-result");
     }
   }),
 
   _onCommand: function () {
     if (this.searchBox.value.length === 0) {
       this._onSearch();
     }
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -67,17 +67,17 @@
              hidden="true"/>
       </tabs>
       <tabpanels flex="1">
         <tabpanel id="sidebar-panel-ruleview" class="devtools-monospace theme-sidebar inspector-tabpanel">
           <html:div id="ruleview-toolbar-container" class="devtools-toolbar">
             <html:div id="ruleview-toolbar">
               <html:div class="devtools-searchbox">
                 <html:input id="ruleview-searchbox"
-                            class="devtools-searchinput devtools-rule-searchbox"
+                            class="devtools-filterinput devtools-rule-searchbox"
                             type="search"
                             placeholder="&filterStylesPlaceholder;"/>
                 <html:button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
               </html:div>
               <html:div id="ruleview-command-toolbar">
                 <html:button id="ruleview-add-rule-button" title="&addRuleButtonTooltip;" class="devtools-button"></html:button>
                 <html:button id="pseudo-class-panel-toggle" title="&togglePseudoClassPanel;" class="devtools-button"></html:button>
               </html:div>
@@ -92,17 +92,17 @@
           <html:div id="ruleview-container" class="ruleview">
           </html:div>
         </tabpanel>
 
         <tabpanel id="sidebar-panel-computedview" class="devtools-monospace theme-sidebar inspector-tabpanel">
           <html:div class="devtools-toolbar">
             <html:div class="devtools-searchbox">
               <html:input id="computedview-searchbox"
-                          class="devtools-searchinput devtools-rule-searchbox"
+                          class="devtools-filterinput devtools-rule-searchbox"
                           type="search"
                           placeholder="&filterStylesPlaceholder;"/>
               <html:button id="computedview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
             </html:div>
             <checkbox id="browser-style-checkbox"
                       class="includebrowserstyles"
                       checked="false"
                       label="&browserStylesLabel;"/>
--- a/devtools/client/inspector/test/browser_inspector_search-01.js
+++ b/devtools/client/inspector/test/browser_inspector_search-01.js
@@ -83,14 +83,14 @@ add_task(function* () {
     yield inspector.searchSuggestions._lastQuery;
 
     info(inspector.selection.nodeFront.id + " is selected with text " +
          searchBox.value);
     let nodeFront = yield getNodeFront("#" + id, inspector);
     is(inspector.selection.nodeFront, nodeFront,
        "Correct node is selected for state " + index);
 
-    is(!searchBox.classList.contains("devtools-no-search-result"), isValid,
+    is(!searchBox.classList.contains("devtools-style-searchbox-no-match"), isValid,
        "Correct searchbox result state for state " + index);
 
     index++;
   }
 });
--- a/devtools/client/inspector/test/browser_inspector_search-06.js
+++ b/devtools/client/inspector/test/browser_inspector_search-06.js
@@ -70,17 +70,17 @@ function* synthesizeKeys(keys, inspector
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield eventHandled;
     info("Waiting for the search query to complete");
     yield inspector.searchSuggestions._lastQuery;
   }
 }
 
 function assertHasResult(inspector, expectResult) {
-  is(inspector.searchBox.classList.contains("devtools-no-search-result"),
+  is(inspector.searchBox.classList.contains("devtools-style-searchbox-no-match"),
      !expectResult,
      "There are" + (expectResult ? "" : " no") + " search results");
 }
 
 function* mutatePage(inspector, testActor, expression) {
   let onMutation = inspector.once("markupmutation");
   yield testActor.eval(expression);
   yield onMutation;
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -213,16 +213,18 @@ devtools.jar:
     skin/webaudioeditor.css (themes/webaudioeditor.css)
     skin/components-frame.css (themes/components-frame.css)
     skin/components-h-split-box.css (themes/components-h-split-box.css)
     skin/jit-optimizations.css (themes/jit-optimizations.css)
     skin/images/magnifying-glass.png (themes/images/magnifying-glass.png)
     skin/images/magnifying-glass@2x.png (themes/images/magnifying-glass@2x.png)
     skin/images/magnifying-glass-light.png (themes/images/magnifying-glass-light.png)
     skin/images/magnifying-glass-light@2x.png (themes/images/magnifying-glass-light@2x.png)
+    skin/images/filter.svg (themes/images/filter.svg)
+    skin/images/search.svg (themes/images/search.svg)
     skin/images/itemToggle.png (themes/images/itemToggle.png)
     skin/images/itemToggle@2x.png (themes/images/itemToggle@2x.png)
     skin/images/itemArrow-dark-rtl.svg (themes/images/itemArrow-dark-rtl.svg)
     skin/images/itemArrow-dark-ltr.svg (themes/images/itemArrow-dark-ltr.svg)
     skin/images/itemArrow-rtl.svg (themes/images/itemArrow-rtl.svg)
     skin/images/itemArrow-ltr.svg (themes/images/itemArrow-ltr.svg)
     skin/images/noise.png (themes/images/noise.png)
     skin/images/dropmarker.svg (themes/images/dropmarker.svg)
--- a/devtools/client/jsonview/css/toolbar.css
+++ b/devtools/client/jsonview/css/toolbar.css
@@ -3,19 +3,18 @@
  * 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/. */
 
 /******************************************************************************/
 /* Toolbar */
 
 .toolbar {
   line-height: 20px;
-  font-size: 12px;
   height: 22px;
-  font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+  font: message-box;
   padding: 4px 0 3px 0;
 }
 
 .toolbar .btn {
   margin-left: 5px;
   background-color: #E6E6E6;
   border: 1px solid rgb(204, 204, 204);
   text-decoration: none;
--- a/devtools/client/memory/components/toolbar.js
+++ b/devtools/client/memory/components/toolbar.js
@@ -104,17 +104,17 @@ module.exports = createClass({
           )
         ),
 
         dom.div({ id: "toolbar-spacer", className: "spacer" }),
 
         dom.input({
           id: "filter",
           type: "search",
-          className: "devtools-searchinput",
+          className: "devtools-filterinput",
           placeholder: L10N.getStr("filter.placeholder"),
           title: L10N.getStr("filter.tooltip"),
           onChange: event => setFilterString(event.target.value),
           value: filterString || undefined,
         })
       );
     } else if (view.state == viewState.TREE_MAP) {
       assert(treeMapDisplays.length >= 1,
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -155,17 +155,17 @@
           </button>
         </hbox>
         <spacer id="requests-menu-spacer"
                 flex="1"/>
         <toolbarbutton id="requests-menu-network-summary-button"
                        class="devtools-toolbarbutton icon-and-text"
                        tooltiptext="&netmonitorUI.footer.perf;"/>
         <textbox id="requests-menu-filter-freetext-text"
-                 class="devtools-searchinput"
+                 class="devtools-filterinput"
                  type="search"
                  required="true"
                  placeholder="&netmonitorUI.footer.filterFreetext.label;"/>
         <toolbarbutton id="details-pane-toggle"
                        class="devtools-toolbarbutton"
                        tooltiptext="&netmonitorUI.panesButton.tooltip;"
                        disabled="true"
                        tabindex="0"/>
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -453,17 +453,17 @@ VariablesView.prototype = {
     let container = this._searchboxContainer = document.createElement("hbox");
     container.className = "devtools-toolbar";
 
     // Hide the variables searchbox container if there are no variables or
     // properties to display.
     container.hidden = !this._store.length;
 
     let searchbox = this._searchboxNode = document.createElement("textbox");
-    searchbox.className = "variables-view-searchinput devtools-searchinput";
+    searchbox.className = "variables-view-searchinput devtools-filterinput";
     searchbox.setAttribute("placeholder", this._searchboxPlaceholder);
     searchbox.setAttribute("type", "search");
     searchbox.setAttribute("flex", "1");
     searchbox.addEventListener("command", this._onSearchboxInput, false);
     searchbox.addEventListener("keypress", this._onSearchboxKeyPress, false);
 
     container.appendChild(searchbox);
     ownerNode.insertBefore(container, this._parent);
--- a/devtools/client/storage/storage.xul
+++ b/devtools/client/storage/storage.xul
@@ -37,17 +37,17 @@
   </popupset>
 
   <box flex="1" class="devtools-responsive-container theme-body">
     <vbox id="storage-tree"/>
     <splitter class="devtools-side-splitter"/>
     <vbox flex="1">
       <hbox id="storage-toolbar" class="devtools-toolbar">
         <textbox id="storage-searchbox"
-                 class="devtools-searchinput"
+                 class="devtools-filterinput"
                  type="search"
                  timeout="200"
                  placeholder="&searchBox.placeholder;"/>
       </hbox>
       <vbox id="storage-table" class="theme-sidebar" flex="1"/>
     </vbox>
     <splitter class="devtools-side-splitter"/>
     <vbox id="storage-sidebar" class="devtools-sidebar-tabs" hidden="true">
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -369,17 +369,18 @@ div.CodeMirror span.eval-text {
   border-bottom: 1px solid #434850;
 }
 
 .theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
   border-bottom: 0;
 }
 
 .devtools-textinput,
-.devtools-searchinput {
+.devtools-searchinput,
+.devtools-filterinput {
   background-color: rgba(24, 29, 32, 1);
   color: rgba(184, 200, 217, 1);
 }
 
 .CodeMirror-Tern-fname {
   color: #f7f7f7;
 }
 
--- a/devtools/client/themes/fonts.css
+++ b/devtools/client/themes/fonts.css
@@ -30,17 +30,17 @@
 
 #font-showall {
   border-radius: 0;
   border: 1px solid black;
   margin: 3px;
   cursor: pointer;
   position: absolute;
   bottom: 0;
-  right: 0;
+  offset-inline-end: 0;
 }
 
 .dim > #font-container,
 .font:not(.has-code) .font-css-code,
 .font-is-local,
 .font-is-remote,
 .font.is-local .font-format-url,
 #font-template {
@@ -65,17 +65,16 @@
   width: 100%;
 }
 
 .font-preview-container {
   overflow-x: auto;
 }
 
 #font-preview-text-input {
-  font: inherit;
   margin-top: 1px;
   margin-bottom: 1px;
   padding-top: 0;
   padding-bottom: 0;
   flex: 1;
 }
 
 .font {
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/filter.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#aaa">
+  <path fill-opacity="0.3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3C4.9 7.7 2.4 4 2.4 4h11.3s-2.5 3.4-2.5 4.1c0 .3-2.1-.1-2.1.3v5.9H7s-.4-3.9-.4-5.9z"/>
+  <path d="M2 2v2.3l2.7 4.5h1.6v5.5s1.1.6 1.8.6c.5 0 1.8-.6 1.8-.6V8.8h1.6L14 4.3V2H2zm10.8 2l-2.1 3.6H8.5v5.9c-.1 0-.1.1-.2.1-.2.1-.3.1-.3.1s-.1 0-.2-.1c-.1 0-.2-.1-.3-.1V7.6H5.4L3.2 4v-.8h9.5V4z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/search.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#aaa">
+  <path d="M10.716 10.032C11.516 9.077 12 7.845 12 6.5 12 3.462 9.538 1 6.5 1S1 3.462 1 6.5 3.462 12 6.5 12c1.345 0 2.577-.483 3.532-1.284l4.143 4.142c.19.19.495.19.683 0 .19-.188.19-.494 0-.683l-4.142-4.143zM6.5 11C8.985 11 11 8.985 11 6.5S8.985 2 6.5 2 2 4.015 2 6.5 4.015 11 6.5 11z" fill-rule="evenodd"/>
+</svg>
--- a/devtools/client/themes/styleeditor.css
+++ b/devtools/client/themes/styleeditor.css
@@ -141,17 +141,18 @@ li.linked-file-error .stylesheet-linked-
   font-size: 110%;
 }
 
 .stylesheet-more > h3 {
   font-size: 11px;
   margin-inline-end: 2px;
 }
 
-.devtools-searchinput {
+.devtools-searchinput,
+.devtools-filterinput {
   max-width: 25ex;
   font-size: 11px;
 }
 
 .placeholder a {
   text-decoration: underline;
 }
 
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -6,18 +6,18 @@
 /* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
 .theme-light {
   --toolbar-tab-hover: rgba(170, 170, 170, .2);
   --toolbar-tab-hover-active: rgba(170, 170, 170, .4);
   --searchbox-background-color: #ffee99;
   --searchbox-border-color: #ffbf00;
   --searcbox-no-match-background-color: #ffe5e5;
   --searcbox-no-match-border-color: #e52e2e;
-  --magnifying-glass-image: url(images/magnifying-glass-light.png);
-  --magnifying-glass-image-2x: url(images/magnifying-glass-light@2x.png);
+  --magnifying-glass-image: url(images/search.svg);
+  --filter-image: url(images/filter.svg);
   --tool-options-image: url(images/tool-options.svg);
   --close-button-image: url(chrome://devtools/skin/images/close.svg);
   --icon-filter: invert(1);
   --dock-bottom-image: url(chrome://devtools/skin/images/dock-bottom.svg);
   --dock-side-image: url(chrome://devtools/skin/images/dock-side.svg);
   --dock-undock-image: url(chrome://devtools/skin/images/dock-undock.svg);
   --toolbar-button-border-color: rgba(170, 170, 170, .5);
 
@@ -37,18 +37,18 @@
 
 .theme-dark {
   --toolbar-tab-hover: hsla(206, 37%, 4%, .2);
   --toolbar-tab-hover-active: hsla(206, 37%, 4%, .4);
   --searchbox-background-color: #4d4222;
   --searchbox-border-color: #d99f2b;
   --searcbox-no-match-background-color: #402325;
   --searcbox-no-match-border-color: #cc3d3d;
-  --magnifying-glass-image: url(images/magnifying-glass.png);
-  --magnifying-glass-image-2x: url(images/magnifying-glass@2x.png);
+  --magnifying-glass-image: url(images/search.svg);
+  --filter-image: url(images/filter.svg);
   --tool-options-image: url(images/tool-options.svg);
   --close-button-image: url(chrome://devtools/skin/images/close.svg);
   --icon-filter: none;
   --dock-bottom-image: url(chrome://devtools/skin/images/dock-bottom.svg);
   --dock-side-image: url(chrome://devtools/skin/images/dock-side.svg);
   --dock-undock-image: url(chrome://devtools/skin/images/dock-undock.svg);
   --toolbar-button-border-color: rgba(0, 0, 0, .4);
 
@@ -371,111 +371,129 @@
 
 .devtools-separator + .devtools-toolbarbutton {
   margin-inline-start: 1px;
 }
 
 /* Text input */
 
 .devtools-textinput,
-.devtools-searchinput {
+.devtools-searchinput,
+.devtools-filterinput {
   -moz-appearance: none;
   margin: 1px 3px;
   border: 1px solid;
   border-radius: 2px;
   padding: 4px 6px;
   border-color: var(--theme-splitter-color);
+  font: message-box;
 }
 
 :root[platform="mac"] .devtools-textinput,
-:root[platform="mac"] .devtools-searchinput {
+:root[platform="mac"] .devtools-searchinput,
+:root[platform="mac"] .devtools-filterinput {
   border-radius: 20px;
 }
 
-.devtools-searchinput {
+.devtools-searchinput,
+.devtools-filterinput {
   padding: 0;
   padding-inline-start: 22px;
   padding-inline-end: 4px;
-  background-image: var(--magnifying-glass-image);
   background-position: 8px center;
   background-size: 11px 11px;
   background-repeat: no-repeat;
   font-size: inherit;
 }
 
+.devtools-searchinput {
+  background-image: var(--magnifying-glass-image);
+}
+
+.devtools-filterinput {
+  background-image: var(--filter-image);
+}
+
 .devtools-searchinput:-moz-locale-dir(rtl),
-.devtools-searchinput:dir(rtl) {
+.devtools-searchinput:dir(rtl),
+.devtools-filterinput:-moz-locale-dir(rtl),
+.devtools-filterinput:dir(rtl) {
   background-position: calc(100% - 8px) center;
 }
 
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
   visibility: hidden;
 }
 
+.devtools-searchinput .textbox-input::-moz-placeholder,
+.devtools-filterinput .textbox-input::-moz-placeholder {
+  font-style: normal;
+}
+
 /* Searchbox is a div container element for a search input element */
 .devtools-searchbox {
   display: flex;
   flex: 1;
   position: relative;
   padding: 0 3px;
 }
 
 /* The spacing is accomplished with a padding on the searchbox */
 .devtools-searchbox > .devtools-textinput,
-.devtools-searchbox > .devtools-searchinput {
+.devtools-searchbox > .devtools-searchinput,
+.devtools-searchbox > .devtools-filterinput {
   margin-left: 0;
   margin-right: 0;
 }
 
 .devtools-searchbox > .devtools-textinput:-moz-focusring,
-.devtools-searchbox > .devtools-searchinput:-moz-focusring {
+.devtools-searchbox > .devtools-searchinput:-moz-focusring,
+.devtools-searchbox > .devtools-filterinput:-moz-focusring {
   border-color: var(--theme-focus-border-color-textbox);
   box-shadow: var(--theme-focus-box-shadow-textbox);
   transition: all 0.2s ease-in-out;
   outline: none;
 }
 
 /* Don't add 'double spacing' for inputs that are at beginning / end
    of a toolbar (since the toolbar has it's own spacing). */
 .devtools-toolbar > .devtools-textinput:first-child,
-.devtools-toolbar > .devtools-searchinput:first-child {
+.devtools-toolbar > .devtools-searchinput:first-child,
+.devtools-toolbar > .devtools-filterinput:first-child {
   margin-inline-start: 0;
 }
 .devtools-toolbar > .devtools-textinput:last-child,
-.devtools-toolbar > .devtools-searchinput:last-child {
+.devtools-toolbar > .devtools-searchinput:last-child,
+.devtools-toolbar > .devtools-filterinput:last-child {
   margin-inline-end: 0;
 }
 .devtools-toolbar > .devtools-searchbox:first-child {
   padding-inline-start: 0;
 }
 .devtools-toolbar > .devtools-searchbox:last-child {
   padding-inline-end: 0;
 }
 
 .devtools-rule-searchbox {
   -moz-box-flex: 1;
   width: 100%;
-  font: inherit;
 }
 
 .devtools-rule-searchbox[filled] {
   background-color: var(--searchbox-background-color);
   border-color: var(--searchbox-border-color);
   padding-inline-end: 23px;
 }
 
 .devtools-style-searchbox-no-match {
   background-color: var(--searcbox-no-match-background-color) !important;
   border-color: var(--searcbox-no-match-border-color) !important;
 }
 
-.devtools-no-search-result {
-  border-color: var(--theme-highlight-red) !important;
-}
-
 .devtools-searchinput-clear {
   position: absolute;
   top: 3.5px;
   right: 7px;
   padding: 0;
   border: 0;
   width: 16px;
   height: 16px;
@@ -500,40 +518,38 @@
 .devtools-style-searchbox-no-match + .devtools-searchinput-clear {
   background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
 }
 
 .devtools-searchinput-clear:hover {
   background-position: -16px 0;
 }
 
-.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
   list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
   list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
   margin-bottom: 0;
 }
 
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
-@media (min-resolution: 1.1dppx) {
-  .devtools-searchinput {
-    background-image: var(--magnifying-glass-image-2x);
-  }
-}
-
 /* Close button */
 
 #toolbox-close::before {
   background-image: var(--close-button-image);
 }
 
 /* In-tools sidebar */
 .devtools-sidebar-tabs {
--- a/devtools/client/webconsole/webconsole.xul
+++ b/devtools/client/webconsole/webconsole.xul
@@ -174,17 +174,17 @@ function goUpdateConsoleCommands() {
               <menuitem label="&btnServerLog;" type="checkbox" autocheck="false"
                         prefKey="serverlog"/>
             </menupopup>
           </toolbarbutton>
         </hbox>
 
         <spacer flex="1"/>
 
-        <textbox class="compact hud-filter-box devtools-searchinput" type="search"
+        <textbox class="compact hud-filter-box devtools-filterinput" type="search"
                  placeholder="&filterOutput.placeholder;" tabindex="2"/>
       </toolbar>
 
       <hbox id="output-wrapper" flex="1" context="output-contextmenu" tooltip="aHTMLTooltip">
         <!-- Wrapper element to make scrolling in output-container much faster.
              See Bug 1237368 -->
         <div xmlns="http://www.w3.org/1999/xhtml">
           <div xmlns="http://www.w3.org/1999/xhtml" id="output-container"
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2508,20 +2508,16 @@ nsDocShell::GetFullscreenAllowed(bool* a
   if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
     *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
     return NS_OK;
   }
 
   // Assume false until we determine otherwise...
   *aFullscreenAllowed = false;
 
-  // If it is sandboxed, fullscreen is not allowed.
-  if (mSandboxFlags & SANDBOXED_FULLSCREEN) {
-    return NS_OK;
-  }
   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   if (!win) {
     return NS_OK;
   }
   nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
   if (frameElement && !frameElement->IsXULElement()) {
     // We do not allow document inside any containing element other
     // than iframe to enter fullscreen.
@@ -2551,20 +2547,16 @@ nsDocShell::GetFullscreenAllowed(bool* a
       if (!frameElement->HasAttr(kNameSpaceID_None,
                                  nsGkAtoms::allowfullscreen)) {
         return NS_OK;
       }
     } else {
       // neither iframe nor embed
       return NS_OK;
     }
-    nsIDocument* doc = frameElement->GetUncomposedDoc();
-    if (!doc || !doc->FullscreenEnabledInternal()) {
-      return NS_OK;
-    }
   }
 
   // If we have no parent then we're the root docshell; no ancestor of the
   // original docshell doesn't have a allowfullscreen attribute, so
   // report fullscreen as allowed.
   RefPtr<nsDocShell> parent = GetParentDocshell();
   if (!parent) {
     *aFullscreenAllowed = true;
@@ -3832,23 +3824,16 @@ nsDocShell::IsSandboxedFrom(nsIDocShell*
       return false;
     }
   }
 
   // Otherwise, we are sandboxed from aTargetDocShell.
   return true;
 }
 
-void
-nsDocShell::ApplySandboxAndFullscreenFlags(nsIDocument* aDoc)
-{
-  aDoc->SetSandboxFlags(mSandboxFlags);
-  aDoc->SetFullscreenEnabled(GetFullscreenAllowed());
-}
-
 NS_IMETHODIMP
 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
 {
   NS_ENSURE_ARG_POINTER(aTreeOwner);
 
   *aTreeOwner = mTreeOwner;
   NS_IF_ADDREF(*aTreeOwner);
   return NS_OK;
@@ -8057,19 +8042,19 @@ nsDocShell::CreateAboutBlankContentViewe
                                     getter_AddRefs(blankDoc));
     if (blankDoc) {
       // Hack: set the base URI manually, since this document never
       // got Reset() with a channel.
       blankDoc->SetBaseURI(aBaseURI);
 
       blankDoc->SetContainer(this);
 
-      // Apply the sandbox and fullscreen enabled flags to the document.
-      // These are immutable after being set here.
-      ApplySandboxAndFullscreenFlags(blankDoc);
+      // Copy our sandbox flags to the document. These are immutable
+      // after being set here.
+      blankDoc->SetSandboxFlags(mSandboxFlags);
 
       // create a content viewer for us and the new document
       docFactory->CreateInstanceForDocument(
         NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
         getter_AddRefs(viewer));
 
       // hook 'em up
       if (viewer) {
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -20,17 +20,16 @@ class nsIPresShell;
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
-interface nsIDocument;
 interface nsIEditor;
 interface nsIEditingSession;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
@@ -897,21 +896,16 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * Returns true if we are sandboxed from aTargetDocShell.
    * aTargetDocShell - the browsing context we are attempting to navigate.
    */
   [noscript,notxpcom,nostdcall] bool isSandboxedFrom(in nsIDocShell aTargetDocShell);
 
   /**
-   * Apply sandbox flags and fullscreen enabled flag to the given document.
-   */
-  [noscript,notxpcom] void applySandboxAndFullscreenFlags(in nsIDocument aDoc);
-
-  /**
    * This member variable determines whether a document has Mixed Active Content that
    * was initially blocked from loading, but the user has choosen to override the
    * block and allow the content to load. mMixedContentChannel is set to the document's
    * channel when the user allows mixed content. The nsMixedContentBlocker content policy
    * checks if the document's root channel matches the mMixedContentChannel.  If it matches,
    * then Mixed Content is loaded.  If it does match, mixed content is blocked.
    *
    * A match implies that there is definitely mixed active content on a page that was
--- a/dom/base/ImportManager.cpp
+++ b/dom/base/ImportManager.cpp
@@ -596,17 +596,16 @@ ImportLoader::OnStartRequest(nsIRequest*
   // The imported document must know which master document it belongs to.
   mDocument = do_QueryInterface(importDoc);
   nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
   mDocument->SetMasterDocument(master);
 
   // We want to inherit the sandbox flags and fullscreen enabled flag
   // from the master document.
   mDocument->SetSandboxFlags(master->GetSandboxFlags());
-  mDocument->SetFullscreenEnabled(master->FullscreenEnabledInternal());
 
   // We have to connect the blank document we created with the channel we opened,
   // and create its own LoadGroup for it.
   nsCOMPtr<nsIStreamListener> listener;
   nsCOMPtr<nsILoadGroup> loadGroup;
   channel->GetLoadGroup(getter_AddRefs(loadGroup));
   nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1450,17 +1450,16 @@ nsIDocument::nsIDocument()
     // unless we get a window, and in that case the docshell value will get
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
     mFontFaceSetDirty(true),
     mGetUserFontSetCalled(false),
     mPostedFlushUserFontSet(false),
-    mFullscreenEnabled(false),
     mPartID(0),
     mDidFireDOMContentLoaded(true),
     mHasScrollLinkedEffect(false),
     mUserHasInteracted(false)
 {
   SetInDocument();
 
   PR_INIT_CLIST(&mDOMMediaQueryLists);
@@ -2589,17 +2588,18 @@ nsDocument::StartDocumentLoad(const char
   }
 
   // If this document is being loaded by a docshell, copy its sandbox flags
   // to the document, and store the fullscreen enabled flag. These are
   // immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
-    docShell->ApplySandboxAndFullscreenFlags(this);
+    nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
   }
 
   // The CSP directive upgrade-insecure-requests not only applies to the
   // toplevel document, but also to nested documents. Let's propagate that
   // flag from the parent to the nested document.
   nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
   if (treeItem) {
@@ -11797,17 +11797,21 @@ GetFullscreenError(nsIDocument* aDoc, bo
     // in a Runnable, so don't use GetMozFullScreenEnabled() from a
     // Runnable!
     return nullptr;
   }
 
   if (!nsContentUtils::IsFullScreenApiEnabled()) {
     return "FullscreenDeniedDisabled";
   }
-  if (!aDoc->FullscreenEnabledInternal()) {
+
+  // Ensure that all containing elements are <iframe> and have
+  // allowfullscreen attribute set.
+  nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
+  if (!docShell || !docShell->GetFullscreenAllowed()) {
     return "FullscreenDeniedContainerNotAllowed";
   }
   return nullptr;
 }
 
 bool
 nsDocument::FullscreenElementReadyCheck(Element* aElement,
                                         bool aWasCallerChrome)
@@ -12361,20 +12365,17 @@ public:
     // Also, don't let the page to try to get the permission too many times.
     if (!mUserInputOrChromeCaller ||
         doc->mCancelledPointerLockRequests > kPointerLockRequestLimit) {
       Handled();
       DispatchPointerLockError(d);
       return NS_OK;
     }
 
-    // Handling a request from user input in non-fullscreen mode.
-    // Do a normal permission check.
-    nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow();
-    nsContentPermissionUtils::AskPermission(this, window);
+    Allow(JS::UndefinedHandleValue);
     return NS_OK;
   }
 
   void Handled()
   {
     mElement = nullptr;
     mDocument = nullptr;
     if (gPendingPointerLockRequest == this) {
@@ -12487,16 +12488,20 @@ nsPointerLockPermissionRequest::Allow(JS
   d->mCancelledPointerLockRequests = 0;
   e->SetPointerLock();
   EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
   EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
   NS_ASSERTION(EventStateManager::sPointerLockedElement &&
                EventStateManager::sPointerLockedDoc,
                "aElement and this should support weak references!");
 
+  nsContentUtils::DispatchEventOnlyToChrome(
+    doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
+    /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
+
   DispatchPointerLockChange(d);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPointerLockPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
 {
   NS_ENSURE_ARG_POINTER(aRequester);
@@ -12761,16 +12766,22 @@ nsDocument::UnlockPointer(nsIDocument* a
   if (pointerLockedElement) {
     pointerLockedElement->ClearPointerLock();
   }
 
   EventStateManager::sPointerLockedElement = nullptr;
   EventStateManager::sPointerLockedDoc = nullptr;
   static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
   gPendingPointerLockRequest = nullptr;
+
+  nsContentUtils::DispatchEventOnlyToChrome(
+    doc, ToSupports(pointerLockedElement),
+    NS_LITERAL_STRING("MozDOMPointerLock:Exited"),
+    /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
+
   DispatchPointerLockChange(pointerLockedDoc);
 }
 
 void
 nsIDocument::UnlockPointer(nsIDocument* aDoc)
 {
   nsDocument::UnlockPointer(aDoc);
 }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2588,21 +2588,16 @@ public:
   // Not const because all the full-screen goop is not const
   virtual bool FullscreenEnabled() = 0;
   virtual Element* GetFullscreenElement() = 0;
   bool Fullscreen()
   {
     return !!GetFullscreenElement();
   }
   void ExitFullscreen();
-  bool FullscreenEnabledInternal() const { return mFullscreenEnabled; }
-  void SetFullscreenEnabled(bool aEnabled)
-  {
-    mFullscreenEnabled = aEnabled;
-  }
   Element* GetMozPointerLockElement();
   void MozExitPointerLock()
   {
     UnlockPointer(this);
   }
   bool Hidden() const
   {
     return mVisibilityState != mozilla::dom::VisibilityState::Visible;
@@ -3054,20 +3049,16 @@ protected:
   bool mFontFaceSetDirty : 1;
 
   // Has GetUserFontSet() been called?
   bool mGetUserFontSetCalled : 1;
 
   // Do we currently have an event posted to call FlushUserFontSet?
   bool mPostedFlushUserFontSet : 1;
 
-  // Whether fullscreen is enabled for this document. This corresponds
-  // to the "fullscreen enabled flag" in the HTML spec.
-  bool mFullscreenEnabled : 1;
-
   enum Type {
     eUnknown, // should never be used
     eHTML,
     eXHTML,
     eGenericXML,
     eSVG,
     eXUL
   };
--- a/dom/base/nsSandboxFlags.h
+++ b/dom/base/nsSandboxFlags.h
@@ -80,38 +80,33 @@ const unsigned long SANDBOXED_AUTOMATIC_
 /**
  * This flag prevents URL schemes that use storage areas from being able to
  * access the origin's data.
  */
 // We don't have an explicit representation of this one, apparently?
 // const unsigned long SANDBOXED_STORAGE_AREA_URLS = 0x200;
 
 /**
- * This flag prevents content from using the requestFullscreen() method.
- */
-const unsigned long SANDBOXED_FULLSCREEN = 0x400;
-
-/**
  * This flag blocks the document from changing document.domain.
  */
-const unsigned long SANDBOXED_DOMAIN = 0x800;
+const unsigned long SANDBOXED_DOMAIN = 0x400;
 
 /**
  * This flag prevents content from using window.alert(), window.confirm(),
  * window.print(), window.prompt() and the beforeunload event from putting up
  * dialogs.
  */
-const unsigned long SANDBOXED_MODALS = 0x1000;
+const unsigned long SANDBOXED_MODALS = 0x800;
 
 /**
  * This flag prevents content from escaping the sandbox by ensuring that any
  * auxiliary browsing context it creates inherits the content's active
  * sandboxing flag set.
  */
-const unsigned long SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS = 0x2000;
+const unsigned long SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS = 0x1000;
 
 /**
  * This flag prevents locking screen orientation.
  */
-const unsigned long SANDBOXED_ORIENTATION_LOCK = 0x4000;
+const unsigned long SANDBOXED_ORIENTATION_LOCK = 0x2000;
 
-const unsigned long SANDBOX_ALL_FLAGS = 0x7FFF;
+const unsigned long SANDBOX_ALL_FLAGS = 0x3FFF;
 #endif
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -199,21 +199,17 @@ HTMLIFrameElement::SetAttr(int32_t aName
   return NS_OK;
 }
 
 nsresult
 HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue,
                                 bool aNotify)
 {
-  if ((aName == nsGkAtoms::sandbox ||
-       // The allowfullscreen attribute affects the sandboxed fullscreen
-       // flag, thus we should also reapply it if that is changed.
-       aName == nsGkAtoms::allowfullscreen ||
-       aName == nsGkAtoms::mozallowfullscreen) &&
+  if (aName == nsGkAtoms::sandbox &&
       aNameSpaceID == kNameSpaceID_None && mFrameLoader) {
     // If we have an nsFrameLoader, apply the new sandbox flags.
     // Since this is called after the setter, the sandbox flags have
     // alreay been updated.
     mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
   }
   return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                                  aNotify);
@@ -239,25 +235,17 @@ HTMLIFrameElement::UnsetAttr(int32_t aNa
 uint32_t
 HTMLIFrameElement::GetSandboxFlags()
 {
   const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox);
   // No sandbox attribute, no sandbox flags.
   if (!sandboxAttr) {
     return SANDBOXED_NONE;
   }
-
-  uint32_t out = nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
-
-  if (GetParsedAttr(nsGkAtoms::allowfullscreen) ||
-      GetParsedAttr(nsGkAtoms::mozallowfullscreen)) {
-    out &= ~SANDBOXED_FULLSCREEN;
-  }
-
-  return out;
+  return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
 }
 
 JSObject*
 HTMLIFrameElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLIFrameElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/media/tests/mochitest/identity/mochitest.ini
+++ b/dom/media/tests/mochitest/identity/mochitest.ini
@@ -38,12 +38,8 @@ support-files =
 [test_setIdentityProvider.html]
 [test_setIdentityProviderWithErrors.html]
 [test_peerConnection_peerIdentity.html]
 [test_peerConnection_asymmetricIsolation.html]
 [test_loginNeeded.html]
 support-files =
   /.well-known/idp-proxy/login.html
   /.well-known/idp-proxy/idp.sjs
-
-
-# Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
-[../test_zmedia_cleanup.html]
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -279,10 +279,8 @@ skip-if = toolkit == 'gonk' || buildapp 
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_remoteRollback.html]
 skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # b2g (Bug 1059867), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_remoteReofferRollback.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_selftest.html]
 # Bug 1227781: Crash with bogus TURN server.
 [test_peerConnection_bug1227781.html]
-# Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
-[test_zmedia_cleanup.html]
--- a/dom/media/tests/mochitest/network.js
+++ b/dom/media/tests/mochitest/network.js
@@ -1,122 +1,20 @@
 /* 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";
 
 /**
- * Query function for determining if any IP address is available for
- * generating SDP.
+ * A stub function for preparing the network if needed
  *
- * @return false if required additional network setup.
  */
-function isNetworkReady() {
-  // for gonk platform
-  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
-    var listService = SpecialPowers.Cc["@mozilla.org/network/interface-list-service;1"]
-                        .getService(SpecialPowers.Ci.nsINetworkInterfaceListService);
-    var itfList = listService.getDataInterfaceList(
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_MMS_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_SUPL_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_IMS_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_DUN_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_FOTA_INTERFACES);
-    var num = itfList.getNumberOfInterface();
-    for (var i = 0; i < num; i++) {
-      var ips = {};
-      var prefixLengths = {};
-      var length = itfList.getInterfaceInfo(i).getAddresses(ips, prefixLengths);
-
-      for (var j = 0; j < length; j++) {
-        var ip = ips.value[j];
-        // skip IPv6 address until bug 797262 is implemented
-        if (ip.indexOf(":") < 0) {
-          info("Network interface is ready with address: " + ip);
-          return true;
-        }
-      }
-    }
-    // ip address is not available
-    info("Network interface is not ready, required additional network setup");
-    return false;
-  }
-  info("Network setup is not required");
-  return true;
+function startNetworkAndTest() {
+  return Promise.resolve();
 }
 
 /**
- * Network setup utils for Gonk
- *
- * @return {object} providing functions for setup/teardown data connection
- */
-function getNetworkUtils() {
-  var url = SimpleTest.getTestFileURL("NetworkPreparationChromeScript.js");
-  var script = SpecialPowers.loadChromeScript(url);
-
-  var utils = {
-    /**
-     * Utility for setting up data connection.
-     *
-     * @param aCallback callback after data connection is ready.
-     */
-    prepareNetwork: function() {
-      return new Promise(resolve => {
-        script.addMessageListener('network-ready', () =>  {
-          info("Network interface is ready");
-          resolve();
-        });
-        info("Setting up network interface");
-        script.sendAsyncMessage("prepare-network", true);
-      });
-    },
-    /**
-     * Utility for tearing down data connection.
-     *
-     * @param aCallback callback after data connection is closed.
-     */
-    tearDownNetwork: function() {
-      if (!isNetworkReady()) {
-        info("No network to tear down");
-        return Promise.resolve();
-      }
-      return new Promise(resolve => {
-        script.addMessageListener('network-disabled', message => {
-          info("Network interface torn down");
-          script.destroy();
-          resolve();
-        });
-        info("Tearing down network interface");
-        script.sendAsyncMessage("network-cleanup", true);
-      });
-    }
-  };
-
-  return utils;
-}
-
-/**
- * Setup network on Gonk if needed and execute test once network is up
- *
- */
-function startNetworkAndTest() {
-  if (isNetworkReady()) {
-    return Promise.resolve();
-  }
-  var utils = getNetworkUtils();
-  // Trigger network setup to obtain IP address before creating any PeerConnection.
-  return utils.prepareNetwork();
-}
-
-/**
- * A wrapper around SimpleTest.finish() to handle B2G network teardown
+ * A stub function to shutdown the network if needed
  */
 function networkTestFinished() {
-  var p;
-  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
-    var utils = getNetworkUtils();
-    p = utils.tearDownNetwork();
-  } else {
-    p = Promise.resolve();
-  }
-  return p.then(() => finish());
+  return Promise.resolve().then(() => finish());
 }
deleted file mode 100644
--- a/dom/media/tests/mochitest/test_zmedia_cleanup.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-  <head>
-    <script src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script src="network.js"></script>
-  </head>
-<body>
-<pre id="test">
-<script type="application/javascript">
-
-SimpleTest.waitForExplicitFinish();
-
-if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
-  getNetworkUtils().tearDownNetwork()
-    .then(() =>
-          ok(true, 'Successfully teared down network interface'),
-          () =>
-          ok(true, 'Network interface was in down state already'))
-    .then(() => SimpleTest.finish());
-} else {
-  ok(true, 'No need to cleanup network interface');
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -378,17 +378,16 @@ XULDocument::StartDocumentLoad(const cha
             }
         }
     }
     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
     // we'll possibly need to reset our content type afterwards.
     mStillWalking = true;
     mMayStartLayout = false;
     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
-    mFullscreenEnabled = true;
 
     mChannel = aChannel;
 
     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
     nsresult rv =
         NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
     NS_ENSURE_SUCCESS(rv, rv);
     
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1667,26 +1667,56 @@ public abstract class GeckoApp
 
             // If we are doing an OOM restore, parse the session data and
             // stub the restored tabs immediately. This allows the UI to be
             // updated before Gecko has restored.
             if (mShouldRestore) {
                 final JSONArray tabs = new JSONArray();
                 final JSONObject windowObject = new JSONObject();
                 SessionParser parser = new SessionParser() {
+                    private boolean selectNextTab;
+
                     @Override
                     public void onTabRead(final SessionTab sessionTab) {
+                        if (sessionTab.isAboutHomeWithoutHistory()) {
+                            // This is a tab pointing to about:home with no history. We won't restore
+                            // this tab. If we end up restoring no tabs then the browser will decide
+                            // whether it needs to open about:home or a different 'homepage'. If we'd
+                            // always restore about:home only tabs then we'd never open the homepage.
+                            // See bug 1261008.
+
+                            if (sessionTab.isSelected()) {
+                                // Unfortunately this tab is the selected tab. Let's just try to select
+                                // the first tab. If we haven't restored any tabs so far then remember
+                                // to select the next tab that gets restored.
+
+                                if (!Tabs.getInstance().selectLastTab()) {
+                                    selectNextTab = true;
+                                }
+                            }
+
+                            // Do not restore this tab.
+                            return;
+                        }
+
                         JSONObject tabObject = sessionTab.getTabObject();
 
                         int flags = Tabs.LOADURL_NEW_TAB;
                         flags |= ((isExternalURL || !sessionTab.isSelected()) ? Tabs.LOADURL_DELAY_LOAD : 0);
                         flags |= (tabObject.optBoolean("desktopMode") ? Tabs.LOADURL_DESKTOP : 0);
                         flags |= (tabObject.optBoolean("isPrivate") ? Tabs.LOADURL_PRIVATE : 0);
 
                         final Tab tab = Tabs.getInstance().loadUrl(sessionTab.getUrl(), flags);
+
+                        if (selectNextTab) {
+                            // We did not restore the selected tab previously. Now let's select this tab.
+                            Tabs.getInstance().selectTab(tab.getId());
+                            selectNextTab = false;
+                        }
+
                         ThreadUtils.postToUiThread(new Runnable() {
                             @Override
                             public void run() {
                                 tab.updateTitle(sessionTab.getTitle());
                             }
                         });
 
                         try {
--- a/mobile/android/base/java/org/mozilla/gecko/SessionParser.java
+++ b/mobile/android/base/java/org/mozilla/gecko/SessionParser.java
@@ -43,16 +43,24 @@ public abstract class SessionParser {
 
         public boolean isSelected() {
             return mIsSelected;
         }
 
         public JSONObject getTabObject() {
             return mTabObject;
         }
+
+        /**
+         * Is this tab pointing to about:home and does not contain any other history?
+         */
+        public boolean isAboutHomeWithoutHistory() {
+            JSONArray entries = mTabObject.optJSONArray("entries");
+            return entries != null && entries.length() == 1 && AboutPages.isAboutHome(mUrl);
+        }
     };
 
     abstract public void onTabRead(SessionTab tab);
 
     /**
      * Placeholder method that must be overloaded to handle closedTabs while parsing session data.
      *
      * @param closedTabs, JSONArray of recently closed tab entries.
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -255,16 +255,25 @@ public class Tabs implements GeckoEventL
             notifyListeners(oldTab, TabEvents.UNSELECTED);
         }
 
         // Pass a message to Gecko to update tab state in BrowserApp.
         GeckoAppShell.notifyObservers("Tab:Selected", String.valueOf(tab.getId()));
         return tab;
     }
 
+    public synchronized boolean selectLastTab() {
+        if (mOrder.isEmpty()) {
+            return false;
+        }
+
+        selectTab(mOrder.get(mOrder.size() - 1).getId());
+        return true;
+    }
+
     private int getIndexOf(Tab tab) {
         return mOrder.lastIndexOf(tab);
     }
 
     private Tab getNextTabFrom(Tab tab, boolean getPrivate) {
         int numTabs = mOrder.size();
         int index = getIndexOf(tab);
         for (int i = index + 1; i < numTabs; i++) {
--- a/mobile/android/config/mozconfigs/common
+++ b/mobile/android/config/mozconfigs/common
@@ -47,28 +47,35 @@ fi
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
 ac_add_options --enable-warnings-as-errors
 
 ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-fennec-geoloc-api.key
+
+# MOZ_INSTALL_TRACKING does not guarantee MOZ_UPDATE_CHANNEL will be set so we
+# provide a default state. Currently, the default state provides a default
+# keyfile because an assertion will be thrown if MOZ_INSTALL_TRACKING is
+# specified but a keyfile is not. This assertion can catch if we misconfigure a
+# release or beta build and it does not have a valid keyfile.
+#
+# However, by providing a default keyfile, if we misconfigure beta or release,
+# the default keyfile may be used instead and the assertion won't catch the
+# error.  Therefore, it would be ideal to have MOZ_INSTALL_TRACKING guarantee
+# MOZ_UPDATE_CHANNEL was set so we can remove the default case. This may occur
+# when release promotion is implemented on Android.
+#
+# In all cases, we don't upload Adjust pings in automation.
 if test "$MOZ_UPDATE_CHANNEL" = "release" ; then
     ac_add_options --with-adjust-sdk-keyfile=/builds/adjust-sdk.token
 elif test "$MOZ_UPDATE_CHANNEL" = "beta" ; then
     ac_add_options --with-adjust-sdk-keyfile=/builds/adjust-sdk-beta.token
 else
-    # (bug 1277553) In Aurora -> Beta simulation builds, no update channel is
-    # specified, causing an assertion to throw that MOZ_INSTALL_TRACKING is
-    # specified but the keyfile is not. In this case, we add a default keyfile.
-    # This has the disadvantage that if our beta/release checks above ever
-    # fail, we'll come to this default case and the compile-time check to
-    # specify a valid keyfile will be broken. I don't have any better
-    # alternatives.
     ac_add_options --with-adjust-sdk-keyfile="$topsrcdir/mobile/android/base/adjust-sdk-sandbox.token"
 fi
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Use ccache
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/setup/activities/WebURLFinder.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/setup/activities/WebURLFinder.java
@@ -6,20 +6,46 @@ package org.mozilla.gecko.sync.setup.act
 
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.regex.Matcher;
-
-import android.util.Patterns;
+import java.util.regex.Pattern;
 
 public class WebURLFinder {
+  /**
+   * These regular expressions are taken from Android's Patterns.java.
+   * We brought them in to standardize URL matching across Android versions, instead of relying
+   * on Android version-dependent built-ins that can vary across Android versions.
+   * The original code can be found here:
+   * http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/util/Patterns.java
+   *
+   */
+  public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
+  public static final String GOOD_GTLD_CHAR = "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
+  public static final String IRI = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}";
+  public static final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}";
+  public static final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD;
+  public static final Pattern IP_ADDRESS = Pattern.compile("((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+          + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+          + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+          + "|[1-9][0-9]|[0-9]))");
+  public static final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
+  public static final Pattern WEB_URL = Pattern.compile("((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+          + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+          + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+          + "(?:" + DOMAIN_NAME + ")"
+          + "(?:\\:\\d{1,5})?)"
+          + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~"
+          + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+          + "(?:\\b|$)");
+
   public final List<String> candidates;
 
   public WebURLFinder(String string) {
     if (string == null) {
       throw new IllegalArgumentException("string must not be null");
     }
 
     this.candidates = candidateWebURLs(string);
@@ -85,17 +111,17 @@ public class WebURLFinder {
 
       candidates.addAll(candidateWebURLs(string));
     }
 
     return candidates;
   }
 
   protected static List<String> candidateWebURLs(String string) {
-    Matcher matcher = Patterns.WEB_URL.matcher(string);
+    Matcher matcher = WEB_URL.matcher(string);
     List<String> matches = new LinkedList<String>();
 
     while (matcher.find()) {
       // Remove URLs with bad schemes.
       if (!isWebURL(matcher.group())) {
         continue;
       }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4669,16 +4669,20 @@ pref("full-screen-api.transition-duratio
 pref("full-screen-api.transition-duration.leave", "200 200");
 // timeout for black screen in fullscreen transition, unit: ms
 pref("full-screen-api.transition.timeout", 1000);
 // time for the warning box stays on the screen before sliding out, unit: ms
 pref("full-screen-api.warning.timeout", 3000);
 // delay for the warning box to show when pointer stays on the top, unit: ms
 pref("full-screen-api.warning.delay", 500);
 
+// DOM pointerlock API
+// time for the warning box stays on the screen before sliding out, unit: ms
+pref("pointer-lock-api.warning.timeout", 3000);
+
 // DOM idle observers API
 pref("dom.idle-observers-api.enabled", true);
 
 // Time limit, in milliseconds, for EventStateManager::IsHandlingUserInput().
 // Used to detect long running handlers of user-generated events.
 pref("dom.event.handling-user-input-time-limit", 1000);
 
 // Whether we should layerize all animated images (if otherwise possible).
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1099,17 +1099,17 @@ void HandshakeCallback(PRFileDesc* fd, v
           fd, static_cast<unsigned int>(versions.min),
               static_cast<unsigned int>(versions.max)));
 
   // If the handshake completed, then we know the site is TLS tolerant
   ioLayerHelpers.rememberTolerantAtVersion(infoObject->GetHostName(),
                                            infoObject->GetPort(),
                                            versions.max);
 
-  bool usesWeakCipher = false;
+  bool usesFallbackCipher = false;
   SSLChannelInfo channelInfo;
   rv = SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo));
   MOZ_ASSERT(rv == SECSuccess);
   if (rv == SECSuccess) {
     // Get the protocol version for telemetry
     // 1=tls1, 2=tls1.1, 3=tls1.2
     unsigned int versionEnum = channelInfo.protocolVersion & 0xFF;
     MOZ_ASSERT(versionEnum > 0);
@@ -1119,17 +1119,17 @@ void HandshakeCallback(PRFileDesc* fd, v
                                     : Telemetry::SSL_CIPHER_SUITE_RESUMED,
       channelInfo);
 
     SSLCipherSuiteInfo cipherInfo;
     rv = SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
                                 sizeof cipherInfo);
     MOZ_ASSERT(rv == SECSuccess);
     if (rv == SECSuccess) {
-      usesWeakCipher = cipherInfo.symCipher == ssl_calg_rc4;
+      usesFallbackCipher = cipherInfo.keaType == ssl_kea_dh;
 
       // keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4
       Telemetry::Accumulate(
         infoObject->IsFullHandshake()
           ? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL
           : Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED,
         cipherInfo.keaType);
 
@@ -1202,30 +1202,29 @@ void HandshakeCallback(PRFileDesc* fd, v
     status = new nsSSLStatus();
     infoObject->SetSSLStatus(status);
   }
 
   RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject,
                                                              status);
 
   uint32_t state;
-  if (usesWeakCipher || renegotiationUnsafe) {
+  if (renegotiationUnsafe) {
     state = nsIWebProgressListener::STATE_IS_BROKEN;
-    if (usesWeakCipher) {
-      state |= nsIWebProgressListener::STATE_USES_WEAK_CRYPTO;
-    }
   } else {
     state = nsIWebProgressListener::STATE_IS_SECURE |
             nsIWebProgressListener::STATE_SECURE_HIGH;
-    SSLVersionRange defVersion;
-    rv = SSL_VersionRangeGetDefault(ssl_variant_stream, &defVersion);
-    if (rv == SECSuccess && versions.max >= defVersion.max) {
-      // we know this site no longer requires a weak cipher
-      ioLayerHelpers.removeInsecureFallbackSite(infoObject->GetHostName(),
-                                                infoObject->GetPort());
+    if (!usesFallbackCipher) {
+      SSLVersionRange defVersion;
+      rv = SSL_VersionRangeGetDefault(ssl_variant_stream, &defVersion);
+      if (rv == SECSuccess && versions.max >= defVersion.max) {
+        // we know this site no longer requires a fallback cipher
+        ioLayerHelpers.removeInsecureFallbackSite(infoObject->GetHostName(),
+                                                  infoObject->GetPort());
+      }
     }
   }
 
   if (status->HasServerCert()) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("HandshakeCallback KEEPING existing cert\n"));
   } else {
     UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd));
--- a/security/manager/ssl/tests/unit/test_fallback_cipher.js
+++ b/security/manager/ssl/tests/unit/test_fallback_cipher.js
@@ -219,21 +219,27 @@ add_task(function* () {
   // So we will see the crafted NS_ERROR_NET_RESET when we use
   // nsISocketTransport directly.
   yield startClient("server: fallback only, client: fallback enabled, public",
                     port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
                     {allowReset: true});
   // retry manually to simulate the HTTP layer
   yield startClient("server: fallback only, client: fallback enabled retry, public",
                     port, Cr.NS_OK);
+  // make sure that we remember the TLS intolerance
+  yield startClient("server: fallback only, client: second try after fallback success, public",
+                    port, Cr.NS_OK);
   yield startClient("server: fallback only, client: fallback enabled, private",
                     port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
                     {isPrivate: true, allowReset: true});
   yield startClient("server: fallback only, client: fallback enabled retry, private",
                     port, Cr.NS_OK, {isPrivate: true});
+  // make sure that we remember the TLS intolerance
+  yield startClient("server: fallback only, client: second try after fallback success, private",
+                    port, Cr.NS_OK);
 });
 
 do_register_cleanup(function() {
   do_print("reset modified prefs");
   for (let pref of fallback_cipher_prefs) {
     Services.prefs.clearUserPref(pref);
   }
 });
--- a/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
+++ b/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
@@ -153,31 +153,24 @@ class UpdateTestCase(FirefoxTestCase):
         Wait(self.marionette, timeout=self.TIMEOUT_UPDATE_CHECK).until(
             lambda _: about_window.deck.selected_panel not in
             (about_window.deck.check_for_updates, about_window.deck.checking_for_updates),
             message='Check for updates has been finished.')
 
         return about_window.deck.selected_panel != about_window.deck.no_updates_found
 
     def check_update_applied(self):
-        self.updates[self.current_update_index]['build_post'] = self.software_update.build_info
+        """Check that the update has been applied correctly"""
+        update = self.updates[self.current_update_index]
+        update['build_post'] = self.software_update.build_info
 
         about_window = self.browser.open_about_window()
         try:
             update_available = self.check_for_updates(about_window)
 
-            # No further updates should be offered now with the same update type
-            if update_available:
-                self.download_update(about_window, wait_for_finish=False)
-                self.assertNotEqual(self.software_update.active_update.type,
-                                    self.updates[self.current_update_index].type)
-
-            # Check that the update has been applied correctly
-            update = self.updates[self.current_update_index]
-
             # The upgraded version should be identical with the version given by
             # the update and we shouldn't have run a downgrade
             check = self.marionette.execute_script("""
               Components.utils.import("resource://gre/modules/Services.jsm");
 
               return  Services.vc.compare(arguments[0], arguments[1]);
             """, script_args=[update['build_post']['version'], update['build_pre']['version']])
 
@@ -197,16 +190,27 @@ class UpdateTestCase(FirefoxTestCase):
 
             # An upgrade should not change the builds locale
             self.assertEqual(update['build_post']['locale'], update['build_pre']['locale'])
 
             # Check that no application-wide add-ons have been disabled
             self.assertEqual(update['build_post']['disabled_addons'],
                              update['build_pre']['disabled_addons'])
 
+            # Bug 604364 - We do not support watershed releases yet.
+            if update_available:
+                self.download_update(about_window, wait_for_finish=False)
+                self.assertNotEqual(self.software_update.active_update.type,
+                                    update['patch']['type'],
+                                    'No further update of the same type gets offered: '
+                                    '{0} != {1}'.format(
+                                        self.software_update.active_update.type,
+                                        update['patch']['type']
+                                    ))
+
             update['success'] = True
 
         finally:
             about_window.close()
 
     def download_update(self, window, wait_for_finish=True, timeout=TIMEOUT_UPDATE_DOWNLOAD):
         """ Download the update patch.
 
--- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html
@@ -5,95 +5,35 @@
 <link rel="author" title="Mozilla" href="https://www.mozilla.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#initialise-the-document-object">
 <link rel="help" href="https://fullscreen.spec.whatwg.org/#fullscreen-enabled-flag">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <div id="log"></div>
 <script>
-  function test_allowfullscreen(t, setup_iframe) {
+  async_test(function(t) {
     var iframe = document.createElement("iframe");
-    setup_iframe(iframe);
     iframe.src = "support/blank.htm";
     var eventWatcher = new EventWatcher(t, iframe, "load");
     document.body.appendChild(iframe);
     t.add_cleanup(function() {
       document.body.removeChild(iframe);
     });
 
     assert_true(document.fullscreenEnabled, "Top level document has fullscreen enabled flag set");
-    eventWatcher.wait_for("load").then(t.step_func(function() {
+    eventWatcher.wait_for("load").then(t.step_func_done(function() {
       assert_false(iframe.contentDocument.fullscreenEnabled, "Document inside iframe without allowfullscreen attribute should not have fullscreen enabled flag set");
       iframe.setAttribute("allowfullscreen", true);
-      assert_false(iframe.contentDocument.fullscreenEnabled, "Setting allowfullscreen attribute after document load should not affect fullscreen enabled flag");
-      iframe.contentWindow.location.reload();
-      return eventWatcher.wait_for("load");
-    })).then(t.step_func(function() {
-      assert_true(iframe.contentDocument.fullscreenEnabled, "Fullscreen enabled flag should be set when a new document is loaded with allowfullscreen attribute present");
+      assert_true(iframe.contentDocument.fullscreenEnabled, "Fullscreen should be allowed when allowfullscreen attribute is set");
       iframe.removeAttribute("allowfullscreen");
-      assert_true(iframe.contentDocument.fullscreenEnabled, "Removing allowfullscreen attribute should not affect fullscreen enabled flag");
-      iframe.contentWindow.location.reload();
-      return eventWatcher.wait_for("load");
-    })).then(t.step_func_done(function() {
-      assert_false(iframe.contentDocument.fullscreenEnabled, "Fullscreen enabled flag should be reset when a new document is loaded with allowfullscreen attribute absent");
+      assert_false(iframe.contentDocument.fullscreenEnabled, "Fullscreen should be denied when allowfullscreen attribute is removed");
     }));
-  }
-
-  async_test(function(t) {
-    test_allowfullscreen(t, function(iframe) {});
   }, "iframe-allowfullscreen");
 
-  async_test(function(t) {
-    test_allowfullscreen(t, function(iframe) {
-      iframe.setAttribute("sandbox", "allow-same-origin");
-    });
-  }, "iframe-sandbox-allowfullscreen");
-
-  /* Fullscreen enabled flag with dialog */
-
-  function test_allowfullscreen_dialog(t, setup_iframe, check) {
-    var iframe = document.createElement("iframe");
-    setup_iframe(iframe);
-    iframe.src = "support/blank.htm";
-    var eventWatcher = new EventWatcher(t, iframe, "load");
-    document.body.appendChild(iframe);
-    t.add_cleanup(function() {
-      document.body.removeChild(iframe);
-    });
-
-    var newWin;
-    assert_true(document.fullscreenEnabled, "Top level document has fullscreen enabled flag set");
-    eventWatcher.wait_for("load").then(t.step_func(function() {
-      assert_false(iframe.contentDocument.fullscreenEnabled, "Document inside iframe without allowfullscreen attribute should not have fullscreen enabled flag set");
-      newWin = iframe.contentWindow.open("support/blank.htm");
-      t.add_cleanup(function() {
-        newWin.close();
-      });
-      var newWinEventWatcher = new EventWatcher(t, newWin, "load");
-      return newWinEventWatcher.wait_for("load");
-    })).then(t.step_func_done(function() {
-      check(newWin);
-    }));
-  }
-
-  async_test(function(t) {
-    test_allowfullscreen_dialog(t, function() {}, function(newWin) {
-      assert_true(newWin.document.fullscreenEnabled, "Document in the new window is a top level document, thus should has fullscreen enabled flag set");
-    });
-  }, "iframe-allowfullscreen-dialog");
-
-  async_test(function(t) {
-    test_allowfullscreen_dialog(t, function(iframe) {
-      iframe.setAttribute("sandbox", "allow-same-origin allow-popups");
-    }, function(newWin) {
-      assert_false(newWin.document.fullscreenEnabled, "Document in the new window should inherit the sandboxed fullscreen flag and should not have fullscreen enabled flag set");
-    });
-  }, "iframe-sandbox-allowfullscreen-dialog");
-
   /* Fullscreen enabled flag with about:blank */
 
   function test_allowfullscreen_noload(setup_iframe, check) {
     var iframe = document.createElement("iframe");
     setup_iframe(iframe);
     document.body.appendChild(iframe);
     check(iframe.contentDocument);
     document.body.removeChild(iframe);
--- a/toolkit/components/extensions/NativeMessaging.jsm
+++ b/toolkit/components/extensions/NativeMessaging.jsm
@@ -79,21 +79,22 @@ this.HostManifestManager = {
         throw new Error(`Native messaging is not supported on ${AppConstants.platform}`);
       }
       this._initializePromise = Schemas.load(HOST_MANIFEST_SCHEMA);
     }
     return this._initializePromise;
   },
 
   _winLookup(application, context) {
+    let regPath = `${REGPATH}\\${application}`;
     let path = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                                          REGPATH, application);
+                                          regPath, "");
     if (!path) {
       path = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
-                                        REGPATH, application);
+                                        regPath, "");
     }
     if (!path) {
       return null;
     }
     return this._tryPath(path, application, context)
       .then(manifest => manifest ? {path, manifest} : null);
   },
 
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_native_messaging.html
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_native_messaging.html
@@ -229,28 +229,28 @@ add_task(function* setup() {
           path: batPath,
           type: "stdio",
           allowed_extensions: [ID],
         };
         let manifestPath = getPath(`${script.name}.json`);
         yield OS.File.writeAtomic(manifestPath, JSON.stringify(manifest));
 
         registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                          REGKEY, script.name, manifestPath);
+                          `${REGKEY}\\${script.name}`, "", manifestPath);
 
         // Create a version of the manifest with a relative path
         let relativeName = `relative.${script.name}`;
         manifest.name = relativeName;
         manifest.path = `${script.name}.bat`;
 
         manifestPath = getPath(`${relativeName}.json`);
         yield OS.File.writeAtomic(manifestPath, JSON.stringify(manifest));
 
         registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                          REGKEY, relativeName, manifestPath);
+                          `${REGKEY}\\${relativeName}`, "", manifestPath);
       }
       break;
 
     default:
       ok(false, `Native messaing is not supported on ${AppConstants.platform}`);
   }
 });
 
--- a/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
@@ -14,17 +14,17 @@ let registry = null;
 if (AppConstants.platform == "win") {
   Cu.import("resource://testing-common/MockRegistry.jsm");
   registry = new MockRegistry();
   do_register_cleanup(() => {
     registry.shutdown();
   });
 }
 
-const REGKEY = "Software\\Mozilla\\NativeMessagingHosts";
+const REGPATH = "Software\\Mozilla\\NativeMessagingHosts";
 
 const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
 
 let dir = FileUtils.getDir("TmpD", ["NativeMessaging"]);
 dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
 let userDir = dir.clone();
 userDir.append("user");
@@ -97,17 +97,17 @@ add_task(function* test_nonexistent_mani
 });
 
 const USER_TEST_JSON = OS.Path.join(userDir.path, "test.json");
 
 add_task(function* test_good_manifest() {
   yield writeManifest(USER_TEST_JSON, templateManifest);
   if (registry) {
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      REGKEY, "test", USER_TEST_JSON);
+                      `${REGPATH}\\test`, "", USER_TEST_JSON);
   }
 
   let result = yield HostManifestManager.lookupApplication("test", context);
   notEqual(result, null, "lookupApplication finds a good manifest");
   equal(result.path, USER_TEST_JSON, "lookupApplication returns the correct path");
   deepEqual(result.manifest, templateManifest, "lookupApplication returns the manifest contents");
 });
 
@@ -172,33 +172,33 @@ const GLOBAL_TEST_JSON = OS.Path.join(gl
 let globalManifest = Object.assign({}, templateManifest);
 globalManifest.description = "This manifest is from the systemwide directory";
 
 add_task(function* good_manifest_system_dir() {
   yield OS.File.remove(USER_TEST_JSON);
   yield writeManifest(GLOBAL_TEST_JSON, globalManifest);
   if (registry) {
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      REGKEY, "test", null);
+                      `${REGPATH}\\test`, "", null);
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
-                      REGKEY, "test", GLOBAL_TEST_JSON);
+                      `${REGPATH}\\test`, "", GLOBAL_TEST_JSON);
   }
 
   let where = (AppConstants.platform == "win") ? "registry location" : "directory";
   let result = yield HostManifestManager.lookupApplication("test", context);
   notEqual(result, null, `lookupApplication finds a manifest in the system-wide ${where}`);
   equal(result.path, GLOBAL_TEST_JSON, `lookupApplication returns path in the system-wide ${where}`);
   deepEqual(result.manifest, globalManifest, `lookupApplication returns manifest contents from the system-wide ${where}`);
 });
 
 add_task(function* test_user_dir_precedence() {
   yield writeManifest(USER_TEST_JSON, templateManifest);
   if (registry) {
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      REGKEY, "test", USER_TEST_JSON);
+                      `${REGPATH}\\test`, "", USER_TEST_JSON);
   }
   // global test.json and LOCAL_MACHINE registry key on windows are
   // still present from the previous test
 
   let result = yield HostManifestManager.lookupApplication("test", context);
   notEqual(result, null, "lookupApplication finds a manifest when entries exist in both user-specific and system-wide locations");
   equal(result.path, USER_TEST_JSON, "lookupApplication returns the user-specific path when user-specific and system-wide entries both exist");
   deepEqual(result.manifest, templateManifest, "lookupApplication returns user-specific manifest contents with user-specific and system-wide entries both exist");
@@ -242,17 +242,17 @@ while True:
     let batBody = `@ECHO OFF\n${PYTHON} -u ${scriptPath} %*\n`;
     yield OS.File.writeAtomic(batPath, batBody);
     yield OS.File.setPermissions(batPath, {unixMode: 0o755});
 
     manifest.path = batPath;
     yield writeManifest(manifestPath, manifest);
 
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      REGKEY, "wontdie", manifestPath);
+                      `${REGPATH}\\wontdie`, "", manifestPath);
   } else {
     yield OS.File.writeAtomic(scriptPath, `#!${PYTHON} -u\n${SCRIPT}`);
     yield OS.File.setPermissions(scriptPath, {unixMode: 0o755});
     manifest.path = scriptPath;
     yield writeManifest(manifestPath, manifest);
   }
 
   let extension = {id: ID};
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -280,19 +280,24 @@ this.SafeBrowsing = {
         listManager.enableUpdate(downloadAllowLists[i]);
       } else {
         listManager.disableUpdate(downloadAllowLists[i]);
       }
     }
     for (let i = 0; i < trackingProtectionLists.length; ++i) {
       if (this.trackingEnabled) {
         listManager.enableUpdate(trackingProtectionLists[i]);
+      } else {
+        listManager.disableUpdate(trackingProtectionLists[i]);
+      }
+    }
+    for (let i = 0; i < trackingProtectionWhitelists.length; ++i) {
+      if (this.trackingEnabled) {
         listManager.enableUpdate(trackingProtectionWhitelists[i]);
       } else {
-        listManager.disableUpdate(trackingProtectionLists[i]);
         listManager.disableUpdate(trackingProtectionWhitelists[i]);
       }
     }
     for (let i = 0; i < forbiddenLists.length; ++i) {
       if (this.forbiddenEnabled) {
         listManager.enableUpdate(forbiddenLists[i]);
       } else {
         listManager.disableUpdate(forbiddenLists[i]);
--- a/toolkit/mozapps/extensions/test/browser/browser-common.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -1,11 +1,57 @@
 [DEFAULT]
 support-files =
+  addons/*
+  addon_about.xul
+  addon_prefs.xul
+  cancelCompatCheck.sjs
+  discovery.html
+  discovery_frame.html
+  discovery_install.html
   head.js
+  signed_hotfix.rdf
+  signed_hotfix.xpi
+  unsigned_hotfix.rdf
+  unsigned_hotfix.xpi
+  more_options.xul
+  options.xul
+  plugin_test.html
+  redirect.sjs
+  releaseNotes.xhtml
+  blockNoPlugins.xml
+  blockPluginHard.xml
+  browser_bug557956.rdf
+  browser_bug557956_8_2.xpi
+  browser_bug557956_9_2.xpi
+  browser_bug557956.xml
+  browser_bug591465.xml
+  browser_bug593535.xml
+  browser_searching.xml
+  browser_searching_empty.xml
+  browser_updatessl.rdf
+  browser_updatessl.rdf^headers^
+  browser_install.rdf
+  browser_install.rdf^headers^
+  browser_install.xml
+  browser_install1_3.xpi
+  browser_eula.xml
+  browser_purchase.xml
+  webapi_addon_listener.html
+  webapi_checkavailable.html
+  webapi_checkchromeframe.xul
+  webapi_checkframed.html
+  webapi_checknavigatedwindow.html
+  !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
+  !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
+  !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
+  !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
+  !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
+  !/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
+  !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
 
 [browser_about.js]
 skip-if = os == 'linux' || os == 'win' # bug 632290
 [browser_bug523784.js]
 [browser_bug557943.js]
 [browser_bug562797.js]
 [browser_bug562854.js]
 [browser_bug562890.js]
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -1,59 +1,11 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet'
 tags = addons
-support-files =
-  addons/*
-  addon_about.xul
-  addon_prefs.xul
-  cancelCompatCheck.sjs
-  discovery.html
-  discovery_frame.html
-  discovery_install.html
-  head.js
-  signed_hotfix.rdf
-  signed_hotfix.xpi
-  unsigned_hotfix.rdf
-  unsigned_hotfix.xpi
-  more_options.xul
-  options.xul
-  plugin_test.html
-  redirect.sjs
-  releaseNotes.xhtml
-  blockNoPlugins.xml
-  blockPluginHard.xml
-  browser_bug557956.rdf
-  browser_bug557956_8_2.xpi
-  browser_bug557956_9_2.xpi
-  browser_bug557956.xml
-  browser_bug591465.xml
-  browser_bug593535.xml
-  browser_searching.xml
-  browser_searching_empty.xml
-  browser_updatessl.rdf
-  browser_updatessl.rdf^headers^
-  browser_install.rdf
-  browser_install.rdf^headers^
-  browser_install.xml
-  browser_install1_3.xpi
-  browser_eula.xml
-  browser_purchase.xml
-  webapi_addon_listener.html
-  webapi_checkavailable.html
-  webapi_checkchromeframe.xul
-  webapi_checkframed.html
-  webapi_checknavigatedwindow.html
-  !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
-  !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
-  !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
-  !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
-  !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
-  !/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi
-  !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
 
 [browser_addonrepository_performance.js]
 [browser_bug557956.js]
 [browser_bug616841.js]
 [browser_cancelCompatCheck.js]
 [browser_checkAddonCompatibility.js]
 [browser_gmpProvider.js]
 [browser_hotfix.js]
--- a/tools/mach_commands.py
+++ b/tools/mach_commands.py
@@ -16,25 +16,16 @@ from mach.decorators import (
     Command,
 )
 
 from mozbuild.base import MachCommandBase, MozbuildObject
 
 
 @CommandProvider
 class SearchProvider(object):
-    @Command('mxr', category='misc',
-        description='Search for something in MXR.')
-    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
-    def mxr(self, term):
-        import webbrowser
-        term = ' '.join(term)
-        uri = 'https://mxr.mozilla.org/mozilla-central/search?string=%s' % term
-        webbrowser.open_new_tab(uri)
-
     @Command('dxr', category='misc',
         description='Search for something in DXR.')
     @CommandArgument('term', nargs='+', help='Term(s) to search for.')
     def dxr(self, term):
         import webbrowser
         term = ' '.join(term)
         uri = 'http://dxr.mozilla.org/mozilla-central/search?q=%s&redirect=true' % term
         webbrowser.open_new_tab(uri)
@@ -55,22 +46,22 @@ class SearchProvider(object):
         import webbrowser
         term = ' '.join(term)
         uri = 'https://www.google.com/search?q=%s' % term
         webbrowser.open_new_tab(uri)
 
     @Command('search', category='misc',
         description='Search for something on the Internets. '
         'This will open 3 new browser tabs and search for the term on Google, '
-        'MDN, and MXR.')
+        'MDN, and DXR.')
     @CommandArgument('term', nargs='+', help='Term(s) to search for.')
     def search(self, term):
         self.google(term)
         self.mdn(term)
-        self.mxr(term)
+        self.dxr(term)
 
 
 @CommandProvider
 class UUIDProvider(object):
     @Command('uuid', category='misc',
         description='Generate a uuid.')
     @CommandArgument('--format', '-f', choices=['idl', 'cpp', 'c++'],
                      help='Output format for the generated uuid.')