Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 03 Feb 2017 10:39:57 -0500
changeset 342250 f925e9ed9b94792d761e34c0c3a63af3a9b44e0d
parent 342249 fca2fcdfbc9f9bd8a156b203e0916549f5c36418 (current diff)
parent 332464 1ac998be4f4df44740552a900ab5f35af80727e6 (diff)
child 342251 ae8db963d2f4a0a6930945cc53d1d7ef48c85097
push id86826
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 23:33:17 +0000
treeherdermozilla-inbound@2c7816419218 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.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 graphics MozReview-Commit-ID: 8BjfShvh5gp
browser/base/content/test/general/browser_bug1045809.js
browser/base/content/test/general/browser_bug435035.js
browser/base/content/test/general/browser_bug822367.js
browser/base/content/test/general/browser_bug902156.js
browser/base/content/test/general/browser_bug906190.js
browser/base/content/test/general/browser_csp_block_all_mixedcontent.js
browser/base/content/test/general/browser_identity_UI.js
browser/base/content/test/general/browser_insecureLoginForms.js
browser/base/content/test/general/browser_mcb_redirect.js
browser/base/content/test/general/browser_mixedContentFramesOnHttp.js
browser/base/content/test/general/browser_mixedContentFromOnunload.js
browser/base/content/test/general/browser_mixed_content_cert_override.js
browser/base/content/test/general/browser_mixedcontent_securityflags.js
browser/base/content/test/general/browser_no_mcb_on_http_site.js
browser/base/content/test/general/file_bug1045809_1.html
browser/base/content/test/general/file_bug1045809_2.html
browser/base/content/test/general/file_bug822367_1.html
browser/base/content/test/general/file_bug822367_1.js
browser/base/content/test/general/file_bug822367_2.html
browser/base/content/test/general/file_bug822367_3.html
browser/base/content/test/general/file_bug822367_4.html
browser/base/content/test/general/file_bug822367_4.js
browser/base/content/test/general/file_bug822367_4B.html
browser/base/content/test/general/file_bug822367_5.html
browser/base/content/test/general/file_bug822367_6.html
browser/base/content/test/general/file_bug902156.js
browser/base/content/test/general/file_bug902156_1.html
browser/base/content/test/general/file_bug902156_2.html
browser/base/content/test/general/file_bug902156_3.html
browser/base/content/test/general/file_bug906190.js
browser/base/content/test/general/file_bug906190.sjs
browser/base/content/test/general/file_bug906190_1.html
browser/base/content/test/general/file_bug906190_2.html
browser/base/content/test/general/file_bug906190_3_4.html
browser/base/content/test/general/file_bug906190_redirected.html
browser/base/content/test/general/file_csp_block_all_mixedcontent.html
browser/base/content/test/general/file_csp_block_all_mixedcontent.js
browser/base/content/test/general/file_mixedContentFramesOnHttp.html
browser/base/content/test/general/file_mixedContentFromOnunload.html
browser/base/content/test/general/file_mixedContentFromOnunload_test1.html
browser/base/content/test/general/file_mixedContentFromOnunload_test2.html
browser/base/content/test/general/file_mixedPassiveContent.html
browser/base/content/test/general/insecure_opener.html
browser/base/content/test/general/test-mixedcontent-securityerrors.html
browser/base/content/test/general/test_bug435035.html
browser/base/content/test/general/test_mcb_double_redirect_image.html
browser/base/content/test/general/test_mcb_redirect.html
browser/base/content/test/general/test_mcb_redirect.js
browser/base/content/test/general/test_mcb_redirect.sjs
browser/base/content/test/general/test_mcb_redirect_image.html
browser/base/content/test/general/test_no_mcb_on_http_site_font.css
browser/base/content/test/general/test_no_mcb_on_http_site_font.html
browser/base/content/test/general/test_no_mcb_on_http_site_font2.css
browser/base/content/test/general/test_no_mcb_on_http_site_font2.html
browser/base/content/test/general/test_no_mcb_on_http_site_img.css
browser/base/content/test/general/test_no_mcb_on_http_site_img.html
config/rules.mk
dom/tests/browser/browser_localStorage_e10s.js
dom/tests/browser/page_localstorage_e10s.html
dom/workers/test/serviceworkers/test_close.html
gfx/gl/GLContextGLX.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEAGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/layers/composite/CompositableHost.h
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/ImageHost.h
gfx/thebes/gfxPrefs.h
ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
ipc/ipdl/test/cxx/PTestOpens.ipdl
ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
ipc/ipdl/test/cxx/TestBridgeMain.cpp
ipc/ipdl/test/cxx/TestBridgeMain.h
ipc/ipdl/test/cxx/TestOpens.cpp
ipc/ipdl/test/cxx/TestOpens.h
ipc/ipdl/test/ipdl/ok/compositor.ipdl
ipc/ipdl/test/ipdl/ok/content.ipdl
ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl
js/src/jit/BaselineCacheIRCompiler.cpp
layout/painting/nsDisplayList.cpp
mobile/android/base/resources/drawable-hdpi/pin.png
mobile/android/base/resources/drawable-xhdpi/pin.png
mobile/android/base/resources/drawable/as_dimiss.xml
modules/libpref/init/all.js
taskcluster/docker/desktop-build/Dockerfile
testing/web-platform/meta/html/browsers/windows/nested-browsing-contexts/frameElement.html.ini
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/library/gtest/rust/Cargo.toml
toolkit/library/rust/Cargo.toml
toolkit/library/rust/shared/Cargo.toml
toolkit/library/rust/shared/lib.rs
widget/windows/nsWindow.cpp
--- a/.cron.yml
+++ b/.cron.yml
@@ -4,26 +4,24 @@
 
 jobs:
     - name: nightly-desktop
       job:
           type: decision-task
           treeherder-symbol: Nd
           triggered-by: nightly
           target-tasks-method: nightly_linux
-      projects:
+      run-on-projects:
           - mozilla-central
           - date
-      when:
-          - {hour: 16, minute: 0}
+      when: []  # never (temporary)
 
     - name: nightly-android
       job:
           type: decision-task
           treeherder-symbol: Na
           triggered-by: nightly
           target-tasks-method: nightly_fennec
-      projects:
+      run-on-projects:
           - mozilla-central
           - date
-      when:
-          - {hour: 16, minute: 0}
+      when: []  # never (temporary)
 
--- a/.eslintignore
+++ b/.eslintignore
@@ -33,16 +33,17 @@ modules/**
 mozglue/**
 netwerk/**
 nsprpub/**
 other-licenses/**
 parser/**
 probes/**
 python/**
 rdf/**
+servo/**
 startupcache/**
 testing/**
 tools/update-packaging/**
 uriloader/**
 view/**
 widget/**
 xpcom/**
 xpfe/**
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -179,17 +179,17 @@ static const nsRoleMapEntry sWAIRoleMaps
   { // directory
     &nsGkAtoms::directory,
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eList,
-    kNoReqStates
+    states::READONLY
   },
   { // document
     &nsGkAtoms::document,
     roles::DOCUMENT,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -58,16 +58,18 @@ if CONFIG['A11Y_LOG']:
     UNIFIED_SOURCES += [
         'Logging.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
+    '/dom/base',
+    '/dom/xul',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     LOCAL_INCLUDES += [
         '/accessible/ipc/win',
     ]
 else:
     LOCAL_INCLUDES += [
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -9,26 +9,28 @@
 
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocument.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsRange.h"
 #include "nsIBoxObject.h"
-#include "nsIDOMXULElement.h"
+#include "nsXULElement.h"
+#include "mozilla/dom/BoxObject.h"
 #include "nsIDocShell.h"
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIScrollableFrame.h"
 #include "nsISelectionPrivate.h"
 #include "nsISelectionController.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/dom/TouchEvent.h"
+#include "mozilla/ErrorResult.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "nsView.h"
 #include "nsGkAtoms.h"
 
 #include "nsComponentManagerUtils.h"
@@ -92,19 +94,20 @@ nsCoreUtils::DispatchClickEvent(nsITreeB
   // Calculate x and y coordinates.
   int32_t x = 0, y = 0, width = 0, height = 0;
   nsresult rv = aTreeBoxObj->GetCoordsForCellItem(aRowIndex, aColumn,
                                                   aPseudoElt,
                                                   &x, &y, &width, &height);
   if (NS_FAILED(rv))
     return;
 
-  nsCOMPtr<nsIDOMXULElement> tcXULElm(do_QueryInterface(tcElm));
-  nsCOMPtr<nsIBoxObject> tcBoxObj;
-  tcXULElm->GetBoxObject(getter_AddRefs(tcBoxObj));
+  nsCOMPtr<nsIContent> tcXULElm(do_QueryInterface(tcElm));
+  IgnoredErrorResult ignored;
+  nsCOMPtr<nsIBoxObject> tcBoxObj =
+    nsXULElement::FromContent(tcXULElm)->GetBoxObject(ignored);
 
   int32_t tcX = 0;
   tcBoxObj->GetX(&tcX);
 
   int32_t tcY = 0;
   tcBoxObj->GetY(&tcY);
 
   // Dispatch mouse events.
@@ -484,42 +487,41 @@ nsCoreUtils::GetLanguageFor(nsIContent *
     walkUp = walkUp->GetParent();
 }
 
 already_AddRefed<nsIBoxObject>
 nsCoreUtils::GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj)
 {
   nsCOMPtr<nsIDOMElement> tcElm;
   aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
-  nsCOMPtr<nsIDOMXULElement> tcXULElm(do_QueryInterface(tcElm));
+  nsCOMPtr<nsIContent> tcContent(do_QueryInterface(tcElm));
+  RefPtr<nsXULElement> tcXULElm = nsXULElement::FromContentOrNull(tcContent);
   if (!tcXULElm)
     return nullptr;
 
-  nsCOMPtr<nsIBoxObject> boxObj;
-  tcXULElm->GetBoxObject(getter_AddRefs(boxObj));
-  return boxObj.forget();
+  IgnoredErrorResult ignored;
+  return tcXULElm->GetBoxObject(ignored);
 }
 
 already_AddRefed<nsITreeBoxObject>
 nsCoreUtils::GetTreeBoxObject(nsIContent *aContent)
 {
   // Find DOMNode's parents recursively until reach the <tree> tag
   nsIContent* currentContent = aContent;
   while (currentContent) {
     if (currentContent->NodeInfo()->Equals(nsGkAtoms::tree,
                                            kNameSpaceID_XUL)) {
       // We will get the nsITreeBoxObject from the tree node
-      nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(currentContent));
-      if (xulElement) {
-        nsCOMPtr<nsIBoxObject> box;
-        xulElement->GetBoxObject(getter_AddRefs(box));
-        nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
-        if (treeBox)
-          return treeBox.forget();
-      }
+      RefPtr<nsXULElement> xulElement =
+        nsXULElement::FromContent(currentContent);
+      IgnoredErrorResult ignored;
+      nsCOMPtr<nsIBoxObject> box = xulElement->GetBoxObject(ignored);
+      nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
+      if (treeBox)
+        return treeBox.forget();
     }
     currentContent = currentContent->GetFlattenedTreeParent();
   }
 
   return nullptr;
 }
 
 already_AddRefed<nsITreeColumn>
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -37,23 +37,31 @@ DocAccessibleParent::RecvShowEvent(const
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
   // XXX This should really never happen, but sometimes we fail to fire the
   // required show events.
   if (!parent) {
     NS_ERROR("adding child to unknown accessible");
+#ifdef DEBUG
     return IPC_FAIL(this, "unknown parent accessible");
+#else
+    return IPC_OK();
+#endif
   }
 
   uint32_t newChildIdx = aData.Idx();
   if (newChildIdx > parent->ChildrenCount()) {
     NS_ERROR("invalid index to add child at");
+#ifdef DEBUG
     return IPC_FAIL(this, "invalid index");
+#else
+    return IPC_OK();
+#endif
   }
 
   uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 
   // XXX This shouldn't happen, but if we failed to add children then the below
   // is pointless and can crash.
   if (!consumed) {
--- a/accessible/ipc/ProxyAccessibleBase.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -34,17 +34,17 @@ ProxyAccessibleBase<Derived>::Shutdown()
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
   } else {
-    if (mChildren.Length() != 1)
+    if (mChildren.Length() > 1)
       MOZ_CRASH("outer doc doesn't own adoc!");
 
     mChildren[0]->AsDoc()->Unbind();
   }
 
   mChildren.Clear();
   ProxyDestroyed(static_cast<Derived*>(this));
   mDoc->RemoveAccessible(static_cast<Derived*>(this));
--- a/accessible/tests/mochitest/states/test_aria.html
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -174,16 +174,19 @@
                  STATE_READONLY, 0);
       testStates("aria_treegrid_readonly_rowheader_inherited", STATE_READONLY, 0,
                  0, EXT_STATE_EDITABLE);
       testStates("aria_treegrid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
                  STATE_READONLY, 0);
       testStates("aria_treegrid_readonly_cell_inherited", STATE_READONLY, 0,
                  0, EXT_STATE_EDITABLE);
 
+      // aria-readonly on directory
+      testStates("aria_directory", STATE_READONLY);
+
       // aria-selectable
       testStates("aria_selectable_listitem", STATE_SELECTABLE | STATE_SELECTED);
 
       // active state caused by aria-activedescendant
       testStates("as_item1", 0, EXT_STATE_ACTIVE);
       testStates("as_item2", 0, 0, 0, EXT_STATE_ACTIVE);
 
       // universal ARIA properties inherited from file input control
@@ -620,10 +623,13 @@
     <div role="treeitem">G</div>
     <div role="treeitem">g</div>
   </div>
   <div id="aria_treegrid_disabled" role="treegrid" aria-disabled="true">
     <div role="row"><div role="gridcell">H</div></div>
     <div role="row"><div role="gridcell">h</div></div>
   </div>
 
+  <!-- Test that directory is readonly -->
+  <div id="aria_directory" role="directory"></div>
+
 </body>
 </html>
--- a/accessible/windows/sdn/sdnAccessible.cpp
+++ b/accessible/windows/sdn/sdnAccessible.cpp
@@ -161,39 +161,37 @@ sdnAccessible::get_attributesForNames(un
     return E_INVALIDARG;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!mNode->IsElement())
     return S_FALSE;
 
-  nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mNode));
+  dom::Element* domElement = mNode->AsElement();
   nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance();
 
   int32_t index = 0;
   for (index = 0; index < aMaxAttribs; index++) {
     aAttribValues[index] = nullptr;
     if (aAttribNames[index]) {
       nsAutoString attributeValue, nameSpaceURI;
       nsAutoString attributeName(nsDependentString(
         static_cast<const wchar_t*>(aAttribNames[index])));
 
-      nsresult rv = NS_OK;
       if (aNameSpaceID[index]>0 &&
         NS_SUCCEEDED(nameSpaceManager->GetNameSpaceURI(aNameSpaceID[index],
                                                        nameSpaceURI))) {
-          rv = domElement->GetAttributeNS(nameSpaceURI, attributeName,
+          domElement->GetAttributeNS(nameSpaceURI, attributeName,
                                           attributeValue);
       } else {
-        rv = domElement->GetAttribute(attributeName, attributeValue);
+        domElement->GetAttribute(attributeName, attributeValue);
       }
 
-      if (NS_SUCCEEDED(rv))
-        aAttribValues[index] = ::SysAllocString(attributeValue.get());
+      aAttribValues[index] = ::SysAllocString(attributeValue.get());
     }
   }
 
   return S_OK;
 }
 
 STDMETHODIMP
 sdnAccessible::get_computedStyle(unsigned short aMaxStyleProperties,
--- a/accessible/xul/XULTabAccessible.cpp
+++ b/accessible/xul/XULTabAccessible.cpp
@@ -10,16 +10,19 @@
 #include "Role.h"
 #include "States.h"
 
 // NOTE: alphabetically ordered
 #include "nsIDocument.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULRelatedElement.h"
+#include "nsXULElement.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTabAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTabAccessible::
@@ -43,19 +46,20 @@ XULTabAccessible::ActionNameAt(uint8_t a
   if (aIndex == eAction_Switch)
     aName.AssignLiteral("switch");
 }
 
 bool
 XULTabAccessible::DoAction(uint8_t index)
 {
   if (index == eAction_Switch) {
-    nsCOMPtr<nsIDOMXULElement> tab(do_QueryInterface(mContent));
+    // XXXbz Could this just FromContent?
+    RefPtr<nsXULElement> tab = nsXULElement::FromContentOrNull(mContent);
     if (tab) {
-      tab->Click();
+      tab->Click(mozilla::dom::CallerType::System);
       return true;
     }
   }
   return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTabAccessible: Accessible
--- a/accessible/xul/moz.build
+++ b/accessible/xul/moz.build
@@ -19,16 +19,18 @@ UNIFIED_SOURCES += [
     'XULTreeGridAccessible.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/xpcom',
+    '/dom/base',
+    '/dom/xul',
     '/layout/generic',
     '/layout/xul',
     '/layout/xul/tree',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     LOCAL_INCLUDES += [
         '/accessible/atk',
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -1,16 +1,17 @@
 /* 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 gFxAccounts = {
 
   _initialized: false,
   _inCustomizationMode: false,
+  _profileFetched: false,
 
   get weave() {
     delete this.weave;
     return this.weave = Cc["@mozilla.org/weave/service;1"]
                           .getService(Ci.nsISupports)
                           .wrappedJSObject;
   },
 
@@ -135,19 +136,19 @@ var gFxAccounts = {
       Services.obs.removeObserver(this, topic);
     }
 
     this._initialized = false;
   },
 
   observe(subject, topic, data) {
     switch (topic) {
-      case this.FxAccountsCommon.ONPROFILE_IMAGE_CHANGE_NOTIFICATION:
-        this.updateUI();
-        break;
+      case this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION:
+        this._profileFetched = false;
+        // Fallthrough intended
       default:
         this.updateUI();
         break;
     }
   },
 
   handleEvent(event) {
     this._inCustomizationMode = event.type == "customizationstarting";
@@ -249,28 +250,29 @@ var gFxAccounts = {
       }
     }
 
     return fxAccounts.getSignedInUser().then(userData => {
       // userData may be null here when the user is not signed-in, but that's expected
       updateWithUserData(userData);
       // unverified users cause us to spew log errors fetching an OAuth token
       // to fetch the profile, so don't even try in that case.
-      if (!userData || !userData.verified || !profileInfoEnabled) {
+      if (!userData || !userData.verified || !profileInfoEnabled || this._profileFetched) {
         return null; // don't even try to grab the profile.
       }
       return fxAccounts.getSignedInUserProfile().catch(err => {
         // Not fetching the profile is sad but the FxA logs will already have noise.
         return null;
       });
     }).then(profile => {
       if (!profile) {
         return;
       }
       updateWithProfile(profile);
+      this._profileFetched = true; // Try to avoid fetching the profile on every UI update
     }).catch(error => {
       // This is most likely in tests, were we quickly log users in and out.
       // The most likely scenario is a user logged out, so reflect that.
       // Bug 995134 calls for better errors so we could retry if we were
       // sure this was the failure reason.
       this.FxAccountsCommon.log.error("Error updating FxA account info", error);
       updateWithUserData(null);
     });
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -187,24 +187,16 @@ XPCOMUtils.defineLazyGetter(gEMEHandler,
 const TELEMETRY_DDSTAT_SHOWN = 0;
 const TELEMETRY_DDSTAT_SHOWN_FIRST = 1;
 const TELEMETRY_DDSTAT_CLICKED = 2;
 const TELEMETRY_DDSTAT_CLICKED_FIRST = 3;
 const TELEMETRY_DDSTAT_SOLVED = 4;
 
 let gDecoderDoctorHandler = {
   getLabelForNotificationBox(type) {
-    if (type == "adobe-cdm-not-found" &&
-        AppConstants.platform == "win") {
-      return gNavigatorBundle.getString("decoder.noCodecs.message");
-    }
-    if (type == "adobe-cdm-not-activated" &&
-        AppConstants.platform == "win") {
-      return gNavigatorBundle.getString("decoder.noCodecs.message");
-    }
     if (type == "platform-decoder-not-found") {
       if (AppConstants.platform == "win") {
         return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
       }
       if (AppConstants.platform == "linux") {
         return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
       }
     }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -869,16 +869,22 @@ function serializeInputStream(aStream) {
   }
 
   return data;
 }
 
 // A shared function used by both remote and non-remote browser XBL bindings to
 // load a URI or redirect it to the correct process.
 function _loadURIWithFlags(browser, uri, params) {
+  let tab = gBrowser.getTabForBrowser(browser);
+  // Preloaded browsers don't have tabs, so we ignore those.
+  if (tab) {
+    maybeRecordAbandonmentTelemetry(tab, "newURI");
+  }
+
   if (!uri) {
     uri = "about:blank";
   }
   let triggeringPrincipal = params.triggeringPrincipal || null;
   let flags = params.flags || 0;
   let referrer = params.referrerURI;
   let referrerPolicy = ("referrerPolicy" in params ? params.referrerPolicy :
                         Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
@@ -1784,27 +1790,39 @@ function HandleAppCommandEvent(evt) {
     break;
   default:
     return;
   }
   evt.stopPropagation();
   evt.preventDefault();
 }
 
+function maybeRecordAbandonmentTelemetry(tab, type) {
+  if (!tab.hasAttribute("busy")) {
+    return;
+  }
+
+  let histogram = Services.telemetry
+                          .getHistogramById("BUSY_TAB_ABANDONED");
+  histogram.add(type);
+}
+
 function gotoHistoryIndex(aEvent) {
   let index = aEvent.target.getAttribute("index");
   if (!index)
     return false;
 
   let where = whereToOpenLink(aEvent);
 
   if (where == "current") {
     // Normal click. Go there in the current tab and update session history.
 
     try {
+      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab,
+                                      "historyNavigation");
       gBrowser.gotoIndex(index);
     } catch (ex) {
       return false;
     }
     return true;
   }
   // Modified click. Go there in a new tab/window.
 
@@ -1813,29 +1831,31 @@ function gotoHistoryIndex(aEvent) {
   return true;
 }
 
 function BrowserForward(aEvent) {
   let where = whereToOpenLink(aEvent, false, true);
 
   if (where == "current") {
     try {
+      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "forward");
       gBrowser.goForward();
     } catch (ex) {
     }
   } else {
     duplicateTabIn(gBrowser.selectedTab, where, 1);
   }
 }
 
 function BrowserBack(aEvent) {
   let where = whereToOpenLink(aEvent, false, true);
 
   if (where == "current") {
     try {
+      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "back");
       gBrowser.goBack();
     } catch (ex) {
     }
   } else {
     duplicateTabIn(gBrowser.selectedTab, where, -1);
   }
 }
 
@@ -1858,16 +1878,17 @@ function BrowserHandleShiftBackspace() {
   case 1:
     goDoCommand("cmd_scrollPageDown");
     break;
   }
 }
 
 function BrowserStop() {
   const stopFlags = nsIWebNavigation.STOP_ALL;
+  maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "stop");
   gBrowser.webNavigation.stop(stopFlags);
 }
 
 function BrowserReloadOrDuplicate(aEvent) {
   let metaKeyPressed = AppConstants.platform == "macosx"
                        ? aEvent.metaKey
                        : aEvent.ctrlKey;
   var backgroundTabModifier = aEvent.button == 1 || metaKeyPressed;
@@ -3250,16 +3271,22 @@ function BrowserReloadWithFlags(reloadFl
   if (gBrowser.updateBrowserRemotenessByURL(gBrowser.selectedBrowser, url)) {
     // If the remoteness has changed, the new browser doesn't have any
     // information of what was loaded before, so we need to load the previous
     // URL again.
     gBrowser.loadURIWithFlags(url, reloadFlags);
     return;
   }
 
+  // Do this after the above case where we might flip remoteness.
+  // Unfortunately, we'll count the remoteness flip case as a
+  // "newURL" load, since we're using loadURIWithFlags, but hopefully
+  // that's rare enough to not matter.
+  maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "reload");
+
   // Reset temporary permissions on the current tab. This is done here
   // because we only want to reset permissions on user reload.
   SitePermissions.clearTemporaryPermissions(gBrowser.selectedBrowser);
 
   let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
 
   gBrowser.selectedBrowser
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -161,17 +161,18 @@
            overflowpadding="30" />
 
     <panel id="DateTimePickerPanel"
            type="arrow"
            hidden="true"
            orient="vertical"
            noautofocus="true"
            consumeoutsideclicks="false"
-           level="parent">
+           level="parent"
+           tabspecific="true">
       <iframe id="dateTimePopupFrame"/>
     </panel>
 
     <!-- for select dropdowns. The menupopup is what shows the list of options,
          and the popuponly menulist makes things like the menuactive attributes
          work correctly on the menupopup. ContentSelectDropdown expects the
          popuponly menulist to be its immediate parent. -->
     <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2493,16 +2493,18 @@
         <body>
           <![CDATA[
             if (aParams) {
               var animate = aParams.animate;
               var byMouse = aParams.byMouse;
               var skipPermitUnload = aParams.skipPermitUnload;
             }
 
+            window.maybeRecordAbandonmentTelemetry(aTab, "tabClosed");
+
             // Handle requests for synchronously removing an already
             // asynchronously closing tab.
             if (!animate &&
                 aTab.closing) {
               this._endRemoveTab(aTab);
               return;
             }
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -27,41 +27,17 @@ support-files =
   contextmenu_common.js
   ctxmenu-image.png
   discovery.html
   download_page.html
   dummy_page.html
   feed_tab.html
   file_generic_favicon.ico
   file_with_favicon.html
-  file_bug822367_1.html
-  file_bug822367_1.js
-  file_bug822367_2.html
-  file_bug822367_3.html
-  file_bug822367_4.html
-  file_bug822367_4.js
-  file_bug822367_4B.html
-  file_bug822367_5.html
-  file_bug822367_6.html
-  file_bug902156.js
-  file_bug902156_1.html
-  file_bug902156_2.html
-  file_bug902156_3.html
-  file_bug906190_1.html
-  file_bug906190_2.html
-  file_bug906190_3_4.html
-  file_bug906190_redirected.html
-  file_bug906190.js
-  file_bug906190.sjs
   file_mediaPlayback.html
-  file_mixedContentFromOnunload.html
-  file_mixedContentFromOnunload_test1.html
-  file_mixedContentFromOnunload_test2.html
-  file_mixedContentFramesOnHttp.html
-  file_mixedPassiveContent.html
   file_bug970276_popup1.html
   file_bug970276_popup2.html
   file_bug970276_favicon1.ico
   file_bug970276_favicon2.ico
   file_documentnavigation_frameset.html
   file_double_close_tab.html
   file_favicon_change.html
   file_favicon_change_not_in_document.html
@@ -80,56 +56,36 @@ support-files =
   popup_blocker.html
   print_postdata.sjs
   searchSuggestionEngine.sjs
   searchSuggestionEngine.xml
   searchSuggestionEngine2.xml
   subtst_contextmenu.html
   subtst_contextmenu_input.html
   subtst_contextmenu_xul.xul
-  test-mixedcontent-securityerrors.html
-  test_bug435035.html
   test_bug462673.html
   test_bug628179.html
   test_bug839103.html
   test_bug959531.html
   test_process_flags_chrome.html
   title_test.svg
   unknownContentType_file.pif
   unknownContentType_file.pif^headers^
   video.ogg
   web_video.html
   web_video1.ogv
   web_video1.ogv^headers^
   zoom_test.html
-  test_no_mcb_on_http_site_img.html
-  test_no_mcb_on_http_site_img.css
-  test_no_mcb_on_http_site_font.html
-  test_no_mcb_on_http_site_font.css
-  test_no_mcb_on_http_site_font2.html
-  test_no_mcb_on_http_site_font2.css
-  test_mcb_redirect.html
-  test_mcb_redirect_image.html
-  test_mcb_double_redirect_image.html
-  test_mcb_redirect.js
-  test_mcb_redirect.sjs
-  file_bug1045809_1.html
-  file_bug1045809_2.html
-  file_csp_block_all_mixedcontent.html
-  file_csp_block_all_mixedcontent.js
   file_install_extensions.html
   browser_webext_permissions.xpi
   browser_webext_nopermissions.xpi
   browser_webext_update1.xpi
   browser_webext_update2.xpi
   browser_webext_update.json
   !/image/test/mochitest/blue.png
-  !/toolkit/components/passwordmgr/test/browser/form_basic.html
-  !/toolkit/components/passwordmgr/test/browser/insecure_test.html
-  !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
   !/toolkit/content/tests/browser/common/mockTransfer.js
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/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/redirect.sjs
   !/toolkit/mozapps/extensions/test/xpinstall/restartless-unsigned.xpi
@@ -179,17 +135,16 @@ skip-if = true # browser_bug321000.js is
 [browser_bug419612.js]
 [browser_bug422590.js]
 [browser_bug423833.js]
 skip-if = true # bug 428712
 [browser_bug424101.js]
 [browser_bug427559.js]
 [browser_bug431826.js]
 [browser_bug432599.js]
-[browser_bug435035.js]
 [browser_bug435325.js]
 [browser_bug441778.js]
 [browser_bug455852.js]
 [browser_bug460146.js]
 [browser_bug462289.js]
 skip-if = toolkit == "cocoa"
 [browser_bug462673.js]
 [browser_bug477014.js]
@@ -251,36 +206,27 @@ skip-if = true # bug 1057615
 [browser_bug655584.js]
 [browser_bug664672.js]
 [browser_bug676619.js]
 skip-if = os == "mac" # mac: Intermittent failures, bug 925225
 [browser_bug678392.js]
 skip-if = os == "mac" # Bug 1102331 - does focus things on the content window which break in e10s mode (still causes orange on Mac 10.10)
 [browser_bug710878.js]
 [browser_bug719271.js]
+skip-if = os == "win" && debug && e10s # Bug 1315042
 [browser_bug724239.js]
 [browser_bug734076.js]
 [browser_bug735471.js]
 [browser_bug749738.js]
 [browser_bug763468_perwindowpb.js]
 [browser_bug767836_perwindowpb.js]
 [browser_bug817947.js]
-[browser_bug822367.js]
-tags = mcb
 [browser_bug832435.js]
 [browser_bug839103.js]
 [browser_bug882977.js]
-[browser_bug902156.js]
-tags = mcb
-[browser_bug906190.js]
-tags = mcb
-[browser_mixedContentFromOnunload.js]
-tags = mcb
-[browser_mixedContentFramesOnHttp.js]
-tags = mcb
 [browser_bug970746.js]
 [browser_bug1015721.js]
 skip-if = os == 'win'
 [browser_accesskeys.js]
 [browser_clipboard.js]
 subsuite = clipboard
 [browser_clipboard_pastefile.js]
 skip-if = true # Disabled due to the clipboard not supporting real file types yet (bug 1288773)
@@ -318,34 +264,28 @@ skip-if = os == "linux" # Linux: Intermi
 [browser_fxaccounts.js]
 support-files = fxa_profile_handler.sjs
 [browser_fxa_web_channel.js]
 [browser_gestureSupport.js]
 skip-if = e10s # Bug 863514 - no gesture support.
 [browser_getshortcutoruri.js]
 [browser_hide_removing.js]
 [browser_homeDrop.js]
-[browser_identity_UI.js]
-[browser_insecureLoginForms.js]
-support-files = insecure_opener.html
 [browser_invalid_uri_back_forward_manipulation.js]
 [browser_keywordBookmarklets.js]
 [browser_keywordSearch.js]
 [browser_keywordSearch_postData.js]
 [browser_lastAccessedTab.js]
 skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
 [browser_menuButtonFitts.js]
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 subsuite = clipboard
 [browser_minimize.js]
 [browser_misused_characters_in_strings.js]
-[browser_mixed_content_cert_override.js]
-[browser_mixedcontent_securityflags.js]
-tags = mcb
 [browser_modifiedclick_inherit_principal.js]
 [browser_offlineQuotaNotification.js]
 [browser_feed_discovery.js]
 support-files = feed_discovery.html
 [browser_gZipOfflineChild.js]
 support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
 [browser_overflowScroll.js]
 [browser_pageInfo.js]
@@ -478,37 +418,29 @@ skip-if = true # Bug 1005420 - fails int
 [browser_visibleTabs_contextMenu.js]
 [browser_visibleTabs_tabPreview.js]
 skip-if = (os == "win" && !debug)
 [browser_web_channel.js]
 [browser_windowopen_reflows.js]
 [browser_zbug569342.js]
 skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
 [browser_registerProtocolHandler_notification.js]
-[browser_no_mcb_on_http_site.js]
-tags = mcb
 [browser_addCertException.js]
-[browser_bug1045809.js]
-tags = mcb
 [browser_e10s_about_page_triggeringprincipal.js]
 [browser_e10s_switchbrowser.js]
 [browser_e10s_about_process.js]
 [browser_e10s_chrome_process.js]
 [browser_e10s_javascript.js]
 [browser_blockHPKP.js]
 tags = psm
-[browser_mcb_redirect.js]
-tags = mcb
 [browser_windowactivation.js]
 [browser_contextmenu_childprocess.js]
 [browser_bug963945.js]
 [browser_domFullscreen_fullscreenMode.js]
 tags = fullscreen
 [browser_menuButtonBadgeManager.js]
 [browser_newTabDrop.js]
 [browser_newWindowDrop.js]
-[browser_csp_block_all_mixedcontent.js]
-tags = mcb
 [browser_newwindow_focus.js]
 skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
 [browser_bug1299667.js]
 [browser_close_dependent_tabs.js]
 skip-if = !e10s # GroupedSHistory is e10s-only
--- a/browser/base/content/test/general/browser_decoderDoctor.js
+++ b/browser/base/content/test/general/browser_decoderDoctor.js
@@ -41,36 +41,16 @@ function* test_decoder_doctor_notificati
                          "fix-video-audio-problems-firefox-windows");
     let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url);
     button.click();
     let sumoTab = yield awaitNewTab;
     yield BrowserTestUtils.removeTab(sumoTab);
   });
 }
 
-add_task(function* test_adobe_cdm_not_found() {
-  // This is only sent on Windows.
-  if (AppConstants.platform != "win") {
-    return;
-  }
-
-  let message = gNavigatorBundle.getString("decoder.noCodecs.message");
-  yield test_decoder_doctor_notification("adobe-cdm-not-found", message);
-});
-
-add_task(function* test_adobe_cdm_not_activated() {
-  // This is only sent on Windows.
-  if (AppConstants.platform != "win") {
-    return;
-  }
-
-  let message = gNavigatorBundle.getString("decoder.noCodecs.message");
-  yield test_decoder_doctor_notification("adobe-cdm-not-activated", message);
-});
-
 add_task(function* test_platform_decoder_not_found() {
   let message;
   let isLinux = AppConstants.platform == "linux";
   if (isLinux) {
     message = gNavigatorBundle.getString("decoder.noCodecsLinux.message");
   } else {
     message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
   }
--- a/browser/base/content/test/general/browser_extension_update.js
+++ b/browser/base/content/test/general/browser_extension_update.js
@@ -1,13 +1,28 @@
 const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
 const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
 const ID = "update@tests.mozilla.org";
 
+function promiseInstallAddon(url) {
+  return AddonManager.getInstallForURL(url, null, "application/x-xpinstall")
+                     .then(install => {
+                       ok(install, "Created install");
+                       return new Promise(resolve => {
+                         install.addListener({
+                           onInstallEnded(_install, addon) {
+                             resolve(addon);
+                           },
+                         });
+                         install.install();
+                       });
+                     });
+}
+
 function promiseViewLoaded(tab, viewid) {
   let win = tab.linkedBrowser.contentWindow;
   if (win.gViewController && !win.gViewController.isLoading &&
       win.gViewController.currentViewId == viewid) {
      return Promise.resolve();
   }
 
   return new Promise(resolve => {
@@ -39,80 +54,60 @@ function promisePopupNotificationShown(n
   });
 }
 
 function getBadgeStatus() {
   let menuButton = document.getElementById("PanelUI-menu-button");
   return menuButton.getAttribute("badge-status");
 }
 
-function promiseUpdateDownloaded(addon) {
+function promiseInstallEvent(addon, event) {
   return new Promise(resolve => {
-    let listener = {
-      onDownloadEnded(install) {
-        if (install.addon.id == addon.id) {
-          AddonManager.removeInstallListener(listener);
-          resolve();
-        }
-      },
+    let listener = {};
+    listener[event] = (install, ...args) => {
+      if (install.addon.id == addon.id) {
+        AddonManager.removeInstallListener(listener);
+        resolve(...args);
+      }
     };
     AddonManager.addInstallListener(listener);
   });
 }
 
-function promiseUpgrade(addon) {
-  return new Promise(resolve => {
-    let listener = {
-      onInstallEnded(install, newAddon) {
-        if (newAddon.id == addon.id) {
-          AddonManager.removeInstallListener(listener);
-          resolve(newAddon);
-        }
-      },
-    };
-    AddonManager.addInstallListener(listener);
-  });
-}
-
-add_task(function* () {
-  yield SpecialPowers.pushPrefEnv({set: [
-    // Turn on background updates
-    ["extensions.update.enabled", true],
-
-    // Point updates to the local mochitest server
-    ["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
-
+// Set some prefs that apply to all the tests in this file
+add_task(function setup() {
+  return SpecialPowers.pushPrefEnv({set: [
     // We don't have pre-pinned certificates for the local mochitest server
     ["extensions.install.requireBuiltInCerts", false],
     ["extensions.update.requireBuiltInCerts", false],
 
     // XXX remove this when prompts are enabled by default
     ["extensions.webextPermissionPrompts", true],
   ]});
+});
+
+add_task(function* test_background_update() {
+  yield SpecialPowers.pushPrefEnv({set: [
+    // Turn on background updates
+    ["extensions.update.enabled", true],
+
+    // Point updates to the local mochitest server
+    ["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
+  ]});
 
   // Install version 1.0 of the test extension
-  let url1 = `${URL_BASE}/browser_webext_update1.xpi`;
-  let install = yield AddonManager.getInstallForURL(url1, null, "application/x-xpinstall");
-  ok(install, "Created install");
-
-  let addon = yield new Promise(resolve => {
-    install.addListener({
-      onInstallEnded(_install, _addon) {
-        resolve(_addon);
-      },
-    });
-    install.install();
-  });
+  let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
 
   ok(addon, "Addon was installed");
   is(getBadgeStatus(), "", "Should not start out with an addon alert badge");
 
   // Trigger an update check and wait for the update for this addon
   // to be downloaded.
-  let updatePromise = promiseUpdateDownloaded(addon);
+  let updatePromise = promiseInstallEvent(addon, "onDownloadEnded");
+
   AddonManagerPrivate.backgroundUpdateCheck();
   yield updatePromise;
 
   is(getBadgeStatus(), "addon-alert", "Should have addon alert badge");
 
   // Find the menu entry for the update
   yield PanelUI.show();
 
@@ -147,17 +142,17 @@ add_task(function* () {
   is(getBadgeStatus(), "", "Addon alert badge should be gone");
 
   yield PanelUI.show();
   addons = document.getElementById("PanelUI-footer-addons");
   is(addons.children.length, 0, "Update menu entries should be gone");
   yield PanelUI.hide();
 
   // Re-check for an update
-  updatePromise = promiseUpdateDownloaded(addon);
+  updatePromise = promiseInstallEvent(addon, "onDownloadEnded");
   yield AddonManagerPrivate.backgroundUpdateCheck();
   yield updatePromise;
 
   is(getBadgeStatus(), "addon-alert", "Should have addon alert badge");
 
   // Find the menu entry for the update
   yield PanelUI.show();
 
@@ -174,19 +169,134 @@ add_task(function* () {
   is(tab.linkedBrowser.currentURI.spec, "about:addons");
 
   yield promiseViewLoaded(tab, VIEW);
   win = tab.linkedBrowser.contentWindow;
   ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
   is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
 
   // Wait for the permission prompt and accept it this time
-  updatePromise = promiseUpgrade(addon);
+  updatePromise = promiseInstallEvent(addon, "onInstallEnded");
   panel = yield popupPromise;
   panel.button.click();
 
   addon = yield updatePromise;
   is(addon.version, "2.0", "Should have upgraded to the new version");
 
   yield BrowserTestUtils.removeTab(tab);
 
   is(getBadgeStatus(), "", "Addon alert badge should be gone");
+
+  addon.uninstall();
+  yield SpecialPowers.popPrefEnv();
 });
+
+// Helper function to test a specific scenario for interactive updates.
+// `checkFn` is a callable that triggers a check for updates.
+// `autoUpdate` specifies whether the test should be run with
+// updates applied automatically or not.
+function* interactiveUpdateTest(autoUpdate, checkFn) {
+  yield SpecialPowers.pushPrefEnv({set: [
+    ["extensions.update.autoUpdateDefault", autoUpdate],
+
+    // Point updates to the local mochitest server
+    ["extensions.update.url", `${URL_BASE}/browser_webext_update.json`],
+  ]});
+
+  // Trigger an update check, manually applying the update if we're testing
+  // without auto-update.
+  function* triggerUpdate(win, addon) {
+    let manualUpdatePromise;
+    if (!autoUpdate) {
+      manualUpdatePromise = new Promise(resolve => {
+        let listener = {
+          onNewInstall() {
+            AddonManager.removeInstallListener(listener);
+            resolve();
+          },
+        };
+        AddonManager.addInstallListener(listener);
+      });
+    }
+
+    checkFn(win, addon);
+
+    if (manualUpdatePromise) {
+      yield manualUpdatePromise;
+
+      let item = win.document.getElementById("addon-list")
+                    .children.find(_item => _item.value == ID);
+      EventUtils.synthesizeMouseAtCenter(item._updateBtn, {}, win);
+    }
+  }
+
+  // Install version 1.0 of the test extension
+  let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
+  ok(addon, "Addon was installed");
+  is(addon.version, "1.0", "Version 1 of the addon is installed");
+
+  // Open add-ons manager and navigate to extensions list
+  let loadPromise = new Promise(resolve => {
+    let listener = (subject, topic) => {
+      if (subject.location.href == "about:addons") {
+        Services.obs.removeObserver(listener, topic);
+        resolve(subject);
+      }
+    };
+    Services.obs.addObserver(listener, "EM-loaded", false);
+  });
+  let tab = gBrowser.addTab("about:addons");
+  gBrowser.selectedTab = tab;
+  let win = yield loadPromise;
+
+  const VIEW = "addons://list/extension";
+  let viewPromise = promiseViewLoaded(tab, VIEW);
+  win.loadView(VIEW);
+  yield viewPromise;
+
+  // Trigger an update check
+  let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  yield triggerUpdate(win, addon);
+  let panel = yield popupPromise;
+
+  // Click the cancel button, wait to see the cancel event
+  let cancelPromise = promiseInstallEvent(addon, "onInstallCancelled");
+  panel.secondaryButton.click();
+  yield cancelPromise;
+
+  addon = yield AddonManager.getAddonByID(ID);
+  is(addon.version, "1.0", "Should still be running the old version");
+
+  // Trigger a new update check
+  popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  yield triggerUpdate(win, addon);
+
+  // This time, accept the upgrade
+  let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
+  panel = yield popupPromise;
+  panel.button.click();
+
+  addon = yield updatePromise;
+  is(addon.version, "2.0", "Should have upgraded");
+
+  yield BrowserTestUtils.removeTab(tab);
+  addon.uninstall();
+  yield SpecialPowers.popPrefEnv();
+}
+
+// Invoke the "Check for Updates" menu item
+function checkAll(win) {
+  win.gViewController.doCommand("cmd_findAllUpdates");
+}
+
+// Test "Check for Updates" with both auto-update settings
+add_task(() => interactiveUpdateTest(true, checkAll));
+add_task(() => interactiveUpdateTest(false, checkAll));
+
+
+// Invoke an invidual extension's "Find Updates" menu item
+function checkOne(win, addon) {
+  win.gViewController.doCommand("cmd_findItemUpdates", addon);
+}
+
+// Test "Find Updates" with both auto-update settings
+add_task(() => interactiveUpdateTest(true, checkOne));
+add_task(() => interactiveUpdateTest(false, checkOne));
--- a/browser/base/content/test/general/browser_fxaccounts.js
+++ b/browser/base/content/test/general/browser_fxaccounts.js
@@ -111,16 +111,17 @@ add_task(function* test_unverifiedUser()
 });
 */
 
 add_task(function* test_verifiedUserEmptyProfile() {
   // We see 2 updateUI() calls - one for the signedInUser and one after
   // we first fetch the profile. We want them both to fire or we aren't testing
   // the state we think we are testing.
   let promiseUpdateDone = promiseObserver("test:browser_fxaccounts:updateUI", 2);
+  gFxAccounts._profileFetched = false;
   configureProfileURL({}); // successful but empty profile.
   yield setSignedInUser(true); // this will fire the observer that does the update.
   yield promiseUpdateDone;
 
   // Check the world.
   Assert.ok(isFooterVisible())
   Assert.equal(panelUILabel.getAttribute("label"), "foo@example.com");
   Assert.equal(panelUIStatus.getAttribute("tooltiptext"),
@@ -130,16 +131,17 @@ add_task(function* test_verifiedUserEmpt
   let promisePreferencesOpened = promiseObserver("test:browser_fxaccounts:openPreferences");
   panelUIStatus.click();
   yield promisePreferencesOpened;
   yield signOut();
 });
 
 add_task(function* test_verifiedUserDisplayName() {
   let promiseUpdateDone = promiseObserver("test:browser_fxaccounts:updateUI", 2);
+  gFxAccounts._profileFetched = false;
   configureProfileURL({ displayName: "Test User Display Name" });
   yield setSignedInUser(true); // this will fire the observer that does the update.
   yield promiseUpdateDone;
 
   Assert.ok(isFooterVisible())
   Assert.equal(panelUILabel.getAttribute("label"), "Test User Display Name");
   Assert.equal(panelUIStatus.getAttribute("tooltiptext"),
                panelUIStatus.getAttribute("signedinTooltiptext"));
--- a/browser/base/content/test/general/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/general/browser_misused_characters_in_strings.js
@@ -104,20 +104,16 @@ let gWhitelist = [{
     file: "netErrorApp.dtd",
     key: "securityOverride.warningContent",
     type: "single-quote"
   }, {
     file: "pocket.properties",
     key: "tos",
     type: "double-quote"
   }, {
-    file: "pocket.properties",
-    key: "tos",
-    type: "apostrophe"
-  }, {
     file: "aboutNetworking.dtd",
     key: "aboutNetworking.logTutorial",
     type: "single-quote"
   }
 ];
 
 var moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
 var {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -640,180 +640,16 @@ function promiseTabLoadEvent(tab, url) {
  *        Resolved when the new tab has been opened.
  * @resolves to the TabOpen event that was fired.
  * @rejects Never.
  */
 function waitForNewTabEvent(aTabBrowser) {
   return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
 }
 
-/**
- * Test the state of the identity box and control center to make
- * sure they are correctly showing the expected mixed content states.
- *
- * @note The checks are done synchronously, but new code should wait on the
- *       returned Promise object to ensure the identity panel has closed.
- *       Bug 1221114 is filed to fix the existing code.
- *
- * @param tabbrowser
- * @param Object states
- *        MUST include the following properties:
- *        {
- *           activeLoaded: true|false,
- *           activeBlocked: true|false,
- *           passiveLoaded: true|false,
- *        }
- *
- * @return {Promise}
- * @resolves When the operation has finished and the identity panel has closed.
- */
-function assertMixedContentBlockingState(tabbrowser, states = {}) {
-  if (!tabbrowser || !("activeLoaded" in states) ||
-      !("activeBlocked" in states) || !("passiveLoaded" in states)) {
-    throw new Error("assertMixedContentBlockingState requires a browser and a states object");
-  }
-
-  let {passiveLoaded, activeLoaded, activeBlocked} = states;
-  let {gIdentityHandler} = tabbrowser.ownerGlobal;
-  let doc = tabbrowser.ownerDocument;
-  let identityBox = gIdentityHandler._identityBox;
-  let classList = identityBox.classList;
-  let connectionIcon = doc.getElementById("connection-icon");
-  let connectionIconImage = tabbrowser.ownerGlobal.getComputedStyle(connectionIcon).
-                         getPropertyValue("list-style-image");
-
-  let stateSecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
-  let stateBroken = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
-  let stateInsecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
-  let stateActiveBlocked = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
-  let stateActiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
-  let statePassiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
-
-  is(activeBlocked, !!stateActiveBlocked, "Expected state for activeBlocked matches UI state");
-  is(activeLoaded, !!stateActiveLoaded, "Expected state for activeLoaded matches UI state");
-  is(passiveLoaded, !!statePassiveLoaded, "Expected state for passiveLoaded matches UI state");
-
-  if (stateInsecure) {
-    // HTTP request, there should be no MCB classes for the identity box and the non secure icon
-    // should always be visible regardless of MCB state.
-    ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
-    is_element_hidden(connectionIcon);
-
-    ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
-    ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
-    ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
-    ok(!classList.contains("mixedDisplayContentLoadedActiveBlocked"), "No MCB icon on HTTP page");
-  } else {
-    // Make sure the identity box UI has the correct mixedcontent states and icons
-    is(classList.contains("mixedActiveContent"), activeLoaded,
-        "identityBox has expected class for activeLoaded");
-    is(classList.contains("mixedActiveBlocked"), activeBlocked && !passiveLoaded,
-        "identityBox has expected class for activeBlocked && !passiveLoaded");
-    is(classList.contains("mixedDisplayContent"), passiveLoaded && !(activeLoaded || activeBlocked),
-       "identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)");
-    is(classList.contains("mixedDisplayContentLoadedActiveBlocked"), passiveLoaded && activeBlocked,
-       "identityBox has expected class for passiveLoaded && activeBlocked");
-
-    is_element_visible(connectionIcon);
-    if (activeLoaded) {
-      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-active-loaded.svg#icon\")",
-        "Using active loaded icon");
-    }
-    if (activeBlocked && !passiveLoaded) {
-      is(connectionIconImage, "url(\"chrome://browser/skin/connection-secure.svg\")",
-        "Using active blocked icon");
-    }
-    if (passiveLoaded && !(activeLoaded || activeBlocked)) {
-      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
-        "Using passive loaded icon");
-    }
-    if (passiveLoaded && activeBlocked) {
-      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
-        "Using active blocked and passive loaded icon");
-    }
-  }
-
-  // Make sure the identity popup has the correct mixedcontent states
-  gIdentityHandler._identityBox.click();
-  let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
-  let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
-
-  is(popupAttr.includes("active-loaded"), activeLoaded,
-      "identity-popup has expected attr for activeLoaded");
-  is(bodyAttr.includes("active-loaded"), activeLoaded,
-      "securityView-body has expected attr for activeLoaded");
-
-  is(popupAttr.includes("active-blocked"), activeBlocked,
-      "identity-popup has expected attr for activeBlocked");
-  is(bodyAttr.includes("active-blocked"), activeBlocked,
-      "securityView-body has expected attr for activeBlocked");
-
-  is(popupAttr.includes("passive-loaded"), passiveLoaded,
-      "identity-popup has expected attr for passiveLoaded");
-  is(bodyAttr.includes("passive-loaded"), passiveLoaded,
-      "securityView-body has expected attr for passiveLoaded");
-
-  // Make sure the correct icon is visible in the Control Center.
-  // This logic is controlled with CSS, so this helps prevent regressions there.
-  let securityView = doc.getElementById("identity-popup-securityView");
-  let securityViewBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
-                       getPropertyValue("background-image");
-  let securityContentBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
-                          getPropertyValue("background-image");
-
-  if (stateInsecure) {
-    is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
-      "CC using 'not secure' icon");
-    is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
-      "CC using 'not secure' icon");
-  }
-
-  if (stateSecure) {
-    is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
-      "CC using secure icon");
-    is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
-      "CC using secure icon");
-  }
-
-  if (stateBroken) {
-    if (activeLoaded) {
-      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
-        "CC using active loaded icon");
-      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
-        "CC using active loaded icon");
-    } else if (activeBlocked || passiveLoaded) {
-      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
-        "CC using degraded icon");
-      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
-        "CC using degraded icon");
-    } else {
-      // There is a case here with weak ciphers, but no bc tests are handling this yet.
-      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
-        "CC using degraded icon");
-      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
-        "CC using degraded icon");
-    }
-  }
-
-  if (activeLoaded || activeBlocked || passiveLoaded) {
-    doc.getElementById("identity-popup-security-expander").click();
-    is(Array.filter(doc.querySelectorAll("[observes=identity-popup-mcb-learn-more]"),
-                    element => !is_hidden(element)).length, 1,
-       "The 'Learn more' link should be visible once.");
-  }
-
-  gIdentityHandler._identityPopup.hidden = true;
-
-  // Wait for the panel to be closed before continuing. The promisePopupHidden
-  // function cannot be used because it's unreliable unless promisePopupShown is
-  // also called before closing the panel. This cannot be done until all callers
-  // are made asynchronous (bug 1221114).
-  return new Promise(resolve => executeSoon(resolve));
-}
-
 function is_hidden(element) {
   var style = element.ownerGlobal.getComputedStyle(element);
   if (style.display == "none")
     return true;
   if (style.visibility != "visible")
     return true;
   if (style.display == "-moz-popup")
     return ["hiding", "closed"].indexOf(element.state) != -1;
@@ -918,45 +754,16 @@ function promiseNewSearchEngine(basename
       onError(errCode) {
         Assert.ok(false, "addEngine failed with error code " + errCode);
         reject();
       },
     });
   });
 }
 
-// Compares the security state of the page with what is expected
-function isSecurityState(expectedState) {
-  let ui = gTestBrowser.securityUI;
-  if (!ui) {
-    ok(false, "No security UI to get the security state");
-    return;
-  }
-
-  const wpl = Components.interfaces.nsIWebProgressListener;
-
-  // determine the security state
-  let isSecure = ui.state & wpl.STATE_IS_SECURE;
-  let isBroken = ui.state & wpl.STATE_IS_BROKEN;
-  let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
-
-  let actualState;
-  if (isSecure && !(isBroken || isInsecure)) {
-    actualState = "secure";
-  } else if (isBroken && !(isSecure || isInsecure)) {
-    actualState = "broken";
-  } else if (isInsecure && !(isSecure || isBroken)) {
-    actualState = "insecure";
-  } else {
-    actualState = "unknown";
-  }
-
-  is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
-}
-
 /**
  * Resolves when a bookmark with the given uri is added.
  */
 function promiseOnBookmarkItemAdded(aExpectedURI) {
   return new Promise((resolve, reject) => {
     let bookmarksObserver = {
       onItemAdded(aItemId, aFolderId, aIndex, aItemType, aURI) {
         info("Added a bookmark to " + aURI.spec);
--- a/browser/base/content/test/siteIdentity/browser.ini
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -1,6 +1,91 @@
 [DEFAULT]
 support-files =
   head.js
+  !/image/test/mochitest/blue.png
 
+[browser_bug435035.js]
+support-files =
+  test_bug435035.html
+[browser_bug822367.js]
+tags = mcb
+support-files =
+  file_bug822367_1.html
+  file_bug822367_1.js
+  file_bug822367_2.html
+  file_bug822367_3.html
+  file_bug822367_4.html
+  file_bug822367_4.js
+  file_bug822367_4B.html
+  file_bug822367_5.html
+  file_bug822367_6.html
+[browser_bug902156.js]
+tags = mcb
+support-files =
+  file_bug902156.js
+  file_bug902156_1.html
+  file_bug902156_2.html
+  file_bug902156_3.html
+[browser_bug906190.js]
+tags = mcb
+support-files =
+  file_bug906190_1.html
+  file_bug906190_2.html
+  file_bug906190_3_4.html
+  file_bug906190_redirected.html
+  file_bug906190.js
+  file_bug906190.sjs
+[browser_bug1045809.js]
+tags = mcb
+support-files =
+  file_bug1045809_1.html
+  file_bug1045809_2.html
+[browser_csp_block_all_mixedcontent.js]
+tags = mcb
+support-files =
+  file_csp_block_all_mixedcontent.html
+  file_csp_block_all_mixedcontent.js
+[browser_identity_UI.js]
 [browser_identityBlock_focus.js]
 support-files = ../general/permissions.html
+[browser_insecureLoginForms.js]
+support-files =
+  insecure_opener.html
+  !/toolkit/components/passwordmgr/test/browser/form_basic.html
+  !/toolkit/components/passwordmgr/test/browser/insecure_test.html
+  !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
+[browser_mcb_redirect.js]
+tags = mcb
+support-files =
+  test_mcb_redirect.html
+  test_mcb_redirect_image.html
+  test_mcb_double_redirect_image.html
+  test_mcb_redirect.js
+  test_mcb_redirect.sjs
+[browser_mixed_content_cert_override.js]
+tags = mcb
+support-files =
+  test-mixedcontent-securityerrors.html
+[browser_mixedcontent_securityflags.js]
+tags = mcb
+support-files =
+  test-mixedcontent-securityerrors.html
+[browser_mixedContentFramesOnHttp.js]
+tags = mcb
+support-files =
+  file_mixedContentFramesOnHttp.html
+  file_mixedPassiveContent.html
+[browser_mixedContentFromOnunload.js]
+tags = mcb
+support-files =
+  file_mixedContentFromOnunload.html
+  file_mixedContentFromOnunload_test1.html
+  file_mixedContentFromOnunload_test2.html
+[browser_no_mcb_on_http_site.js]
+tags = mcb
+support-files =
+  test_no_mcb_on_http_site_img.html
+  test_no_mcb_on_http_site_img.css
+  test_no_mcb_on_http_site_font.html
+  test_no_mcb_on_http_site_font.css
+  test_no_mcb_on_http_site_font2.html
+  test_no_mcb_on_http_site_font2.css
rename from browser/base/content/test/general/browser_bug1045809.js
rename to browser/base/content/test/siteIdentity/browser_bug1045809.js
--- a/browser/base/content/test/general/browser_bug1045809.js
+++ b/browser/base/content/test/siteIdentity/browser_bug1045809.js
@@ -1,33 +1,31 @@
 // Test that the Mixed Content Doorhanger Action to re-enable protection works
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
+const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "file_bug1045809_1.html";
 
 var origBlockActive;
 
 add_task(function* () {
   registerCleanupFunction(function() {
     Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
     gBrowser.removeCurrentTab();
   });
 
   // Store original preferences so we can restore settings after testing
   origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
 
   // Make sure mixed content blocking is on
   Services.prefs.setBoolPref(PREF_ACTIVE, true);
 
-  var url =
-    "https://test1.example.com/browser/browser/base/content/test/general/" +
-    "file_bug1045809_1.html";
   let tab = gBrowser.selectedTab = gBrowser.addTab();
 
   // Test 1: mixed content must be blocked
-  yield promiseTabLoadEvent(tab, url);
+  yield promiseTabLoadEvent(tab, TEST_URL);
   yield* test1(gBrowser.getBrowserForTab(tab));
 
   yield promiseTabLoadEvent(tab);
   // Test 2: mixed content must NOT be blocked
   yield* test2(gBrowser.getBrowserForTab(tab));
 
   // Test 3: mixed content must be blocked again
   yield promiseTabLoadEvent(tab);
rename from browser/base/content/test/general/browser_bug435035.js
rename to browser/base/content/test/siteIdentity/browser_bug435035.js
--- a/browser/base/content/test/general/browser_bug435035.js
+++ b/browser/base/content/test/siteIdentity/browser_bug435035.js
@@ -1,17 +1,17 @@
+const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test_bug435035.html";
+
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
     is(document.getElementById("identity-box").className,
        "unknownIdentity mixedDisplayContent",
        "identity box has class name for mixed content");
 
     gBrowser.removeCurrentTab();
     finish();
   });
 
-  gBrowser.loadURI(
-    "https://example.com/browser/browser/base/content/test/general/test_bug435035.html"
-  );
+  gBrowser.loadURI(TEST_URL);
 }
rename from browser/base/content/test/general/browser_bug822367.js
rename to browser/base/content/test/siteIdentity/browser_bug822367.js
--- a/browser/base/content/test/general/browser_bug822367.js
+++ b/browser/base/content/test/siteIdentity/browser_bug822367.js
@@ -2,18 +2,18 @@
  * User Override Mixed Content Block - Tests for Bug 822367
  */
 
 
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 
 // We alternate for even and odd test cases to simulate different hosts
-const gHttpTestRoot = "https://example.com/browser/browser/base/content/test/general/";
-const gHttpTestRoot2 = "https://test1.example.com/browser/browser/base/content/test/general/";
+const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
 
 var gTestBrowser = null;
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.clearUserPref(PREF_DISPLAY);
   Services.prefs.clearUserPref(PREF_ACTIVE);
 });
@@ -32,164 +32,164 @@ function test() {
 
   var newTab = gBrowser.addTab();
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
   newTab.linkedBrowser.stop()
 
   // Mixed Script Test
   gTestBrowser.addEventListener("load", MixedTest1A, true);
-  var url = gHttpTestRoot + "file_bug822367_1.html";
+  var url = HTTPS_TEST_ROOT + "file_bug822367_1.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 // Mixed Script Test
 function MixedTest1A() {
   gTestBrowser.removeEventListener("load", MixedTest1A, true);
   gTestBrowser.addEventListener("load", MixedTest1B, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 function MixedTest1B() {
-  waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", MixedTest1C, "Waited too long for mixed script to run in Test 1");
+  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 1").then(MixedTest1C);
 }
 function MixedTest1C() {
   ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 1");
   gTestBrowser.removeEventListener("load", MixedTest1B, true);
   MixedTest2();
 }
 
 // Mixed Display Test - Doorhanger should not appear
 function MixedTest2() {
   gTestBrowser.addEventListener("load", MixedTest2A, true);
-  var url = gHttpTestRoot2 + "file_bug822367_2.html";
+  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_2.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function MixedTest2A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: false});
   MixedTest3();
 }
 
 // Mixed Script and Display Test - User Override should cause both the script and the image to load.
 function MixedTest3() {
   gTestBrowser.removeEventListener("load", MixedTest2A, true);
   gTestBrowser.addEventListener("load", MixedTest3A, true);
-  var url = gHttpTestRoot + "file_bug822367_3.html";
+  var url = HTTPS_TEST_ROOT + "file_bug822367_3.html";
   gTestBrowser.contentWindow.location = url;
 }
 function MixedTest3A() {
   gTestBrowser.removeEventListener("load", MixedTest3A, true);
   gTestBrowser.addEventListener("load", MixedTest3B, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 function MixedTest3B() {
-  waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", MixedTest3C, "Waited too long for mixed script to run in Test 3");
+  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 3").then(MixedTest3C);
 }
 function MixedTest3C() {
-  waitForCondition(() => content.document.getElementById("p2").innerHTML == "bye", MixedTest3D, "Waited too long for mixed image to load in Test 3");
+  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p2").innerHTML == "bye", "Waited too long for mixed image to load in Test 3").then(MixedTest3D);
 }
 function MixedTest3D() {
   ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 3");
   ok(content.document.getElementById("p2").innerHTML == "bye", "Mixed image didn't load in Test 3");
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: true});
   MixedTest4();
 }
 
 // Location change - User override on one page doesn't propogate to another page after location change.
 function MixedTest4() {
   gTestBrowser.removeEventListener("load", MixedTest3B, true);
   gTestBrowser.addEventListener("load", MixedTest4A, true);
-  var url = gHttpTestRoot2 + "file_bug822367_4.html";
+  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_4.html";
   gTestBrowser.contentWindow.location = url;
 }
 function MixedTest4A() {
   gTestBrowser.removeEventListener("load", MixedTest4A, true);
   gTestBrowser.addEventListener("load", MixedTest4B, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 function MixedTest4B() {
-  waitForCondition(() => content.document.location == gHttpTestRoot + "file_bug822367_4B.html", MixedTest4C, "Waited too long for mixed script to run in Test 4");
+  BrowserTestUtils.waitForCondition(() => content.document.location == HTTPS_TEST_ROOT + "file_bug822367_4B.html", "Waited too long for mixed script to run in Test 4").then(MixedTest4C);
 }
 function MixedTest4C() {
-  ok(content.document.location == gHttpTestRoot + "file_bug822367_4B.html", "Location didn't change in test 4");
+  ok(content.document.location == HTTPS_TEST_ROOT + "file_bug822367_4B.html", "Location didn't change in test 4");
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
-  waitForCondition(() => content.document.getElementById("p1").innerHTML == "", MixedTest4D, "Mixed script loaded in test 4 after location change!");
+  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "", "Mixed script loaded in test 4 after location change!").then(MixedTest4D);
 }
 function MixedTest4D() {
   ok(content.document.getElementById("p1").innerHTML == "", "p1.innerHTML changed; mixed script loaded after location change in Test 4");
   MixedTest5();
 }
 
 // Mixed script attempts to load in a document.open()
 function MixedTest5() {
   gTestBrowser.removeEventListener("load", MixedTest4B, true);
   gTestBrowser.addEventListener("load", MixedTest5A, true);
-  var url = gHttpTestRoot + "file_bug822367_5.html";
+  var url = HTTPS_TEST_ROOT + "file_bug822367_5.html";
   gTestBrowser.contentWindow.location = url;
 }
 function MixedTest5A() {
   gTestBrowser.removeEventListener("load", MixedTest5A, true);
   gTestBrowser.addEventListener("load", MixedTest5B, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 function MixedTest5B() {
-  waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", MixedTest5C, "Waited too long for mixed script to run in Test 5");
+  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 5").then(MixedTest5C);
 }
 function MixedTest5C() {
   ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 5");
   MixedTest6();
 }
 
 // Mixed script attempts to load in a document.open() that is within an iframe.
 function MixedTest6() {
   gTestBrowser.removeEventListener("load", MixedTest5B, true);
   gTestBrowser.addEventListener("load", MixedTest6A, true);
-  var url = gHttpTestRoot2 + "file_bug822367_6.html";
+  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_6.html";
   gTestBrowser.contentWindow.location = url;
 }
 function MixedTest6A() {
   gTestBrowser.removeEventListener("load", MixedTest6A, true);
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
-  waitForCondition(() => gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), MixedTest6B, "Waited too long for control center to get mixed active blocked state");
+  BrowserTestUtils.waitForCondition(() => gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), "Waited too long for control center to get mixed active blocked state").then(MixedTest6B);
 }
 
 function MixedTest6B() {
   gTestBrowser.addEventListener("load", MixedTest6C, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 
 function MixedTest6C() {
   gTestBrowser.removeEventListener("load", MixedTest6C, true);
-  waitForCondition(function() {
+  BrowserTestUtils.waitForCondition(function() {
     try {
       return content.document.getElementById("f1").contentDocument.getElementById("p1").innerHTML == "hello";
     } catch (e) {
       return false;
     }
-  }, MixedTest6D, "Waited too long for mixed script to run in Test 6");
+  }, "Waited too long for mixed script to run in Test 6").then(MixedTest6D);
 }
 function MixedTest6D() {
   ok(content.document.getElementById("f1").contentDocument.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 6");
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
   MixedTestsCompleted();
 }
rename from browser/base/content/test/general/browser_bug902156.js
rename to browser/base/content/test/siteIdentity/browser_bug902156.js
--- a/browser/base/content/test/general/browser_bug902156.js
+++ b/browser/base/content/test/siteIdentity/browser_bug902156.js
@@ -20,18 +20,18 @@
  *      we navigated away from html page where we disabled the protection.
  *
  * Note, for all tests we set gHttpTestRoot to use 'https'.
  */
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 
 // We alternate for even and odd test cases to simulate different hosts
-const gHttpTestRoot1 = "https://test1.example.com/browser/browser/base/content/test/general/";
-const gHttpTestRoot2 = "https://test2.example.com/browser/browser/base/content/test/general/";
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test2.example.com");
 
 var origBlockActive;
 var gTestBrowser = null;
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
 });
@@ -51,30 +51,30 @@ function test1A() {
 
   // Disable Mixed Content Protection for the page (and reload)
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 
 function test1B() {
   var expected = "Mixed Content Blocker disabled";
-  waitForCondition(
+  BrowserTestUtils.waitForCondition(
     () => content.document.getElementById("mctestdiv").innerHTML == expected,
-    test1C, "Error: Waited too long for mixed script to run in Test 1B");
+    "Error: Waited too long for mixed script to run in Test 1B").then(test1C);
 }
 
 function test1C() {
   var actual = content.document.getElementById("mctestdiv").innerHTML;
   is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1C");
 
   // The Script loaded after we disabled the page, now we are going to reload the
   // page and see if our decision is persistent
   BrowserTestUtils.browserLoaded(gTestBrowser).then(test1D);
 
-  var url = gHttpTestRoot1 + "file_bug902156_2.html";
+  var url = HTTPS_TEST_ROOT_1 + "file_bug902156_2.html";
   gTestBrowser.loadURI(url);
 }
 
 function test1D() {
   // The Control Center button should appear but isMixedContentBlocked should be NOT true,
   // because our decision of disabling the mixed content blocker is persistent.
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
 
@@ -84,35 +84,35 @@ function test1D() {
   // move on to Test 2
   test2();
 }
 
 // ------------------------ Test 2 ------------------------------
 
 function test2() {
   BrowserTestUtils.browserLoaded(gTestBrowser).then(test2A);
-  var url = gHttpTestRoot2 + "file_bug902156_2.html";
+  var url = HTTPS_TEST_ROOT_2 + "file_bug902156_2.html";
   gTestBrowser.loadURI(url);
 }
 
 function test2A() {
   BrowserTestUtils.browserLoaded(gTestBrowser).then(test2B);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   // Disable Mixed Content Protection for the page (and reload)
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
 }
 
 function test2B() {
   var expected = "Mixed Content Blocker disabled";
-  waitForCondition(
+  BrowserTestUtils.waitForCondition(
     () => content.document.getElementById("mctestdiv").innerHTML == expected,
-    test2C, "Error: Waited too long for mixed script to run in Test 2B");
+    "Error: Waited too long for mixed script to run in Test 2B").then(test2C);
 }
 
 function test2C() {
   var actual = content.document.getElementById("mctestdiv").innerHTML;
   is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 2C");
 
   // The Script loaded after we disabled the page, now we are going to reload the
   // page and see if our decision is persistent
@@ -134,17 +134,17 @@ function test2D() {
   // move on to Test 3
   test3();
 }
 
 // ------------------------ Test 3 ------------------------------
 
 function test3() {
   BrowserTestUtils.browserLoaded(gTestBrowser).then(test3A);
-  var url = gHttpTestRoot1 + "file_bug902156_3.html";
+  var url = HTTPS_TEST_ROOT_1 + "file_bug902156_3.html";
   gTestBrowser.loadURI(url);
 }
 
 function test3A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   // We are done with tests, clean up
   cleanUpAfterTests();
@@ -164,11 +164,11 @@ function test() {
   // Not really sure what this is doing
   var newTab = gBrowser.addTab();
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
   newTab.linkedBrowser.stop()
 
   // Starting Test Number 1:
   BrowserTestUtils.browserLoaded(gTestBrowser).then(test1A);
-  var url = gHttpTestRoot1 + "file_bug902156_1.html";
+  var url = HTTPS_TEST_ROOT_1 + "file_bug902156_1.html";
   gTestBrowser.loadURI(url);
 }
rename from browser/base/content/test/general/browser_bug906190.js
rename to browser/base/content/test/siteIdentity/browser_bug906190.js
--- a/browser/base/content/test/general/browser_bug906190.js
+++ b/browser/base/content/test/siteIdentity/browser_bug906190.js
@@ -5,18 +5,18 @@
  * Tests the persistence of the "disable protection" option for Mixed Content
  * Blocker in child tabs (bug 906190).
  */
 
 requestLongerTimeout(2);
 
 // We use the different urls for testing same origin checks before allowing
 // mixed content on child tabs.
-const gHttpTestRoot1 = "https://test1.example.com/browser/browser/base/content/test/general/";
-const gHttpTestRoot2 = "https://test2.example.com/browser/browser/base/content/test/general/";
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test2.example.com");
 
 /**
  * For all tests, we load the pages over HTTPS and test both:
  *   - |CTRL+CLICK|
  *   - |RIGHT CLICK -> OPEN LINK IN TAB|
  */
 function* doTest(parentTabSpec, childTabSpec, testTaskFn, waitForMetaRefresh) {
   yield BrowserTestUtils.withNewTab({
@@ -30,17 +30,17 @@ function* doTest(parentTabSpec, childTab
 
     // Disable the Mixed Content Blocker for the page, which reloads it.
     let promiseReloaded = BrowserTestUtils.browserLoaded(browser);
     gIdentityHandler.disableMixedContentProtection();
     yield promiseReloaded;
 
     // Wait for the script in the page to update the contents of the test div.
     let testDiv = content.document.getElementById("mctestdiv");
-    yield promiseWaitForCondition(
+    yield BrowserTestUtils.waitForCondition(
       () => testDiv.innerHTML == "Mixed Content Blocker disabled");
 
     // Add the link for the child tab to the page.
     let mainDiv = content.document.createElement("div");
     mainDiv.innerHTML =
       '<p><a id="linkToOpenInNewTab" href="' + childTabSpec + '">Link</a></p>';
     content.document.body.appendChild(mainDiv);
 
@@ -106,18 +106,18 @@ add_task(function* test_initialize() {
 
 /**
  * 1. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a subpage from the same origin in a new tab simulating a click
  *    - Doorhanger should >> NOT << appear anymore!
  */
 add_task(function* test_same_origin() {
-  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
-               gHttpTestRoot1 + "file_bug906190_2.html", function* () {
+  yield doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_1 + "file_bug906190_2.html", function* () {
     // The doorhanger should appear but activeBlocked should be >> NOT << true,
     // because our decision of disabling the mixed content blocker is persistent
     // across tabs.
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: true, activeBlocked: false, passiveLoaded: false,
     });
 
     is(content.document.getElementById("mctestdiv").innerHTML,
@@ -127,18 +127,18 @@ add_task(function* test_same_origin() {
 
 /**
  * 2. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from a different origin in a new tab simulating a click
  *    - Doorhanger >> SHOULD << appear again!
  */
 add_task(function* test_different_origin() {
-  yield doTest(gHttpTestRoot1 + "file_bug906190_2.html",
-               gHttpTestRoot2 + "file_bug906190_2.html", function* () {
+  yield doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_2.html",
+               HTTPS_TEST_ROOT_2 + "file_bug906190_2.html", function* () {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<,
     // because our decision of disabling the mixed content blocker should only
     // persist if pages are from the same domain.
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
     is(content.document.getElementById("mctestdiv").innerHTML,
@@ -150,18 +150,18 @@ add_task(function* test_different_origin
  * 3. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from the same origin using meta-refresh
  *    - Doorhanger should >> NOT << appear again!
  */
 add_task(function* test_same_origin_metarefresh_same_origin() {
   // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
-  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
-               gHttpTestRoot1 + "file_bug906190_3_4.html", function* () {
+  yield doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_1 + "file_bug906190_3_4.html", function* () {
     // The doorhanger should appear but activeBlocked should be >> NOT << true!
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: true, activeBlocked: false, passiveLoaded: false,
     });
 
     is(content.document.getElementById("mctestdiv").innerHTML,
        "Mixed Content Blocker disabled", "OK: Executed mixed script");
   }, true);
@@ -170,18 +170,18 @@ add_task(function* test_same_origin_meta
 /**
  * 4. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from a different origin using meta-refresh
  *    - Doorhanger >> SHOULD << appear again!
  */
 add_task(function* test_same_origin_metarefresh_different_origin() {
-  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
-               gHttpTestRoot2 + "file_bug906190_3_4.html", function* () {
+  yield doTest(HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_2 + "file_bug906190_3_4.html", function* () {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<.
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
     is(content.document.getElementById("mctestdiv").innerHTML,
        "Mixed Content Blocker enabled", "OK: Blocked mixed script");
   }, true);
@@ -190,18 +190,18 @@ add_task(function* test_same_origin_meta
 /**
  * 5. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from the same origin using 302 redirect
  */
 add_task(function* test_same_origin_302redirect_same_origin() {
   // the sjs files returns a 302 redirect- note, same origins
-  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
-               gHttpTestRoot1 + "file_bug906190.sjs", function* () {
+  yield doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_1 + "file_bug906190.sjs", function* () {
     // The doorhanger should appear but activeBlocked should be >> NOT << true.
     // Currently it is >> TRUE << - see follow up bug 914860
     ok(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
        "OK: Mixed Content is NOT being blocked");
 
     is(content.document.getElementById("mctestdiv").innerHTML,
        "Mixed Content Blocker disabled", "OK: Executed mixed script");
   });
@@ -210,31 +210,31 @@ add_task(function* test_same_origin_302r
 /**
  * 6. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from a different origin using 302 redirect
  */
 add_task(function* test_same_origin_302redirect_different_origin() {
   // the sjs files returns a 302 redirect - note, different origins
-  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
-               gHttpTestRoot2 + "file_bug906190.sjs", function* () {
+  yield doTest(HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_2 + "file_bug906190.sjs", function* () {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<.
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
     is(content.document.getElementById("mctestdiv").innerHTML,
        "Mixed Content Blocker enabled", "OK: Blocked mixed script");
   });
 });
 
 /**
  * 7. - Test memory leak issue on redirection error. See Bug 1269426.
  */
 add_task(function* test_bad_redirection() {
   // the sjs files returns a 302 redirect - note, different origins
-  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
-               gHttpTestRoot2 + "file_bug906190.sjs?bad-redirection=1", function* () {
+  yield doTest(HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+               HTTPS_TEST_ROOT_2 + "file_bug906190.sjs?bad-redirection=1", function* () {
     // Nothing to do. Just see if memory leak is reported in the end.
     ok(true, "Nothing to do");
   });
 });
rename from browser/base/content/test/general/browser_csp_block_all_mixedcontent.js
rename to browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js
--- a/browser/base/content/test/general/browser_csp_block_all_mixedcontent.js
+++ b/browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js
@@ -1,17 +1,17 @@
 /*
  * Description of the Test:
  * We load an https page which uses a CSP including block-all-mixed-content.
  * The page tries to load a script over http. We make sure the UI is not
  * influenced when blocking the mixed content. In particular the page
  * should still appear fully encrypted with a green lock.
  */
 
-const PRE_PATH = "https://example.com/browser/browser/base/content/test/general/";
+const PRE_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
 var gTestBrowser = null;
 
 // ------------------------------------------------------
 function cleanUpAfterTests() {
   gBrowser.removeCurrentTab();
   window.focus();
   finish();
 }
rename from browser/base/content/test/general/browser_identity_UI.js
rename to browser/base/content/test/siteIdentity/browser_identity_UI.js
--- a/browser/base/content/test/general/browser_identity_UI.js
+++ b/browser/base/content/test/siteIdentity/browser_identity_UI.js
@@ -94,22 +94,22 @@ function nextTest() {
     }
 
     // Navigate to the next page, which will cause checkResult to fire.
     let spec = gBrowser.selectedBrowser.currentURI.spec;
     if (spec == "about:blank" || spec == gCurrentTest.location) {
       BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.location);
     } else {
       // Open the Control Center and make sure it closes after nav (Bug 1207542).
-      let popupShown = promisePopupShown(gIdentityHandler._identityPopup);
-      gPopupHidden = promisePopupHidden(gIdentityHandler._identityPopup);
+      let popupShown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
+      gPopupHidden = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
       gIdentityHandler._identityBox.click();
       info("Waiting for the Control Center to be shown");
       popupShown.then(() => {
-        is_element_visible(gIdentityHandler._identityPopup, "Control Center is visible");
+        ok(!is_hidden(gIdentityHandler._identityPopup), "Control Center is visible");
         // Show the subview, which is an easy way in automation to reproduce
         // Bug 1207542, where the CC wouldn't close on navigation.
         gBrowser.ownerDocument.querySelector("#identity-popup-security-expander").click();
         BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.location);
       });
     }
   } else {
     gCheckETLD = false;
@@ -135,15 +135,15 @@ function checkResult(event) {
   } else {
     is(gIdentityHandler.getEffectiveHost(), gCurrentTest.effectiveHost, "effectiveHost matches for test " + gTestDesc);
   }
 
   if (gPopupHidden) {
     info("Waiting for the Control Center to hide");
     gPopupHidden.then(() => {
       gPopupHidden = null;
-      is_element_hidden(gIdentityHandler._identityPopup, "control center is hidden");
+      ok(is_hidden(gIdentityHandler._identityPopup), "Control Center is hidden");
       executeSoon(nextTest);
     });
   } else {
     executeSoon(nextTest);
   }
 }
rename from browser/base/content/test/general/browser_insecureLoginForms.js
rename to browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
--- a/browser/base/content/test/general/browser_insecureLoginForms.js
+++ b/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
@@ -36,17 +36,17 @@ add_task(function* test_simple() {
       waitForInsecureLoginFormsStateChange(browser, 2),
     ]);
 
     let { gIdentityHandler } = gBrowser.ownerGlobal;
     gIdentityHandler._identityBox.click();
     document.getElementById("identity-popup-security-expander").click();
 
     if (expectWarning) {
-      is_element_visible(document.getElementById("connection-icon"));
+      ok(is_visible(document.getElementById("connection-icon")), "Connection icon should be visible");
       let connectionIconImage = gBrowser.ownerGlobal
             .getComputedStyle(document.getElementById("connection-icon"))
             .getPropertyValue("list-style-image");
       let securityViewBG = gBrowser.ownerGlobal
             .getComputedStyle(document.getElementById("identity-popup-securityView"))
             .getPropertyValue("background-image");
       let securityContentBG = gBrowser.ownerGlobal
             .getComputedStyle(document.getElementById("identity-popup-security-content"))
rename from browser/base/content/test/general/browser_mcb_redirect.js
rename to browser/base/content/test/siteIdentity/browser_mcb_redirect.js
--- a/browser/base/content/test/general/browser_mcb_redirect.js
+++ b/browser/base/content/test/siteIdentity/browser_mcb_redirect.js
@@ -50,18 +50,18 @@
  *    https inside an https page
  *    - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
  *      but instead we try to use the cached image.
  *    - the image should not load
  */
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
-const gHttpsTestRoot = "https://example.com/browser/browser/base/content/test/general/";
-const gHttpTestRoot = "http://example.com/browser/browser/base/content/test/general/";
+const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
+const HTTP_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 
 var origBlockActive;
 var origBlockDisplay;
 var gTestBrowser = null;
 
 // ------------------------ Helper Functions ---------------------
 
 registerCleanupFunction(function() {
@@ -96,17 +96,17 @@ function waitForCondition(condition, nex
     clearInterval(interval); nextTest();
   };
 }
 
 // ------------------------ Test 1 ------------------------------
 
 function test1() {
   gTestBrowser.addEventListener("load", checkUIForTest1, true);
-  var url = gHttpsTestRoot + "test_mcb_redirect.html"
+  var url = HTTPS_TEST_ROOT + "test_mcb_redirect.html"
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkUIForTest1() {
   gTestBrowser.removeEventListener("load", checkUIForTest1, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
@@ -116,17 +116,17 @@ function checkUIForTest1() {
     test2, "Error: Waited too long for status in Test 1!",
     "OK: Expected result in innerHTML for Test1!");
 }
 
 // ------------------------ Test 2 ------------------------------
 
 function test2() {
   gTestBrowser.addEventListener("load", checkUIForTest2, true);
-  var url = gHttpTestRoot + "test_mcb_redirect.html"
+  var url = HTTP_TEST_ROOT + "test_mcb_redirect.html"
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkUIForTest2() {
   gTestBrowser.removeEventListener("load", checkUIForTest2, true);
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: false});
 
@@ -136,17 +136,17 @@ function checkUIForTest2() {
     test3, "Error: Waited too long for status in Test 2!",
     "OK: Expected result in innerHTML for Test2!");
 }
 
 // ------------------------ Test 3 ------------------------------
 // HTTPS page loading insecure image
 function test3() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest3, true);
-  var url = gHttpsTestRoot + "test_mcb_redirect_image.html"
+  var url = HTTPS_TEST_ROOT + "test_mcb_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest3() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest3, true);
 
   var expected = "image blocked"
   waitForCondition(
@@ -154,17 +154,17 @@ function checkLoadEventForTest3() {
     test4, "Error: Waited too long for status in Test 3!",
     "OK: Expected result in innerHTML for Test3!");
 }
 
 // ------------------------ Test 4 ------------------------------
 // HTTP page loading insecure image
 function test4() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest4, true);
-  var url = gHttpTestRoot + "test_mcb_redirect_image.html"
+  var url = HTTP_TEST_ROOT + "test_mcb_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest4() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest4, true);
 
   var expected = "image loaded"
   waitForCondition(
@@ -177,17 +177,17 @@ function checkLoadEventForTest4() {
 // HTTP page laoding insecure cached image
 // Assuming test 4 succeeded, the image has already been loaded once
 // and hence should be cached per the sjs cache-control header
 // Going into offline mode to ensure we are loading from the cache.
 function test5() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest5, true);
   // Go into offline mode
   Services.io.offline = true;
-  var url = gHttpTestRoot + "test_mcb_redirect_image.html"
+  var url = HTTP_TEST_ROOT + "test_mcb_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest5() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest5, true);
 
   var expected = "image loaded"
   waitForCondition(
@@ -202,17 +202,17 @@ function checkLoadEventForTest5() {
 // HTTPS page loading insecure cached image
 // Assuming test 4 succeeded, the image has already been loaded once
 // and hence should be cached per the sjs cache-control header
 // Going into offline mode to ensure we are loading from the cache.
 function test6() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest6, true);
   // Go into offline mode
   Services.io.offline = true;
-  var url = gHttpsTestRoot + "test_mcb_redirect_image.html"
+  var url = HTTPS_TEST_ROOT + "test_mcb_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest6() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest6, true);
 
   var expected = "image blocked"
   waitForCondition(
@@ -222,17 +222,17 @@ function checkLoadEventForTest6() {
   // Go back online
   Services.io.offline = false;
 }
 
 // ------------------------ Test 7 ------------------------------
 // HTTP page loading insecure image that went through a double redirect
 function test7() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest7, true);
-  var url = gHttpTestRoot + "test_mcb_double_redirect_image.html"
+  var url = HTTP_TEST_ROOT + "test_mcb_double_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest7() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest7, true);
 
   var expected = "image loaded"
   waitForCondition(
@@ -245,17 +245,17 @@ function checkLoadEventForTest7() {
 // HTTP page loading insecure cached image that went through a double redirect
 // Assuming test 7 succeeded, the image has already been loaded once
 // and hence should be cached per the sjs cache-control header
 // Going into offline mode to ensure we are loading from the cache.
 function test8() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest8, true);
   // Go into offline mode
   Services.io.offline = true;
-  var url = gHttpTestRoot + "test_mcb_double_redirect_image.html"
+  var url = HTTP_TEST_ROOT + "test_mcb_double_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest8() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest8, true);
 
   var expected = "image loaded"
   waitForCondition(
@@ -270,17 +270,17 @@ function checkLoadEventForTest8() {
 // HTTPS page loading insecure cached image that went through a double redirect
 // Assuming test 7 succeeded, the image has already been loaded once
 // and hence should be cached per the sjs cache-control header
 // Going into offline mode to ensure we are loading from the cache.
 function test9() {
   gTestBrowser.addEventListener("load", checkLoadEventForTest9, true);
   // Go into offline mode
   Services.io.offline = true;
-  var url = gHttpsTestRoot + "test_mcb_double_redirect_image.html"
+  var url = HTTPS_TEST_ROOT + "test_mcb_double_redirect_image.html";
   gTestBrowser.contentWindow.location = url;
 }
 
 function checkLoadEventForTest9() {
   gTestBrowser.removeEventListener("load", checkLoadEventForTest9, true);
 
   var expected = "image blocked"
   waitForCondition(
@@ -298,17 +298,19 @@ function test() {
   waitForExplicitFinish();
 
   // Store original preferences so we can restore settings after testing
   origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
   origBlockDisplay = Services.prefs.getBoolPref(PREF_DISPLAY);
   Services.prefs.setBoolPref(PREF_ACTIVE, true);
   Services.prefs.setBoolPref(PREF_DISPLAY, true);
 
-  pushPrefs(["dom.ipc.processCount", 1]).then(() => {
+  // TODO (Bug 1301015): This was forced into single process mode in
+  // bug 1301340, need to find a real fix.
+  SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, () => {
     var newTab = gBrowser.addTab();
     gBrowser.selectedTab = newTab;
     gTestBrowser = gBrowser.selectedBrowser;
     newTab.linkedBrowser.stop();
 
     executeSoon(test1);
   });
 }
rename from browser/base/content/test/general/browser_mixedContentFramesOnHttp.js
rename to browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
--- a/browser/base/content/test/general/browser_mixedContentFramesOnHttp.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
@@ -5,28 +5,23 @@
  * Test for Bug 1182551 -
  *
  * This test has a top level HTTP page with an HTTPS iframe.  The HTTPS iframe
  * includes an HTTP image.  We check that the top level security state is
  * STATE_IS_INSECURE.  The mixed content from the iframe shouldn't "upgrade"
  * the HTTP top level page to broken HTTPS.
  */
 
-const gHttpTestUrl = "http://example.com/browser/browser/base/content/test/general/file_mixedContentFramesOnHttp.html";
-
-var gTestBrowser = null;
+const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com") + "file_mixedContentFramesOnHttp.html";
 
 add_task(function *() {
   yield SpecialPowers.pushPrefEnv({
     "set": [
       ["security.mixed_content.block_active_content", true],
       ["security.mixed_content.block_display_content", false]
     ]});
 
-  let url = gHttpTestUrl
-  yield BrowserTestUtils.withNewTab({gBrowser, url}, function*() {
-    gTestBrowser = gBrowser.selectedBrowser;
-    // check security state is insecure
-    isSecurityState("insecure");
-    assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: true});
+  yield BrowserTestUtils.withNewTab(TEST_URL, function*(browser) {
+    isSecurityState(browser, "insecure");
+    assertMixedContentBlockingState(browser, {activeLoaded: false, activeBlocked: false, passiveLoaded: true});
   });
 });
 
rename from browser/base/content/test/general/browser_mixedContentFromOnunload.js
rename to browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
--- a/browser/base/content/test/general/browser_mixedContentFromOnunload.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
@@ -3,45 +3,43 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Tests for Bug 947079 - Fix bug in nsSecureBrowserUIImpl that sets the wrong
  * security state on a page because of a subresource load that is not on the
  * same page.
  */
 
 // We use different domains for each test and for navigation within each test
-const gHttpTestRoot1 = "http://example.com/browser/browser/base/content/test/general/";
-const gHttpsTestRoot1 = "https://test1.example.com/browser/browser/base/content/test/general/";
-const gHttpTestRoot2 = "http://example.net/browser/browser/base/content/test/general/";
-const gHttpsTestRoot2 = "https://test2.example.com/browser/browser/base/content/test/general/";
+const HTTP_TEST_ROOT_1 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
+const HTTP_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.net");
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test2.example.com");
 
-var gTestBrowser = null;
 add_task(function *() {
-  let url = gHttpTestRoot1 + "file_mixedContentFromOnunload.html";
-  yield BrowserTestUtils.withNewTab({gBrowser, url}, function*() {
+  let url = HTTP_TEST_ROOT_1 + "file_mixedContentFromOnunload.html";
+  yield BrowserTestUtils.withNewTab(url, function*(browser) {
     yield SpecialPowers.pushPrefEnv({
       "set": [
         ["security.mixed_content.block_active_content", true],
         ["security.mixed_content.block_display_content", false]
       ]
     });
-  gTestBrowser = gBrowser.selectedBrowser;
-  // Navigation from an http page to a https page with no mixed content
-  // The http page loads an http image on unload
-  url = gHttpsTestRoot1 + "file_mixedContentFromOnunload_test1.html";
-  yield BrowserTestUtils.loadURI(gTestBrowser, url);
-  yield BrowserTestUtils.browserLoaded(gTestBrowser);
-  // check security state.  Since current url is https and doesn't have any
-  // mixed content resources, we expect it to be secure.
-  isSecurityState("secure");
-  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: false});
-  // Navigation from an http page to a https page that has mixed display content
-  // The https page loads an http image on unload
-  url = gHttpTestRoot2 + "file_mixedContentFromOnunload.html";
-  yield BrowserTestUtils.loadURI(gTestBrowser, url);
-  yield BrowserTestUtils.browserLoaded(gTestBrowser);
-  url = gHttpsTestRoot2 + "file_mixedContentFromOnunload_test2.html";
-  yield BrowserTestUtils.loadURI(gTestBrowser, url);
-  yield BrowserTestUtils.browserLoaded(gTestBrowser);
-  isSecurityState("broken");
-  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: true});
+    // Navigation from an http page to a https page with no mixed content
+    // The http page loads an http image on unload
+    url = HTTPS_TEST_ROOT_1 + "file_mixedContentFromOnunload_test1.html";
+    yield BrowserTestUtils.loadURI(browser, url);
+    yield BrowserTestUtils.browserLoaded(browser);
+    // check security state.  Since current url is https and doesn't have any
+    // mixed content resources, we expect it to be secure.
+    isSecurityState(browser, "secure");
+    assertMixedContentBlockingState(browser, {activeLoaded: false, activeBlocked: false, passiveLoaded: false});
+    // Navigation from an http page to a https page that has mixed display content
+    // The https page loads an http image on unload
+    url = HTTP_TEST_ROOT_2 + "file_mixedContentFromOnunload.html";
+    yield BrowserTestUtils.loadURI(browser, url);
+    yield BrowserTestUtils.browserLoaded(browser);
+    url = HTTPS_TEST_ROOT_2 + "file_mixedContentFromOnunload_test2.html";
+    yield BrowserTestUtils.loadURI(browser, url);
+    yield BrowserTestUtils.browserLoaded(browser);
+    isSecurityState(browser, "broken");
+    assertMixedContentBlockingState(browser, {activeLoaded: false, activeBlocked: false, passiveLoaded: true});
   });
 });
rename from browser/base/content/test/general/browser_mixed_content_cert_override.js
rename to browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
--- a/browser/base/content/test/general/browser_mixed_content_cert_override.js
+++ b/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
@@ -1,15 +1,15 @@
 /*
  * Bug 1253771 - check mixed content blocking in combination with overriden certificates
  */
 
 "use strict";
 
-const MIXED_CONTENT_URL = "https://self-signed.example.com/browser/browser/base/content/test/general/test-mixedcontent-securityerrors.html";
+const MIXED_CONTENT_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://self-signed.example.com") + "test-mixedcontent-securityerrors.html";
 
 function getConnectionState() {
   return document.getElementById("identity-popup").getAttribute("connection");
 }
 
 function getPopupContentVerifier() {
   return document.getElementById("identity-popup-content-verifier");
 }
rename from browser/base/content/test/general/browser_mixedcontent_securityflags.js
rename to browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
--- a/browser/base/content/test/general/browser_mixedcontent_securityflags.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
@@ -5,17 +5,17 @@
 // makes sure that the mixed content flags on the docshell are set correctly.
 // * Using default about:config prefs (mixed active blocked, mixed display
 //   loaded) we load the page and check the flags.
 // * We change the about:config prefs (mixed active blocked, mixed display
 //   blocked), reload the page, and check the flags again.
 // * We override protection so all mixed content can load and check the
 //   flags again.
 
-const TEST_URI = "https://example.com/browser/browser/base/content/test/general/test-mixedcontent-securityerrors.html";
+const TEST_URI = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test-mixedcontent-securityerrors.html";
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 var gTestBrowser = null;
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.clearUserPref(PREF_DISPLAY);
   Services.prefs.clearUserPref(PREF_ACTIVE);
rename from browser/base/content/test/general/browser_no_mcb_on_http_site.js
rename to browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
--- a/browser/base/content/test/general/browser_no_mcb_on_http_site.js
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
@@ -21,29 +21,29 @@
 *
  * Since the top-domain is >> NOT << served using https, the MCB
  * should >> NOT << trigger a warning.
  */
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
 
-const gHttpTestRoot = "http://example.com/browser/browser/base/content/test/general/";
+const HTTP_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 
 var gTestBrowser = null;
 
 function cleanUpAfterTests() {
   gBrowser.removeCurrentTab();
   window.focus();
 }
 
 add_task(function* init() {
   yield SpecialPowers.pushPrefEnv({ set: [[ PREF_ACTIVE, true ],
                                           [ PREF_DISPLAY, true ]] });
-  let url = gHttpTestRoot + "test_no_mcb_on_http_site_img.html";
+  let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_img.html";
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url)
   gTestBrowser = tab.linkedBrowser;
 });
 
 // ------------- TEST 1 -----------------------------------------
 
 add_task(function* test1() {
   let expected = "Verifying MCB does not trigger warning/error for an http page ";
@@ -54,17 +54,17 @@ add_task(function* test1() {
       () => content.document.getElementById("testDiv").innerHTML == condition,
       "Waited too long for status in Test 1!");
   });
 
   // Explicit OKs needed because the harness requires at least one call to ok.
   ok(true, "test 1 passed");
 
   // set up test 2
-  let url = gHttpTestRoot + "test_no_mcb_on_http_site_font.html";
+  let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_font.html";
   BrowserTestUtils.loadURI(gTestBrowser, url);
   yield BrowserTestUtils.browserLoaded(gTestBrowser);
 });
 
 // ------------- TEST 2 -----------------------------------------
 
 add_task(function* test2() {
   let expected = "Verifying MCB does not trigger warning/error for an http page ";
@@ -74,17 +74,17 @@ add_task(function* test2() {
     yield ContentTaskUtils.waitForCondition(
       () => content.document.getElementById("testDiv").innerHTML == condition,
       "Waited too long for status in Test 2!");
   });
 
   ok(true, "test 2 passed");
 
   // set up test 3
-  let url = gHttpTestRoot + "test_no_mcb_on_http_site_font2.html";
+  let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_font2.html";
   BrowserTestUtils.loadURI(gTestBrowser, url);
   yield BrowserTestUtils.browserLoaded(gTestBrowser);
 });
 
 // ------------- TEST 3 -----------------------------------------
 
 add_task(function* test3() {
   let expected = "Verifying MCB does not trigger warning/error for an http page "
rename from browser/base/content/test/general/file_bug1045809_1.html
rename to browser/base/content/test/siteIdentity/file_bug1045809_1.html
--- a/browser/base/content/test/general/file_bug1045809_1.html
+++ b/browser/base/content/test/siteIdentity/file_bug1045809_1.html
@@ -1,7 +1,7 @@
 <html>
   <head>
   </head>
   <body>
-    <iframe src="http://test1.example.com/browser/browser/base/content/test/general/file_bug1045809_2.html"></iframe>
+    <iframe src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug1045809_2.html"></iframe>
   </body>
 </html>
rename from browser/base/content/test/general/file_bug1045809_2.html
rename to browser/base/content/test/siteIdentity/file_bug1045809_2.html
rename from browser/base/content/test/general/file_bug822367_1.html
rename to browser/base/content/test/siteIdentity/file_bug822367_1.html
--- a/browser/base/content/test/general/file_bug822367_1.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_1.html
@@ -7,12 +7,12 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test 1 for Bug 822367</title>
 </head>
 <body>
   <div id="testContent">
     <p id="p1"></p>
   </div>
-  <script src="http://example.com/browser/browser/base/content/test/general/file_bug822367_1.js">
+  <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
   </script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug822367_1.js
rename to browser/base/content/test/siteIdentity/file_bug822367_1.js
rename from browser/base/content/test/general/file_bug822367_2.html
rename to browser/base/content/test/siteIdentity/file_bug822367_2.html
rename from browser/base/content/test/general/file_bug822367_3.html
rename to browser/base/content/test/siteIdentity/file_bug822367_3.html
--- a/browser/base/content/test/general/file_bug822367_3.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_3.html
@@ -16,12 +16,12 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
   </script>
 </head>
 <body>
   <div id="testContent">
     <p id="p1"></p>
     <img src="http://example.com/tests/image/test/mochitest/blue.png" onload="foo()">
   </div>
-  <script src="http://example.com/browser/browser/base/content/test/general/file_bug822367_1.js">
+  <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
   </script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug822367_4.html
rename to browser/base/content/test/siteIdentity/file_bug822367_4.html
--- a/browser/base/content/test/general/file_bug822367_4.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4.html
@@ -7,12 +7,12 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test 4 for Bug 822367</title>
 </head>
 <body>
   <div id="testContent">
     <p id="p1"></p>
   </div>
-  <script src="http://example.com/browser/browser/base/content/test/general/file_bug822367_4.js">
+  <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4.js">
   </script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug822367_4.js
rename to browser/base/content/test/siteIdentity/file_bug822367_4.js
--- a/browser/base/content/test/general/file_bug822367_4.js
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4.js
@@ -1,1 +1,1 @@
-document.location = "https://example.com/browser/browser/base/content/test/general/file_bug822367_4B.html";
+document.location = "https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4B.html";
rename from browser/base/content/test/general/file_bug822367_4B.html
rename to browser/base/content/test/siteIdentity/file_bug822367_4B.html
--- a/browser/base/content/test/general/file_bug822367_4B.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4B.html
@@ -7,12 +7,12 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test 4B Location Change for Bug 822367</title>
 </head>
 <body>
   <div id="testContent">
     <p id="p1"></p>
   </div>
-  <script src="http://example.com/browser/browser/base/content/test/general/file_bug822367_1.js">
+  <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
   </script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug822367_5.html
rename to browser/base/content/test/siteIdentity/file_bug822367_5.html
--- a/browser/base/content/test/general/file_bug822367_5.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_5.html
@@ -5,17 +5,17 @@ Test 5 for Mixed Content Blocker User Ov
 https://bugzilla.mozilla.org/show_bug.cgi?id=822367
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 5 for Bug 822367</title>
   <script>
     function createDoc() {
       var doc = document.open("text/html", "replace");
-      doc.write('<!DOCTYPE html><html><body><p id="p1">This is some content</p><script src="http://example.com/browser/browser/base/content/test/general/file_bug822367_1.js">\<\/script\>\<\/body>\<\/html>');
+      doc.write('<!DOCTYPE html><html><body><p id="p1">This is some content</p><script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">\<\/script\>\<\/body>\<\/html>');
       doc.close();
     }
   </script>
 </head>
 <body>
   <div id="testContent">
     <img src="https://example.com/tests/image/test/mochitest/blue.png" onload="createDoc()">
   </div>
rename from browser/base/content/test/general/file_bug822367_6.html
rename to browser/base/content/test/siteIdentity/file_bug822367_6.html
--- a/browser/base/content/test/general/file_bug822367_6.html
+++ b/browser/base/content/test/siteIdentity/file_bug822367_6.html
@@ -5,12 +5,12 @@ Test 6 for Mixed Content Blocker User Ov
 https://bugzilla.mozilla.org/show_bug.cgi?id=822367
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 6 for Bug 822367</title>
 </head>
 <body>
   <div id="testContent">
-    <iframe name="f1" id="f1" src="https://example.com/browser/browser/base/content/test/general/file_bug822367_5.html"></iframe>
+    <iframe name="f1" id="f1" src="https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_5.html"></iframe>
   </div>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug902156.js
rename to browser/base/content/test/siteIdentity/file_bug902156.js
rename from browser/base/content/test/general/file_bug902156_1.html
rename to browser/base/content/test/siteIdentity/file_bug902156_1.html
--- a/browser/base/content/test/general/file_bug902156_1.html
+++ b/browser/base/content/test/siteIdentity/file_bug902156_1.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=902156
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 1 for Bug 902156</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <script src="http://test1.example.com/browser/browser/base/content/test/general/file_bug902156.js" ></script>
+  <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug902156_2.html
rename to browser/base/content/test/siteIdentity/file_bug902156_2.html
--- a/browser/base/content/test/general/file_bug902156_2.html
+++ b/browser/base/content/test/siteIdentity/file_bug902156_2.html
@@ -5,13 +5,13 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=902156
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 2 for Bug 902156</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <a href="https://test2.example.com/browser/browser/base/content/test/general/file_bug902156_1.html"
+  <a href="https://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156_1.html"
      id="mctestlink" target="_top">Go to http site</a>
-  <script src="http://test2.example.com/browser/browser/base/content/test/general/file_bug902156.js" ></script>
+  <script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug902156_3.html
rename to browser/base/content/test/siteIdentity/file_bug902156_3.html
--- a/browser/base/content/test/general/file_bug902156_3.html
+++ b/browser/base/content/test/siteIdentity/file_bug902156_3.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=902156
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 3 for Bug 902156</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <script src="http://test1.example.com/browser/browser/base/content/test/general/file_bug902156.js" ></script>
+  <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug906190.js
rename to browser/base/content/test/siteIdentity/file_bug906190.js
rename from browser/base/content/test/general/file_bug906190.sjs
rename to browser/base/content/test/siteIdentity/file_bug906190.sjs
--- a/browser/base/content/test/general/file_bug906190.sjs
+++ b/browser/base/content/test/siteIdentity/file_bug906190.sjs
@@ -1,11 +1,11 @@
 function handleRequest(request, response) {
   var page = "<!DOCTYPE html><html><body>bug 906190</body></html>";
-  var path = "https://test1.example.com/browser/browser/base/content/test/general/";
+  var path = "https://test1.example.com/browser/browser/base/content/test/siteIdentity/";
   var url;
 
   if (request.queryString.includes('bad-redirection=1')) {
     url = path + "this_page_does_not_exist.html";
   } else {
     url = path + "file_bug906190_redirected.html";
   }
 
rename from browser/base/content/test/general/file_bug906190_1.html
rename to browser/base/content/test/siteIdentity/file_bug906190_1.html
--- a/browser/base/content/test/general/file_bug906190_1.html
+++ b/browser/base/content/test/siteIdentity/file_bug906190_1.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=906190
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 1 for Bug 906190</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <script src="http://test1.example.com/browser/browser/base/content/test/general/file_bug906190.js" ></script>
+  <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug906190_2.html
rename to browser/base/content/test/siteIdentity/file_bug906190_2.html
--- a/browser/base/content/test/general/file_bug906190_2.html
+++ b/browser/base/content/test/siteIdentity/file_bug906190_2.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=906190
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 2 for Bug 906190</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <script src="http://test2.example.com/browser/browser/base/content/test/general/file_bug906190.js" ></script>
+  <script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug906190_3_4.html
rename to browser/base/content/test/siteIdentity/file_bug906190_3_4.html
--- a/browser/base/content/test/general/file_bug906190_3_4.html
+++ b/browser/base/content/test/siteIdentity/file_bug906190_3_4.html
@@ -1,14 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <!--
   Test 3 and 4 for Bug 906190 - See file browser_bug902156.js for description.
   https://bugzilla.mozilla.org/show_bug.cgi?id=906190
 -->
 <head>
   <meta charset="utf-8">
-  <meta http-equiv="refresh" content="0; url=https://test1.example.com/browser/browser/base/content/test/general/file_bug906190_redirected.html">
+  <meta http-equiv="refresh" content="0; url=https://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190_redirected.html">
   <title>Test 3 and 4 for Bug 906190</title>
 </head>
 <body>
 </body>
 </html>
rename from browser/base/content/test/general/file_bug906190_redirected.html
rename to browser/base/content/test/siteIdentity/file_bug906190_redirected.html
--- a/browser/base/content/test/general/file_bug906190_redirected.html
+++ b/browser/base/content/test/siteIdentity/file_bug906190_redirected.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=906190
 -->
 <head>
   <meta charset="utf-8">
   <title>Redirected Page for Bug 906190</title>
 </head>
 <body>
   <div id="mctestdiv">Mixed Content Blocker enabled</div>
-  <script src="http://test1.example.com/browser/browser/base/content/test/general/file_bug906190.js" ></script>
+  <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_csp_block_all_mixedcontent.html
rename to browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html
--- a/browser/base/content/test/general/file_csp_block_all_mixedcontent.html
+++ b/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html
@@ -1,9 +1,11 @@
 <!DOCTYPE HTML>
-<html><head><meta charset="utf-8">
-<title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+  <meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
 </head>
-<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
 <body>
-<script src="http://example.com/browser/browser/base/content/test/general/file_csp_block_all_mixedcontent.js"/>
+  <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js"></script>
 </body>
 </html>
rename from browser/base/content/test/general/file_csp_block_all_mixedcontent.js
rename to browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js
rename from browser/base/content/test/general/file_mixedContentFramesOnHttp.html
rename to browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html
--- a/browser/base/content/test/general/file_mixedContentFramesOnHttp.html
+++ b/browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html
@@ -4,11 +4,11 @@
 Test for https://bugzilla.mozilla.org/show_bug.cgi?id=1182551
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1182551</title>
 </head>
 <body>
   <p>Test for Bug 1182551.  This is an HTTP top level page.  We include an HTTPS iframe that loads mixed passive content.</p>
-  <iframe src="https://example.org/browser/browser/base/content/test/general/file_mixedPassiveContent.html"></iframe>
+  <iframe src="https://example.org/browser/browser/base/content/test/siteIdentity/file_mixedPassiveContent.html"></iframe>
 </body>
 </html>
rename from browser/base/content/test/general/file_mixedContentFromOnunload.html
rename to browser/base/content/test/siteIdentity/file_mixedContentFromOnunload.html
rename from browser/base/content/test/general/file_mixedContentFromOnunload_test1.html
rename to browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test1.html
rename from browser/base/content/test/general/file_mixedContentFromOnunload_test2.html
rename to browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test2.html
rename from browser/base/content/test/general/file_mixedPassiveContent.html
rename to browser/base/content/test/siteIdentity/file_mixedPassiveContent.html
--- a/browser/base/content/test/siteIdentity/head.js
+++ b/browser/base/content/test/siteIdentity/head.js
@@ -1,6 +1,340 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
+
+function is_hidden(element) {
+  var style = element.ownerGlobal.getComputedStyle(element);
+  if (style.display == "none")
+    return true;
+  if (style.visibility != "visible")
+    return true;
+  if (style.display == "-moz-popup")
+    return ["hiding", "closed"].indexOf(element.state) != -1;
+
+  // Hiding a parent element will hide all its children
+  if (element.parentNode != element.ownerDocument)
+    return is_hidden(element.parentNode);
+
+  return false;
+}
+
+function is_visible(element) {
+  var style = element.ownerGlobal.getComputedStyle(element);
+  if (style.display == "none")
+    return false;
+  if (style.visibility != "visible")
+    return false;
+  if (style.display == "-moz-popup" && element.state != "open")
+    return false;
+
+  // Hiding a parent element will hide all its children
+  if (element.parentNode != element.ownerDocument)
+    return is_visible(element.parentNode);
+
+  return true;
+}
+
+/**
+ * Returns a Promise that resolves once a new tab has been opened in
+ * a xul:tabbrowser.
+ *
+ * @param aTabBrowser
+ *        The xul:tabbrowser to monitor for a new tab.
+ * @return {Promise}
+ *        Resolved when the new tab has been opened.
+ * @resolves to the TabOpen event that was fired.
+ * @rejects Never.
+ */
+function waitForNewTabEvent(aTabBrowser) {
+  return BrowserTestUtils.waitForEvent(aTabBrowser.tabContainer, "TabOpen");
+}
+
+/**
+ * Waits for a load (or custom) event to finish in a given tab. If provided
+ * load an uri into the tab.
+ *
+ * @param tab
+ *        The tab to load into.
+ * @param [optional] url
+ *        The url to load, or the current url.
+ * @return {Promise} resolved when the event is handled.
+ * @resolves to the received event
+ * @rejects if a valid load event is not received within a meaningful interval
+ */
+function promiseTabLoadEvent(tab, url) {
+  info("Wait tab event: load");
+
+  function handle(loadedUrl) {
+    if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
+      info(`Skipping spurious load event for ${loadedUrl}`);
+      return false;
+    }
+
+    info("Tab event received: load");
+    return true;
+  }
+
+  let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
+
+  if (url)
+    BrowserTestUtils.loadURI(tab.linkedBrowser, url);
+
+  return loaded;
+}
+
+// Compares the security state of the page with what is expected
+function isSecurityState(browser, expectedState) {
+  let ui = browser.securityUI;
+  if (!ui) {
+    ok(false, "No security UI to get the security state");
+    return;
+  }
+
+  const wpl = Components.interfaces.nsIWebProgressListener;
+
+  // determine the security state
+  let isSecure = ui.state & wpl.STATE_IS_SECURE;
+  let isBroken = ui.state & wpl.STATE_IS_BROKEN;
+  let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
+
+  let actualState;
+  if (isSecure && !(isBroken || isInsecure)) {
+    actualState = "secure";
+  } else if (isBroken && !(isSecure || isInsecure)) {
+    actualState = "broken";
+  } else if (isInsecure && !(isSecure || isBroken)) {
+    actualState = "insecure";
+  } else {
+    actualState = "unknown";
+  }
+
+  is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
+}
+
+/**
+ * Test the state of the identity box and control center to make
+ * sure they are correctly showing the expected mixed content states.
+ *
+ * @note The checks are done synchronously, but new code should wait on the
+ *       returned Promise object to ensure the identity panel has closed.
+ *       Bug 1221114 is filed to fix the existing code.
+ *
+ * @param tabbrowser
+ * @param Object states
+ *        MUST include the following properties:
+ *        {
+ *           activeLoaded: true|false,
+ *           activeBlocked: true|false,
+ *           passiveLoaded: true|false,
+ *        }
+ *
+ * @return {Promise}
+ * @resolves When the operation has finished and the identity panel has closed.
+ */
+function assertMixedContentBlockingState(tabbrowser, states = {}) {
+  if (!tabbrowser || !("activeLoaded" in states) ||
+      !("activeBlocked" in states) || !("passiveLoaded" in states)) {
+    throw new Error("assertMixedContentBlockingState requires a browser and a states object");
+  }
+
+  let {passiveLoaded, activeLoaded, activeBlocked} = states;
+  let {gIdentityHandler} = tabbrowser.ownerGlobal;
+  let doc = tabbrowser.ownerDocument;
+  let identityBox = gIdentityHandler._identityBox;
+  let classList = identityBox.classList;
+  let connectionIcon = doc.getElementById("connection-icon");
+  let connectionIconImage = tabbrowser.ownerGlobal.getComputedStyle(connectionIcon).
+                         getPropertyValue("list-style-image");
+
+  let stateSecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
+  let stateBroken = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
+  let stateInsecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
+  let stateActiveBlocked = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
+  let stateActiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
+  let statePassiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
+
+  is(activeBlocked, !!stateActiveBlocked, "Expected state for activeBlocked matches UI state");
+  is(activeLoaded, !!stateActiveLoaded, "Expected state for activeLoaded matches UI state");
+  is(passiveLoaded, !!statePassiveLoaded, "Expected state for passiveLoaded matches UI state");
+
+  if (stateInsecure) {
+    // HTTP request, there should be no MCB classes for the identity box and the non secure icon
+    // should always be visible regardless of MCB state.
+    ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
+    ok(is_hidden(connectionIcon), "connection icon should be hidden");
+
+    ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
+    ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
+    ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
+    ok(!classList.contains("mixedDisplayContentLoadedActiveBlocked"), "No MCB icon on HTTP page");
+  } else {
+    // Make sure the identity box UI has the correct mixedcontent states and icons
+    is(classList.contains("mixedActiveContent"), activeLoaded,
+        "identityBox has expected class for activeLoaded");
+    is(classList.contains("mixedActiveBlocked"), activeBlocked && !passiveLoaded,
+        "identityBox has expected class for activeBlocked && !passiveLoaded");
+    is(classList.contains("mixedDisplayContent"), passiveLoaded && !(activeLoaded || activeBlocked),
+       "identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)");
+    is(classList.contains("mixedDisplayContentLoadedActiveBlocked"), passiveLoaded && activeBlocked,
+       "identityBox has expected class for passiveLoaded && activeBlocked");
+
+    ok(!is_hidden(connectionIcon), "connection icon should be visible");
+    if (activeLoaded) {
+      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-active-loaded.svg#icon\")",
+        "Using active loaded icon");
+    }
+    if (activeBlocked && !passiveLoaded) {
+      is(connectionIconImage, "url(\"chrome://browser/skin/connection-secure.svg\")",
+        "Using active blocked icon");
+    }
+    if (passiveLoaded && !(activeLoaded || activeBlocked)) {
+      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
+        "Using passive loaded icon");
+    }
+    if (passiveLoaded && activeBlocked) {
+      is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
+        "Using active blocked and passive loaded icon");
+    }
+  }
+
+  // Make sure the identity popup has the correct mixedcontent states
+  gIdentityHandler._identityBox.click();
+  let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
+  let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
+
+  is(popupAttr.includes("active-loaded"), activeLoaded,
+      "identity-popup has expected attr for activeLoaded");
+  is(bodyAttr.includes("active-loaded"), activeLoaded,
+      "securityView-body has expected attr for activeLoaded");
+
+  is(popupAttr.includes("active-blocked"), activeBlocked,
+      "identity-popup has expected attr for activeBlocked");
+  is(bodyAttr.includes("active-blocked"), activeBlocked,
+      "securityView-body has expected attr for activeBlocked");
+
+  is(popupAttr.includes("passive-loaded"), passiveLoaded,
+      "identity-popup has expected attr for passiveLoaded");
+  is(bodyAttr.includes("passive-loaded"), passiveLoaded,
+      "securityView-body has expected attr for passiveLoaded");
+
+  // Make sure the correct icon is visible in the Control Center.
+  // This logic is controlled with CSS, so this helps prevent regressions there.
+  let securityView = doc.getElementById("identity-popup-securityView");
+  let securityViewBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
+                       getPropertyValue("background-image");
+  let securityContentBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
+                          getPropertyValue("background-image");
+
+  if (stateInsecure) {
+    is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
+      "CC using 'not secure' icon");
+    is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
+      "CC using 'not secure' icon");
+  }
+
+  if (stateSecure) {
+    is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
+      "CC using secure icon");
+    is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
+      "CC using secure icon");
+  }
+
+  if (stateBroken) {
+    if (activeLoaded) {
+      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
+        "CC using active loaded icon");
+      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
+        "CC using active loaded icon");
+    } else if (activeBlocked || passiveLoaded) {
+      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
+        "CC using degraded icon");
+      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
+        "CC using degraded icon");
+    } else {
+      // There is a case here with weak ciphers, but no bc tests are handling this yet.
+      is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
+        "CC using degraded icon");
+      is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
+        "CC using degraded icon");
+    }
+  }
+
+  if (activeLoaded || activeBlocked || passiveLoaded) {
+    doc.getElementById("identity-popup-security-expander").click();
+    is(Array.filter(doc.querySelectorAll("[observes=identity-popup-mcb-learn-more]"),
+                    element => !is_hidden(element)).length, 1,
+       "The 'Learn more' link should be visible once.");
+  }
+
+  gIdentityHandler._identityPopup.hidden = true;
+
+  // Wait for the panel to be closed before continuing. The promisePopupHidden
+  // function cannot be used because it's unreliable unless promisePopupShown is
+  // also called before closing the panel. This cannot be done until all callers
+  // are made asynchronous (bug 1221114).
+  return new Promise(resolve => executeSoon(resolve));
+}
+
+function* loadBadCertPage(url) {
+  const EXCEPTION_DIALOG_URI = "chrome://pippki/content/exceptionDialog.xul";
+  let exceptionDialogResolved = new Promise(function(resolve) {
+    // When the certificate exception dialog has opened, click the button to add
+    // an exception.
+    let certExceptionDialogObserver = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic == "cert-exception-ui-ready") {
+          Services.obs.removeObserver(this, "cert-exception-ui-ready");
+          let certExceptionDialog = getCertExceptionDialog(EXCEPTION_DIALOG_URI);
+          ok(certExceptionDialog, "found exception dialog");
+          executeSoon(function() {
+            certExceptionDialog.documentElement.getButton("extra1").click();
+            resolve();
+          });
+        }
+      }
+    };
+
+    Services.obs.addObserver(certExceptionDialogObserver,
+                             "cert-exception-ui-ready", false);
+  });
+
+  let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+  yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
+  yield loaded;
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+    content.document.getElementById("exceptionDialogButton").click();
+  });
+  yield exceptionDialogResolved;
+  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+}
+
+// Utility function to get a handle on the certificate exception dialog.
+// Modified from toolkit/components/passwordmgr/test/prompt_common.js
+function getCertExceptionDialog(aLocation) {
+  let enumerator = Services.wm.getXULWindowEnumerator(null);
+
+  while (enumerator.hasMoreElements()) {
+    let win = enumerator.getNext();
+    let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
+
+    let containedDocShells = windowDocShell.getDocShellEnumerator(
+                                      Ci.nsIDocShellTreeItem.typeChrome,
+                                      Ci.nsIDocShell.ENUMERATE_FORWARDS);
+    while (containedDocShells.hasMoreElements()) {
+      // Get the corresponding document for this docshell
+      let childDocShell = containedDocShells.getNext();
+      let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
+                                  .contentViewer
+                                  .DOMDocument;
+
+      if (childDoc.location.href == aLocation) {
+        return childDoc;
+      }
+    }
+  }
+  return undefined;
+}
rename from browser/base/content/test/general/insecure_opener.html
rename to browser/base/content/test/siteIdentity/insecure_opener.html
rename from browser/base/content/test/general/test-mixedcontent-securityerrors.html
rename to browser/base/content/test/siteIdentity/test-mixedcontent-securityerrors.html
rename from browser/base/content/test/general/test_bug435035.html
rename to browser/base/content/test/siteIdentity/test_bug435035.html
--- a/browser/base/content/test/general/test_bug435035.html
+++ b/browser/base/content/test/siteIdentity/test_bug435035.html
@@ -1,1 +1,1 @@
-<img src="http://example.com/browser/browser/base/content/test/general/moz.png">
+<img src="http://example.com/browser/browser/base/content/test/siteIdentity/moz.png">
rename from browser/base/content/test/general/test_mcb_double_redirect_image.html
rename to browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html
--- a/browser/base/content/test/general/test_mcb_double_redirect_image.html
+++ b/browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html
@@ -13,11 +13,11 @@
     }
     function image_blocked() {
       document.getElementById("mctestdiv").innerHTML = "image blocked";
     }
   </script>
 </head>
 <body>
   <div id="mctestdiv"></div>
-  <img src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_redirect_http_sjs" onload="image_loaded()" onerror="image_blocked()" ></image>
+  <img src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_http_sjs" onload="image_loaded()" onerror="image_blocked()" ></image>
 </body>
 </html>
rename from browser/base/content/test/general/test_mcb_redirect.html
rename to browser/base/content/test/siteIdentity/test_mcb_redirect.html
--- a/browser/base/content/test/general/test_mcb_redirect.html
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect.html
@@ -5,11 +5,11 @@
   https://bugzilla.mozilla.org/show_bug.cgi?id=418354
 -->
 <head>
   <meta charset="utf-8">
   <title>Bug 418354</title>
 </head>
 <body>
   <div id="mctestdiv">script blocked</div>
-  <script src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?script" ></script>
+  <script src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?script" ></script>
 </body>
 </html>
rename from browser/base/content/test/general/test_mcb_redirect.js
rename to browser/base/content/test/siteIdentity/test_mcb_redirect.js
rename from browser/base/content/test/general/test_mcb_redirect.sjs
rename to browser/base/content/test/siteIdentity/test_mcb_redirect.sjs
--- a/browser/base/content/test/general/test_mcb_redirect.sjs
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs
@@ -1,19 +1,19 @@
 function handleRequest(request, response) {
   var page = "<!DOCTYPE html><html><body>bug 418354 and bug 1082837</body></html>";
 
   if (request.queryString === "script") { 
-    var redirect = "http://example.com/browser/browser/base/content/test/general/test_mcb_redirect.js";
+    var redirect = "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.js";
     response.setHeader("Cache-Control", "no-cache", false);
   } else if (request.queryString === "image_http") {
     var redirect = "http://example.com/tests/image/test/mochitest/blue.png";
     response.setHeader("Cache-Control", "max-age=3600", false);
   } else if (request.queryString === "image_redirect_http_sjs") {
-    var redirect = "http://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_redirect_https";
+    var redirect = "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_https";
     response.setHeader("Cache-Control", "max-age=3600", false);
   } else if (request.queryString === "image_redirect_https") {
     var redirect = "https://example.com/tests/image/test/mochitest/blue.png";
     response.setHeader("Cache-Control", "max-age=3600", false);
   }
 
   response.setHeader("Content-Type", "text/html", false);
   response.setStatusLine(request.httpVersion, "302", "Found");
rename from browser/base/content/test/general/test_mcb_redirect_image.html
rename to browser/base/content/test/siteIdentity/test_mcb_redirect_image.html
--- a/browser/base/content/test/general/test_mcb_redirect_image.html
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect_image.html
@@ -13,11 +13,11 @@
     }
     function image_blocked() {
       document.getElementById("mctestdiv").innerHTML = "image blocked";
     }
   </script>
 </head>
 <body>
   <div id="mctestdiv"></div>
-  <img src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_http" onload="image_loaded()" onerror="image_blocked()" ></image>
+  <img src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_http" onload="image_loaded()" onerror="image_blocked()" ></image>
 </body>
 </html>
rename from browser/base/content/test/general/test_no_mcb_on_http_site_font.css
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css
rename from browser/base/content/test/general/test_no_mcb_on_http_site_font.html
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
--- a/browser/base/content/test/general/test_no_mcb_on_http_site_font.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
@@ -2,17 +2,17 @@
 <html>
 <!--
   Test 2 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
   https://bugzilla.mozilla.org/show_bug.cgi?id=909920
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 2 for Bug 909920</title>
-  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/general/test_no_mcb_on_http_site_font.css" />
+  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
    var ui = SpecialPowers.wrap(window)
             .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
              .getInterface(SpecialPowers.Ci.nsIWebNavigation)
              .QueryInterface(SpecialPowers.Ci.nsIDocShell)
rename from browser/base/content/test/general/test_no_mcb_on_http_site_font2.css
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css
--- a/browser/base/content/test/general/test_no_mcb_on_http_site_font2.css
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css
@@ -1,1 +1,1 @@
-@import url(http://example.com/browser/browser/base/content/test/general/test_no_mcb_on_http_site_font.css);
+@import url(http://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css);
rename from browser/base/content/test/general/test_no_mcb_on_http_site_font2.html
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
--- a/browser/base/content/test/general/test_no_mcb_on_http_site_font2.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
@@ -2,17 +2,17 @@
 <html>
 <!--
   Test 3 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
   https://bugzilla.mozilla.org/show_bug.cgi?id=909920
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 3 for Bug 909920</title>
-  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/general/test_no_mcb_on_http_site_font2.css" />
+  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
    var ui = SpecialPowers.wrap(window)
         .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
              .getInterface(SpecialPowers.Ci.nsIWebNavigation)
              .QueryInterface(SpecialPowers.Ci.nsIDocShell)
rename from browser/base/content/test/general/test_no_mcb_on_http_site_img.css
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css
rename from browser/base/content/test/general/test_no_mcb_on_http_site_img.html
rename to browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
--- a/browser/base/content/test/general/test_no_mcb_on_http_site_img.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
@@ -2,17 +2,17 @@
 <html>
 <!--
   Test 1 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
   https://bugzilla.mozilla.org/show_bug.cgi?id=909920
 -->
 <head>
   <meta charset="utf-8">
   <title>Test 1 for Bug 909920</title>
-  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/general/test_no_mcb_on_http_site_img.css" />
+  <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
    var ui = SpecialPowers.wrap(window)
             .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
              .getInterface(SpecialPowers.Ci.nsIWebNavigation)
              .QueryInterface(SpecialPowers.Ci.nsIDocShell)
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   dummy_page.html
 
+[browser_abandonment_telemetry.js]
 [browser_allow_process_switches_despite_related_browser.js]
 [browser_tabSpinnerProbe.js]
 skip-if = !e10s # Tab spinner is e10s only.
 [browser_tabSwitchPrintPreview.js]
 skip-if = os == 'mac'
 [browser_navigatePinnedTab.js]
 [browser_opened_file_tab_navigated_to_web.js]
 [browser_reload_deleted_file.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_abandonment_telemetry.js
@@ -0,0 +1,305 @@
+"use strict";
+
+const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+
+// Keep this in sync with the order in Histograms.json for
+// BUSY_TAB_ABANDONED
+const CATEGORIES = [
+  "stop",
+  "back",
+  "forward",
+  "historyNavigation",
+  "reload",
+  "tabClosed",
+  "newURI",
+];
+
+const PAGE_2 = "data:text/html,<html>Page 2</html>";
+
+/**
+ * This little framework helps to extract the unique things
+ * involved in testing each category of the BUSY_TAB_ABANDONED
+ * probe from all of the common things. The little framework
+ * in this test will take each item in PROBE_TEST, let it
+ * do some test preparation, then create a "busy" tab to be
+ * manipulated by the test. The "category" of the test will then
+ * be used to determine if the appropriate index in the
+ * histogram was bumped.
+ *
+ * Here's a more verbose breakdown of what each PROBE_TEST
+ * should supply:
+ *
+ * name (String):
+ *   Human readable description of the test. This is dumped
+ *   out via info().
+ *
+ * category (String):
+ *   The string representation of the index that will get
+ *   incremented in the BUSY_TAB_ABANDONED probe. This should
+ *   be a value inside CATEGORIES.
+ *
+ * prepare (function*)
+ *   A function that can yield Promises. This will be run once
+ *   we have a brand new browser to deal with, and should be used by
+ *   the PROBE_TEST to do any history preparation before the browser
+ *   is made to appear "busy" and is tested.
+ *
+ *   @param browser (<xul:browser>)
+ *          The newly created browser that the test will use.
+ *
+ * doAction (function*)
+ *   A function that can yield Promises. This will be run once
+ *   the browser appears busy, and should cause the user action
+ *   that will change the BUSY_TAB_ABANDONED probe.
+ *
+ *   @param browser (<xul:browser>)
+ *          The busy browser to perform the action on.
+ */
+const PROBE_TESTS = [
+
+  // Test stopping the tab
+  {
+    name: "Stopping the browser",
+
+    category: "stop",
+
+    * prepare(browser) {},
+
+    * doAction(browser) {
+      document.getElementById("Browser:Stop").doCommand();
+    },
+  },
+
+  // Test going back to a previous page
+  {
+    name: "Going back to a previous page",
+
+    category: "back",
+
+    * prepare(browser) {
+      browser.loadURI(PAGE_2);
+      yield BrowserTestUtils.browserLoaded(browser);
+    },
+
+    * doAction(browser) {
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      document.getElementById("Browser:Back").doCommand();
+      yield pageShow;
+    },
+  },
+
+  // Test going forward to a previous page
+  {
+    name: "Going forward to the next page",
+
+    category: "forward",
+
+    * prepare(browser) {
+      browser.loadURI(PAGE_2);
+      yield BrowserTestUtils.browserLoaded(browser);
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      browser.goBack();
+      yield pageShow;
+    },
+
+    * doAction(browser) {
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      document.getElementById("Browser:Forward").doCommand();
+      yield pageShow;
+    },
+  },
+
+  // Test going backwards more than one page back via gotoIndex
+  {
+    name: "Going backward to a previous page via gotoIndex",
+
+    category: "historyNavigation",
+
+    * prepare(browser) {
+      browser.loadURI(PAGE_2);
+      yield BrowserTestUtils.browserLoaded(browser);
+      browser.loadURI("http://example.com");
+      yield BrowserTestUtils.browserLoaded(browser);
+      yield TabStateFlusher.flush(browser);
+    },
+
+    * doAction(browser) {
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      synthesizeHistoryNavigationToIndex(0);
+      yield pageShow;
+    },
+  },
+
+  // Test going forwards more than one page back via gotoIndex
+  {
+    name: "Going forward to a previous page via gotoIndex",
+
+    category: "historyNavigation",
+
+    * prepare(browser) {
+      browser.loadURI(PAGE_2);
+      yield BrowserTestUtils.browserLoaded(browser);
+      browser.loadURI("http://example.com");
+      yield BrowserTestUtils.browserLoaded(browser);
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      browser.gotoIndex(0);
+      yield pageShow;
+      yield TabStateFlusher.flush(browser);
+    },
+
+    * doAction(browser) {
+      let pageShow =
+        BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+      synthesizeHistoryNavigationToIndex(2);
+      yield pageShow;
+    },
+  },
+
+  // Test reloading the tab
+  {
+    name: "Reloading the browser",
+
+    category: "reload",
+
+    * prepare(browser) {},
+
+    * doAction(browser) {
+      document.getElementById("Browser:Reload").doCommand();
+      yield BrowserTestUtils.browserLoaded(browser);
+    },
+  },
+
+  // Testing closing the tab is done in its own test later on
+  // in this file.
+
+  // Test browsing to a new URL
+  {
+    name: "Browsing to a new URL",
+
+    category: "newURI",
+
+    * prepare(browser) {},
+
+    * doAction(browser) {
+      openUILinkIn(PAGE_2, "current");
+      yield BrowserTestUtils.browserLoaded(browser);
+    },
+  },
+]
+
+/**
+ * Takes a Telemetry histogram snapshot and makes sure
+ * that the index for that value (as defined by CATEGORIES)
+ * has a count of 1, and that it's the only value that
+ * has been incremented.
+ *
+ * @param snapshot (Object)
+ *        The Telemetry histogram snapshot to examine.
+ * @param category (String)
+ *        The category in CATEGORIES whose index we expect to have
+ *        been set to 1.
+ */
+function assertOnlyOneTypeSet(snapshot, category) {
+  let categoryIndex = CATEGORIES.indexOf(category);
+  Assert.equal(snapshot.counts[categoryIndex], 1,
+               `Should have seen the ${category} count increment.`);
+  // Use Array.prototype.reduce to sum up all of the
+  // snapshot.count entries
+  Assert.equal(snapshot.counts.reduce((a, b) => a + b), 1,
+               "Should only be 1 collected value.");
+}
+
+/**
+ * A helper function for simulating clicking on the history
+ * navigation popup menu that you get if you click and hold
+ * on the back or forward buttons when you have some browsing
+ * history.
+ *
+ * @param index (int)
+ *        The index for the menuitem we want to simulate
+ *        clicking on.
+ */
+function synthesizeHistoryNavigationToIndex(index) {
+  let popup = document.getElementById("backForwardMenu");
+  // I don't want to deal with popup listening - that's
+  // notoriously flake-y in automated tests. I'll just
+  // directly call the function that populates the menu.
+  FillHistoryMenu(popup);
+  Assert.ok(popup.childElementCount > 0,
+            "Should have some items in the back/forward menu");
+  let menuitem = popup.querySelector(`menuitem[index="${index}"]`);
+  Assert.ok(menuitem, `Should find a menuitem with index ${index}`);
+  // Now pretend we clicked on the right item.
+  let cmdEvent = new CustomEvent("command", {
+    bubbles: true,
+    cancelable: true,
+  });
+  menuitem.dispatchEvent(cmdEvent);
+}
+
+/**
+ * Goes through each of the categories for the BUSY_TAB_ABANDONED
+ * probe, and tests that they're properly changed.
+ */
+add_task(function* test_probes() {
+  let oldCanRecord = Services.telemetry.canRecordExtended;
+  Services.telemetry.canRecordExtended = true;
+
+  registerCleanupFunction(() => {
+    Services.telemetry.canRecordExtended = oldCanRecord;
+  });
+
+  let histogram = Services.telemetry
+                          .getHistogramById("BUSY_TAB_ABANDONED");
+
+  // If you want to add new tests for probes that don't involve
+  // the tab or window hosting the tab closing, see the documentation
+  // above PROBE_TESTS for how to hook into this little framework.
+  for (let probeTest of PROBE_TESTS) {
+    yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: "http://example.com",
+    }, function*(browser) {
+      let tab = gBrowser.getTabForBrowser(browser);
+      info(`Test: "${probeTest.name}"`);
+
+      yield* probeTest.prepare(browser);
+      // Instead of trying to fiddle with network state or
+      // anything, we'll just set this attribute to fool our
+      // telemetry probes into thinking the browser is in the
+      // middle of loading some resources.
+      tab.setAttribute("busy", true);
+
+      histogram.clear();
+      yield* probeTest.doAction(browser);
+      let snapshot = histogram.snapshot();
+      assertOnlyOneTypeSet(snapshot, probeTest.category);
+    });
+  }
+
+  // The above tests used BrowserTestUtils.withNewTab, which is
+  // fine for almost all categories for this probe, except for
+  // "tabClosed", since withNewTab closes the tab automatically
+  // before resolving, which doesn't work well for a tabClosed
+  // test in the above framework. So the tabClosed tests are
+  // done separately below.
+
+  histogram.clear();
+  // Now test that we can close a busy tab and get the tabClosed
+  // measurement bumped.
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+  // As above, instead of trying to fiddle with network state
+  // or anything, we'll just set this attribute to fool our
+  // telemetry probes into thinking the browser is in the
+  // middle of loading some resources.
+  newTab.setAttribute("busy", true);
+
+  yield BrowserTestUtils.removeTab(newTab);
+  let snapshot = histogram.snapshot();
+  assertOnlyOneTypeSet(snapshot, "tabClosed");
+});
--- a/browser/extensions/pocket/locale/ast/pocket.properties
+++ b/browser/extensions/pocket/locale/ast/pocket.properties
@@ -1,17 +1,17 @@
 # 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/.
 
 addtags = Amestar etiquetes
 alreadyhaveacct = ¿Yá yes un usuariu de Pocket?
 continueff = Siguir con Firefox
 errorgeneric = Hebo un fallu tentando de guardar en Pocket.
-learnmore = Depriendi más
+learnmore = Deprendi más
 loginnow = Aniciar sesión
 maxtaglength = Les etiquetes lléndense a 25 caráuteres
 mustbeconnected = Has tar coneutáu a internet pa guardar en Pocket. Comprueba la to conexón y volvi tentalo, por favor.
 onlylinkssaved = Namái puen guardase enllaces
 pagenotsaved = Páxina non guardada
 pageremoved = Páxina desaniciada
 pagesaved = Guardóse en Pocket
 processingremove = Desaniciando páxina…
@@ -19,17 +19,17 @@ processingtags = Amestando etiquetes…
 removepage = Desanicia páxina
 save = Guardar
 saving = Guardando…
 signupemail = Rexistrase con corréu
 signuptosave = Rexístrate en Pocket. Ye de baldre.
 suggestedtags = Etiquetes suxeríes
 tagline = Guardar artículos y vídeos dende Firefox pa ver en Pocket o en cualquier preséu, en cualquier momentu.
 taglinestory_one = Fai clic nel botón de Pocket pa guardar cualquier artículu, videu o páxina dende Firefox.
-taglinestory_two = Ver en Pocker o en cualquier preséu, en cualquier momentu.
+taglinestory_two = Ver en Pocket o en cualquier preséu, en cualquier momentu.
 tagssaved = Etiquetes amestaes
 tos = Sigiuiendo, tas acordies colos <a href="%1$S" target="_blank">Términos de Serviciu</a> y la <a href="%2$S" target="_blank">Política de privacidá</a> de Pocket
 tryitnow = Pruébalu agora
 signinfirefox = Anicia sesión con Firefox
 signupfirefox = Rexístrate con Firefox
 viewlist = Ver llista
 
 # LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/bn-IN/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = ট্যাগ যোগ করুন
+alreadyhaveacct = ইতিমধ্যে কি একজন Pocket ব্যবহারকারী?
+continueff = Firefox দিয়ে এগোন
+errorgeneric = Pocket-এ সংরক্ষণ করার সময় ত্রুটি হয়েছিল।
+learnmore = আরও জানুন
+loginnow = লগ ইন করুন
+maxtaglength = ট্যাগগুলি 25 টি অক্ষরে সীমাবদ্ধ
+mustbeconnected = আপনি Pocket এ সংরক্ষণ করার জন্য ইন্টারনেটের সাথে সংযুক্ত করা আবশ্যক। আপনার সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।
+onlylinkssaved = শুধু লিঙ্ক সংরক্ষণ করা যাবে
+pagenotsaved = পেজ সংরক্ষিত হয়নি
+pageremoved = পেজ অপসারিত
+pagesaved = Pocket এ সংরক্ষিত
+processingremove = পেজ মুছে ফেলা হচ্ছে…
+processingtags = ট্যাগ যোগ করা হচ্ছে…
+removepage = Page অপসারণ করুন
+save = সংরক্ষণ করুন
+saving = সংরক্ষণ করা হচ্ছে…
+signupemail = ই-মেইল দিয়ে সাইন-ইন করুন
+signuptosave = Pocket এর জন্য সাইন আপ করুন। এটা বিনামূল্যে।
+suggestedtags = প্রস্তাবিত ট্যাগসমুহ
+tagline = কোনো ডিভাইসে যে কোনো সময় Firefox থেকে Pocket এর মধ্যে দেখতে নিবন্ধ এবং ভিডিও সংরক্ষণ করুন।
+taglinestory_one = Firefox থেকে একটি নিবন্ধ, ভিডিও বা পেজ সংরক্ষণ করতে পকেট বাটন ক্লিক করুন।
+taglinestory_two = কোনো ডিভাইসে, যে কোনো সময় Pocket এর মধ্যে দেখুন।
+tagssaved = ট্যাগগুলি যোগ করা হয়েছে
+tos = এগিয়ে, আপনি Pocket এর <a href="%1$S" target="_blank">পরিসেবার নিয়মাবলী</a> এবং <a href="%2$S" target="_blank">গোপনীয়তা সংক্রান্ত নীতি</a> মানলেন
+tryitnow = এটা এখনি চেষ্টা করুন
+signinfirefox = Firefox দিয়ে সাইন ইন করুন
+signupfirefox = Firefox দিয়ে সাইন আপ করুন
+viewlist = তালিকা দেখুন
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Pocket এ সংরক্ষণ করুন
+saveToPocketCmd.label = Pocket এ পেজ সংরক্ষণ করুন
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Pocket এ লিঙ্ক সংরক্ষণ করুন
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Pocket এর তালিকা দেখুন
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/el/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = Προσθήκη ετικετών
+alreadyhaveacct = Είστε ήδη χρήστης του Pocket;
+continueff = Συνέχεια με το Firefox
+errorgeneric = Προέκυψε σφάλμα κατά την προσπάθεια αποθήκευσης στο Pocket.
+learnmore = Μάθετε περισσότερα
+loginnow = Σύνδεση
+maxtaglength = Οι ετικέτες έχουν όριο 25 χαρακτήρες
+mustbeconnected = Πρέπει να είστε συνδεδεμένοι στο διαδίκτυο προκειμένου να αποθηκεύσετε στο Pocket. Παρακαλώ ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά.
+onlylinkssaved = Μόνο οι σύνδεσμοι μπορούν να αποθηκευτούν
+pagenotsaved = Η σελίδα δεν αποθηκεύτηκε
+pageremoved = Η σελίδα αφαιρέθηκε
+pagesaved = Αποθηκεύτηκε στο Pocket
+processingremove = Αφαίρεση σελίδας…
+processingtags = Προσθήκη ετικετών…
+removepage = Αφαίρεση σελίδας
+save = Αποθήκευση
+saving = Αποθήκευση…
+signupemail = Εγγραφή με email
+signuptosave = Εγγραφείτε στο Pocket. Δωρεάν.
+suggestedtags = Προτεινόμενες ετικέτες
+tagline = Αποθηκεύστε άρθρα και βίντεο από το Firefox για προβολή στο Pocket από οποιαδήποτε συσκευή, ανά πάσα στιγμή.
+taglinestory_one = Κάντε κλικ στο κουμπί του Pocket για να αποθηκεύσετε οποιοδήποτε άρθρο, βίντεο ή σελίδα από το Firefox.
+taglinestory_two = Προβολή στο Pocket σε οποιαδήποτε συσκευή, ανά πάσα στιγμή.
+tagssaved = Προστέθηκαν ετικέτες
+tos = Συνεχίζοντας, συμφωνείτε με τους <a href="%1$S" target="_blank">όρους υπηρεσίας</a> και την <a href="%2$S" target="_blank">πολιτική απορρήτου</a> του Pocket
+tryitnow = Δοκιμάστε το τώρα
+signinfirefox = Σύνδεση με Λογαριασμό Firefox
+signupfirefox = Εγγραφή με Λογαριασμό Firefox
+viewlist = Προβολή λίστας
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Αποθήκευση στο Pocket
+saveToPocketCmd.label = Αποθήκευση της σελίδας στο Pocket
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Αποθήκευση συνδέσμου στο Pocket
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Προβολή λίστας Pocket
--- a/browser/extensions/pocket/locale/en-US/pocket.properties
+++ b/browser/extensions/pocket/locale/en-US/pocket.properties
@@ -21,17 +21,17 @@ save = Save
 saving = Saving…
 signupemail = Sign up with email
 signuptosave = Sign up for Pocket. It’s free.
 suggestedtags = Suggested Tags
 tagline = Save articles and videos from Firefox to view in Pocket on any device, any time.
 taglinestory_one = Click the Pocket Button to save any article, video or page from Firefox.
 taglinestory_two = View in Pocket on any device, any time.
 tagssaved = Tags Added
-tos = By continuing, you agree to Pocket's <a href="%1$S" target="_blank">Terms of Service</a> and <a href="%2$S" target="_blank">Privacy Policy</a>
+tos = By continuing, you agree to Pocket’s <a href="%1$S" target="_blank">Terms of Service</a> and <a href="%2$S" target="_blank">Privacy Policy</a>
 tryitnow = Try It Now
 signinfirefox = Sign in with Firefox
 signupfirefox = Sign up with Firefox
 viewlist = View List
 
 # LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
 # "Pocket" is a brand name.
 pocket-button.label = Pocket
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/eo/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = Aldoni langetojn
+alreadyhaveacct = Ĉu vi jam estas uzanto de Pocket?
+continueff = Daŭrigi per Firefox
+errorgeneric = Okazis eraro dum la klopodo konservi en Pocket.
+learnmore = Pli da informo
+loginnow = Komenci seancon
+maxtaglength = Etikedoj povas enhavi ĝis 25 signojn
+mustbeconnected = Vi devas esti konektita al la interreto por povi konservi en Pocket. Bonvolu kontroli vian retaliron kaj provi denove.
+onlylinkssaved = Nur ligiloj povas esti konservitaj
+pagenotsaved = Paĝo ne konservita
+pageremoved = Paĝo forigita
+pagesaved = Konservita en Pocket
+processingremove = Paĝo forigata…
+processingtags = Etikedoj aldonataj…
+removepage = Forigi paĝon
+save = Konservi
+saving = Konservo…
+signupemail = Enskribiĝi per retpoŝto
+signuptosave = Enskribiĝi al Pocket. Estas senpage.
+suggestedtags = Sugestitaj etikedoj
+tagline = Konservi artikolojn kaj filmetojn el Firefox por povi vidi en Pocket en iu ajn aparato, iam ajn.
+taglinestory_one = Alklaku la butonon Pocket por konservi iun ajn artikolon, filmeton aŭ paĝon el Firefox.
+taglinestory_two = Vidi Pocket en iu ajn aparato, iam ajn.
+tagssaved = Etikedoj aldonitaj
+tos = Se vi daŭrigas, vi akceptas la <a href="%1$S" target="_blank">kondiĉojn de la servo</a> Pocket kaj ĝian <a href="%2$S" target="_blank">politikon pri privateco</a>
+tryitnow = Provi ĝin nun
+signinfirefox = Komenci seancon per Firefox
+signupfirefox = Enskribiĝi per Firefox
+viewlist = Vidi liston
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Konservi en Pocket
+saveToPocketCmd.label = Konservi paĝon en Pocket
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Konservi ligilon en Pocket
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Vidi liston de Pocket
--- a/browser/extensions/pocket/locale/es-MX/pocket.properties
+++ b/browser/extensions/pocket/locale/es-MX/pocket.properties
@@ -19,17 +19,17 @@ processingtags = Agregando etiquetas…
 removepage = Eliminar página
 save = Guardar
 saving = Guardando…
 signupemail = Regístrate con un correo electrónico
 signuptosave = Regístrate en Pocket. Es gratis.
 suggestedtags = Etiquetas sugeridas
 tagline = Guardar artículos y videos desde Firefox para ver en Pocket o en cualquier dispositivo, en cualquier momento.
 taglinestory_one = Haz clic en el botón de Pocket para guardar cualquier artículo, video o página desde Firefox.
-taglinestory_two = Ver en Pocker o en cualquier dispositivo, en cualquier momento.
+taglinestory_two = Ver en Pocket o en cualquier dispositivo, en cualquier momento.
 tagssaved = Etiquetas agregadas
 tos = Al continuar, aceptas los <a href="%1$S" target="_blank">Términos del servicio</a> y la <a href="%2$S" target="_blank">Política de privacidad</a> de Pocket
 tryitnow = Pruébalo ahora
 signinfirefox = Ingresa con Firefox
 signupfirefox = Regístrate con Firefox
 viewlist = Ver lista
 
 # LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/fa/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = افزودن برچسب
+alreadyhaveacct = از قبل کاربر Pocket هستید؟
+continueff = ادامه در فایرفاکس
+errorgeneric = خطایی در هنگام تلاش برای ذخیره‌سازی در Pocket رخ داده است.
+learnmore = بیشتر بدانید
+loginnow = ورود به سیستم
+maxtaglength = برچسب‌ها شامل محدودیت ۲۵ حرفی می‌باشند
+mustbeconnected = جهت ذخیره‌سازی در Pocket بایستی به اینترنت متصل باشید. لطفا اتصال اینترنت خود را بررسی کنید و مجددا تلاش کنید.
+onlylinkssaved = تنها لینک‌ها می توانند ذخیره شوند
+pagenotsaved = صفحه ذخیره نشد
+pageremoved = صفحه حذف شد
+pagesaved = در ‌Pocket ذخیره شده
+processingremove = در حال حذف صفحه…
+processingtags = در حال اضافه کردن برچسب‌ها…
+removepage = حذف صفحه
+save = ذخیره
+saving = در حال ذخیره…
+signupemail = ثبت‌نام با ایمیل
+signuptosave = در Pocket ثبت‌نام کنید. رایگان است.
+suggestedtags = برچسب‌های پیشنهادی
+tagline = مقاله‌ها و ویدئوها را با فایرفاکس ذخیره کنید و در هر زمان و دستگاهی به وسیله‌ی Pocket ببینید.
+taglinestory_one = بر روی دکمه Pocket کلیک کنید تا مقاله، ویدئو یا صفحات را از طریق فایرفاکس ذخیره کنید.
+taglinestory_two = نمایش در Pocket در هر دستگاه و در هر زمانی.
+tagssaved = برچسب‌ها اضافه شد
+tos = با ادامه دادن، شما با <a href="%1$S" target="_blank">قوانین سرویس</a> و <a href="%2$S" target="_blank">سیاست حفظ حریم‌خصوصی</a> Pocket موافقت می‌کنید.
+tryitnow = حالا امتحان کنید
+signinfirefox = ورود از طریق فایرفاکس
+signupfirefox = ثبت نام توسط فایرفاکس
+viewlist = \u0020نمایش‌ فهرست
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = ذخیره‌سازی در Pocket
+saveToPocketCmd.label = ذخیرهٔ صفحه در Pocket
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = ذخیرهٔ پیوند در Pocket
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = نمایش فهرست Pocket
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/ff/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = Ɓeydu Tage
+alreadyhaveacct = Aɗa huutoroo Pocket kisa?
+continueff = Jokku to Firefox
+errorgeneric = Waɗii juumre tuma etagol danndude to Pocket.
+learnmore = Ɓeydi Humpito
+loginnow = Seŋo
+maxtaglength = Tage mbaawa ɓurde alkule 25
+mustbeconnected = Alaa e sago ceŋo-ɗaa e Enternet ngam danndude to Pocket. Tiiɗno ƴeewto ceŋol maa kisa eto-ɗaa kadi.
+onlylinkssaved = Ko jokke tan mbaawi danndeede
+pagenotsaved = Hello Danndaaka
+pageremoved = Hello Momtaama
+pagesaved = Danndaama to Pocket
+processingremove = Nana Momta Hello…
+processingtags = Nana ɓeyda tage…
+removepage = Momtu Hello
+save = Danndu
+saving = Nana dannda…
+signupemail = Winnditoro iimeel
+signuptosave = Winndito e Pocket. Yoɓetaake.
+suggestedtags = Tage Basiyaaɗe
+tagline = Danndu binndanɗe e widewooji Firedox ngam yiyde e Pockete kaɓirgol kala, sahaa kala.
+taglinestory_one = Dobo e Butoŋ Pocket oo ngam danndude winndannde, widewoo wala hello iwde e Firefox.
+taglinestory_two = Yiy e Pocket e kaɓirgol kala, sahaa kala.
+tagssaved = Tage Ɓeydaama
+tos = Ɓennude ɗoo firti ko e jaɓii <a href="%1$S" target="_blank">Sarɗiiji Carwe</a> e <a href="%2$S" target="_blank">Dawirgol Suturo</a> Pocket
+tryitnow = Ƴeewndo ɗum Jooni
+signinfirefox = Seŋoro Firefox
+signupfirefox = Winnditoro Firefox
+viewlist = Yiy Doggol
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Danndu to Pocket
+saveToPocketCmd.label = Danndu Hello to Pocket
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Danndu Jokkol to Pocket
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Yiy Doggol Pocket
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/gd/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = Cuir tagaichean ris
+alreadyhaveacct = A bheil thu a’ cleachdadh Pocket mu thràth?
+continueff = Lean ort ann am Firefox
+errorgeneric = Thachair mearachd nuair a dh’fheuch sinn ri rud a shàbhaladh ann am Pocket.
+learnmore = Barrachd fiosrachaidh
+loginnow = Clàraich a-steach
+maxtaglength = Chan fhaod taga a bhith nas fhaide na 25 caractar
+mustbeconnected = Feumaidh ceangal ris an eadar-lìon a bhith agad mus urrainn dhut rud a shàbhaladh ann am Pocket. Thoir sùil air a’ cheangal agad is feuch ris a-rithist.
+onlylinkssaved = Cha ghabh ach ceanglaichean a shàbhaladh
+pagenotsaved = Cha deach an duilleag a shàbhaladh
+pageremoved = Chaidh an duilleag a thoirt air falbh
+pagesaved = Air a shàbhaladh ann am Pocket
+processingremove = A’ toirt air falbh na duilleige…
+processingtags = A’ cur ris nan tagaichean…
+removepage = Thoir an duilleag air falbh
+save = Sàbhail
+saving = ’Ga shàbhaladh…
+signupemail = Clàraich slighe puist-d
+signuptosave = Cha chosg clàradh aig Pocket sgillinn.
+suggestedtags = Tagaichean a mholar
+tagline = Sàbhail artaigilean is videothan o Firefox ann am Pocket agus coimhead orra air uidheam sam bith, uair sam bith.
+taglinestory_one = Briog air a’ phutan Pocket gus artaigeal, video no duilleag a shàbhaladh o Firefox.
+taglinestory_two = Seall ann am Pocket air uidheam sam bith, uair sam bith.
+tagssaved = Tagaichean air an cur ris
+tos = Ma leanas tu air adhart, bidh thu ag aontachadh ri <a href="%1$S" target="_blank">teirmichean a’ chleachdaidh</a> agus <a href="%2$S" target="_blank">am poileasaidh prìobhaideachd</a> aig Pocket
+tryitnow = Feuch e an-dràsta
+signinfirefox = Clàraich a-steach le Firefox
+signupfirefox = Clàraich le Firefox
+viewlist = Seall an liosta
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Sàbhail ann am Pocket
+saveToPocketCmd.label = Sàbhail an duilleag ann am Pocket
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Sàbhail an ceangail ann am Pocket
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Seall liosta Pocket
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/he/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = הוספת תגיות
+alreadyhaveacct = כבר התחלת להשתמש ב־Pocket?
+continueff = להמשיך עם Firefox
+errorgeneric = אירעה שגיאה בעת ניסיון לשמור ל־Pocket.
+learnmore = מידע נוסף
+loginnow = כניסה
+maxtaglength = התגיות מוגבלות ל־25 תווים
+mustbeconnected = יש להתחבר לאינטרנט כדי לשמור ל־Pocket. נא לבדוק את החיבור שלך ולנסות שוב.
+onlylinkssaved = ניתן לשמור קישורים בלבד
+pagenotsaved = העמוד לא נשמר
+pageremoved = העמוד הוסר
+pagesaved = נשמר ל־Pocket
+processingremove = העמוד מוסר…
+processingtags = התגיות נוספות…
+removepage = הסרת עמוד
+save = שמירה
+saving = שמירה…
+signupemail = הרשמה עם דוא״ל
+signuptosave = להירשם ל־Pocket. זה חינם.
+suggestedtags = תגיות מוצעות
+tagline = לשמור מאמרים וסרטונים מ־Firefox כדי להציג ב־Pocket בכל מכשיר, בכל זמן.
+taglinestory_one = כדי לשמור כל מאמר, סרטון או דף מ־Firefox יש ללחוץ על כפתור ה‏־Pocket.
+taglinestory_two = להציג ב־Pocket בכל מכשיר, בכל זמן.
+tagssaved = נוספו תגיות
+tos = השימוש ברכיב זה מהווה אישור ל<a href="%1$S" target="_blank">תנאי השימוש</a> ב־Pocket ו<a href="%2$S" target="_blank">מדיניות הפרטיות</a>
+tryitnow = נסו את זה כעת
+signinfirefox = כניסה עם Firefox
+signupfirefox = רישום עם Firefox
+viewlist = הצגת הרשימה
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = שמירה אל Pocket
+saveToPocketCmd.label = שמירת דף אל Pocket
+saveToPocketCmd.accesskey = ד
+saveLinkToPocketCmd.label = שמירת קישור ב־Pocket
+saveLinkToPocketCmd.accesskey = ק
+pocketMenuitem.label = הצגת רשימת Pocket
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/hi-IN/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = टैग जोड़ें
+alreadyhaveacct = पहले से Pocket के उपयोगकर्ता हैं?
+continueff = Firefox के साथ जारी रखें
+errorgeneric = पॉकेट में सहेजने की कोशिश करते समय त्रुटि हुई थी.
+learnmore = और जानें
+loginnow = लॉग इन
+maxtaglength = टैग 25 वर्णों तक सीमीत है
+mustbeconnected = Pocket में सहेजने के लिए आप इन्टरनेट से जुडें होना आवश्यक हैं. कृपया अपना कनेक्शन जॉंचे और फिरसे कोशिश करें.
+onlylinkssaved = सिर्फ लिंक सहेजा जा सकता हैं
+pagenotsaved = पेज सहेजा नही गया
+pageremoved = पृष्ठ हटाया गया
+pagesaved = Pocket में सहेजा
+processingremove = पृष्ठ मिटा रहा है…
+processingtags = टैग्स जोड़ रहे हैं...
+removepage = पृष्ठ हटाएं
+save = सहेजें
+saving = सहेजा जा रहा है ...
+signupemail = ईमेल के साथ साइन अप करें
+signuptosave = पॉकेट के लिए साइन अप करें। यह मुफ़्त है।\u0020
+suggestedtags = सुझाये हुए टैग्स.
+tagline = किसी भी समय, Pocket में कोई भी डिवाइस पर देखने के लिए Firefox से आलेख और वीडियो सहेजें.
+taglinestory_one = Firefox से कोई भी आलेख, वीडियो या पृष्ठ को सहेजने के लिए Pocket बटन को क्लिक करे.
+taglinestory_two = किसी भी समय, पॉकेट में कोई भी डिवाइस पर देखे.
+tagssaved = टैग जोड़ा गया
+tos = जारी रखने के द्वारा, आप Pocket के <a href="%1$S" target="_blank">सेवा की शर्तों</a> और <a href="%2$S" target="_blank">गोपनीयता नीति</a> पर सहमति प्रदान करते हैं
+tryitnow = अभी आज़माएँ
+signinfirefox = Firefox के साथ साइन इन करें
+signupfirefox = Firefox के साथ साइन अप करें
+viewlist = सूची देखें
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Pocket में सहेजा
+saveToPocketCmd.label = Pocket में पृष्ठ को सहेजे
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Pocket में लिंक को सहेजे
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Pocket सूची देखे
--- a/browser/extensions/pocket/locale/ja/pocket.properties
+++ b/browser/extensions/pocket/locale/ja/pocket.properties
@@ -1,31 +1,31 @@
 # 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/.
 
 addtags = タグを追加
-alreadyhaveacct = Pocket に登録済みですか?
+alreadyhaveacct = Pocket にユーザー登録済みですか?
 continueff = Firefox で続行
 errorgeneric = Pocket への保存中にエラーがありました。
 learnmore = 詳細
 loginnow = ログイン
 maxtaglength = タグは 25 文字までです
 mustbeconnected = Pocket に保存するには、インターネット接続が必要です。接続状況を確認してから、試してみたください。
 onlylinkssaved = リンクのみ保存しました
 pagenotsaved = ページを保存しませんでした
 pageremoved = ページを削除しました
 pagesaved = Pocket に保存しました
 processingremove = ページを削除しています...
 processingtags = タグを追加しています...
 removepage = ページを削除
 save = 保存
 saving = 保存しています...
 signupemail = メールアドレスで新規登録
-signuptosave = Pocket に新規登録します。無料です。
+signuptosave = Pocket にユーザー登録しましょう。無料です。
 suggestedtags = 提案タグ
 tagline = Firefox で記事や動画を保存すると、いつでもどこでも Pocket で閲覧できます。
 taglinestory_one = Firefox で Pocket ボタンをクリックすると、様々な記事や動画やページを保存できます。
 taglinestory_two = Pocket でいつでもどこでも閲覧できます。
 tagssaved = タグを追加しました
 tos = 続けることで、Pocket の <a href="%1$S" target="_blank">利用規約</a> と <a href="%2$S" target="_blank">プライバシーポリシー</a> に同意したことになります
 tryitnow = 今すぐ試す
 signinfirefox = Firefox でログイン
--- a/browser/extensions/pocket/locale/jar.mn
+++ b/browser/extensions/pocket/locale/jar.mn
@@ -1,33 +1,35 @@
 #filter substitution
 # 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/.
 
 # These are used for the big if statement, as the preprocessor can't handle
 # dashes.
 #define bn_BD bn-BD
+#define bn_IN bn-IN
 #define en_GB en-GB
 #define en_US en-US
 #define es_AR es-AR
 #define es_CL es-CL
 #define es_ES es-ES
 #define es_MX es-MX
 #define fy_NL fy-NL
 #define gu_IN gu-IN
+#define hi_IN hi-IN
 #define nn_NO nn-NO
 #define pt_BR pt-BR
 #define pt_PT pt-PT
 #define sv_SE sv-SE
 #define zh_CN zh-CN
 #define zh_TW zh-TW
 
 [features/firefox@getpocket.com] @AB_CD@.jar:
 % locale pocket @AB_CD@ %locale/@AB_CD@/
   # For locales we support, include the file from the locale's directory in the
   # source tree.
   # For other locales (and en-US) fallback to the en-US directory.
-#if AB_CD == ast || AB_CD == az || AB_CD == bg || AB_CD == bn_BD || AB_CD == cs || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == en_GB || AB_CD == en_US || AB_CD == es_AR || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == et || AB_CD == fi || AB_CD == fr || AB_CD == fy_NL || AB_CD == gu_IN || AB_CD == hr || AB_CD == hsb || AB_CD == hu || AB_CD == it || AB_CD == ja || AB_CD == ka || AB_CD == kab || AB_CD == lt || AB_CD == lv || AB_CD == mr || AB_CD == ms || AB_CD == nl || AB_CD == nn_NO || AB_CD == or || AB_CD == pl || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == sk || AB_CD == sl || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == zh_CN || AB_CD == zh_TW
+#if AB_CD == ast || AB_CD == az || AB_CD == bg || AB_CD == bn_BD || AB_CD == bn_IN || AB_CD == cs || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == en_GB || AB_CD == en_US || AB_CD == es_AR || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == el || AB_CD == eo || AB_CD == et || AB_CD == fa || AB_CD == fi || AB_CD == ff || AB_CD == fr || AB_CD == fy_NL || AB_CD == gd || AB_CD == gu_IN || AB_CD == he || AB_CD == hi_IN || AB_CD == hr || AB_CD == hsb || AB_CD == hu || AB_CD == it || AB_CD == ja || AB_CD == ka || AB_CD == kab || AB_CD == ko || AB_CD == lt || AB_CD == lv || AB_CD == mr || AB_CD == ms || AB_CD == nl || AB_CD == nn_NO || AB_CD == or || AB_CD == pl || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == sk || AB_CD == sl || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == zh_CN || AB_CD == zh_TW
   locale/@AB_CD@/                (@AB_CD@/*)
 #else
   locale/@AB_CD@/                (en-US/*)
 #endif
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/locale/ko/pocket.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+addtags = 태그 추가
+alreadyhaveacct = 이미 Pocket을 쓰고 있습니까?
+continueff = Firefox로 계속하기
+errorgeneric = Pocket에 저장하다 잘못되었습니다.
+learnmore = 더 알아보기
+loginnow = 로그인
+maxtaglength = 태그는 25자를 넘으면 안됨
+mustbeconnected = Pocket에 저장하려면 인터넷에 연결되어 있어야 합니다. 연결을 확인하고 다시 시도해 주십시오.
+onlylinkssaved = 링크만 저장할 수 있음
+pagenotsaved = 페이지를 저장하지 못했음
+pageremoved = 페이지가 삭제됨
+pagesaved = Pocket에 저장
+processingremove = 페이지를 삭제하는 중…
+processingtags = 페이지를 추가하는 중…
+removepage = 페이지 삭제
+save = 저장
+saving = 저장 중…
+signupemail = 이메일로 가입
+signuptosave = Pocket에 가입하십시오. 무료입니다.
+suggestedtags = 추천하는 태그
+tagline = Firefox에서 글과 동영상을 저장하면 모든 기기에서 아무 때나 보실 수 있습니다.
+taglinestory_one = Pocket 단추를 누르면 Firefox에서 어떠한 글, 동영상, 또는 페이지도 저장합니다.
+taglinestory_two = 모든 기기에서 아무 때나 Pocket에서 볼 수 있습니다.
+tagssaved = 태그를 추가함
+tos = 계속하시면 Pocket의 <a href="%1$S" target="_blank">약관</a> 과 <a href="%2$S" target="_blank">개인정보정책</a> 에 동의하게 됩니다
+tryitnow = 지금 사용해보기
+signinfirefox = Firefox로 로그인
+signupfirefox = Firefox로 가입하기
+viewlist = 목록 보기
+
+# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
+# "Pocket" is a brand name.
+pocket-button.label = Pocket
+pocket-button.tooltiptext = Pocket에 저장
+saveToPocketCmd.label = Pocket에 페이지 저장
+saveToPocketCmd.accesskey = k
+saveLinkToPocketCmd.label = Pocket에 링크 저장
+saveLinkToPocketCmd.accesskey = o
+pocketMenuitem.label = Pocket의 목록 보기
--- a/browser/extensions/pocket/locale/pt-BR/pocket.properties
+++ b/browser/extensions/pocket/locale/pt-BR/pocket.properties
@@ -14,27 +14,27 @@ onlylinkssaved = Somente links podem ser
 pagenotsaved = Página não salva
 pageremoved = Página removida
 pagesaved = Salva no Pocket
 processingremove = Removendo página…
 processingtags = Adicionando etiquetas…
 removepage = Remover página
 save = Salvar
 saving = Salvando…
-signupemail = Registrar com e-mail
+signupemail = Registre-se via e-mail
 signuptosave = Registre-se no Pocket. É gratuito.
 suggestedtags = Etiquetas sugeridas
 tagline = Salve os artigos e vídeos do Firefox no Pocket para vê-los mais tarde e em qualquer local.
 taglinestory_one = Clique no botão Pocket para salvar um artigo, vídeo ou página do Firefox.
 taglinestory_two = Ver no Pocket em qualquer dispositivo, a qualquer hora.
 tagssaved = Etiquetas adicionadas
-tos = Continuando, você concorda com os <a href="%1$S" target="_blank">Termos de serviço</a> e <a href="%2$S" target="_blank">Política de privacidade</a> do Pocket
+tos = Continuando, você concorda com os <a href="%1$S" target="_blank">Termos de serviço</a> e a <a href="%2$S" target="_blank">Política de privacidade</a> do Pocket
 tryitnow = Experimente-o agora
-signinfirefox = Entrar com o Firefox
-signupfirefox = Cadastre-se com o Firefox
+signinfirefox = Identifique-se via Firefox
+signupfirefox = Registre-se via Firefox
 viewlist = Ver lista
 
 # LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
 # "Pocket" is a brand name.
 pocket-button.label = Pocket
 pocket-button.tooltiptext = Salvar no Pocket
 saveToPocketCmd.label = Salvar página no Pocket
 saveToPocketCmd.accesskey = k
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -131,17 +131,18 @@ function handleGUMStop(aSubject, aTopic,
 
   let request = {
     windowID: aSubject.windowID,
     rawID: aSubject.rawID,
     mediaSource: aSubject.mediaSource,
   };
 
   let mm = getMessageManagerForWindow(contentWindow);
-  mm.sendAsyncMessage("webrtc:StopRecording", request);
+  if (mm)
+    mm.sendAsyncMessage("webrtc:StopRecording", request);
 }
 
 function handleGUMRequest(aSubject, aTopic, aData) {
   let constraints = aSubject.getConstraints();
   let secure = aSubject.isSecure;
   let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);
 
   contentWindow.navigator.mozGetUserMediaDevices(
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -123,18 +123,21 @@ this.ExtensionsUI = {
       // there are multiple simultaneous installs happening, see
       // bug 1329884 for a longer explanation.
       let progressNotification = target.ownerGlobal.PopupNotifications.getNotification("addon-progress", target);
       if (progressNotification) {
         progressNotification.remove();
       }
 
       let reply = answer => {
-        Services.obs.notifyObservers(subject, "webextension-permission-response",
-                                     JSON.stringify(answer));
+        if (answer) {
+          info.resolve();
+        } else {
+          info.reject();
+        }
       };
 
       let perms = info.addon.userPermissions;
       if (!perms) {
         reply(true);
       } else {
         info.permissions = perms;
         this.showPermissionsPrompt(target, info).then(reply);
@@ -169,38 +172,38 @@ this.ExtensionsUI = {
     let win = target.ownerGlobal;
 
     let name = info.addon.name;
     if (name.length > 50) {
       name = name.slice(0, 49) + "…";
     }
     name = this._sanitizeName(name);
 
-    let addonLabel = `<label class="addon-webext-name">${name}</label>`;
+    let addonName = `<span class="addon-webext-name">${name}</span>`;
     let bundle = win.gNavigatorBundle;
 
-    let header = bundle.getFormattedString("webextPerms.header", [addonLabel]);
+    let header = bundle.getFormattedString("webextPerms.header", [addonName]);
     let text = "";
     let listIntro = bundle.getString("webextPerms.listIntro");
 
     let acceptText = bundle.getString("webextPerms.add.label");
     let acceptKey = bundle.getString("webextPerms.add.accessKey");
     let cancelText = bundle.getString("webextPerms.cancel.label");
     let cancelKey = bundle.getString("webextPerms.cancel.accessKey");
 
     if (info.type == "sideload") {
-      header = bundle.getFormattedString("webextPerms.sideloadHeader", [addonLabel]);
+      header = bundle.getFormattedString("webextPerms.sideloadHeader", [addonName]);
       text = bundle.getString("webextPerms.sideloadText");
       acceptText = bundle.getString("webextPerms.sideloadEnable.label");
       acceptKey = bundle.getString("webextPerms.sideloadEnable.accessKey");
       cancelText = bundle.getString("webextPerms.sideloadDisable.label");
       cancelKey = bundle.getString("webextPerms.sideloadDisable.accessKey");
     } else if (info.type == "update") {
       header = "";
-      text = bundle.getFormattedString("webextPerms.updateText", [addonLabel]);
+      text = bundle.getFormattedString("webextPerms.updateText", [addonName]);
       acceptText = bundle.getString("webextPerms.updateAccept.label");
       acceptKey = bundle.getString("webextPerms.updateAccept.accessKey");
     }
 
     let msgs = [];
     for (let permission of perms.permissions) {
       let key = `webextPerms.description.${permission}`;
       if (permission == "nativeMessaging") {
@@ -316,26 +319,26 @@ this.ExtensionsUI = {
     });
   },
 
   showInstallNotification(target, addon) {
     let win = target.ownerGlobal;
     let popups = win.PopupNotifications;
 
     let name = this._sanitizeName(addon.name);
-    let addonLabel = `<label class="addon-webext-name">${name}</label>`;
+    let addonName = `<span class="addon-webext-name">${name}</span>`;
     let addonIcon = '<image class="addon-addon-icon"/>';
     let toolbarIcon = '<image class="addon-toolbar-icon"/>';
 
     let brandBundle = win.document.getElementById("bundle_brand");
     let appName = brandBundle.getString("brandShortName");
 
     let bundle = win.gNavigatorBundle;
     let msg1 = bundle.getFormattedString("addonPostInstall.message1",
-                                         [addonLabel, appName]);
+                                         [addonName, appName]);
     let msg2 = bundle.getFormattedString("addonPostInstall.messageDetail",
                                          [addonIcon, toolbarIcon]);
 
     return new Promise(resolve => {
       let action = {
         label: bundle.getString("addonPostInstall.okay.label"),
         accessKey: bundle.getString("addonPostInstall.okay.key"),
         callback: resolve,
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -833,16 +833,17 @@ menuitem.bookmark-item {
   font-weight: bold;
 }
 
 .addon-webext-perm-header {
   font-size: 1.3em;
 }
 
 .addon-webext-name {
+  display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon {
   width: 14px;
   height: 14px;
   list-style-image: url("chrome://browser/skin/menuPanel.svg");
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3091,16 +3091,17 @@ menulist.translate-infobar-element > .me
   font-weight: bold;
 }
 
 .addon-webext-perm-header {
   font-size: 1.3em;
 }
 
 .addon-webext-name {
+  display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon {
   width: 14px;
   height: 14px;
   list-style-image: url("chrome://browser/skin/menuPanel.svg");
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -670,16 +670,17 @@ toolbarpaletteitem[place="palette"] > to
 
 #PanelUI-help,
 #PanelUI-quit {
   min-width: 46px;
 }
 
 #PanelUI-update-status > .toolbarbutton-text,
 #PanelUI-fxa-label > .toolbarbutton-text,
+#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-text,
 #PanelUI-customize > .toolbarbutton-text {
   margin: 0;
   padding: 0 6px;
   text-align: start;
 }
 
 #PanelUI-help > .toolbarbutton-text,
 #PanelUI-quit > .toolbarbutton-text,
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -95,34 +95,26 @@
 #newtab-customize-button:-moz-any(:hover, :active, [active]) {
   background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 64, 32, 32);
   background-color: #FFFFFF;
   border: solid 1px #CCCCCC;
   border-radius: 2px;
 }
 
 /* GRID */
-body.compact #newtab-grid {
-  /* Allow #topsites-heading to use position:absolute such that it doesn't
-     affect how many cells we can fit into the grid. */
-  position: relative;
-  padding-top: 1em;
-  margin-top: -1em;
-}
-
 #topsites-heading {
   color: #7A7A7A;
   font-size: 1em;
   font-weight: normal;
   /* Position the heading such that it doesn't affect how many cells we
      can fit into the grid. */
   position: absolute;
-  top: 0;
-  /* The horizontal margin aligns the heading with the cells. */
-  margin: 0 10px;
+  /* The top margin moves the heading away from the grid.
+     The horizontal margin aligns the heading with the cells. */
+  margin: -1em 10px 0;
 }
 
 /* CELLS */
 .newtab-cell {
   --cell-corner-radius: 8px;
   background-color: rgba(255,255,255,.2);
   border-radius: var(--cell-corner-radius);
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2136,16 +2136,17 @@ toolbarbutton.bookmark-item[dragover="tr
   font-weight: bold;
 }
 
 .addon-webext-perm-header {
   font-size: 1.3em;
 }
 
 .addon-webext-name {
+  display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon {
   width: 14px;
   height: 14px;
   list-style-image: url("chrome://browser/skin/menuPanel.svg");
--- a/build/docs/rust.rst
+++ b/build/docs/rust.rst
@@ -4,25 +4,16 @@
 Including Rust Code in Firefox
 ==============================
 
 The build system has support for building and linking Rust crates.
 Rust code is built using ``cargo`` in the typical way, so it is
 straightforward to take an existing Rust crate and integrate it
 into Firefox.
 
-.. important::
-
-   Rust code is enabled by default in Firefox builds. Until we have
-   a required component written in Rust, you can build without by
-   setting ``ac_add_options --disable-rust`` in your mozconfig.
-   This option will be around for a little longer
-   (`bug 1284816 <https://bugzilla.mozilla.org/show_bug.cgi?id=1284816>`_).
-
-
 Linking Rust Crates into libxul
 ===============================
 
 Rust crates that you want to link into libxul should be listed in the
 ``dependencies`` section of `toolkit/library/rust/shared/Cargo.toml <https://dxr.mozilla.org/mozilla-central/source/toolkit/library/rust/shared/Cargo.toml>`_.
 You'll also need to add an ``extern crate`` reference to `toolkit/library/rust/shared/lib.rs <https://dxr.mozilla.org/mozilla-central/source/toolkit/library/rust/shared/lib.rs>`_.
 This ensures that the Rust code will be linked properly into libxul as well
 as the copy of libxul used for gtests.
--- a/build/moz.configure/compilers-util.configure
+++ b/build/moz.configure/compilers-util.configure
@@ -38,17 +38,17 @@ def compiler_class(compiler):
 
             if check_msg:
                 def checking_fn(fn):
                     return checking(check_msg)(fn)
             else:
                 def checking_fn(fn):
                     return fn
 
-            @depends_when(self, dependable(flags), extra_toolchain_flags, when=when)
+            @depends(self, dependable(flags), extra_toolchain_flags, when=when)
             @checking_fn
             def func(compiler, flags, extra_flags):
                 flags = flags or []
                 flags += extra_flags or []
                 flags.append('-c')
 
                 if try_invoke_compiler(
                     compiler.wrapper + [compiler.compiler] + compiler.flags,
--- a/build/moz.configure/headers.configure
+++ b/build/moz.configure/headers.configure
@@ -69,21 +69,21 @@ have_perf_event_h = check_header('linux/
                                  when=building_linux)
 
 js_option('--with-linux-headers',
           help='location where the Linux kernel headers can be found',
           nargs=1)
 
 passed_linux_header_flags = depends_if('--with-linux-headers')(lambda v: ['-I%s' % v[0]])
 
-@depends_when(try_compile(includes=['asm/unistd.h'],
-                          body='return sizeof(__NR_perf_event_open);',
-                          flags=passed_linux_header_flags,
-                          check_msg='for perf_event_open system call'),
-              when=have_perf_event_h)
+@depends(try_compile(includes=['asm/unistd.h'],
+                     body='return sizeof(__NR_perf_event_open);',
+                     flags=passed_linux_header_flags,
+                     check_msg='for perf_event_open system call'),
+         when=have_perf_event_h)
 def have_perf_event_open(have_perf_event_open):
     if have_perf_event_open:
         return True
 
 set_config('HAVE_LINUX_PERF_EVENT_H', have_perf_event_open)
 
 @depends(passed_linux_header_flags, have_perf_event_open)
 def linux_headers_includes(passed_linux_header_flags, have_perf_event_open):
--- a/build/moz.configure/pkg.configure
+++ b/build/moz.configure/pkg.configure
@@ -35,29 +35,29 @@ def pkg_check_modules(var, package_desc,
     if isinstance(package_desc, (tuple, list)):
         package_desc = ' '.join(package_desc)
     package_desc = dependable(package_desc)
 
     @depends(when, '--enable-compile-environment')
     def when_and_compile_environment(when, compile_environment):
         return when and compile_environment
 
-    @depends_when(pkg_config, pkg_config_version,
-                  when=when_and_compile_environment)
+    @depends(pkg_config, pkg_config_version,
+             when=when_and_compile_environment)
     def check_pkg_config(pkg_config, version):
         min_version = '0.9.0'
         if pkg_config is None:
             die("*** The pkg-config script could not be found. Make sure it is\n"
                 "*** in your path, or set the PKG_CONFIG environment variable\n"
                 "*** to the full path to pkg-config.")
         if version < min_version:
             die("*** Your version of pkg-config is too old. You need version %s or newer.",
                 min_version)
 
-    @depends_when(pkg_config, package_desc, when=when_and_compile_environment)
+    @depends(pkg_config, package_desc, when=when_and_compile_environment)
     @imports('subprocess')
     @imports('sys')
     @imports(_from='mozbuild.configure.util', _import='LineIO')
     def package(pkg_config, package_desc):
         # package_desc may start as a depends function, so we can't use
         # @checking here.
         log.info("checking for %s... " % package_desc)
         with log.queue_debug():
@@ -69,29 +69,29 @@ def pkg_check_modules(var, package_desc,
             except subprocess.CalledProcessError as e:
                 log.info("no")
                 log_writer = log.warning if allow_missing else log.error
                 with LineIO(lambda l: log_writer(l)) as o:
                     o.write(e.output)
                 if not allow_missing:
                     sys.exit(1)
 
-    @depends_when(pkg_config, package_desc, when=package)
+    @depends(pkg_config, package_desc, when=package)
     @checking('%s_CFLAGS' % var, callback=lambda t: ' '.join(t))
     def pkg_cflags(pkg_config, package_desc):
         flags = check_cmd_output(pkg_config, '--cflags', package_desc)
         return tuple(flags.split())
 
-    @depends_when(pkg_config, package_desc, when=package)
+    @depends(pkg_config, package_desc, when=package)
     @checking('%s_LIBS' % var, callback=lambda t: ' '.join(t))
     def pkg_libs(pkg_config, package_desc):
         libs = check_cmd_output(pkg_config, '--libs', package_desc)
         # Remove evil flags like -Wl,--export-dynamic
         return tuple(libs.replace('-Wl,--export-dynamic', '').split())
 
-    @depends_when(pkg_cflags, pkg_libs, when=package)
+    @depends(pkg_cflags, pkg_libs, when=package)
     def pkg_info(cflags, libs):
         return namespace(cflags=cflags, libs=libs)
 
     set_config('%s_CFLAGS' % var, pkg_cflags)
     set_config('%s_LIBS' % var, pkg_libs)
 
     return pkg_info
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -1,28 +1,18 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-option('--disable-rust', help='Don\'t include Rust language sources')
-
-@depends('--enable-rust')
-def rust_compiler_names(value):
-    if value:
-        return ['rustc']
-
-@depends('--enable-rust')
-def cargo_binary_names(value):
-    if value:
-        return ['cargo']
-
-rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)
-cargo = check_prog('CARGO', cargo_binary_names, allow_missing=True)
+# Rust is required by `rust_compiler` below. We allow_missing here
+# to propagate failures to the better error message there.
+rustc = check_prog('RUSTC', ['rustc'], allow_missing=True)
+cargo = check_prog('CARGO', ['cargo'], allow_missing=True)
 
 @depends_if(rustc)
 @checking('rustc version', lambda info: info.version)
 def rustc_info(rustc):
         out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
         info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
         return namespace(
             version=Version(info.get('release', '0')),
@@ -42,46 +32,45 @@ def cargo_supports_frozen(cargo):
         if 'MOZ_AUTOMATION' in os.environ and not supported:
             die('cargo in automation must support --frozen')
         return supported
     except subprocess.CalledProcessError as e:
         die('Failed to call cargo: %s', e.message)
 
 set_config('MOZ_CARGO_SUPPORTS_FROZEN', cargo_supports_frozen)
 
-@depends('--enable-rust', rustc, rustc_info)
+@depends(rustc, rustc_info)
 @imports(_from='textwrap', _import='dedent')
-def rust_compiler(value, rustc, rustc_info):
-    if value:
-        if not rustc:
-            die(dedent('''\
-            Rust compiler not found.
-            To compile rust language sources, you must have 'rustc' in your path.
-            See https//www.rust-lang.org/ for more information.
+def rust_compiler(rustc, rustc_info):
+    if not rustc:
+        die(dedent('''\
+        Rust compiler not found.
+        To compile rust language sources, you must have 'rustc' in your path.
+        See https//www.rust-lang.org/ for more information.
 
-            You can install rust by running './mach bootstrap'
-            or by directly running the installer from https://rustup.rs/
-            '''))
-        version = rustc_info.version
-        min_version = Version('1.13')
-        if version < min_version:
-            die(dedent('''\
-            Rust compiler {} is too old.
+        You can install rust by running './mach bootstrap'
+        or by directly running the installer from https://rustup.rs/
+        '''))
+    version = rustc_info.version
+    min_version = Version('1.13')
+    if version < min_version:
+        die(dedent('''\
+        Rust compiler {} is too old.
 
-            To compile Rust language sources please install at least
-            version {} of the 'rustc' toolchain and make sure it is
-            first in your path.
+        To compile Rust language sources please install at least
+        version {} of the 'rustc' toolchain and make sure it is
+        first in your path.
 
-            You can verify this by typing 'rustc --version'.
+        You can verify this by typing 'rustc --version'.
 
-            If you have the 'rustup' tool installed you can upgrade
-            to the latest release by typing 'rustup update'. The
-            installer is available from https://rustup.rs/
-            '''.format(version, min_version)))
-        return True
+        If you have the 'rustup' tool installed you can upgrade
+        to the latest release by typing 'rustup update'. The
+        installer is available from https://rustup.rs/
+        '''.format(version, min_version)))
+    return True
 
 set_config('MOZ_RUST', rust_compiler)
 
 @template
 def rust_triple_alias(host_or_target):
     """Template defining the alias used for rustc's --target flag.
     `host_or_target` is either `host` or `target` (the @depends functions
     from init.configure).
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -394,36 +394,16 @@ def depends_if(*args):
     def decorator(func):
         @depends(*args)
         def wrapper(*args):
             if any(arg for arg in args):
                 return func(*args)
         return wrapper
     return decorator
 
-# Like @depends_if, but a distinguished value passed as a keyword argument
-# "when" is truth tested instead of every argument. This value is not passed
-# to the function if it is called.
-@template
-def depends_when(*args, **kwargs):
-    if not len(kwargs) == 1 and kwargs.get('when'):
-        die('depends_when requires a single keyword argument, "when"')
-    when = kwargs['when']
-    if not when:
-        return depends(*args)
-
-    def decorator(fn):
-        @depends(when, *args)
-        def wrapper(val, *args):
-            if val:
-                return fn(*args)
-        return wrapper
-    return decorator
-
-
 # Hacks related to old-configure
 # ==============================
 
 @dependable
 def old_configure_assignments():
     return []
 
 @dependable
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -177,16 +177,32 @@ http://itisatracker.org:80
 http://trackertest.org:80
 
 https://malware.example.com:443
 https://unwanted.example.com:443
 https://tracking.example.com:443
 https://not-tracking.example.com:443
 https://tracking.example.org:443
 
+#
+# Used while testing flash blocking (Bug 1307604)
+#
+http://flashallow.example.com:80
+http://exception.flashallow.example.com:80
+http://flashblock.example.com:80
+http://exception.flashblock.example.com:80
+http://subdocument.example.com:80
+http://exception.subdocument.example.com:80
+
+#
+# Flash usage can fail unless this URL exists
+#
+http://fpdownload2.macromedia.com:80
+https://fpdownload2.macromedia.com:443
+
 # Bug 1281083
 http://bug1281083.example.com:80
 
 # Bug 483437, 484111
 https://www.bank1.com:443           privileged,cert=escapeattack1
 
 #
 # CONNECT for redirproxy results in a 302 redirect to
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -645,16 +645,25 @@ nsChromeRegistry::MustLoadURLRemotely(ns
     *aResult = !!(flags & REMOTE_REQUIRED);
   }
   return NS_OK;
 }
 
 bool
 nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale)
 {
+#ifdef ENABLE_INTL_API
+  int pref = mozilla::Preferences::GetInt("intl.uidirection", -1);
+  if (pref >= 0) {
+    return (pref > 0);
+  }
+  nsAutoCString locale(aLocale);
+  SanitizeForBCP47(locale);
+  return uloc_isRightToLeft(locale.get());
+#else
   // first check the intl.uidirection.<locale> preference, and if that is not
   // set, check the same preference but with just the first two characters of
   // the locale. If that isn't set, default to left-to-right.
   nsAutoCString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + aLocale;
   nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (!prefBranch) {
     return false;
   }
@@ -665,16 +674,17 @@ nsChromeRegistry::GetDirectionForLocale(
     int32_t hyphen = prefString.FindChar('-');
     if (hyphen >= 1) {
       nsAutoCString shortPref(Substring(prefString, 0, hyphen));
       prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir));
     }
   }
 
   return dir.EqualsLiteral("rtl");
+#endif
 }
 
 NS_IMETHODIMP_(bool)
 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
 {
   nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
   if (!chromeURL)
     return false;
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -21,16 +21,17 @@
 #include "nsEnumeratorUtils.h"
 #include "nsNetUtil.h"
 #include "nsStringEnumerator.h"
 #include "nsTextFormatter.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Unused.h"
+#include "mozilla/intl/LocaleService.h"
 
 #include "nsICommandLine.h"
 #include "nsILocaleService.h"
 #include "nsIObserverService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "mozilla/Preferences.h"
 #include "nsIResProtocolHandler.h"
@@ -381,16 +382,17 @@ nsresult nsChromeRegistryChrome::UpdateS
   if (prefs) {
     rv = SelectLocaleFromPref(prefs);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();
       NS_ASSERTION(obsSvc, "Couldn't get observer service.");
       obsSvc->NotifyObservers((nsIChromeRegistry*) this,
                               "selected-locale-has-changed", nullptr);
+      mozilla::intl::LocaleService::GetInstance()->Refresh();
     }
   }
 
   return rv;
 }
 
 static void
 SerializeURI(nsIURI* aURI,
--- a/chrome/nsIChromeRegistry.idl
+++ b/chrome/nsIChromeRegistry.idl
@@ -53,17 +53,18 @@ interface nsIXULChromeRegistry : nsIChro
 
   // If the optional asBCP47 parameter is true, the locale code will be
   // converted to a BCP47 language tag; in particular, this means that
   // "ja-JP-mac" will be returned as "ja-JP-x-lvariant-mac", which can be
   // passed to ECMA402 Intl API methods without throwing a RangeError.
   ACString getSelectedLocale(in ACString packageName,
                              [optional] in boolean asBCP47);
   
-  // Get the direction of the locale via the intl.uidirection.<locale> pref
+  // Get whether the default writing direction of the locale is RTL
+  // (or may be overridden by intl.uidirection pref)
   boolean isLocaleRTL(in ACString package);
 
   /* Should be called when skins change. Reloads only stylesheets. */
   void refreshSkins();
 
   /**
    * Installable skin XBL is not always granted the same privileges as other
    * chrome. This asks the chrome registry whether scripts are allowed to be
--- a/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js
+++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js
@@ -44,21 +44,21 @@ add_task(function* () {
 
   yield toolbox.switchHost(Toolbox.HostType.WINDOW);
   let hostWindow = toolbox.win.parent;
   let originalWidth = hostWindow.outerWidth;
   let originalHeight = hostWindow.outerHeight;
   hostWindow.resizeTo(640, 300);
 
   info("Testing transitions ltr");
-  yield pushPref("intl.uidirection.en-US", "ltr");
+  yield pushPref("intl.uidirection", 0);
   yield testBreadcrumbTransitions(hostWindow, inspector);
 
   info("Testing transitions rtl");
-  yield pushPref("intl.uidirection.en-US", "rtl");
+  yield pushPref("intl.uidirection", 1);
   yield testBreadcrumbTransitions(hostWindow, inspector);
 
   hostWindow.resizeTo(originalWidth, originalHeight);
 });
 
 function* testBreadcrumbTransitions(hostWindow, inspector) {
   let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
   let startBtn = breadcrumbs.querySelector(".scrollbutton-up");
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -24,16 +24,17 @@ function configureStore() {
   const initialState = {
     prefs: new PrefState({
       logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
     }),
     filters: new FilterState({
       error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
       warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
       info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
+      debug: Services.prefs.getBoolPref(PREFS.FILTER.DEBUG),
       log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
       css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
       net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
       netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
     }),
     ui: new UiState({
       filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
     })
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -25,16 +25,17 @@ subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_webconsole_context_menu_copy_link_location.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_webconsole_context_menu_open_in_var_view.js]
 [browser_webconsole_context_menu_open_url.js]
 [browser_webconsole_context_menu_store_as_global.js]
 [browser_webconsole_filters.js]
+[browser_webconsole_filters_persist.js]
 [browser_webconsole_init.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_keyboard_accessibility.js]
 [browser_webconsole_location_debugger_link.js]
 [browser_webconsole_location_scratchpad_link.js]
 [browser_webconsole_location_styleeditor_link.js]
 [browser_webconsole_nodes_highlight.js]
 [browser_webconsole_observer_notifications.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_filters_persist.js
@@ -0,0 +1,72 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests all filters persist.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
+
+add_task(function* () {
+  let hud = yield openNewTabAndConsole(TEST_URI);
+
+  let filterButtons = yield getFilterButtons(hud);
+  info("Disable all filters");
+  filterButtons.forEach(filterButton => {
+    if (filterIsEnabled(filterButton)) {
+      filterButton.click();
+    }
+  });
+
+  info("Close and re-open the console");
+  yield closeTabAndToolbox();
+  hud = yield openNewTabAndConsole(TEST_URI);
+
+  info("Check that all filters are disabled, and enable them");
+  filterButtons = yield getFilterButtons(hud);
+  filterButtons.forEach(filterButton => {
+    ok(!filterIsEnabled(filterButton), "filter is disabled");
+    filterButton.click();
+  });
+
+  info("Close and re-open the console");
+  yield closeTabAndToolbox();
+  hud = yield openNewTabAndConsole(TEST_URI);
+
+  info("Check that all filters are enabled");
+  filterButtons = yield getFilterButtons(hud);
+  filterButtons.forEach(filterButton => {
+    ok(filterIsEnabled(filterButton), "filter is enabled");
+  });
+
+  // Check that the ui settings were persisted.
+  yield closeTabAndToolbox();
+});
+
+function* getFilterButtons(hud) {
+  const outputNode = hud.ui.experimentalOutputNode;
+
+  info("Wait for console toolbar to appear");
+  const toolbar = yield waitFor(() => {
+    return outputNode.querySelector(".webconsole-filterbar-primary");
+  });
+
+  // Show the filter bar if it is hidden
+  if (!outputNode.querySelector(".webconsole-filterbar-secondary")) {
+    toolbar.querySelector(".devtools-filter-icon").click();
+  }
+
+  info("Wait for console filterbar to appear");
+  const filterBar = yield waitFor(() => {
+    return outputNode.querySelector(".webconsole-filterbar-secondary");
+  });
+  ok(filterBar, "Filter bar is shown when filter icon is clicked.");
+
+  return filterBar.querySelectorAll(".menu-filter-button");
+}
+
+function filterIsEnabled(button) {
+  return button.classList.contains("checked");
+}
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -274,16 +274,17 @@ public:
   bool IsInEffect() const
   {
     return GetEffect() && GetEffect()->IsInEffect();
   }
 
   bool IsPlaying() const
   {
     return mPlaybackRate != 0.0 &&
+           mTimeline &&
            (PlayState() == AnimationPlayState::Running ||
             mPendingState == PendingState::PlayPending);
   }
 
   bool ShouldBeSynchronizedWithMainThread(
     nsCSSPropertyID aProperty,
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning) const;
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -1325,61 +1325,69 @@ BuildSegmentsFromValueEntries(nsTArray<K
         lastProperty = aEntries[i].mProperty;
       } else {
         // Skip this entry if we did not handle the missing entry.
         ++i;
         continue;
       }
     }
 
+    // Skip this entry if the next entry has the same offset except for initial
+    // and final ones. We will handle missing keyframe in the next loop
+    // if the property is changed on the next entry.
+    if (aEntries[i].mProperty == aEntries[i + 1].mProperty &&
+        aEntries[i].mOffset == aEntries[i + 1].mOffset &&
+        aEntries[i].mOffset != 1.0f && aEntries[i].mOffset != 0.0f) {
+      ++i;
+      continue;
+    }
+
     // No keyframe for this property at offset 1.
     if (aEntries[i].mProperty != aEntries[i + 1].mProperty &&
         aEntries[i].mOffset != 1.0f) {
       HandleMissingFinalKeyframe(aResult, aEntries[i], animationProperty);
       // Move on to new property.
       animationProperty = nullptr;
       ++i;
       continue;
     }
 
-    // Starting from i, determine the next [i, j] interval from which to
-    // generate a segment.
-    size_t j;
+    // Starting from i + 1, determine the next [i, j] interval from which to
+    // generate a segment. Basically, j is i + 1, but there are some special
+    // cases for offset 0 and 1, so we need to handle them specifically.
+    // Note: From this moment, we make sure [i + 1] is valid and
+    //       there must be an initial entry (i.e. mOffset = 0.0) and
+    //       a final entry (i.e. mOffset = 1.0). Besides, all the entries
+    //       with the same offsets except for initial/final ones are filtered
+    //       out already.
+    size_t j = i + 1;
     if (aEntries[i].mOffset == 0.0f && aEntries[i + 1].mOffset == 0.0f) {
       // We need to generate an initial zero-length segment.
       MOZ_ASSERT(aEntries[i].mProperty == aEntries[i + 1].mProperty);
-      j = i + 1;
       while (j + 1 < n &&
              aEntries[j + 1].mOffset == 0.0f &&
              aEntries[j + 1].mProperty == aEntries[j].mProperty) {
         ++j;
       }
     } else if (aEntries[i].mOffset == 1.0f) {
       if (aEntries[i + 1].mOffset == 1.0f &&
           aEntries[i + 1].mProperty == aEntries[i].mProperty) {
         // We need to generate a final zero-length segment.
-        j = i + 1;
         while (j + 1 < n &&
                aEntries[j + 1].mOffset == 1.0f &&
                aEntries[j + 1].mProperty == aEntries[j].mProperty) {
           ++j;
         }
       } else {
         // New property.
         MOZ_ASSERT(aEntries[i].mProperty != aEntries[i + 1].mProperty);
         animationProperty = nullptr;
         ++i;
         continue;
       }
-    } else {
-      while (aEntries[i].mOffset == aEntries[i + 1].mOffset &&
-             aEntries[i].mProperty == aEntries[i + 1].mProperty) {
-        ++i;
-      }
-      j = i + 1;
     }
 
     // If we've moved on to a new property, create a new AnimationProperty
     // to insert segments into.
     if (aEntries[i].mProperty != lastProperty) {
       MOZ_ASSERT(aEntries[i].mOffset == 0.0f);
       MOZ_ASSERT(!animationProperty);
       animationProperty = aResult.AppendElement();
--- a/dom/animation/test/chrome.ini
+++ b/dom/animation/test/chrome.ini
@@ -10,10 +10,11 @@ support-files =
 # file_animate_xrays.html needs to go in mochitest.ini since it is served
 # over HTTP
 [chrome/test_animation_observers.html]
 [chrome/test_animation_performance_warning.html]
 [chrome/test_animation_properties.html]
 [chrome/test_generated_content_getAnimations.html]
 [chrome/test_observers_for_sync_api.html]
 [chrome/test_restyles.html]
+skip-if = os == 'android' && processor == 'x86' # bug 1335986
 [chrome/test_running_on_compositor.html]
 [chrome/test_simulate_compute_values_failure.html]
--- a/dom/animation/test/chrome/test_animation_properties.html
+++ b/dom/animation/test/chrome/test_animation_properties.html
@@ -704,32 +704,86 @@ var gTests = [
                             value(1, '5px', 'replace') ] },
                 { property: 'margin-bottom',
                   values: [ value(0, undefined, 'add', 'linear'),
                             value(1, '5px', 'replace') ] },
                 { property: 'margin-left',
                   values: [ value(0, undefined, 'add', 'linear'),
                             value(1, '5px', 'replace') ] } ]
   },
+  { desc:     'a missing property in initial keyframe and there are some ' +
+              'keyframes with the same offset',
+    frames:   [ { },
+                { margin: '10px', offset: 0.5 },
+                { margin: '20px', offset: 0.5 },
+                { margin: '30px'} ],
+    expected: [ { property: 'margin-top',
+                  values: [ value(0, undefined, 'add', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1,   '30px', 'replace') ] },
+                { property: 'margin-right',
+                  values: [ value(0, undefined, 'add', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1,   '30px', 'replace') ] },
+                { property: 'margin-bottom',
+                  values: [ value(0, undefined, 'add', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1,   '30px', 'replace') ] },
+                { property: 'margin-left',
+                  values: [ value(0, undefined, 'add', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1,   '30px', 'replace') ] } ]
+  },
   { desc:     'a missing property in final keyframe',
     frames:   [ { margin: '5px' },
                 { } ],
     expected: [ { property: 'margin-top',
                   values: [ value(0, '5px', 'replace', 'linear'),
                             value(1, undefined, 'add') ] },
                 { property: 'margin-right',
                   values: [ value(0, '5px', 'replace', 'linear'),
                             value(1, undefined, 'add') ] },
                 { property: 'margin-bottom',
                   values: [ value(0, '5px', 'replace', 'linear'),
                             value(1, undefined, 'add') ] },
                 { property: 'margin-left',
                   values: [ value(0, '5px', 'replace', 'linear'),
                             value(1, undefined, 'add') ] } ]
   },
+  { desc:     'a missing property in final keyframe and there are some ' +
+              'keyframes with the same offsets',
+    frames:   [ { margin: '5px' },
+                { margin: '10px', offset: 0.5 },
+                { margin: '20px', offset: 0.5 },
+                { } ],
+    expected: [ { property: 'margin-top',
+                  values: [ value(0,   '5px', 'replace', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1,   undefined, 'add') ] },
+                { property: 'margin-right',
+                  values: [ value(0, '5px', 'replace', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1, undefined, 'add') ] },
+                { property: 'margin-bottom',
+                  values: [ value(0, '5px', 'replace', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1, undefined, 'add') ] },
+                { property: 'margin-left',
+                  values: [ value(0, '5px', 'replace', 'linear'),
+                            value(0.5, '10px', 'replace'),
+                            value(0.5, '20px', 'replace', 'linear'),
+                            value(1, undefined, 'add') ] } ]
+  },
   { desc:     'a missing property in final keyframe where it forms the last'
               + ' segment in the series',
     frames:   [ { margin: '5px' },
                 { marginLeft: '5px',
                   marginRight: '5px',
                   marginBottom: '5px' } ],
     expected: [ { property: 'margin-bottom',
                   values: [ value(0, '5px', 'replace', 'linear'),
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1324554-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  <meta charset="UTF-8">
+  <title>Bug 1324554 - missing final keyframes and zero-length segments together</title>
+  </head>
+  <script>
+    function go() {
+      var div = document.getElementById('target');
+      div.animate([ { "flex": "none" },
+                    { "flex": "initial", offset: 0.5 },
+                    { "flex": "0.0 ", offset: 0.5 },
+                    {} ]);
+    }
+  </script>
+  <body onload="go()">
+    <div id='target' ></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1333539-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+window.onload = function(){
+  let body = document.getElementsByTagName("body")[0];
+  let target = document.createElement("div");
+  let anim1 = new Animation();
+  let anim2 = new Animation();
+  let effect = new KeyframeEffect(target, { opacity: [ 0, 1 ] }, 1000);
+  body.appendChild(target);
+  target.appendChild(document.createElement("meter"));
+  anim1.startTime = 88;
+  anim1.timeline = null;
+  anim1.pause();
+  anim1.effect = effect;
+  anim2.effect = effect;
+  anim1.effect = effect;
+
+  Promise.all([anim1.ready, anim2.ready]).then(function() {
+    document.documentElement.classList.remove("reftest-wait");
+  });
+};
+</script>
+</head>
+<body></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1333539-2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+div {
+  width: 100px;
+  height: 100px;
+  background-color: blue;
+}
+</style>
+<script>
+window.onload = function(){
+  let body = document.getElementsByTagName("body")[0];
+  let target = document.createElement("div");
+  let anim1 = new Animation();
+  let anim2 = new Animation();
+  let effect = new KeyframeEffect(target, { opacity: [ 0, 1 ] }, 1000);
+  body.appendChild(target);
+  anim1.startTime = 88;
+  anim1.timeline = null;
+  anim1.pause();
+  anim1.effect = effect;
+  anim2.effect = effect;
+  anim1.effect = effect;
+  // Put another opacity animation on the top of the effect stack so that we
+  // try to send a lower priority animation that has no timeline to the
+  // compositor.
+  let anim3 = target.animate({ opacity : [ 1, 0 ] }, 1000);
+
+  Promise.all([anim1.ready, anim2.ready, anim2.ready]).then(function() {
+    document.documentElement.classList.remove("reftest-wait");
+  });
+};
+</script>
+</head>
+<body></body>
+</html>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -12,13 +12,16 @@ pref(dom.animations-api.core.enabled,tru
 asserts-if(stylo,10) pref(dom.animations-api.core.enabled,true) load 1277272-1.html # bug 1324694
 pref(dom.animations-api.core.enabled,true) load 1290535-1.html
 pref(dom.animations-api.core.enabled,true) load 1304886-1.html
 pref(dom.animations-api.core.enabled,true) load 1322382-1.html
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1322291-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1322291-2.html # bug 1311257 and bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1323114-1.html # bug 1324690 and bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1323114-2.html # bug 1324690
+skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1324554-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1325193-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330190-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330190-2.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330513-1.html # bug 1311257
+skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333539-1.html
+skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333539-2.html
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333418-1.html # bug 1311257
--- a/dom/animation/test/style/file_missing-keyframe-on-compositor.html
+++ b/dom/animation/test/style/file_missing-keyframe-on-compositor.html
@@ -505,11 +505,38 @@ promise_test(t => {
       SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
     assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 300, 0)',
       'Transform value for animation with no keyframe at offset 1 and its ' +
       'iterationComposite is accumulate');
   });
 }, 'Transform value for animation with no keyframe at offset 1 and its ' +
    'iterationComposite is accumulate');
 
+promise_test(t => {
+  useTestRefreshMode(t);
+
+  var div = addDiv(t);
+  var lowerAnimation =
+    div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] },
+                100 * MS_PER_SEC);
+  var higherAnimation = div.animate({ transform: 'translateX(300px)' },
+                                    100 * MS_PER_SEC);
+
+  lowerAnimation.timeline = null;
+  // Set current time at 50% duration.
+  lowerAnimation.currentTime = 50 * MS_PER_SEC;
+
+  return waitForPaintsFlushed().then(() => {
+    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC);
+
+    var transform =
+      SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+    // (150px + 300px) * (50 * MS_PER_SEC / 100 * MS_PER_SEC) = 225px.
+    assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 225, 0)',
+      'Composed transform value should be composed onto the value of ' +
+      'lower-priority animation without timeline');
+  });
+}, 'Transform value for animation with no keyframe at offset 0 at 50% when ' +
+   'composed onto an animation without timeline');
+
 done();
 </script>
 </body>
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -36,16 +36,19 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
 
 AudioChannelAgent::AudioChannelAgent()
   : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
   , mInnerWindowID(0)
   , mIsRegToService(false)
 {
+  // Init service in the begining, it can help us to know whether there is any
+  // created media component via AudioChannelService::IsServiceStarted().
+  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
 }
 
 AudioChannelAgent::~AudioChannelAgent()
 {
   Shutdown();
 }
 
 void
@@ -176,16 +179,19 @@ AudioChannelAgent::InitInternal(nsPIDOMW
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
   }
 
+  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  service->NotifyCreatedNewAgent(this);
+
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, InitInternal, this = %p, type = %d, "
           "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
           mWindow.get(), (!!mCallback || !!mWeakCallback)));
 
   return NS_OK;
 }
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -196,16 +196,23 @@ AudioChannelService::CreateServiceIfNeed
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gAudioChannelService) {
     gAudioChannelService = new AudioChannelService();
   }
 }
 
+/* static */ bool
+AudioChannelService::IsServiceStarted()
+{
+  // The service would start when the first AudioChannelAgent is created.
+  return !!gAudioChannelService;
+}
+
 /* static */ already_AddRefed<AudioChannelService>
 AudioChannelService::GetOrCreate()
 {
   if (sXPCOMShuttingDown) {
     return nullptr;
   }
 
   CreateServiceIfNeeded();
@@ -282,16 +289,29 @@ AudioChannelService::AudioChannelService
                                "dom.audiochannel.audioCompeting");
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
+AudioChannelService::NotifyCreatedNewAgent(AudioChannelAgent* aAgent)
+{
+  MOZ_ASSERT(aAgent);
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
+  if (!window) {
+    return;
+  }
+
+  window->NotifyCreatedNewMediaComponent();
+}
+
+void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                AudibleState aAudible)
 {
   MOZ_ASSERT(aAgent);
 
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (!winData) {
@@ -1387,17 +1407,28 @@ void
 AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
   if (!window) {
     return;
   }
 
   MOZ_ASSERT(window->IsOuterWindow());
-  if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK) {
+  nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
+  if (!inner) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> doc = inner->GetExtantDoc();
+  if (!doc) {
+    return;
+  }
+
+  if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK ||
+      !doc->Hidden()) {
     return;
   }
 
   NS_DispatchToCurrentThread(NS_NewRunnableFunction([window] () -> void {
       nsCOMPtr<nsIObserverService> observerService =
         services::GetObserverService();
       if (NS_WARN_IF(!observerService)) {
         return;
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -95,16 +95,18 @@ public:
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
   static bool IsAudioChannelMutedByDefault();
 
   static PRLogModuleInfo* GetAudioChannelLog();
 
   static bool IsEnableAudioCompeting();
 
+  static bool IsServiceStarted();
+
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
   void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                  AudibleState aAudible);
 
   /**
@@ -192,16 +194,18 @@ public:
   static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString);
   static void GetDefaultAudioChannelString(nsAString& aString);
 
   void Notify(uint64_t aWindowID);
 
   void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
                            bool aContentOrNormalChannel, bool aAnyChannel);
 
+  void NotifyCreatedNewAgent(AudioChannelAgent* aAgent);
+
 private:
   AudioChannelService();
   ~AudioChannelService();
 
   void RefreshAgents(nsPIDOMWindowOuter* aWindow,
                      std::function<void(AudioChannelAgent*)> aFunc);
 
   static void CreateServiceIfNeeded();
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -597,44 +597,31 @@ Element::ClassList()
   if (!slots->mClassList) {
     slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class);
   }
 
   return slots->mClassList;
 }
 
 void
-Element::GetClassList(nsISupports** aClassList)
-{
-  NS_ADDREF(*aClassList = ClassList());
-}
-
-void
 Element::GetAttributeNames(nsTArray<nsString>& aResult)
 {
   uint32_t count = mAttrsAndChildren.AttrCount();
   for (uint32_t i = 0; i < count; ++i) {
     const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
     name->GetQualifiedName(*aResult.AppendElement());
   }
 }
 
 already_AddRefed<nsIHTMLCollection>
 Element::GetElementsByTagName(const nsAString& aLocalName)
 {
   return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName);
 }
 
-void
-Element::GetElementsByTagName(const nsAString& aLocalName,
-                              nsIDOMHTMLCollection** aResult)
-{
-  *aResult = GetElementsByTagName(aLocalName).take();
-}
-
 nsIFrame*
 Element::GetStyledFrame()
 {
   nsIFrame *frame = GetPrimaryFrame(FlushType::Layout);
   return frame ? nsLayoutUtils::GetStyleFrame(frame) : nullptr;
 }
 
 nsIScrollableFrame*
@@ -1411,31 +1398,16 @@ Element::GetElementsByTagNameNS(const ns
     }
   }
 
   NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
 
   return NS_GetContentList(this, nameSpaceId, aLocalName);
 }
 
-nsresult
-Element::GetElementsByTagNameNS(const nsAString& namespaceURI,
-                                const nsAString& localName,
-                                nsIDOMHTMLCollection** aResult)
-{
-  mozilla::ErrorResult rv;
-  nsCOMPtr<nsIHTMLCollection> list =
-    GetElementsByTagNameNS(namespaceURI, localName, rv);
-  if (rv.Failed()) {
-    return rv.StealNSResult();
-  }
-  list.forget(aResult);
-  return NS_OK;
-}
-
 bool
 Element::HasAttributeNS(const nsAString& aNamespaceURI,
                         const nsAString& aLocalName) const
 {
   int32_t nsid =
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
                                                        nsContentUtils::IsChromeDoc(OwnerDoc()));
 
@@ -1449,25 +1421,16 @@ Element::HasAttributeNS(const nsAString&
 }
 
 already_AddRefed<nsIHTMLCollection>
 Element::GetElementsByClassName(const nsAString& aClassNames)
 {
   return nsContentUtils::GetElementsByClassName(this, aClassNames);
 }
 
-nsresult
-Element::GetElementsByClassName(const nsAString& aClassNames,
-                                nsIDOMHTMLCollection** aResult)
-{
-  *aResult =
-    nsContentUtils::GetElementsByClassName(this, aClassNames).take();
-  return NS_OK;
-}
-
 /**
  * Returns the count of descendants (inclusive of aContent) in
  * the uncomposed document that are explicitly set as editable.
  */
 static uint32_t
 EditableInclusiveDescendantCount(nsIContent* aContent)
 {
   auto htmlElem = nsGenericHTMLElement::FromContent(aContent);
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1088,30 +1088,16 @@ public:
   static CORSMode StringToCORSMode(const nsAString& aValue);
 
   /**
    * Return the CORS mode for a given nsAttrValue (which may be null,
    * but if not should have been parsed via ParseCORSValue).
    */
   static CORSMode AttrValueToCORSMode(const nsAttrValue* aValue);
 
-  // These are just used to implement nsIDOMElement using
-  // NS_FORWARD_NSIDOMELEMENT_TO_GENERIC and for quickstubs.
-  void
-    GetElementsByTagName(const nsAString& aQualifiedName,
-                         nsIDOMHTMLCollection** aResult);
-  nsresult
-    GetElementsByTagNameNS(const nsAString& aNamespaceURI,
-                           const nsAString& aLocalName,
-                           nsIDOMHTMLCollection** aResult);
-  nsresult
-    GetElementsByClassName(const nsAString& aClassNames,
-                           nsIDOMHTMLCollection** aResult);
-  void GetClassList(nsISupports** aClassList);
-
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) final override;
 
   nsINode* GetScopeChainParent() const override;
 
   /**
    * Locate an nsIEditor rooted at this content node, if there is one.
    */
   nsIEditor* GetEditorInternal();
@@ -1650,168 +1636,51 @@ nsresult                                
 
 #define NS_FORWARD_NSIDOMELEMENT_TO_GENERIC                                   \
 typedef mozilla::dom::Element Element;                                        \
 NS_IMETHOD GetTagName(nsAString& aTagName) final override                     \
 {                                                                             \
   Element::GetTagName(aTagName);                                              \
   return NS_OK;                                                               \
 }                                                                             \
-NS_IMETHOD GetId(nsAString& aId) final override                               \
-{                                                                             \
-  Element::GetId(aId);                                                        \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD SetId(const nsAString& aId) final override                         \
-{                                                                             \
-  Element::SetId(aId);                                                        \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD GetClassName(nsAString& aClassName) final override                 \
-{                                                                             \
-  Element::GetClassName(aClassName);                                          \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD SetClassName(const nsAString& aClassName) final override           \
-{                                                                             \
-  Element::SetClassName(aClassName);                                          \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD GetClassList(nsISupports** aClassList) final override              \
-{                                                                             \
-  Element::GetClassList(aClassList);                                          \
-  return NS_OK;                                                               \
-}                                                                             \
 NS_IMETHOD GetAttributes(nsIDOMMozNamedAttrMap** aAttributes) final override  \
 {                                                                             \
   NS_ADDREF(*aAttributes = Attributes());                                     \
   return NS_OK;                                                               \
 }                                                                             \
 using Element::GetAttribute;                                                  \
 NS_IMETHOD GetAttribute(const nsAString& name, nsAString& _retval) final      \
   override                                                                    \
 {                                                                             \
   nsString attr;                                                              \
   GetAttribute(name, attr);                                                   \
   _retval = attr;                                                             \
   return NS_OK;                                                               \
 }                                                                             \
-NS_IMETHOD GetAttributeNS(const nsAString& namespaceURI,                      \
-                          const nsAString& localName,                         \
-                          nsAString& _retval) final override                  \
-{                                                                             \
-  Element::GetAttributeNS(namespaceURI, localName, _retval);                  \
-  return NS_OK;                                                               \
-}                                                                             \
 NS_IMETHOD SetAttribute(const nsAString& name,                                \
                         const nsAString& value) override                      \
 {                                                                             \
   mozilla::ErrorResult rv;                                                    \
   Element::SetAttribute(name, value, rv);                                     \
   return rv.StealNSResult();                                                  \
 }                                                                             \
-NS_IMETHOD SetAttributeNS(const nsAString& namespaceURI,                      \
-                          const nsAString& qualifiedName,                     \
-                          const nsAString& value) final override              \
-{                                                                             \
-  mozilla::ErrorResult rv;                                                    \
-  Element::SetAttributeNS(namespaceURI, qualifiedName, value, rv);            \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
-using Element::RemoveAttribute;                                               \
-NS_IMETHOD RemoveAttribute(const nsAString& name) final override              \
-{                                                                             \
-  mozilla::ErrorResult rv;                                                    \
-  RemoveAttribute(name, rv);                                                  \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
-NS_IMETHOD RemoveAttributeNS(const nsAString& namespaceURI,                   \
-                             const nsAString& localName) final override       \
-{                                                                             \
-  mozilla::ErrorResult rv;                                                    \
-  Element::RemoveAttributeNS(namespaceURI, localName, rv);                    \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
 using Element::HasAttribute;                                                  \
 NS_IMETHOD HasAttribute(const nsAString& name,                                \
                            bool* _retval) final override                      \
 {                                                                             \
   *_retval = HasAttribute(name);                                              \
   return NS_OK;                                                               \
 }                                                                             \
-NS_IMETHOD HasAttributeNS(const nsAString& namespaceURI,                      \
-                          const nsAString& localName,                         \
-                          bool* _retval) final override                       \
-{                                                                             \
-  *_retval = Element::HasAttributeNS(namespaceURI, localName);                \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD HasAttributes(bool* _retval) final override                        \
-{                                                                             \
-  *_retval = Element::HasAttributes();                                        \
-  return NS_OK;                                                               \
-}                                                                             \
 NS_IMETHOD GetAttributeNode(const nsAString& name,                            \
                             nsIDOMAttr** _retval) final override              \
 {                                                                             \
   NS_IF_ADDREF(*_retval = Element::GetAttributeNode(name));                   \
   return NS_OK;                                                               \
 }                                                                             \
-NS_IMETHOD SetAttributeNode(nsIDOMAttr* newAttr,                              \
-                            nsIDOMAttr** _retval) final override              \
-{                                                                             \
-  if (!newAttr) {                                                             \
-    return NS_ERROR_INVALID_POINTER;                                          \
-  }                                                                           \
-  mozilla::ErrorResult rv;                                                    \
-  mozilla::dom::Attr* attr = static_cast<mozilla::dom::Attr*>(newAttr);       \
-  *_retval = Element::SetAttributeNode(*attr, rv).take();                     \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
-NS_IMETHOD RemoveAttributeNode(nsIDOMAttr* oldAttr,                           \
-                               nsIDOMAttr** _retval) final override           \
-{                                                                             \
-  if (!oldAttr) {                                                             \
-    return NS_ERROR_INVALID_POINTER;                                          \
-  }                                                                           \
-  mozilla::ErrorResult rv;                                                    \
-  mozilla::dom::Attr* attr = static_cast<mozilla::dom::Attr*>(oldAttr);       \
-  *_retval = Element::RemoveAttributeNode(*attr, rv).take();                  \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
 NS_IMETHOD GetAttributeNodeNS(const nsAString& namespaceURI,                  \
                               const nsAString& localName,                     \
                               nsIDOMAttr** _retval) final override            \
 {                                                                             \
   NS_IF_ADDREF(*_retval = Element::GetAttributeNodeNS(namespaceURI,           \
                                                       localName));            \
   return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD SetAttributeNodeNS(nsIDOMAttr* newAttr,                            \
-                              nsIDOMAttr** _retval) final override            \
-{                                                                             \
-  mozilla::ErrorResult rv;                                                    \
-  mozilla::dom::Attr* attr = static_cast<mozilla::dom::Attr*>(newAttr);       \
-  *_retval = Element::SetAttributeNodeNS(*attr, rv).take();                   \
-  return rv.StealNSResult();                                                      \
-}                                                                             \
-NS_IMETHOD GetElementsByTagName(const nsAString& name,                        \
-                                nsIDOMHTMLCollection** _retval) final         \
-                                                                override      \
-{                                                                             \
-  Element::GetElementsByTagName(name, _retval);                               \
-  return NS_OK;                                                               \
-}                                                                             \
-NS_IMETHOD GetElementsByTagNameNS(const nsAString& namespaceURI,              \
-                                  const nsAString& localName,                 \
-                                  nsIDOMHTMLCollection** _retval) final       \
-                                                                  override    \
-{                                                                             \
-  return Element::GetElementsByTagNameNS(namespaceURI, localName,             \
-                                         _retval);                            \
-}                                                                             \
-NS_IMETHOD GetElementsByClassName(const nsAString& classes,                   \
-                                  nsIDOMHTMLCollection** _retval) final       \
-                                                                  override    \
-{                                                                             \
-  return Element::GetElementsByClassName(classes, _retval);                   \
 }
 #endif // mozilla_dom_Element_h__
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3,16 +3,17 @@
 /* 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/. */
 
 /*
  * Base class for all our document implementations.
  */
 
+#include "AudioChannelService.h"
 #include "nsDocument.h"
 #include "nsIDocumentInlines.h"
 #include "mozilla/AnimationComparator.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EffectSet.h"
@@ -258,16 +259,18 @@
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 #ifdef MOZ_WEBRTC
 #include "IPeerConnection.h"
 #endif // MOZ_WEBRTC
 
+#include "nsIURIClassifier.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
 static LazyLogModule gCspPRLog("CSP");
 
@@ -1375,16 +1378,17 @@ nsIDocument::nsIDocument()
   PR_INIT_CLIST(&mDOMMediaQueryLists);
 }
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
   , mIsTopLevelContentDocument(false)
   , mIsContentDocument(false)
   , mSubDocuments(nullptr)
+  , mFlashClassification(FlashClassification::Unclassified)
   , mHeaderData(nullptr)
   , mIsGoingAway(false)
   , mInDestructor(false)
   , mMayHaveTitleElement(false)
   , mHasWarnedAboutBoxObjects(false)
   , mDelayFrameLoaderInitialization(false)
   , mSynchronousDOMContentLoaded(false)
   , mInXBLUpdate(false)
@@ -3150,16 +3154,21 @@ nsDocument::GetAllowPlugins(bool * aAllo
     docShell->GetAllowPlugins(aAllowPlugins);
 
     // If the docshell allows plugins, we check whether
     // we are sandboxed and plugins should not be allowed.
     if (*aAllowPlugins)
       *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
   }
 
+  if (*aAllowPlugins) {
+    FlashClassification classification = DocumentFlashClassification();
+    *aAllowPlugins = (classification != FlashClassification::Denied);
+  }
+
   return NS_OK;
 }
 
 bool
 nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -6955,16 +6964,18 @@ nsDocument::GetBoxObjectFor(Element* aEl
     if (boxObject) {
       return boxObject.forget().downcast<BoxObject>();
     }
   }
 
   int32_t namespaceID;
   nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
 
+  // XXXbz Can we just switch to using "new" directly instead of this
+  // createInstance insanity?
   nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
   if (namespaceID == kNameSpaceID_XUL) {
     if (tag == nsGkAtoms::browser ||
         tag == nsGkAtoms::editor ||
         tag == nsGkAtoms::iframe)
       contractID += "-container";
     else if (tag == nsGkAtoms::menu)
       contractID += "-menu";
@@ -12069,19 +12080,17 @@ nsDocument::MaybeActiveMediaComponents()
     return;
   }
 
   if (!mWindow) {
     return;
   }
 
   mEverInForeground = true;
-  if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) {
-    GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
-  }
+  GetWindow()->MaybeActiveMediaComponents();
 }
 
 NS_IMETHODIMP
 nsDocument::GetHidden(bool* aHidden)
 {
   *aHidden = Hidden();
   return NS_OK;
 }
@@ -12968,8 +12977,203 @@ nsDocument::CheckCustomElementName(const
   // Throw NotFoundError if 'is' is not-null and definition is null
   if (!nsContentUtils::LookupCustomElementDefinition(this, aLocalName,
                                                      aNamespaceID, is)) {
       rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
   return is;
 }
+
+/**
+ * Helper function for |nsDocument::PrincipalFlashClassification|
+ *
+ * Adds a table name string to a table list (a comma separated string). The
+ * table will not be added if the name is an empty string.
+ */
+static void
+MaybeAddTableToTableList(const nsACString& aTableNames,
+                         nsACString& aTableList)
+{
+  if (aTableNames.IsEmpty()) {
+    return;
+  }
+  if (!aTableList.IsEmpty()) {
+    aTableList.AppendLiteral(",");
+  }
+  aTableList.Append(aTableNames);
+}
+
+/**
+ * Helper function for |nsDocument::PrincipalFlashClassification|
+ *
+ * Takes an array of table names and a comma separated list of table names
+ * Returns |true| if any table name in the array matches a table name in the
+ * comma separated list.
+ */
+static bool
+ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
+                   const nsACString& aTableNames)
+{
+  for (const nsCString& table : aTableArray) {
+    // This check is sufficient because table names cannot contain commas and
+    // cannot contain another existing table name.
+    if (FindInReadable(table, aTableNames)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Retrieves the classification of the Flash plugins in the document based on
+ * the classification lists.
+ *
+ * For more information, see
+ * toolkit/components/url-classifier/flash-block-lists.rst
+ */
+nsIDocument::FlashClassification
+nsDocument::PrincipalFlashClassification(bool aIsTopLevel)
+{
+  nsresult rv;
+
+  // If flash blocking is disabled, it is equivalent to all sites being
+  // whitelisted.
+  if (!Preferences::GetBool("plugins.flashBlock.enabled")) {
+    return FlashClassification::Allowed;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
+  if (principal->GetIsNullPrincipal()) {
+    return FlashClassification::Denied;
+  }
+
+  nsCOMPtr<nsIURI> classificationURI;
+  rv = principal->GetURI(getter_AddRefs(classificationURI));
+  if (NS_FAILED(rv) || !classificationURI) {
+    return FlashClassification::Denied;
+  }
+
+  nsAutoCString allowTables, allowExceptionsTables,
+                denyTables, denyExceptionsTables,
+                subDocDenyTables, subDocDenyExceptionsTables,
+                tables;
+  Preferences::GetCString("urlclassifier.flashAllowTable", &allowTables);
+  MaybeAddTableToTableList(allowTables, tables);
+  Preferences::GetCString("urlclassifier.flashAllowExceptTable",
+                          &allowExceptionsTables);
+  MaybeAddTableToTableList(allowExceptionsTables, tables);
+  Preferences::GetCString("urlclassifier.flashTable", &denyTables);
+  MaybeAddTableToTableList(denyTables, tables);
+  Preferences::GetCString("urlclassifier.flashExceptTable",
+                          &denyExceptionsTables);
+  MaybeAddTableToTableList(denyExceptionsTables, tables);
+  if (!aIsTopLevel) {
+    Preferences::GetCString("urlclassifier.flashSubDocTable",
+                            &subDocDenyTables);
+    MaybeAddTableToTableList(subDocDenyTables, tables);
+    Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
+                            &subDocDenyExceptionsTables);
+    MaybeAddTableToTableList(subDocDenyExceptionsTables, tables);
+  }
+
+  if (tables.IsEmpty()) {
+    return FlashClassification::Unknown;
+  }
+
+  nsCOMPtr<nsIURIClassifier> uriClassifier =
+    do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return FlashClassification::Denied;
+  }
+
+  nsTArray<nsCString> results;
+  rv = uriClassifier->ClassifyLocalWithTables(classificationURI,
+                                              tables,
+                                              results);
+  if (NS_FAILED(rv)) {
+    if (rv == NS_ERROR_MALFORMED_URI) {
+      // This means that the URI had no hostname (ex: file://doc.html). In this
+      // case, we allow the default (Unknown plugin) behavior.
+      return FlashClassification::Unknown;
+    } else {
+      return FlashClassification::Denied;
+    }
+  }
+
+  if (results.IsEmpty()) {
+    return FlashClassification::Unknown;
+  }
+
+  if (ArrayContainsTable(results, denyTables) &&
+      !ArrayContainsTable(results, denyExceptionsTables)) {
+    return FlashClassification::Denied;
+  } else if (ArrayContainsTable(results, allowTables) &&
+             !ArrayContainsTable(results, allowExceptionsTables)) {
+    return FlashClassification::Allowed;
+  }
+
+  if (!aIsTopLevel && ArrayContainsTable(results, subDocDenyTables) &&
+      !ArrayContainsTable(results, subDocDenyExceptionsTables)) {
+    return FlashClassification::Denied;
+  }
+
+  return FlashClassification::Unknown;
+}
+
+nsIDocument::FlashClassification
+nsDocument::ComputeFlashClassification()
+{
+  nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
+  if (!current) {
+    return FlashClassification::Denied;
+  }
+  nsCOMPtr<nsIDocShellTreeItem> parent;
+  DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
+  MOZ_ASSERT(NS_SUCCEEDED(rv),
+             "nsIDocShellTreeItem::GetSameTypeParent should never fail");
+
+  bool isTopLevel = !parent;
+  FlashClassification classification;
+  if (isTopLevel) {
+    classification = PrincipalFlashClassification(isTopLevel);
+  } else {
+    nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
+    FlashClassification parentClassification =
+      parentDocument->DocumentFlashClassification();
+
+    if (parentClassification == FlashClassification::Denied) {
+      classification = FlashClassification::Denied;
+    } else {
+      classification = PrincipalFlashClassification(isTopLevel);
+
+      // Allow unknown children to inherit allowed status from parent, but
+      // do not allow denied children to do so.
+      if (classification == FlashClassification::Unknown &&
+          parentClassification == FlashClassification::Allowed) {
+        classification = FlashClassification::Allowed;
+      }
+    }
+  }
+
+  return classification;
+}
+
+/**
+ * Retrieves the classification of plugins in this document. This is dependent
+ * on the classification of this document and all parent documents.
+ * This function is infallible - It must return some classification that
+ * callers can act on.
+ *
+ * This function will NOT return FlashClassification::Unclassified
+ */
+nsIDocument::FlashClassification
+nsDocument::DocumentFlashClassification()
+{
+  if (mFlashClassification == FlashClassification::Unclassified) {
+    FlashClassification result = ComputeFlashClassification();
+    mFlashClassification = result;
+    MOZ_ASSERT(result != FlashClassification::Unclassified,
+      "nsDocument::GetPluginClassification should never return Unclassified");
+  }
+
+  return mFlashClassification;
+}
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1300,16 +1300,18 @@ protected:
                               bool aPersisted);
 
   virtual nsPIDOMWindowOuter* GetWindowInternal() const override;
   virtual nsIScriptGlobalObject* GetScriptHandlingObjectInternal() const override;
   virtual bool InternalAllowXULXBL() override;
 
   void UpdateScreenOrientation();
 
+  virtual FlashClassification DocumentFlashClassification() override;
+
 #define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_)                        \
   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, nsIDocumentObserver, \
                                            func_, params_);
 
 #ifdef DEBUG
   void VerifyRootContentState();
 #endif
 
@@ -1320,16 +1322,24 @@ protected:
 
   void NotifyStyleSheetApplicableStateChanged();
 
   // Apply the fullscreen state to the document, and trigger related
   // events. It returns false if the fullscreen element ready check
   // fails and nothing gets changed.
   bool ApplyFullscreen(const FullscreenRequest& aRequest);
 
+  // Retrieves the classification of the Flash plugins in the document based on
+  // the classification lists.
+  FlashClassification PrincipalFlashClassification(bool aIsTopLevel);
+
+  // Attempts to determine the Flash classification of this page based on the
+  // the classification lists and the classification of parent documents.
+  FlashClassification ComputeFlashClassification();
+
   nsTArray<nsIObserver*> mCharSetObservers;
 
   PLDHashTable *mSubDocuments;
 
   // Array of owning references to all children
   nsAttrAndChildArray mChildren;
 
   // Pointer to our parser if we're currently in the process of being
@@ -1364,16 +1374,17 @@ protected:
   // full-screen element onto this stack, and when we cancel full-screen we
   // pop one off this stack, restoring the previous full-screen state
   nsTArray<nsWeakPtr> mFullScreenStack;
 
   // The root of the doc tree in which this document is in. This is only
   // non-null when this document is in fullscreen mode.
   nsWeakPtr mFullscreenRoot;
 
+  FlashClassification mFlashClassification;
 private:
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
   /**
    * Check if the passed custom element name, aOptions.mIs, is a registered
    * custom element type or not, then return the custom element name for future
    * usage.
    *
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -945,17 +945,19 @@ GK_ATOM(onunderflow, "onunderflow")
 GK_ATOM(onunload, "onunload")
 GK_ATOM(onupdatefound, "onupdatefound")
 GK_ATOM(onupdateready, "onupdateready")
 GK_ATOM(onupgradeneeded, "onupgradeneeded")
 GK_ATOM(onussdreceived, "onussdreceived")
 GK_ATOM(onversionchange, "onversionchange")
 GK_ATOM(onvoicechange, "onvoicechange")
 GK_ATOM(onvoiceschanged, "onvoiceschanged")
+GK_ATOM(onvrdisplayactivate, "onvrdisplayactivate")
 GK_ATOM(onvrdisplayconnect, "onvrdisplayconnect")
+GK_ATOM(onvrdisplaydeactivate, "onvrdisplaydeactivate")
 GK_ATOM(onvrdisplaydisconnect, "onvrdisplaydisconnect")
 GK_ATOM(onvrdisplaypresentchange, "onvrdisplaypresentchange")
 GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd")
 GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration")
 GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart")
 GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd")
 GK_ATOM(onwebkitanimationend, "onwebkitanimationend")
 GK_ATOM(onwebkitanimationiteration, "onwebkitanimationiteration")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -197,16 +197,18 @@
 #include "mozilla/dom/IDBFactory.h"
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/Promise.h"
 
 #include "mozilla/dom/Gamepad.h"
 #include "mozilla/dom/GamepadManager.h"
 
 #include "mozilla/dom/VRDisplay.h"
+#include "mozilla/dom/VRDisplayEvent.h"
+#include "mozilla/dom/VRDisplayEventBinding.h"
 #include "mozilla/dom/VREventObserver.h"
 
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 
 #include "mozilla/AddonPathService.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Services.h"
@@ -1438,16 +1440,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mHasFocus(false),
     mShowFocusRingForContent(false),
     mFocusByKeyOccurred(false),
     mHasGamepad(false),
     mHasVREvents(false),
     mHasSeenGamepadInput(false),
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
+    mTopLevelOuterContentWindow(false),
     mSuspendDepth(0),
     mFreezeDepth(0),
     mFocusMethod(0),
     mSerial(0),
     mIdleRequestCallbackCounter(1),
     mIdleRequestExecutor(nullptr),
 #ifdef DEBUG
     mSetOpenerWindowCalled(false),
@@ -3139,17 +3142,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       mArguments = nullptr;
     }
 
     // Give the new inner window our chrome event handler (since it
     // doesn't have one).
     newInnerWindow->mChromeEventHandler = mChromeEventHandler;
   }
 
-  nsJSContext::PokeGC(JS::gcreason::SET_NEW_DOCUMENT);
+  nsJSContext::PokeGC(JS::gcreason::SET_NEW_DOCUMENT, GetWrapperPreserveColor());
   kungFuDeathGrip->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
     nsContentUtils::AddScriptRunner(
       NewRunnableMethod(newInnerWindow,
                         &nsGlobalWindow::FireOnNewGlobalObject));
@@ -3203,21 +3206,17 @@ nsGlobalWindow::PreloadLocalStorage()
     do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
   if (NS_FAILED(rv)) {
     return;
   }
 
   // private browsing windows do not persist local storage to disk so we should
   // only try to precache storage when we're not a private browsing window.
   if (principal->GetPrivateBrowsingId() == 0) {
-    nsCOMPtr<nsIDOMStorage> storage;
-    rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
-    if (NS_SUCCEEDED(rv)) {
-      mLocalStorage = static_cast<Storage*>(storage.get());
-    }
+    storageManager->PrecacheStorage(principal);
   }
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
   MOZ_ASSERT(IsOuterWindow());
 
@@ -3290,16 +3289,19 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
     return;
   }
 
   mDocShell = aDocShell; // Weak Reference
 
   nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
   MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup || mTabGroup == Cast(parentWindow)->mTabGroup);
 
+  mTopLevelOuterContentWindow =
+    !mIsChrome && GetScriptableTopInternal() == this;
+
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
 
   if (mFrames) {
     mFrames->SetDocShell(aDocShell);
   }
 
   // Get our enclosing chrome shell and retrieve its global window impl, so
   // that we can do some forwarding to the chrome document.
@@ -3369,17 +3371,22 @@ nsGlobalWindow::DetachFromDocShell()
     mFocusedNode = nullptr;
   }
 
   ClearControllers();
 
   mChromeEventHandler = nullptr; // force release now
 
   if (mContext) {
-    nsJSContext::PokeGC(JS::gcreason::SET_DOC_SHELL);
+    // When we're about to destroy a top level content window
+    // (for example a tab), we trigger a full GC by passing null as the last
+    // param. We also trigger a full GC for chrome windows.
+    nsJSContext::PokeGC(JS::gcreason::SET_DOC_SHELL,
+                        (mTopLevelOuterContentWindow || mIsChrome) ?
+                          nullptr : GetWrapperPreserveColor());
     mContext = nullptr;
   }
 
   mDocShell = nullptr; // Weak Reference
 
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
 
   if (mFrames) {
@@ -4156,16 +4163,55 @@ nsPIDOMWindowInner::TimeoutManager()
 }
 
 bool
 nsPIDOMWindowInner::IsRunningTimeout()
 {
   return TimeoutManager().IsRunningTimeout();
 }
 
+void
+nsPIDOMWindowOuter::NotifyCreatedNewMediaComponent()
+{
+  if (mMediaSuspend != nsISuspendedTypes::SUSPENDED_BLOCK) {
+    return;
+  }
+
+  // If the document is already on the foreground but the suspend state is still
+  // suspend-block, that means the media component was created after calling
+  // MaybeActiveMediaComponents, so the window's suspend state doesn't be
+  // changed yet. Therefore, we need to call it again, because the state is only
+  // changed after there exists alive media within the window.
+  MaybeActiveMediaComponents();
+}
+
+void
+nsPIDOMWindowOuter::MaybeActiveMediaComponents()
+{
+  if (IsInnerWindow()) {
+    return mOuterWindow->MaybeActiveMediaComponents();
+  }
+
+  nsCOMPtr<nsPIDOMWindowInner> inner = GetCurrentInnerWindow();
+  if (!inner) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> doc = inner->GetExtantDoc();
+  if (!doc) {
+    return;
+  }
+
+  if (!doc->Hidden() &&
+      mMediaSuspend == nsISuspendedTypes::SUSPENDED_BLOCK &&
+      AudioChannelService::IsServiceStarted()) {
+    SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
+  }
+}
+
 SuspendTypes
 nsPIDOMWindowOuter::GetMediaSuspend() const
 {
   if (IsInnerWindow()) {
     return mOuterWindow->GetMediaSuspend();
   }
 
   return mMediaSuspend;
@@ -11789,53 +11835,58 @@ nsGlobalWindow::Observe(nsISupports* aSu
     } else if (AsInner()->IsCurrentInnerWindow()) {
       MOZ_ASSERT(IsInnerWindow());
       ScheduleActiveTimerCallback();
     }
     return NS_OK;
   }
 
   bool isPrivateBrowsing = IsPrivateBrowsing();
-  // In addition to determining if this is a storage event, the
-  // isPrivateBrowsing checks here enforce that the source storage area's
-  // private browsing state matches this window's state.  These flag checks
-  // and their maintenance independent from the principal's OriginAttributes
-  // matter because chrome docshells that are part of private browsing windows
-  // can be private browsing without having their OriginAttributes set (because
-  // they have the system principal).
   if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
       (!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
     if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
       return NS_OK;
     }
 
-    nsIPrincipal *principal = GetPrincipal();
-    if (!principal) {
-      return NS_OK;
-    }
+    nsIPrincipal *principal;
+    nsresult rv;
 
     RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
     if (!event) {
       return NS_ERROR_FAILURE;
     }
 
+    RefPtr<Storage> changingStorage = event->GetStorageArea();
+    if (!changingStorage) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
+
     bool fireMozStorageChanged = false;
     nsAutoString eventType;
     eventType.AssignLiteral("storage");
-
-    if (!NS_strcmp(aData, u"sessionStorage")) {
-      nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
-      MOZ_ASSERT(changingStorage);
-
+    principal = GetPrincipal();
+    if (!principal) {
+      return NS_OK;
+    }
+
+    if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
+      return NS_OK;
+    }
+
+    switch (changingStorage->GetType())
+    {
+    case Storage::SessionStorage:
+    {
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
       if (storageManager) {
-        nsresult rv = storageManager->CheckStorage(principal, changingStorage,
-                                                   &check);
+        rv = storageManager->CheckStorage(principal, istorage, &check);
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
 
       if (!check) {
         // This storage event is not coming from our storage or is coming
         // from a different docshell, i.e. it is a clone, ignore this event.
@@ -11846,67 +11897,68 @@ nsGlobalWindow::Observe(nsISupports* aSu
         PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
                     this, mSessionStorage.get(), changingStorage.get());
       }
 
       fireMozStorageChanged = mSessionStorage == changingStorage;
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozSessionStorageChanged");
       }
-    }
-
-    else {
-      MOZ_ASSERT(!NS_strcmp(aData, u"localStorage"));
-      nsIPrincipal* storagePrincipal = event->GetPrincipal();
-      if (!storagePrincipal) {
-        return NS_OK;
-      }
+      break;
+    }
+
+    case Storage::LocalStorage:
+    {
+      // Allow event fire only for the same principal storages
+      // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
+      nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
 
       bool equals = false;
-      nsresult rv = storagePrincipal->Equals(principal, &equals);
+      rv = storagePrincipal->Equals(principal, &equals);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (!equals) {
+      if (!equals)
         return NS_OK;
-      }
-
-      fireMozStorageChanged = mLocalStorage == event->GetStorageArea();
-
+
+      fireMozStorageChanged = mLocalStorage == changingStorage;
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozLocalStorageChanged");
       }
+      break;
+    }
+    default:
+      return NS_OK;
     }
 
     // Clone the storage event included in the observer notification. We want
     // to dispatch clones rather than the original event.
     ErrorResult error;
-    RefPtr<StorageEvent> clonedEvent =
-      CloneStorageEvent(eventType, event, error);
+    RefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType, event, error);
     if (error.Failed()) {
       return error.StealNSResult();
     }
 
-    clonedEvent->SetTrusted(true);
+    newEvent->SetTrusted(true);
 
     if (fireMozStorageChanged) {
-      WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
+      WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
       internalEvent->mFlags.mOnlyChromeDispatch = true;
     }
 
     if (IsFrozen()) {
       // This window is frozen, rather than firing the events here,
       // store the domain in which the change happened and fire the
       // events if we're ever thawed.
 
-      mPendingStorageEvents.AppendElement(clonedEvent);
+      mPendingStorageEvents.AppendElement(newEvent);
       return NS_OK;
     }
 
     bool defaultActionEnabled;
-    DispatchEvent(clonedEvent, &defaultActionEnabled);
+    DispatchEvent(newEvent, &defaultActionEnabled);
 
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
     if (mApplicationCache)
       return NS_OK;
 
@@ -11987,41 +12039,32 @@ nsGlobalWindow::CloneStorageEvent(const 
   dict.mBubbles = aEvent->Bubbles();
   dict.mCancelable = aEvent->Cancelable();
   aEvent->GetKey(dict.mKey);
   aEvent->GetOldValue(dict.mOldValue);
   aEvent->GetNewValue(dict.mNewValue);
   aEvent->GetUrl(dict.mUrl);
 
   RefPtr<Storage> storageArea = aEvent->GetStorageArea();
+  MOZ_ASSERT(storageArea);
 
   RefPtr<Storage> storage;
-
-  // If null, this is a localStorage event received by IPC.
-  if (!storageArea) {
-    storage = GetLocalStorage(aRv);
-    if (aRv.Failed() || !storage) {
-      return nullptr;
-    }
-
-    // We must apply the current change to the 'local' localStorage.
-    storage->ApplyEvent(aEvent);
-  } else if (storageArea->GetType() == Storage::LocalStorage) {
+  if (storageArea->GetType() == Storage::LocalStorage) {
     storage = GetLocalStorage(aRv);
   } else {
     MOZ_ASSERT(storageArea->GetType() == Storage::SessionStorage);
     storage = GetSessionStorage(aRv);
   }
 
   if (aRv.Failed() || !storage) {
     return nullptr;
   }
 
   MOZ_ASSERT(storage);
-  MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
+  MOZ_ASSERT(storage->IsForkOf(storageArea));
 
   dict.mStorageArea = storage;
 
   RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
   return event.forget();
 }
 
 void
@@ -13167,28 +13210,23 @@ nsGlobalWindow::SetHasGamepadEventListen
     EnableGamepadUpdates();
   }
 }
 
 
 void
 nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
 {
-  if (aType == nsGkAtoms::onvrdisplayconnect ||
+  if (aType == nsGkAtoms::onvrdisplayactivate ||
+      aType == nsGkAtoms::onvrdisplayconnect ||
+      aType == nsGkAtoms::onvrdisplaydeactivate ||
       aType == nsGkAtoms::onvrdisplaydisconnect ||
       aType == nsGkAtoms::onvrdisplaypresentchange) {
     NotifyVREventListenerAdded();
   }
-
-  // We need to initialize localStorage in order to receive notifications.
-  if (aType == nsGkAtoms::onstorage) {
-    ErrorResult rv;
-    GetLocalStorage(rv);
-    rv.SuppressException();
-  }
 }
 
 void
 nsGlobalWindow::NotifyVREventListenerAdded()
 {
   MOZ_ASSERT(IsInnerWindow());
   mHasVREvents = true;
   EnableVRUpdates();
@@ -13348,16 +13386,72 @@ nsGlobalWindow::NotifyActiveVRDisplaysCh
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mNavigator) {
     mNavigator->NotifyActiveVRDisplaysChanged();
   }
 }
 
+void
+nsGlobalWindow::DispatchVRDisplayActivate(uint32_t aDisplayID,
+                                          mozilla::dom::VRDisplayEventReason aReason)
+{
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID
+        && !display->IsAnyPresenting()) {
+      // We only want to trigger this event if nobody is presenting to the
+      // display already.
+
+      VRDisplayEventInit init;
+      init.mBubbles = true;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      init.mReason.Construct(aReason);
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplayactivate"),
+                                    init);
+      // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
+      // to be used in response to link traversal, user request (chrome UX), and
+      // HMD mounting detection sensors.
+      event->SetTrusted(true);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      break;
+    }
+  }
+}
+
+void
+nsGlobalWindow::DispatchVRDisplayDeactivate(uint32_t aDisplayID,
+                                            mozilla::dom::VRDisplayEventReason aReason)
+{
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
+      // We only want to trigger this event to content that is presenting to
+      // the display already.
+
+      VRDisplayEventInit init;
+      init.mBubbles = true;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      init.mReason.Construct(aReason);
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplaydeactivate"),
+                                    init);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      break;
+    }
+  }
+}
 
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -132,16 +132,17 @@ class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
 class TabGroup;
 class Timeout;
 class U2F;
 class VRDisplay;
+enum class VRDisplayEventReason : uint8_t;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
 class Worklet;
 namespace cache {
 class CacheStorage;
@@ -753,16 +754,21 @@ public:
 
   // Update the VR displays for this window
   bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
 
   // Inner windows only.
   // Called to inform that the set of active VR displays has changed.
   void NotifyActiveVRDisplaysChanged();
 
+  void DispatchVRDisplayActivate(uint32_t aDisplayID,
+                                 mozilla::dom::VRDisplayEventReason aReason);
+  void DispatchVRDisplayDeactivate(uint32_t aDisplayID,
+                                   mozilla::dom::VRDisplayEventReason aReason);
+
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())    \
                : nullptr;                                                     \
   }                                                                           \
   void SetOn##name_(mozilla::dom::EventHandlerNonNull* handler)               \
@@ -1869,16 +1875,18 @@ protected:
   bool mHasSeenGamepadInput;
 
   // whether we've sent the destroy notification for our window id
   bool                   mNotifiedIDDestroyed : 1;
   // whether scripts may close the window,
   // even if "dom.allow_scripts_to_close_windows" is false.
   bool                   mAllowScriptsToClose : 1;
 
+  bool mTopLevelOuterContentWindow : 1;
+
   nsCOMPtr<nsIScriptContext>    mContext;
   nsWeakPtr                     mOpener;
   nsCOMPtr<nsIControllers>      mControllers;
 
   // For |window.arguments|, via |openDialog|.
   nsCOMPtr<nsIArray>            mArguments;
 
   // For |window.dialogArguments|, via |showModalDialog|.
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -29,48 +29,61 @@ inline mozilla::dom::ShadowRoot* nsICont
   if (!IsElement()) {
     return nullptr;
   }
 
   return AsElement()->FastGetShadowRoot();
 }
 
 template<nsIContent::FlattenedParentType Type>
-static inline nsINode*
-GetFlattenedTreeParentNode(const nsINode* aNode)
+static inline bool FlattenedTreeParentIsParent(const nsINode* aNode)
 {
-  nsINode* parent = aNode->GetParentNode();
   // Try to short-circuit past the complicated and not-exactly-fast logic for
   // computing the flattened parent.
   //
-  // There are four cases where we need might something other than parentNode:
-  //   (1) The node is an explicit child of an XBL-bound element, re-bound
-  //       to an XBL insertion point.
-  //   (2) The node is a top-level element in a shadow tree, whose flattened
-  //       parent is the host element (as opposed to the actual parent which
-  //       is the shadow root).
-  //   (3) The node is an explicit child of an element with a shadow root,
-  //       re-bound to an insertion point.
-  //   (4) We want the flattened parent for style, and the node is the root
-  //       of a native anonymous content subtree parented to the document's
-  //       root element.
-  bool needSlowCall = aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
-                      aNode->IsInShadowTree() ||
-                      (parent &&
-                       parent->IsContent() &&
-                       parent->AsContent()->GetShadowRoot()) ||
-                      (Type == nsIContent::eForStyle &&
-                       aNode->IsContent() &&
-                       aNode->AsContent()->IsRootOfNativeAnonymousSubtree() &&
-                       aNode->OwnerDoc()->GetRootElement() == parent);
-  if (MOZ_UNLIKELY(needSlowCall)) {
-    MOZ_ASSERT(aNode->IsContent());
-    return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type);
+  // WARNING! This logic is replicated in Servo to avoid out of line calls.
+  // If you change the cases here, you need to change them in Servo as well!
+
+  // Check if the node is an explicit child of an XBL-bound element, re-bound to
+  // an XBL insertion point, or is a top-level element in a shadow tree, whose
+  // flattened parent is the host element (as opposed to the actual parent which
+  // is the shadow root).
+  if (aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR | NODE_IS_IN_SHADOW_TREE)) {
+    return false;
+  }
+
+  // Check if we want the flattened parent for style, and the node is the root
+  // of a native anonymous content subtree parented to the document's root element.
+  if (Type == nsIContent::eForStyle && aNode->HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT) &&
+      aNode->OwnerDoc()->GetRootElement() == aNode->GetParentNode())
+  {
+    return false;
   }
-  return parent;
+
+  // Check if the node is an explicit child of an element with a shadow root,
+  // re-bound to an insertion point.
+  nsIContent* parent = aNode->GetParent();
+  if (parent && parent->GetShadowRoot()) {
+    return false;
+  }
+
+  // Common case.
+  return true;
+}
+
+template<nsIContent::FlattenedParentType Type>
+static inline nsINode*
+GetFlattenedTreeParentNode(const nsINode* aNode)
+{
+  if (MOZ_LIKELY(FlattenedTreeParentIsParent<Type>(aNode))) {
+    return aNode->GetParentNode();
+  }
+
+  MOZ_ASSERT(aNode->IsContent());
+  return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type);
 }
 
 inline nsINode*
 nsINode::GetFlattenedTreeParentNode() const
 {
   return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this);
 }
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2888,16 +2888,27 @@ public:
   // The URLs passed to these functions should match what
   // JS::DescribeScriptedCaller() returns, since these APIs are used to
   // determine whether some code is being called from a tracking script.
   void NoteScriptTrackingStatus(const nsACString& aURL, bool isTracking);
   bool IsScriptTracking(const nsACString& aURL) const;
 
   bool PrerenderHref(nsIURI* aHref);
 
+  // For more information on Flash classification, see
+  // toolkit/components/url-classifier/flash-block-lists.rst
+  enum class FlashClassification {
+    Unclassified,   // Denotes a classification that has not yet been computed.
+                    // Allows for lazy classification.
+    Unknown,        // Site is not on the whitelist or blacklist
+    Allowed,        // Site is on the Flash whitelist
+    Denied          // Site is on the Flash blacklist
+  };
+  virtual FlashClassification DocumentFlashClassification() = 0;
+
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -33,17 +33,22 @@
 #include "nsContentUtils.h"
 #include "mozilla/EventDispatcher.h"
 #include "nsIContent.h"
 #include "nsCycleCollector.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsIXULRuntime.h"
 #include "nsTextFormatter.h"
 #include "ScriptSettings.h"
-
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h> // for getpid()
+#endif
 #include "xpcpublic.h"
 
 #include "jsapi.h"
 #include "jswrapper.h"
 #include "js/SliceBudget.h"
 #include "nsIArray.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
@@ -599,17 +604,17 @@ nsJSContext::~nsJSContext()
     NS_IF_RELEASE(sSecurityManager);
   }
 }
 
 void
 nsJSContext::Destroy()
 {
   if (mGCOnDestruction) {
-    PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
+    PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY, mWindowProxy);
   }
 
   DropJSObjects(this);
 }
 
 // QueryInterface implementation for nsJSContext
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
 
@@ -1197,18 +1202,21 @@ nsJSContext::GarbageCollectNow(JS::gcrea
     // We're in the middle of incremental GC. Do another slice.
     JS::PrepareForIncrementalGC(sContext);
     JS::IncrementalGCSlice(sContext, aReason, aSliceMillis);
     return;
   }
 
   JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
 
-  if (sNeedsFullGC || aReason != JS::gcreason::CC_WAITING) {
-    sNeedsFullGC = false;
+  if (aIncremental == NonIncrementalGC || aReason == JS::gcreason::FULL_GC_TIMER) {
+    sNeedsFullGC = true;
+  }
+
+  if (sNeedsFullGC) {
     JS::PrepareForFullGC(sContext);
   } else {
     CycleCollectedJSContext::Get()->PrepareWaitingZonesForGC();
   }
 
   if (aIncremental == IncrementalGC) {
     JS::StartIncrementalGC(sContext, gckind, aReason, aSliceMillis);
   } else {
@@ -1575,17 +1583,17 @@ nsJSContext::EndCycleCollectionCallback(
 
   sCCollectedWaitingForGC += aResults.mFreedGCed;
   sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
 
   TimeStamp endCCTimeStamp = TimeStamp::Now();
   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
 
   if (NeedsGCAfterCC()) {
-    PokeGC(JS::gcreason::CC_WAITING,
+    PokeGC(JS::gcreason::CC_WAITING, nullptr,
            NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
   }
 
   // Log information about the CC via telemetry, JSON and the console.
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime);
@@ -1613,21 +1621,21 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      u"CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
+      u"CC(T+%.1f)[%s-%i] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
       u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu");
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
-                                        ProcessNameForCollectorLog(),
+                                        ProcessNameForCollectorLog(), getpid(),
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
                                         sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
                                         gcMsg.get(),
                                         sForgetSkippableBeforeCC,
                                         minForgetSkippableTime / PR_USEC_PER_MSEC,
@@ -1845,19 +1853,17 @@ nsJSContext::LoadEnd()
 
   // sPendingLoadCount is not a well managed load counter (and doesn't
   // need to be), so make sure we don't make it wrap backwards here.
   if (sPendingLoadCount > 0) {
     --sPendingLoadCount;
     return;
   }
 
-  // Its probably a good idea to GC soon since we have finished loading.
   sLoadingInProgress = false;
-  PokeGC(JS::gcreason::LOAD_END);
 }
 
 // Only trigger expensive timers when they have been checked a number of times.
 static bool
 ReadyToTriggerExpensiveCollectorTimer()
 {
   bool ready = kPokesBetweenExpensiveCollectorTriggers < ++sExpensiveCollectorPokes;
   if (ready) {
@@ -1910,21 +1916,32 @@ nsJSContext::RunNextCollectorTimer()
   if (sICCTimer) {
     ICCTimerFired(nullptr, nullptr);
     return;
   }
 }
 
 // static
 void
-nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
+nsJSContext::PokeGC(JS::gcreason::Reason aReason,
+                    JSObject* aObj,
+                    int aDelay)
 {
-  sNeedsFullGC = sNeedsFullGC || aReason != JS::gcreason::CC_WAITING;
-
-  if (sGCTimer || sInterSliceGCTimer || sShuttingDown) {
+  if (sShuttingDown) {
+    return;
+  }
+
+  if (aObj) {
+    JS::Zone* zone = JS::GetObjectZone(aObj);
+    CycleCollectedJSContext::Get()->AddZoneWaitingForGC(zone);
+  } else if (aReason != JS::gcreason::CC_WAITING) {
+    sNeedsFullGC = true;
+  }
+
+  if (sGCTimer || sInterSliceGCTimer) {
     // There's already a timer for GC'ing, just return
     return;
   }
 
   if (sCCTimer) {
     // Make sure CC is called...
     sNeedsFullCC = true;
     // and GC after it.
@@ -2104,22 +2121,23 @@ DOMGCSliceCallback(JSContext* aCx, JS::G
       sCCLockedOut = true;
       break;
     }
 
     case JS::GC_CYCLE_END: {
       PRTime delta = GetCollectionTimeDelta();
 
       if (sPostGCEventsToConsole) {
-        NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f)[%s] ");
+        NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f)[%s-%i] ");
         nsString prefix, gcstats;
         gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
         prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
                                                double(delta) / PR_USEC_PER_SEC,
-                                               ProcessNameForCollectorLog()));
+                                               ProcessNameForCollectorLog(),
+                                               getpid()));
         nsString msg = prefix + gcstats;
         nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         if (cs) {
           cs->LogStringMessage(msg.get());
         }
       }
 
       if (!sShuttingDown) {
@@ -2157,16 +2175,20 @@ DOMGCSliceCallback(JSContext* aCx, JS::G
       } else {
         nsJSContext::KillFullGCTimer();
       }
 
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
+      if (!aDesc.isZone_) {
+        sNeedsFullGC = false;
+      }
+
       break;
     }
 
     case JS::GC_SLICE_BEGIN:
       break;
 
     case JS::GC_SLICE_END:
 
@@ -2181,21 +2203,22 @@ DOMGCSliceCallback(JSContext* aCx, JS::G
                                                       "InterSliceGCTimerFired");
       }
 
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       if (sPostGCEventsToConsole) {
-        NS_NAMED_LITERAL_STRING(kFmt, "[%s] ");
+        NS_NAMED_LITERAL_STRING(kFmt, "[%s-%i] ");
         nsString prefix, gcstats;
         gcstats.Adopt(aDesc.formatSliceMessage(aCx));
         prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
-                                               ProcessNameForCollectorLog()));
+                                               ProcessNameForCollectorLog(),
+                                               getpid()));
         nsString msg = prefix + gcstats;
         nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         if (cs) {
           cs->LogStringMessage(msg.get());
         }
       }
 
       break;
@@ -2239,17 +2262,17 @@ mozilla::dom::StartupJSEnvironment()
   sHasRunGC = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sCCollectedZonesWaitingForGC = 0;
   sLikelyShortLivingObjectsNeedingGC = 0;
   sPostGCEventsToConsole = false;
   sNeedsFullCC = false;
-  sNeedsFullGC = false;
+  sNeedsFullGC = true;
   sNeedsGCAfterCC = false;
   gNameSpaceManager = nullptr;
   sContext = nullptr;
   sIsInitialized = false;
   sDidShutdown = false;
   sShuttingDown = false;
   sContextCount = 0;
   sSecurityManager = nullptr;
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -100,17 +100,18 @@ public:
   static void EndCycleCollectionCallback(mozilla::CycleCollectorResults &aResults);
 
   // Return the longest CC slice time since ClearMaxCCSliceTime() was last called.
   static uint32_t GetMaxCCSliceTimeSinceClear();
   static void ClearMaxCCSliceTime();
 
   static void RunNextCollectorTimer();
 
-  static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
+  // The GC should probably run soon, in the zone of object aObj (if given).
+  static void PokeGC(JS::gcreason::Reason aReason, JSObject* aObj, int aDelay = 0);
   static void KillGCTimer();
 
   static void PokeShrinkingGC();
   static void KillShrinkingGCTimer();
 
   static void MaybePokeCC();
   static void KillCCTimer();
   static void KillICCTimer();
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -864,65 +864,58 @@ nsObjectLoadingContent::GetPluginParamet
 {
   aParameters = mCachedParameters;
 }
 
 void
 nsObjectLoadingContent::GetNestedParams(nsTArray<MozPluginParameter>& aParams,
                                         bool aIgnoreCodebase)
 {
-  nsCOMPtr<nsIDOMElement> domElement =
+  nsCOMPtr<Element> ourElement =
     do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
 
-  nsCOMPtr<nsIDOMHTMLCollection> allParams;
+  nsCOMPtr<nsIHTMLCollection> allParams;
   NS_NAMED_LITERAL_STRING(xhtml_ns, "http://www.w3.org/1999/xhtml");
-  domElement->GetElementsByTagNameNS(xhtml_ns,
-        NS_LITERAL_STRING("param"), getter_AddRefs(allParams));
-
-  if (!allParams)
+  ErrorResult rv;
+  allParams = ourElement->GetElementsByTagNameNS(xhtml_ns,
+                                                 NS_LITERAL_STRING("param"),
+                                                 rv);
+  if (rv.Failed()) {
     return;
-
-  uint32_t numAllParams;
-  allParams->GetLength(&numAllParams);
+  }
+  MOZ_ASSERT(allParams);
+
+  uint32_t numAllParams = allParams->Length();
   for (uint32_t i = 0; i < numAllParams; i++) {
-    nsCOMPtr<nsIDOMNode> pNode;
-    allParams->Item(i, getter_AddRefs(pNode));
-    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(pNode);
-
-    if (!element)
-      continue;
+    RefPtr<Element> element = allParams->Item(i);
 
     nsAutoString name;
     element->GetAttribute(NS_LITERAL_STRING("name"), name);
 
     if (name.IsEmpty())
       continue;
 
-    nsCOMPtr<nsIDOMNode> parent;
+    nsCOMPtr<nsIContent> parent = element->GetParent();
     nsCOMPtr<nsIDOMHTMLObjectElement> domObject;
     nsCOMPtr<nsIDOMHTMLAppletElement> domApplet;
-    pNode->GetParentNode(getter_AddRefs(parent));
     while (!(domObject || domApplet) && parent) {
       domObject = do_QueryInterface(parent);
       domApplet = do_QueryInterface(parent);
-      nsCOMPtr<nsIDOMNode> temp;
-      parent->GetParentNode(getter_AddRefs(temp));
-      parent = temp;
+      parent = parent->GetParent();
     }
 
     if (domApplet) {
       parent = do_QueryInterface(domApplet);
     } else if (domObject) {
       parent = do_QueryInterface(domObject);
     } else {
       continue;
     }
 
-    nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(domElement);
-    if (parent == domNode) {
+    if (parent == ourElement) {
       MozPluginParameter param;
       element->GetAttribute(NS_LITERAL_STRING("name"), param.mName);
       element->GetAttribute(NS_LITERAL_STRING("value"), param.mValue);
 
       param.mName.Trim(" \n\r\t\b", true, true, false);
       param.mValue.Trim(" \n\r\t\b", true, true, false);
 
       // ignore codebase param if it was already added in the attributes array.
@@ -3278,16 +3271,17 @@ nsObjectLoadingContent::ShouldPlay(Fallb
   if (!aIgnoreCurrentType && mType != eType_Plugin) {
     return true;
   }
 
   // Order of checks:
   // * Assume a default of click-to-play
   // * If globally disabled, per-site permissions cannot override.
   // * If blocklisted, override the reason with the blocklist reason
+  // * Check if the flash blocking status for this page denies flash from loading.
   // * Check per-site permissions and follow those if specified.
   // * Honor per-plugin disabled permission
   // * Blocklisted plugins are forced to CtP
   // * Check per-plugin permission and follow that.
 
   aReason = eFallbackClickToPlay;
 
   uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
@@ -3313,32 +3307,42 @@ nsObjectLoadingContent::ShouldPlay(Fallb
 
   if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
     aReason = eFallbackVulnerableUpdatable;
   }
   else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
     aReason = eFallbackVulnerableNoUpdate;
   }
 
-  // Check the permission manager for permission based on the principal of
-  // the toplevel content.
-
+  // Document and window lookup
   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
 
   nsCOMPtr<nsPIDOMWindowOuter> window = ownerDoc->GetWindow();
   if (!window) {
     return false;
   }
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop();
   NS_ENSURE_TRUE(topWindow, false);
   nsCOMPtr<nsIDocument> topDoc = topWindow->GetDoc();
   NS_ENSURE_TRUE(topDoc, false);
 
+  // Check the flash blocking status for this page (this applies to Flash only)
+  nsIDocument::FlashClassification documentClassification = nsIDocument::FlashClassification::Allowed;
+  if (IsFlashMIME(mContentType)) {
+    documentClassification = ownerDoc->DocumentFlashClassification();
+  }
+  if (documentClassification == nsIDocument::FlashClassification::Denied) {
+    aReason = eFallbackSuppressed;
+    return false;
+  }
+
+  // Check the permission manager for permission based on the principal of
+  // the toplevel content.
   nsCOMPtr<nsIPermissionManager> permissionManager = services::GetPermissionManager();
   NS_ENSURE_TRUE(permissionManager, false);
 
   // For now we always say that the system principal uses click-to-play since
   // that maintains current behavior and we have tests that expect this.
   // What we really should do is disable plugins entirely in pages that use
   // the system principal, i.e. in chrome pages. That way the click-to-play
   // code here wouldn't matter at all. Bug 775301 is tracking this.
@@ -3395,17 +3399,17 @@ nsObjectLoadingContent::ShouldPlay(Fallb
 
   if (PreferFallback(enabledState == nsIPluginTag::STATE_CLICKTOPLAY)) {
     aReason = eFallbackAlternate;
     return false;
   }
 
   switch (enabledState) {
   case nsIPluginTag::STATE_ENABLED:
-    return true;
+    return documentClassification == nsIDocument::FlashClassification::Allowed;
   case nsIPluginTag::STATE_CLICKTOPLAY:
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
 bool
 nsObjectLoadingContent::FavorFallbackMode(bool aIsPluginClickToPlay) {
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -958,16 +958,19 @@ public:
   void SetMediaSuspend(SuspendTypes aSuspend);
 
   bool GetAudioMuted() const;
   void SetAudioMuted(bool aMuted);
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
+  void NotifyCreatedNewMediaComponent();
+  void MaybeActiveMediaComponents();
+
   void SetServiceWorkersTestingEnabled(bool aEnabled);
   bool GetServiceWorkersTestingEnabled();
 
   float GetDevicePixelRatio(mozilla::dom::CallerType aCallerType);
 
   void SetLargeAllocStatus(mozilla::dom::LargeAllocStatus aStatus);
 };
 
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -22,17 +22,17 @@
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIControllers.h"
 #include "nsIController.h"
 #include "xpcpublic.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/TabParent.h"
 
 #ifdef MOZ_XUL
-#include "nsIDOMXULElement.h"
+#include "nsXULElement.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter* aWindow)
 {
   mWindow = aWindow;
@@ -226,19 +226,23 @@ nsWindowRoot::GetControllers(nsIControll
   // describes controllers, so this code would have no special
   // knowledge of what object might have controllers.
 
   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   nsIContent* focusedContent =
     nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
   if (focusedContent) {
 #ifdef MOZ_XUL
-    nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(focusedContent));
-    if (xulElement)
-      return xulElement->GetControllers(aResult);
+    RefPtr<nsXULElement> xulElement = nsXULElement::FromContent(focusedContent);
+    if (xulElement) {
+      ErrorResult rv;
+      *aResult = xulElement->GetControllers(rv);
+      NS_IF_ADDREF(*aResult);
+      return rv.StealNSResult();
+    }
 #endif
 
     nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea =
       do_QueryInterface(focusedContent);
     if (htmlTextArea)
       return htmlTextArea->GetControllers(aResult);
 
     nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement =
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -295,16 +295,17 @@ DOMInterfaces = {
 },
 
 'DataChannel': {
     'nativeType': 'nsDOMDataChannel',
 },
 
 'DedicatedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
+    'implicitJSContext': [ 'close' ],
 },
 
 'DeviceAcceleration': {
     'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
 },
 
 'DeviceRotationRate': {
     'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
@@ -821,21 +822,21 @@ DOMInterfaces = {
 
 'ServiceWorkerRegistration': {
     'implicitJSContext': [ 'pushManager' ],
 },
 
 'SharedWorker': {
     'nativeType': 'mozilla::dom::workers::SharedWorker',
     'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
-    'implicitJSContext': [ 'constructor' ],
 },
 
 'SharedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
+    'implicitJSContext': [ 'close' ],
 },
 
 'StyleSheet': {
     'nativeType': 'mozilla::StyleSheet',
     'headerFile': 'mozilla/StyleSheetInlines.h',
 },
 
 'SVGAnimatedLengthList': {
@@ -1286,16 +1287,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLExtensions.h'
 },
 
 'EXT_disjoint_timer_query': {
     'nativeType': 'mozilla::WebGLExtensionDisjointTimerQuery',
     'headerFile': 'WebGLExtensions.h'
 },
 
+'MOZ_debug_get': {
+    'nativeType': 'mozilla::WebGLExtensionDebugGet',
+    'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLFramebuffer': {
     'nativeType': 'mozilla::WebGLFramebuffer',
     'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
     'nativeType': 'mozilla::WebGLProgram',
     'headerFile': 'WebGLProgram.h'
@@ -1399,19 +1405,16 @@ DOMInterfaces = {
     'implicitJSContext': [
         'dump', 'global', 'reportError', 'setConsoleEventHandler',
     ],
 },
 
 'WorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'concrete': False,
-    'implicitJSContext': [
-        'close',
-    ],
     # Rename a few things so we don't have both classes and methods
     # with the same name
     'binaryNames': {
         'performance': 'getPerformance',
     },
 },
 
 'XMLHttpRequest': {
--- a/dom/bindings/mach_commands.py
+++ b/dom/bindings/mach_commands.py
@@ -9,16 +9,17 @@ import sys
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
 from mozbuild.base import MachCommandBase
+from mozbuild.util import mkdir
 
 def get_test_parser():
     import runtests
     return runtests.get_parser
 
 @CommandProvider
 class WebIDLProvider(MachCommandBase):
     @Command('webidl-example', category='misc',
@@ -33,16 +34,20 @@ class WebIDLProvider(MachCommandBase):
             manager.generate_example_files(i)
 
     @Command('webidl-parser-test', category='testing', parser=get_test_parser,
              description='Run WebIDL tests (Interface Browser parser).')
     def webidl_test(self, **kwargs):
         sys.path.insert(0, os.path.join(self.topsrcdir, 'other-licenses',
                         'ply'))
 
+        # Ensure the topobjdir exists. On a Taskcluster test run there won't be
+        # an objdir yet.
+        mkdir(self.topobjdir)
+
         # Make sure we drop our cached grammar bits in the objdir, not
         # wherever we happen to be running from.
         os.chdir(self.topobjdir)
 
         if kwargs["verbose"] is None:
             kwargs["verbose"] = False
 
         # Now we're going to create the cached grammar file in the
--- a/dom/bindings/parser/runtests.py
+++ b/dom/bindings/parser/runtests.py
@@ -83,16 +83,17 @@ def run_tests(tests, verbose):
         print 'Result summary:'
         print 'Successful: %d' % all_passed
         print 'Unexpected: %d' % \
                 sum(len(failures) for _, failures in failed_tests)
         for test, failures in failed_tests:
             print '%s:' % test
             for failure in failures:
                 print 'TEST-UNEXPECTED-FAIL | %s' % failure
+    return 1 if failed_tests else 0
 
 def get_parser():
     usage = """%(prog)s [OPTIONS] [TESTS]
                Where TESTS are relative to the tests directory."""
     parser = argparse.ArgumentParser(usage=usage)
     parser.add_argument('-q', '--quiet', action='store_false', dest='verbose',
                         help="Don't print passing tests.", default=None)
     parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
@@ -100,9 +101,14 @@ def get_parser():
     parser.add_argument('tests', nargs="*", help="Tests to run")
     return parser
 
 if __name__ == '__main__':
     parser = get_parser()
     args = parser.parse_args()
     if args.verbose is None:
         args.verbose = True
-    run_tests(args.tests, verbose=args.verbose)
+
+    # Make sure the current directory is in the python path so we can cache the
+    # result of the webidlyacc.py generation.
+    sys.path.append('.')
+
+    sys.exit(run_tests(args.tests, verbose=args.verbose))
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -10,12 +10,8 @@ include ../webidlsrcs.mk
 # TODO Update this variable in backend.mk.
 CPPSRCS += $(addprefix ../,$(test_sources))
 
 # Include rules.mk before any of our targets so our first target is coming from
 # rules.mk and running make with no target in this dir does the right thing.
 include $(topsrcdir)/config/rules.mk
 
 endif
-
-check::
-	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
-	  $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
--- a/dom/bindings/test/test_crossOriginWindowSymbolAccess.html
+++ b/dom/bindings/test/test_crossOriginWindowSymbolAccess.html
@@ -5,19 +5,19 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <iframe src="http://www1.w3c-test.org/common/blank.html"></iframe>
 <script>
 async_test(function (t) {
   window.addEventListener("load", t.step_func(
     function() {
       assert_equals(document.querySelector("iframe").contentDocument, null, "Should have a crossorigin frame");
-      assert_throws(new Error(), function() {
+      assert_throws("SecurityError", function() {
         frames[0][Symbol.iterator];
       }, "Should throw exception on cross-origin Window symbol-named get");
-      assert_throws(new Error(), function() {
+      assert_throws("SecurityError", function() {
         frames[0].location[Symbol.iterator];
       }, "Should throw exception on cross-origin Location symbol-named get");
       t.done();
     }
   ));
 }, "Check Symbol access on load");
 </script>
--- a/dom/bindings/test/test_exception_messages.html
+++ b/dom/bindings/test/test_exception_messages.html
@@ -42,18 +42,18 @@ https://bugzilla.mozilla.org/show_bug.cg
         "Argument 1 of CanvasRenderingContext2D.fill 'bogus' is not a valid value for enumeration CanvasWindingRule.",
         "bogus enum value" ],
       [ 'document.createTreeWalker(document, 0xFFFFFFFF, { acceptNode: 5 }).nextNode()',
         "Property 'acceptNode' is not callable.",
         "non-callable callback interface operation property" ],
       [ '(new TextDecoder).decode(new Uint8Array(), 5)',
         "Argument 2 of TextDecoder.decode can't be converted to a dictionary.",
         "primitive passed for a dictionary" ],
-      [ 'URL.createObjectURL(null, null)',
-        "Argument 1 is not valid for any of the 2-argument overloads of URL.createObjectURL.",
+      [ 'URL.createObjectURL(null)',
+        "Argument 1 is not valid for any of the 1-argument overloads of URL.createObjectURL.",
         "overload resolution failure" ],
       [ 'document.createElement("select").add({})',
         "Argument 1 of HTMLSelectElement.add could not be converted to any of: HTMLOptionElement, HTMLOptGroupElement.",
         "invalid value passed for union" ],
       [ 'document.createElement("canvas").getContext("2d").createLinearGradient(0, 1, 0, 1).addColorStop(NaN, "")',
         "Argument 1 of CanvasGradient.addColorStop is not a finite floating-point value.",
         "invalid float" ]
   ];
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -66,16 +66,17 @@
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Conversions.h"
+#include "js/HeapAPI.h"
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageData.h"
@@ -114,16 +115,17 @@
 #include "SVGContentUtils.h"
 #include "nsIScreenManager.h"
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 #include "Units.h"
 #include "CanvasUtils.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/layers/CanvasClient.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
@@ -1737,16 +1739,22 @@ CanvasRenderingContext2D::RegisterAlloca
     RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   }
 
   gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
   JSContext* context = nsContentUtils::GetCurrentJSContext();
   if (context) {
     JS_updateMallocCounter(context, mWidth * mHeight * 4);
   }
+
+  JSObject* wrapper = GetWrapperPreserveColor();
+  if (wrapper) {
+    CycleCollectedJSContext::Get()->
+      AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
+  }
 }
 
 static already_AddRefed<LayerManager>
 LayerManagerFromCanvasElement(nsINode* aCanvasElement)
 {
   if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
     return nullptr;
   }
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -35,16 +35,17 @@ WebGLContext::GetExtensionString(WebGLEx
         WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
         WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
         WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
         WEBGL_EXTENSION_IDENTIFIER(EXT_disjoint_timer_query)
+        WEBGL_EXTENSION_IDENTIFIER(MOZ_debug_get)
         WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
@@ -85,16 +86,18 @@ bool WebGLContext::IsExtensionSupported(
     }
 
     if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
         allowPrivilegedExts = true;
     }
 
     if (allowPrivilegedExts) {
         switch (ext) {
+        case WebGLExtensionID::MOZ_debug_get:
+            return true;
         case WebGLExtensionID::WEBGL_debug_renderer_info:
             return true;
         case WebGLExtensionID::WEBGL_debug_shaders:
             return true;
         default:
             // For warnings-as-errors.
             break;
         }
@@ -372,16 +375,21 @@ WebGLContext::EnableExtension(WebGLExten
         break;
     case WebGLExtensionID::EXT_sRGB:
         obj = new WebGLExtensionSRGB(this);
         break;
     case WebGLExtensionID::EXT_texture_filter_anisotropic:
         obj = new WebGLExtensionTextureFilterAnisotropic(this);
         break;
 
+    // MOZ_
+    case WebGLExtensionID::MOZ_debug_get:
+        obj = new WebGLExtensionDebugGet(this);
+        break;
+
     // OES_
     case WebGLExtensionID::OES_element_index_uint:
         obj = new WebGLExtensionElementIndexUint(this);
         break;
     case WebGLExtensionID::OES_standard_derivatives:
         obj = new WebGLExtensionStandardDerivatives(this);
         break;
     case WebGLExtensionID::OES_texture_float:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -56,28 +56,16 @@ WebGLContext::Enable(GLenum cap)
     {
         *trackingSlot = 1;
     }
 
     MakeContextCurrent();
     gl->fEnable(cap);
 }
 
-static JS::Value
-StringValue(JSContext* cx, const nsAString& str, ErrorResult& rv)
-{
-    JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length());
-    if (!jsStr) {
-        rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-        return JS::NullValue();
-    }
-
-    return JS::StringValue(jsStr);
-}
-
 bool
 WebGLContext::GetStencilBits(GLint* const out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -906,9 +906,23 @@ InfoFrom(WebGLTexImageFunc func, WebGLTe
         default:
             MOZ_CRASH("GFX: invalid 3D TexDimensions");
         }
     default:
         MOZ_CRASH("GFX: invalid TexDimensions");
     }
 }
 
+////
+
+JS::Value
+StringValue(JSContext* cx, const nsAString& str, ErrorResult& er)
+{
+    JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length());
+    if (!jsStr) {
+        er.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return JS::NullValue();
+    }
+
+    return JS::StringValue(jsStr);
+}
+
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -89,11 +89,13 @@ WebGLContext::WebGLObjectAsJSObject(JSCo
 }
 
 /**
  * Return the displayable name for the texture function that is the
  * source for validation.
  */
 const char* InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims);
 
+JS::Value StringValue(JSContext* cx, const nsAString& str, ErrorResult& er);
+
 } // namespace mozilla
 
 #endif // WEBGL_CONTEXT_UTILS_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLExtensionDebugGet.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "WebGLExtensions.h"
+
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "WebGLContext.h"
+#include "WebGLContextUtils.h"
+
+namespace mozilla {
+
+WebGLExtensionDebugGet::WebGLExtensionDebugGet(WebGLContext* webgl)
+    : WebGLExtensionBase(webgl)
+{
+}
+
+WebGLExtensionDebugGet::~WebGLExtensionDebugGet()
+{
+}
+
+void
+WebGLExtensionDebugGet::GetParameter(JSContext* cx, GLenum pname,
+                                     JS::MutableHandle<JS::Value> retval,
+                                     ErrorResult& er) const
+{
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+
+    switch (pname) {
+    case LOCAL_GL_EXTENSIONS:
+        {
+            nsString ret;
+            if (!gl->IsCoreProfile()) {
+                const auto rawExts = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
+                ret = NS_ConvertUTF8toUTF16(rawExts);
+            } else {
+                const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
+                for (GLuint i = 0; i < numExts; i++) {
+                    const auto rawExt = (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS,
+                                                                     i);
+                    if (i > 0) {
+                        ret.AppendLiteral(" ");
+                    }
+                    ret.Append(NS_ConvertUTF8toUTF16(rawExt));
+                }
+            }
+            retval.set(StringValue(cx, ret, er));
+            return;
+        }
+
+    case LOCAL_GL_RENDERER:
+    case LOCAL_GL_VENDOR:
+    case LOCAL_GL_VERSION:
+        {
+            const auto raw = (const char*)gl->fGetString(pname);
+            retval.set(StringValue(cx, NS_ConvertUTF8toUTF16(raw), er));
+            return;
+        }
+
+    case 0x10000: // "WSI_INFO"
+        {
+            nsCString info;
+            gl->GetWSIInfo(&info);
+            retval.set(StringValue(cx, NS_ConvertUTF8toUTF16(info), er));
+            return;
+        }
+
+    default:
+        mContext->ErrorInvalidEnumArg("MOZ_debug_get.getParameter", "pname", pname);
+        retval.set(JS::NullValue());
+        return;
+    }
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugGet, MOZ_debug_get)
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -9,16 +9,17 @@
 #include "mozilla/AlreadyAddRefed.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
+class ErrorResult;
 
 namespace dom {
 template<typename> struct Nullable;
 template<typename> class Sequence;
 } // namespace dom
 
 namespace webgl {
 class FormatUsageAuthority;
@@ -398,11 +399,24 @@ public:
     void GetQueryObjectEXT(JSContext* cx, const WebGLQuery& query,
                            GLenum pname, JS::MutableHandleValue retval) const;
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDebugGet final
+    : public WebGLExtensionBase
+{
+public:
+    explicit WebGLExtensionDebugGet(WebGLContext* webgl);
+    virtual ~WebGLExtensionDebugGet();
+
+    void GetParameter(JSContext* cx, GLenum pname,
+                      JS::MutableHandle<JS::Value> retval, ErrorResult& er) const;
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 } // namespace mozilla
 
 #endif // WEBGL_EXTENSIONS_H_
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -144,16 +144,17 @@ enum class WebGLExtensionID : uint8_t {
     EXT_blend_minmax,
     EXT_color_buffer_float,
     EXT_color_buffer_half_float,
     EXT_frag_depth,
     EXT_sRGB,
     EXT_shader_texture_lod,
     EXT_texture_filter_anisotropic,
     EXT_disjoint_timer_query,
+    MOZ_debug_get,
     OES_element_index_uint,
     OES_standard_derivatives,
     OES_texture_float,
     OES_texture_float_linear,
     OES_texture_half_float,
     OES_texture_half_float_linear,
     OES_vertex_array_object,
     WEBGL_color_buffer_float,
--- a/dom/canvas/gtest/moz.build
+++ b/dom/canvas/gtest/moz.build
@@ -1,14 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Canvas: 2D')
+
+with Files('*WebGL*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
 UNIFIED_SOURCES += [
     'TestImageBitmapColorUtils.cpp',
     'TestWebGLElementArrayCache.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/canvas',
     '/media/libyuv/include'
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -1,14 +1,38 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Canvas: 2D')
+
+with Files('TexUnpackBlob.cpp'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('WebGL*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('test/webgl-conf/**'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('test/webgl-mochitest/**'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('test/reftest/webgl*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('test/chrome/*webgl*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+with Files('test/crossorigin/*webgl*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
 TEST_DIRS += [
     'gtest'
 ]
 
 # Change the following line(s) to avoid bug 1081323 (clobber after changing a manifest):
 # * Adjust failure errata for webgl-conf.
 
 MOCHITEST_MANIFESTS += [
@@ -108,16 +132,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionColorBufferFloat.cpp',
     'WebGLExtensionColorBufferHalfFloat.cpp',
     'WebGLExtensionCompressedTextureASTC.cpp',
     'WebGLExtensionCompressedTextureATC.cpp',
     'WebGLExtensionCompressedTextureES3.cpp',
     'WebGLExtensionCompressedTextureETC1.cpp',
     'WebGLExtensionCompressedTexturePVRTC.cpp',
     'WebGLExtensionCompressedTextureS3TC.cpp',
+    'WebGLExtensionDebugGet.cpp',
     'WebGLExtensionDebugRendererInfo.cpp',
     'WebGLExtensionDebugShaders.cpp',
     'WebGLExtensionDepthTexture.cpp',
     'WebGLExtensionDisjointTimerQuery.cpp',
     'WebGLExtensionDrawBuffers.cpp',
     'WebGLExtensionElementIndexUint.cpp',
     'WebGLExtensionEXTColorBufferFloat.cpp',
     'WebGLExtensionFragDepth.cpp',
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -8073,17 +8073,17 @@ skip-if = 1
 [generated/test_conformance__ogles__GL__any__any_001_to_004.html]
 [generated/test_conformance__ogles__GL__array__array_001_to_006.html]
 [generated/test_conformance__ogles__GL__asin__asin_001_to_006.html]
 [generated/test_conformance__ogles__GL__atan__atan_001_to_008.html]
 [generated/test_conformance__ogles__GL__atan__atan_009_to_012.html]
 [generated/test_conformance__ogles__GL__biConstants__biConstants_001_to_008.html]
 [generated/test_conformance__ogles__GL__biConstants__biConstants_009_to_016.html]
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__build__build_001_to_008.html]
 [generated/test_conformance__ogles__GL__build__build_009_to_016.html]
 [generated/test_conformance__ogles__GL__build__build_017_to_024.html]
 [generated/test_conformance__ogles__GL__build__build_025_to_032.html]
 [generated/test_conformance__ogles__GL__build__build_033_to_040.html]
 [generated/test_conformance__ogles__GL__build__build_041_to_048.html]
 [generated/test_conformance__ogles__GL__build__build_049_to_056.html]
 [generated/test_conformance__ogles__GL__build__build_057_to_064.html]
@@ -8120,34 +8120,34 @@ skip-if = (os == 'win')
 [generated/test_conformance__ogles__GL__exp2__exp2_001_to_008.html]
 [generated/test_conformance__ogles__GL__exp2__exp2_009_to_012.html]
 [generated/test_conformance__ogles__GL__exp__exp_001_to_008.html]
 [generated/test_conformance__ogles__GL__exp__exp_009_to_012.html]
 [generated/test_conformance__ogles__GL__faceforward__faceforward_001_to_006.html]
 [generated/test_conformance__ogles__GL__floor__floor_001_to_006.html]
 [generated/test_conformance__ogles__GL__fract__fract_001_to_006.html]
 [generated/test_conformance__ogles__GL__functions__functions_001_to_008.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__functions__functions_009_to_016.html]
 [generated/test_conformance__ogles__GL__functions__functions_017_to_024.html]
 [generated/test_conformance__ogles__GL__functions__functions_025_to_032.html]
 [generated/test_conformance__ogles__GL__functions__functions_033_to_040.html]
 [generated/test_conformance__ogles__GL__functions__functions_041_to_048.html]
 [generated/test_conformance__ogles__GL__functions__functions_049_to_056.html]
 [generated/test_conformance__ogles__GL__functions__functions_057_to_064.html]
 [generated/test_conformance__ogles__GL__functions__functions_065_to_072.html]
 [generated/test_conformance__ogles__GL__functions__functions_073_to_080.html]
 [generated/test_conformance__ogles__GL__functions__functions_081_to_088.html]
 [generated/test_conformance__ogles__GL__functions__functions_089_to_096.html]
 [generated/test_conformance__ogles__GL__functions__functions_097_to_104.html]
 [generated/test_conformance__ogles__GL__functions__functions_105_to_112.html]
 [generated/test_conformance__ogles__GL__functions__functions_113_to_120.html]
 [generated/test_conformance__ogles__GL__functions__functions_121_to_126.html]
 [generated/test_conformance__ogles__GL__gl_FragCoord__gl_FragCoord_001_to_003.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__gl_FrontFacing__gl_FrontFacing_001_to_001.html]
 [generated/test_conformance__ogles__GL__greaterThanEqual__greaterThanEqual_001_to_008.html]
 [generated/test_conformance__ogles__GL__greaterThan__greaterThan_001_to_008.html]
 [generated/test_conformance__ogles__GL__inversesqrt__inversesqrt_001_to_006.html]
 [generated/test_conformance__ogles__GL__length__length_001_to_006.html]
 [generated/test_conformance__ogles__GL__lessThanEqual__lessThanEqual_001_to_008.html]
 [generated/test_conformance__ogles__GL__lessThan__lessThan_001_to_008.html]
 [generated/test_conformance__ogles__GL__log2__log2_001_to_008.html]
@@ -8180,17 +8180,17 @@ skip-if = (os == 'linux')
 [generated/test_conformance__ogles__GL__pow__pow_009_to_016.html]
 [generated/test_conformance__ogles__GL__pow__pow_017_to_024.html]
 [generated/test_conformance__ogles__GL__radians__radians_001_to_006.html]
 [generated/test_conformance__ogles__GL__reflect__reflect_001_to_006.html]
 [generated/test_conformance__ogles__GL__refract__refract_001_to_006.html]
 [generated/test_conformance__ogles__GL__sign__sign_001_to_006.html]
 skip-if = (os == 'linux')
 [generated/test_conformance__ogles__GL__sin__sin_001_to_006.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__smoothstep__smoothstep_001_to_006.html]
 [generated/test_conformance__ogles__GL__sqrt__sqrt_001_to_006.html]
 [generated/test_conformance__ogles__GL__step__step_001_to_006.html]
 [generated/test_conformance__ogles__GL__struct__struct_001_to_008.html]
 [generated/test_conformance__ogles__GL__struct__struct_009_to_016.html]
 [generated/test_conformance__ogles__GL__struct__struct_017_to_024.html]
 [generated/test_conformance__ogles__GL__struct__struct_025_to_032.html]
 [generated/test_conformance__ogles__GL__struct__struct_033_to_040.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -143,19 +143,19 @@ skip-if = (os == 'android') || (os == 'w
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__gl_FragCoord__gl_FragCoord_001_to_003.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 
 [generated/test_conformance__textures__misc__texture-size-limit.html]
 fail-if = (os == 'linux') || (os == 'android')
 skip-if = (os == 'linux' && asan)
 [generated/test_2_conformance2__reading__read-pixels-from-fbo-test.html]
 skip-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__misc__uninitialized-test-2.html]
 skip-if = (os == 'mac') || (os == 'win')
@@ -364,19 +364,19 @@ fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__glsl__bugs__temp-expressions-should-not-crash.html]
 # Coincidentally enough, crashes on Linux and Android 4.0.
 skip-if = (os == 'android') || (os == 'linux')
 [generated/test_conformance__misc__invalid-passed-params.html]
 # Causes consistent *blues*: "DMError: Remote Device Error: unable to
 # connect to 127.0.0.1 after 5 attempts" on 'Android 2.3 Opt'.
 skip-if = (os == 'android') || (os == 'linux')
 [generated/test_conformance__ogles__GL__functions__functions_001_to_008.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__ogles__GL__sin__sin_001_to_006.html]
-fail-if = (os == 'android') || (os == 'linux')
+fail-if = (os == 'android')
 [generated/test_conformance__reading__read-pixels-test.html]
 # Causes consistent *blues*: "DMError: Remote Device Error: unable to
 # connect to 127.0.0.1 after 5 attempts" on 'Android 2.3 Opt'.
 # Crashes near on B2G ICS Emulator.
 skip-if = (os == 'android') || (os == 'linux')
 [generated/test_conformance__textures__misc__texture-upload-size.html]
 # application crashed [@ mozilla::WebGLTexture::TexSubImage]
 skip-if = (os == 'win') || (os == 'android')
--- a/dom/canvas/test/webgl-mochitest/mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest/mochitest.ini
@@ -25,17 +25,17 @@ fail-if = (os == 'android')
 fail-if = (os == 'android') || (os == 'linux')
 [ensure-exts/test_OES_standard_derivatives.html]
 fail-if = (os == 'android')
 [ensure-exts/test_WEBGL_color_buffer_float.html]
 fail-if = (os == 'android')
 [ensure-exts/test_WEBGL_compressed_texture_atc.html]
 fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
 [ensure-exts/test_WEBGL_compressed_texture_es3.html]
-fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
+fail-if = (os == 'android') || (os == 'mac') || (os == 'win')
 [ensure-exts/test_WEBGL_compressed_texture_etc1.html]
 fail-if = (os == 'linux') || (os == 'mac') || (os == 'win')
 [ensure-exts/test_WEBGL_compressed_texture_pvrtc.html]
 fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
 [ensure-exts/test_WEBGL_compressed_texture_s3tc.html]
 fail-if = (os == 'android') || (os == 'linux')
 [ensure-exts/test_WEBGL_depth_texture.html]
 fail-if = (os == 'mac' && os_version == '10.6')
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -602,16 +602,24 @@ WINDOW_ONLY_EVENT(deviceproximity,
 WINDOW_ONLY_EVENT(userproximity,
                   eUserProximity,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(devicelight,
                   eDeviceLight,
                   EventNameType_None,
                   eBasicEventClass)
+WINDOW_ONLY_EVENT(vrdisplayactivate,
+                  eVRDisplayActivate,
+                  EventNameType_None,
+                  eBasicEventClass)
+WINDOW_ONLY_EVENT(vrdisplaydeactivate,
+                  eVRDisplayDeactivate,
+                  EventNameType_None,
+                  eBasicEventClass)
 WINDOW_ONLY_EVENT(vrdisplayconnect,
                   eVRDisplayConnect,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(vrdisplaydisconnect,
                   eVRDisplayDisconnect,
                   EventNameType_None,
                   eBasicEventClass)
--- a/dom/events/NotifyPaintEvent.cpp
+++ b/dom/events/NotifyPaintEvent.cpp
@@ -38,91 +38,64 @@ NotifyPaintEvent::NotifyPaintEvent(Event
 NS_INTERFACE_MAP_BEGIN(NotifyPaintEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNotifyPaintEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 NS_IMPL_ADDREF_INHERITED(NotifyPaintEvent, Event)
 NS_IMPL_RELEASE_INHERITED(NotifyPaintEvent, Event)
 
 nsRegion
-NotifyPaintEvent::GetRegion()
+NotifyPaintEvent::GetRegion(SystemCallerGuarantee)
 {
   nsRegion r;
-  if (!nsContentUtils::IsCallerChrome()) {
-    return r;
-  }
   for (uint32_t i = 0; i < mInvalidateRequests.Length(); ++i) {
     r.Or(r, mInvalidateRequests[i].mRect);
     r.SimplifyOutward(10);
   }
   return r;
 }
 
-NS_IMETHODIMP
-NotifyPaintEvent::GetBoundingClientRect(nsIDOMClientRect** aResult)
-{
-  *aResult = BoundingClientRect().take();
-  return NS_OK;
-}
-
 already_AddRefed<DOMRect>
-NotifyPaintEvent::BoundingClientRect()
+NotifyPaintEvent::BoundingClientRect(SystemCallerGuarantee aGuarantee)
 {
   RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
 
   if (mPresContext) {
-    rect->SetLayoutRect(GetRegion().GetBounds());
+    rect->SetLayoutRect(GetRegion(aGuarantee).GetBounds());
   }
 
   return rect.forget();
 }
 
-NS_IMETHODIMP
-NotifyPaintEvent::GetClientRects(nsIDOMClientRectList** aResult)
-{
-  *aResult = ClientRects().take();
-  return NS_OK;
-}
-
 already_AddRefed<DOMRectList>
-NotifyPaintEvent::ClientRects()
+NotifyPaintEvent::ClientRects(SystemCallerGuarantee aGuarantee)
 {
   nsISupports* parent = ToSupports(this);
   RefPtr<DOMRectList> rectList = new DOMRectList(parent);
 
-  nsRegion r = GetRegion();
+  nsRegion r = GetRegion(aGuarantee);
   for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
     RefPtr<DOMRect> rect = new DOMRect(parent);
     rect->SetLayoutRect(iter.Get());
     rectList->Append(rect);
   }
 
   return rectList.forget();
 }
 
-NS_IMETHODIMP
-NotifyPaintEvent::GetPaintRequests(nsISupports** aResult)
-{
-  RefPtr<PaintRequestList> requests = PaintRequests();
-  requests.forget(aResult);
-  return NS_OK;
-}
-
 already_AddRefed<PaintRequestList>
-NotifyPaintEvent::PaintRequests()
+NotifyPaintEvent::PaintRequests(SystemCallerGuarantee)
 {
   Event* parent = this;
   RefPtr<PaintRequestList> requests = new PaintRequestList(parent);
 
-  if (nsContentUtils::IsCallerChrome()) {
-    for (uint32_t i = 0; i < mInvalidateRequests.Length(); ++i) {
-      RefPtr<PaintRequest> r = new PaintRequest(parent);
-      r->SetRequest(mInvalidateRequests[i]);
-      requests->Append(r);
-    }
+  for (uint32_t i = 0; i < mInvalidateRequests.Length(); ++i) {
+    RefPtr<PaintRequest> r = new PaintRequest(parent);
+    r->SetRequest(mInvalidateRequests[i]);
+    requests->Append(r);
   }
 
   return requests.forget();
 }
 
 NS_IMETHODIMP_(void)
 NotifyPaintEvent::Serialize(IPC::Message* aMsg,
                             bool aSerializeInterfaceType)
@@ -154,31 +127,24 @@ NotifyPaintEvent::Deserialize(const IPC:
     NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &req.mRect), false);
     NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &req.mFlags), false);
     mInvalidateRequests.AppendElement(req);
   }
 
   return true;
 }
 
-NS_IMETHODIMP
-NotifyPaintEvent::GetTransactionId(uint64_t* aTransactionId)
-{
-  *aTransactionId = mTransactionId;
-  return NS_OK;
-}
-
 uint64_t
-NotifyPaintEvent::TransactionId()
+NotifyPaintEvent::TransactionId(SystemCallerGuarantee)
 {
   return mTransactionId;
 }
 
 DOMHighResTimeStamp
-NotifyPaintEvent::PaintTimeStamp()
+NotifyPaintEvent::PaintTimeStamp(SystemCallerGuarantee)
 {
   return mTimeStamp;
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
--- a/dom/events/NotifyPaintEvent.h
+++ b/dom/events/NotifyPaintEvent.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_dom_NotifyPaintEvent_h_
 #define mozilla_dom_NotifyPaintEvent_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/NotifyPaintEventBinding.h"
 #include "nsIDOMNotifyPaintEvent.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 namespace dom {
 
 class DOMRect;
@@ -46,31 +47,31 @@ public:
   NS_IMETHOD_(void) Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) override;
   NS_IMETHOD_(bool) Deserialize(const IPC::Message* aMsg, PickleIterator* aIter) override;
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return NotifyPaintEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
-  already_AddRefed<DOMRectList> ClientRects();
+  already_AddRefed<DOMRectList> ClientRects(SystemCallerGuarantee aGuarantee);
 
-  already_AddRefed<DOMRect> BoundingClientRect();
+  already_AddRefed<DOMRect> BoundingClientRect(SystemCallerGuarantee aGuarantee);
 
-  already_AddRefed<PaintRequestList> PaintRequests();
+  already_AddRefed<PaintRequestList> PaintRequests(SystemCallerGuarantee);
 
-  uint64_t TransactionId();
+  uint64_t TransactionId(SystemCallerGuarantee);
 
-  DOMHighResTimeStamp PaintTimeStamp();
+  DOMHighResTimeStamp PaintTimeStamp(SystemCallerGuarantee);
 
 protected:
   ~NotifyPaintEvent() {}
 
 private:
-  nsRegion GetRegion();
+  nsRegion GetRegion(SystemCallerGuarantee);
 
   nsTArray<nsInvalidateRequestList::Request> mInvalidateRequests;
   uint64_t mTransactionId;
   DOMHighResTimeStamp mTimeStamp;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/StorageEvent.h
+++ b/dom/events/StorageEvent.h
@@ -8,18 +8,16 @@
 #define mozilla_dom_StorageEvent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/StorageEventBinding.h"
 
-class nsIPrincipal;
-
 namespace mozilla {
 namespace dom {
 
 class Storage;
 
 class StorageEvent : public Event
 {
 public:
@@ -31,17 +29,16 @@ public:
 protected:
   virtual ~StorageEvent();
 
   nsString mKey;
   nsString mOldValue;
   nsString mNewValue;
   nsString mUrl;
   RefPtr<Storage> mStorageArea;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
 
 public:
   virtual StorageEvent* AsStorageEvent();
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<StorageEvent>
   Constructor(EventTarget* aOwner, const nsAString& aType,
@@ -77,26 +74,14 @@ public:
   {
     aRetVal = mUrl;
   }
 
   Storage* GetStorageArea() const
   {
     return mStorageArea;
   }
-
-  // Non WebIDL methods
-  void SetPrincipal(nsIPrincipal* aPrincipal)
-  {
-    MOZ_ASSERT(!mPrincipal);
-    mPrincipal = aPrincipal;
-  }
-
-  nsIPrincipal* GetPrincipal() const
-  {
-    return mPrincipal;
-  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StorageEvent_h
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -77,17 +77,16 @@ support-files =
 [test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html]
   support-files = pointerevent_releasepointercapture_onpointerup_mouse-manual.html
 [test_pointerevent_releasepointercapture_release_right_after_capture-manual.html]
   support-files = pointerevent_releasepointercapture_release_right_after_capture-manual.html
 [test_pointerevent_setpointercapture_disconnected-manual.html]
   support-files = pointerevent_setpointercapture_disconnected-manual.html
 [test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
   support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
-  disabled = should be investigated
 [test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
 [test_pointerevent_setpointercapture_override_pending_capture_element-manual.html]
   support-files = pointerevent_setpointercapture_override_pending_capture_element-manual.html
 [test_pointerevent_setpointercapture_relatedtarget-manual.html]
   support-files = pointerevent_setpointercapture_relatedtarget-manual.html
   disabled = should be investigated
 [test_pointerevent_setpointercapture_to_same_element_twice-manual.html]
--- a/dom/events/test/pointerevents/mochitest_support_external.js
+++ b/dom/events/test/pointerevents/mochitest_support_external.js
@@ -76,80 +76,91 @@ var MouseEventHelper = (function() {
     },
 
     checkExitState: function() {
       ok(!this.BUTTONS_STATE, "Mismatched mousedown/mouseup caught.");
     }
   };
 }) ();
 
+function createMouseEvent(aEventType, aParams) {
+  var eventObj = {type: aEventType};
+
+  // Default to mouse.
+  eventObj.inputSource =
+    (aParams && "inputSource" in aParams) ? aParams.inputSource :
+                                          MouseEvent.MOZ_SOURCE_MOUSE;
+  // Compute pointerId
+  eventObj.id =
+    (eventObj.inputSource === MouseEvent.MOZ_SOURCE_MOUSE) ? MouseEventHelper.MOUSE_ID :
+                                                             MouseEventHelper.PEN_ID;
+  // Check or generate a |button| value.
+  var isButtonEvent = aEventType === "mouseup" || aEventType === "mousedown";
+
+  // Set |button| to the default value first.
+  eventObj.button = isButtonEvent ? MouseEventHelper.BUTTON_LEFT
+                                  : MouseEventHelper.BUTTON_NONE;
+
+  // |button| is passed, use and check it.
+  if (aParams && "button" in aParams) {
+    var hasButtonValue = (aParams.button !== MouseEventHelper.BUTTON_NONE);
+    ok(!isButtonEvent || hasButtonValue,
+       "Inappropriate |button| value caught.");
+    eventObj.button = aParams.button;
+  }
+
+  // Generate a |buttons| value and update buttons state
+  var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(eventObj.button);
+  switch(aEventType) {
+    case "mousedown":
+      MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag.
+      break;
+    case "mouseup":
+      MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag.
+      break;
+  }
+  eventObj.buttons = MouseEventHelper.BUTTONS_STATE;
+
+  // Replace the button value for mousemove events.
+  // Since in widget level design, even when no button is pressed at all, the
+  // value of WidgetMouseEvent.button is still 0, which is the same value as
+  // the one for mouse left button.
+  if (aEventType === "mousemove") {
+    eventObj.button = MouseEventHelper.BUTTON_LEFT;
+  }
+  return eventObj;
+}
+
 // Helper function to send MouseEvent with different parameters
 function sendMouseEvent(int_win, elemId, mouseEventType, params) {
   var elem = int_win.document.getElementById(elemId);
-  if(!!elem) {
+  if (elem) {
     var rect = elem.getBoundingClientRect();
-    var eventObj = {type: mouseEventType};
-
-    // Default to mouse.
-    eventObj.inputSource =
-      (params && "inputSource" in params) ? params.inputSource :
-                                            MouseEvent.MOZ_SOURCE_MOUSE;
-    // Compute pointerId
-    eventObj.id =
-      (eventObj.inputSource === MouseEvent.MOZ_SOURCE_MOUSE) ? MouseEventHelper.MOUSE_ID :
-                                                               MouseEventHelper.PEN_ID;
-    // Check or generate a |button| value.
-    var isButtonEvent = mouseEventType === "mouseup" ||
-                        mouseEventType === "mousedown";
-
-    // Set |button| to the default value first.
-    eventObj.button = isButtonEvent ? MouseEventHelper.BUTTON_LEFT
-                                    : MouseEventHelper.BUTTON_NONE;
-
-    // |button| is passed, use and check it.
-    if (params && "button" in params) {
-      var hasButtonValue = (params.button !== MouseEventHelper.BUTTON_NONE);
-      ok(!isButtonEvent || hasButtonValue,
-         "Inappropriate |button| value caught.");
-      eventObj.button = params.button;
-    }
-
-    // Generate a |buttons| value and update buttons state
-    var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(eventObj.button);
-    switch(mouseEventType) {
-      case "mousedown":
-        MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag.
-        break;
-      case "mouseup":
-        MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag.
-        break;
-    }
-    eventObj.buttons = MouseEventHelper.BUTTONS_STATE;
-
-    // Replace the button value for mousemove events.
-    // Since in widget level design, even when no button is pressed at all, the
-    // value of WidgetMouseEvent.button is still 0, which is the same value as
-    // the one for mouse left button.
-    if (mouseEventType === "mousemove") {
-      eventObj.button = MouseEventHelper.BUTTON_LEFT;
-    }
+    var eventObj = createMouseEvent(mouseEventType, params);
 
     // Default to the center of the target element but we can still send to a
     // position outside of the target element.
     var offsetX = params && "offsetX" in params ? params.offsetX : rect.width / 2;
     var offsetY = params && "offsetY" in params ? params.offsetY : rect.height / 2;
 
     console.log(elemId, eventObj);
     synthesizeMouse(elem, offsetX, offsetY, eventObj, int_win);
 
   } else {
     is(!!elem, true, "Document should have element with id: " + elemId);
   }
 }
 
+// Helper function to send MouseEvent with position
+function sendMouseEventAtPoint(aWindow, aLeft, aTop, aMouseEventType, aParams) {
+  var eventObj = createMouseEvent(aMouseEventType, aParams);
+  console.log(eventObj);
+  synthesizeMouseAtPoint(aLeft, aTop, eventObj, aWindow);
+}
+
 // Touch Event Helper Object
 var TouchEventHelper = {
   // State
   TOUCH_ID: utils.DEFAULT_TOUCH_POINTER_ID,
   TOUCH_STATE: false,
 
   // Utils
   checkExitState: function() {
--- a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_inactive_button_mouse-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_inactive_button_mouse-manual.html
@@ -12,17 +12,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     <script type="text/javascript" src="mochitest_support_external.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         runTestInNewWindow("pointerevent_setpointercapture_inactive_button_mouse-manual.html");
       }
       function executeTest(int_win) {
-        sendMouseEvent(int_win, "target1", "mousemove");
-        sendMouseEvent(int_win, "target0", "mousemove");
-        sendMouseEvent(int_win, "target1", "mousemove");
+        let target0 = int_win.document.getElementById("target0");
+        let rect = target0.getBoundingClientRect();
+        sendMouseEventAtPoint(int_win, rect.left + 1, rect.top + 1, "mousemove");
+        sendMouseEventAtPoint(int_win, rect.left - 1, rect.top - 1, "mousemove");
       }
     </script>
   </head>
   <body>
   </body>
 </html>
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -445,16 +445,22 @@ const kEventConstructors = {
   UserProximityEvent:                        { create: function (aName, aProps) {
                                                          return new UserProximityEvent(aName, aProps);
                                                        },
                                              },
   USSDReceivedEvent:                         { create: function (aName, aProps) {
                                                           return new USSDReceivedEvent(aName, aProps);
                                                        },
                                              },
+  VRDisplayEvent:                            { create: null,
+                                               // Required argument expects a VRDisplay that can not
+                                               // be created from Javascript without physical VR hardware
+                                               // connected.  When Bug 1229480 lands, this test can be
+                                               // updated to use the puppet VR device.
+                                             },
   WheelEvent:                                { create: function (aName, aProps) {
                                                          return new WheelEvent(aName, aProps);
                                                        },
                                              },
   WebGLContextEvent:                         { create: function (aName, aProps) {
                                                          return new WebGLContextEvent(aName, aProps);
                                                        },
                                              },
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -424,28 +424,24 @@ GetFilesHelperBase::AddExploredDirectory
 #endif
 
   bool isLink;
   rv = aDir->IsSymlink(&isLink);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsAutoCString path;
-
+  nsAutoString path;
   if (!isLink) {
-    nsAutoString path16;
-    rv = aDir->GetPath(path16);
+    rv = aDir->GetPath(path);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-
-    path = NS_ConvertUTF16toUTF8(path16);
   } else {
-    rv = aDir->GetNativeTarget(path);
+    rv = aDir->GetTarget(path);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   mExploredDirectories.PutEntry(path);
   return NS_OK;
 }
@@ -458,18 +454,18 @@ GetFilesHelperBase::ShouldFollowSymLink(
   if (NS_WARN_IF(NS_FAILED(aDir->IsSymlink(&isLink)) ||
                  NS_FAILED(aDir->IsDirectory(&isDir)))) {
     return false;
   }
 
   MOZ_ASSERT(isLink && isDir, "Why are we here?");
 #endif
 
-  nsAutoCString targetPath;
-  if (NS_WARN_IF(NS_FAILED(aDir->GetNativeTarget(targetPath)))) {
+  nsAutoString targetPath;
+  if (NS_WARN_IF(NS_FAILED(aDir->GetTarget(targetPath)))) {
     return false;
   }
 
   return !mExploredDirectories.Contains(targetPath);
 }
 
 void
 GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise)
--- a/dom/filesystem/GetFilesHelper.h
+++ b/dom/filesystem/GetFilesHelper.h
@@ -60,17 +60,17 @@ protected:
 
   bool
   ShouldFollowSymLink(nsIFile* aDirectory);
 
   bool mRecursiveFlag;
 
   // We populate this array in the I/O thread with the BlobImpl.
   FallibleTArray<RefPtr<BlobImpl>> mTargetBlobImplArray;
-  nsTHashtable<nsCStringHashKey> mExploredDirectories;
+  nsTHashtable<nsStringHashKey> mExploredDirectories;
 };
 
 // Retrieving the list of files can be very time/IO consuming. We use this
 // helper class to do it just once.
 class GetFilesHelper : public Runnable
                      , public GetFilesHelperBase
 {
   friend class GetFilesHelperParent;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1466,16 +1466,43 @@ HTMLMediaElement::GetMozDebugReaderData(
 {
   if (mDecoder && !mSrcStream) {
     nsAutoCString result;
     mDecoder->GetMozDebugReaderData(result);
     aString = NS_ConvertUTF8toUTF16(result);
   }
 }
 
+already_AddRefed<Promise>
+HTMLMediaElement::MozRequestDebugInfo(ErrorResult& aRv)
+{
+  RefPtr<Promise> promise = CreateDOMPromise(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nsAutoString result;
+  GetMozDebugReaderData(result);
+
+  if (mDecoder) {
+    mDecoder->RequestDebugInfo()->Then(
+      AbstractThread::MainThread(), __func__,
+      [promise, result] (const nsACString& aString) {
+        promise->MaybeResolve(result + NS_ConvertUTF8toUTF16(aString));
+      },
+      [promise, result] () {
+        promise->MaybeResolve(result);
+      });
+  } else {
+    promise->MaybeResolve(result);
+  }
+
+  return promise.forget();
+}
+
 void
 HTMLMediaElement::MozDumpDebugInfo()
 {
   if (mDecoder) {
     mDecoder->DumpDebugInfo();
   }
 }
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -599,16 +599,20 @@ public:
     mIsCasting = aShow;
   }
 
   already_AddRefed<MediaSource> GetMozMediaSourceObject() const;
   // Returns a string describing the state of the media player internal
   // data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
+  // Returns a promise which will be resolved after collecting debugging
+  // data from decoder/reader/MDSM. Used for debugging purposes.
+  already_AddRefed<Promise> MozRequestDebugInfo(ErrorResult& aRv);
+
   void MozDumpDebugInfo();
 
   void SetVisible(bool aVisible);
 
   already_AddRefed<DOMMediaStream> GetSrcObject() const;
   void SetSrcObject(DOMMediaStream& aValue);
   void SetSrcObject(DOMMediaStream* aValue);
 
--- a/dom/interfaces/core/nsIDOMElement.idl
+++ b/dom/interfaces/core/nsIDOMElement.idl
@@ -24,50 +24,19 @@ interface nsIDOMMozNamedAttrMap;
  * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-element
  */
 
 [uuid(6289999b-1008-4269-b42a-413ec5a9d3f4)]
 interface nsIDOMElement : nsIDOMNode
 {
   readonly attribute DOMString        tagName;
 
-           attribute DOMString        id;
-           attribute DOMString        className;
-  /**
-   * Returns a DOMTokenList object reflecting the class attribute.
-   */
-  readonly attribute nsISupports classList;
-
   readonly attribute nsIDOMMozNamedAttrMap attributes;
   DOMString          getAttribute(in DOMString name);
-  DOMString          getAttributeNS(in DOMString namespaceURI, 
-                                    in DOMString localName);
   void               setAttribute(in DOMString name, 
                                   in DOMString value);
-  void               setAttributeNS(in DOMString namespaceURI, 
-                                    in DOMString qualifiedName, 
-                                    in DOMString value);
-  void               removeAttribute(in DOMString name);
-  void               removeAttributeNS(in DOMString namespaceURI, 
-                                       in DOMString localName);
   boolean            hasAttribute(in DOMString name);
-  boolean            hasAttributeNS(in DOMString namespaceURI, 
-                                    in DOMString localName);
-  boolean            hasAttributes();
 
   // Obsolete methods.
   nsIDOMAttr         getAttributeNode(in DOMString name);
-  nsIDOMAttr         setAttributeNode(in nsIDOMAttr newAttr);
-  nsIDOMAttr         removeAttributeNode(in nsIDOMAttr oldAttr);
   nsIDOMAttr         getAttributeNodeNS(in DOMString namespaceURI, 
                                         in DOMString localName);
-  nsIDOMAttr         setAttributeNodeNS(in nsIDOMAttr newAttr)
-                                        raises(DOMException);
-
-  nsIDOMHTMLCollection getElementsByTagName(in DOMString name);
-  nsIDOMHTMLCollection getElementsByTagNameNS(in DOMString namespaceURI, 
-                                              in DOMString localName);
-  /**
-   * Retrieve elements matching all classes listed in a
-   * space-separated string.
-   */
-  nsIDOMHTMLCollection getElementsByClassName(in DOMString classes);
 };
--- a/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl
+++ b/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl
@@ -1,41 +1,12 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
 
 #include "nsISupports.idl"
-#include "nsIDOMClientRectList.idl"
-#include "nsIDOMClientRect.idl"
 
-interface nsIDOMPaintRequestList;
-
-/**
- * The nsIDOMNotifyPaintEvent interface is used for the MozDOMAfterPaint
- * event, which fires at a window when painting has happened in
- * that window.
- */
 [builtinclass, uuid(63f573a0-3e4e-474b-a0c2-bb4ca93febaa)]
 interface nsIDOMNotifyPaintEvent : nsISupports
 {
-  /**
-   * Get a list of rectangles which are affected. The rectangles are in CSS pixels
-   * relative to the viewport origin.
-   * If the caller is not trusted (e.g., regular Web content) then only painting
-   * caused by the current document is reported; in particular, painting in subdocuments
-   * is not reported.
-   */
-  readonly attribute nsIDOMClientRectList clientRects;
-  /**
-   * Get the bounding box of the rectangles which are affected. The rectangle
-   * is in CSS pixels relative to the viewport origin.
-   * If the caller is not trusted (e.g., regular Web content) then only painting
-   * caused by the current document is reported; in particular, painting in subdocuments
-   * is not reported.
-   */
-  readonly attribute nsIDOMClientRect boundingClientRect;
-
-  readonly attribute nsISupports /* PaintRequestList */ paintRequests;
-
-  readonly attribute unsigned long long transactionId;
 };
 
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -15,25 +15,18 @@ interface mozIDOMWindow;
  * "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
  */
 [scriptable, uuid(a20c742e-3ed1-44fb-b897-4080a75b1662)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
    * This starts async preloading of a storage cache for scope
    * defined by the principal.
-   *
-   * Because of how multi-e10s support was implemented in bug 1285898, the
-   * StorageCache instance can no longer use a timer to keep itself alive.  So a
-   * Storage instance is returned if precaching believes the principal may have
-   * localStorage data.  (Previously the StorageCache would be brought into
-   * existence and kept alive by the timer so that it could be returned if a
-   * call to createStorage was made due to a request by the page.)
    */
-  nsIDOMStorage precacheStorage(in nsIPrincipal aPrincipal);
+  void precacheStorage(in nsIPrincipal aPrincipal);
 
   /**
    * Returns instance of DOM storage object for given principal.
    * A new object is always returned and it is ensured there is
    * a storage for the scope created.
    *
    * @param aWindow
    *    The parent window.
--- a/dom/interfaces/xul/nsIDOMXULElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULElement.idl
@@ -1,83 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
 
 #include "nsIDOMElement.idl"
 
 interface nsIRDFCompositeDataSource;
-interface nsIXULTemplateBuilder;
 interface nsIRDFResource;
 interface nsIControllers;
 interface nsIBoxObject;
 
 
 [uuid(75435ab3-6863-42a1-ade3-025393d9e80e)]
 interface nsIDOMXULElement : nsIDOMElement
 {
-  // Layout properties
-  attribute DOMString align;
-  attribute DOMString dir;
-  attribute DOMString flex;
-  attribute DOMString flexGroup;
-  attribute DOMString ordinal;
-  attribute DOMString orient;
-  attribute DOMString pack;
-  
-  // Properties for hiding elements.
-  attribute boolean hidden;
-  attribute boolean collapsed;
-  
-  // Property for hooking up to broadcasters
-  attribute DOMString observes;
-  
-  // Properties for hooking up to popups
-  attribute DOMString menu;
-  attribute DOMString contextMenu;
-  attribute DOMString tooltip;
-  
-  // Width/height properties
-  attribute DOMString width;
-  attribute DOMString height;
-  attribute DOMString minWidth;
-  attribute DOMString minHeight;
-  attribute DOMString maxWidth;
-  attribute DOMString maxHeight;
<