Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 19 Dec 2016 13:35:32 -0500
changeset 390942 f778def55c45214a28cc397c63e866cd151b26b0
parent 390941 b9805d2f472b76c55daface4aee4e5f7add88074 (current diff)
parent 373675 d4b3146a5567a7ddbcdfa5244945db55616cb8d1 (diff)
child 390943 ea33a7be34193cf195d4fedb687e750a7a6ff387
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.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: 6ocpYm7MBCV
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_contextMenus_actionMenus.js
browser/extensions/formautofill/content/FormAutofillContent.jsm
browser/extensions/formautofill/content/FormAutofillParent.jsm
browser/extensions/formautofill/content/ProfileStorage.jsm
browser/extensions/formautofill/test/unit/tail.js
browser/themes/linux/Security-broken.png
dom/events/test/pointerevents/pointerevent_attributes_mouse-manual.html
dom/events/test/pointerevents/pointerevent_pointerdown-manual.html
dom/events/test/pointerevents/pointerevent_pointerenter-manual.html
dom/events/test/pointerevents/pointerevent_pointerenter_nohover-manual.html
dom/events/test/pointerevents/pointerevent_pointerleave_after_pointerup_nohover-manual.html
dom/events/test/pointerevents/pointerevent_pointerleave_mouse-manual.html
dom/events/test/pointerevents/pointerevent_pointerleave_touch-manual.html
dom/events/test/pointerevents/pointerevent_pointermove-on-chorded-mouse-button.html
dom/events/test/pointerevents/pointerevent_pointermove_pointertype-manual.html
dom/events/test/pointerevents/pointerevent_pointerout-manual.html
dom/events/test/pointerevents/pointerevent_pointerout_after_pointerup_nohover-manual.html
dom/events/test/pointerevents/pointerevent_pointerover-manual.html
dom/events/test/pointerevents/pointerevent_pointertype_mouse-manual.html
dom/events/test/pointerevents/pointerevent_pointertype_pen-manual.html
dom/events/test/pointerevents/pointerevent_pointertype_touch-manual.html
dom/events/test/pointerevents/pointerevent_pointerup-manual.html
dom/events/test/pointerevents/pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
dom/events/test/pointerevents/pointerevent_pointerup_pointertype-manual.html
dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_click.html
dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerenter_nohover-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointerup_nohover-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerleave_mouse-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerleave_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html
dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerout-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointerup_nohover-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerover-manual.html
dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html
dom/events/test/pointerevents/test_pointerevent_pointertype_pen-manual.html
dom/events/test/pointerevents/test_pointerevent_pointertype_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerup-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
dom/events/test/pointerevents/test_pointerevent_pointerup_pointertype-manual.html
dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html
dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html
dom/media/NextFrameSeekTask.cpp
dom/media/NextFrameSeekTask.h
dom/media/SeekTask.cpp
dom/media/SeekTask.h
dom/svg/nsSVGPathGeometryElement.cpp
dom/svg/nsSVGPathGeometryElement.h
dom/svg/nsSVGPolyElement.cpp
dom/svg/nsSVGPolyElement.h
dom/worklet/tests/file_audioWorklet.html
dom/worklet/tests/file_basic.html
dom/worklet/tests/file_console.html
dom/worklet/tests/file_dump.html
dom/worklet/tests/file_exception.html
dom/worklet/tests/file_import_with_cache.html
gfx/thebes/gfxPrefs.h
layout/base/nsLayoutUtils.cpp
layout/painting/FrameLayerBuilder.cpp
layout/painting/nsDisplayList.cpp
layout/reftests/abs-pos/reftest.list
layout/reftests/border-image/reftest.list
layout/reftests/bugs/reftest.list
layout/reftests/canvas/reftest.list
layout/reftests/columns/reftest.list
layout/reftests/forms/fieldset/reftest.list
layout/reftests/position-dynamic-changes/relative/reftest.list
layout/reftests/position-sticky/reftest.list
layout/reftests/text-overflow/reftest.list
layout/reftests/transform-3d/reftest.list
layout/reftests/writing-mode/reftest.list
layout/svg/nsSVGPathGeometryFrame.cpp
layout/svg/nsSVGPathGeometryFrame.h
mobile/android/base/adjust_sdk_app_token.in
mobile/android/base/resources/drawable/tab_strip_divider.xml
modules/libpref/init/all.js
security/moz.build
testing/firefox-ui/tests/puppeteer/test_prefs.py
testing/marionette/puppeteer/firefox/docs/api/prefs.rst
testing/marionette/puppeteer/firefox/firefox_puppeteer/api/prefs.py
third_party/rust/url/.cargo-checksum.json
third_party/rust/url/Cargo.toml
third_party/rust/url/src/host.rs
third_party/rust/url/src/lib.rs
third_party/rust/url/tests/unit.rs
third_party/rust/url/tests/urltestdata.json
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
toolkit/locales/en-US/chrome/formautofill/requestAutocomplete.dtd
toolkit/moz.configure
toolkit/mozapps/extensions/test/addons/test_install4/addon4.xpi
toolkit/mozapps/extensions/test/addons/test_install4/addon5.jar
toolkit/mozapps/extensions/test/addons/test_install4/addon6.xpi
toolkit/mozapps/extensions/test/addons/test_install4/addon7.jar
toolkit/mozapps/extensions/test/addons/test_install4/badaddon.jar
toolkit/mozapps/extensions/test/addons/test_install4/badaddon.xpi
toolkit/mozapps/extensions/test/addons/test_install4/icon.png
toolkit/mozapps/extensions/test/addons/test_install4/install.rdf
toolkit/mozapps/extensions/test/addons/test_update_multi1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_update_multi1/install.rdf
toolkit/mozapps/extensions/test/addons/test_update_multi2/addon.xpi
toolkit/mozapps/extensions/test/addons/test_update_multi2/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/multi_badid.xpi
toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/multi_broken.xpi
toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/multi_signed.xpi
toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi
toolkit/mozapps/extensions/test/xpcshell/data/test_update_multi.rdf
toolkit/mozapps/extensions/test/xpcshell/test_signed_multi.js
toolkit/mozapps/extensions/test/xpinstall/browser_multipackage.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_multipackage.js
toolkit/mozapps/extensions/test/xpinstall/multipackage.xpi
toolkit/mozapps/extensions/test/xpinstall/signed-multipackage.xpi
widget/PuppetWidget.cpp
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -20,25 +20,24 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // TreeMutation class
 
 EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
 
 TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
   mParent(aParent), mStartIdx(UINT32_MAX),
   mStateFlagsCopy(mParent->mStateFlags),
-  mEventTree(aNoEvents ? kNoEventTree : nullptr),
   mQueueEvents(!aNoEvents)
 {
 #ifdef DEBUG
   mIsDone = false;
 #endif
 
 #ifdef A11Y_LOG
-  if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
+  if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
     logging::MsgBegin("EVENTS_TREE", "reordering tree before");
     logging::AccessibleInfo("reordering for", mParent);
     Controller()->RootEventTree().Log();
     logging::MsgEnd();
 
     if (logging::IsEnabled(logging::eVerbose)) {
       logging::Tree("EVENTS_TREE", "Container tree", mParent->Document(),
                     PrefixLog, static_cast<void*>(this));
@@ -114,17 +113,17 @@ TreeMutation::Done()
   mParent->mEmbeddedObjCollector = nullptr;
   mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
 
 #ifdef DEBUG
   mIsDone = true;
 #endif
 
 #ifdef A11Y_LOG
-  if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
+  if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
     logging::MsgBegin("EVENTS_TREE", "reordering tree after");
     logging::AccessibleInfo("reordering for", mParent);
     Controller()->RootEventTree().Log();
     logging::MsgEnd();
   }
 #endif
 }
 
--- a/accessible/base/EventTree.h
+++ b/accessible/base/EventTree.h
@@ -42,17 +42,16 @@ private:
 
 #ifdef A11Y_LOG
   static const char* PrefixLog(void* aData, Accessible*);
 #endif
 
   Accessible* mParent;
   uint32_t mStartIdx;
   uint32_t mStateFlagsCopy;
-  EventTree* mEventTree;
 
   /*
    * True if mutation events should be queued.
    */
   bool mQueueEvents;
 
 #ifdef DEBUG
   bool mIsDone;
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -837,16 +837,20 @@ NotificationController::WillRefresh(mozi
   }
 
   ProcessEventQueue();
 
   if (IPCAccessibilityActive()) {
     size_t newDocCount = newChildDocs.Length();
     for (size_t i = 0; i < newDocCount; i++) {
       DocAccessible* childDoc = newChildDocs[i];
+      if (childDoc->IsDefunct()) {
+        continue;
+      }
+
       Accessible* parent = childDoc->Parent();
       DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
       uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
       MOZ_ASSERT(id);
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -56,17 +56,17 @@
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 #include "nsImageFrame.h"
 #include "nsIObserverService.h"
 #include "nsLayoutUtils.h"
 #include "nsPluginFrame.h"
-#include "nsSVGPathGeometryFrame.h"
+#include "SVGGeometryFrame.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsXBLBinding.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/Preferences.h"
@@ -1168,18 +1168,18 @@ nsAccessibilityService::CreateAccessible
           frameType == nsGkAtoms::scrollFrame) {
         newAcc = new XULTabpanelAccessible(content, document);
       }
     }
   }
 
   if (!newAcc) {
     if (content->IsSVGElement()) {
-      nsSVGPathGeometryFrame* pathGeometryFrame = do_QueryFrame(frame);
-      if (pathGeometryFrame) {
+      SVGGeometryFrame* geometryFrame = do_QueryFrame(frame);
+      if (geometryFrame) {
         // A graphic elements: rect, circle, ellipse, line, path, polygon,
         // polyline and image. A 'use' and 'text' graphic elements require
         // special support.
         newAcc = new EnumRoleAccessible<roles::GRAPHIC>(content, document);
       } else if (content->IsSVGElement(nsGkAtoms::svg)) {
         newAcc = new EnumRoleAccessible<roles::DIAGRAM>(content, document);
       }
 
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -446,26 +446,16 @@ pref("full-screen-api.enabled", true);
 // fill the whole screen, we'll just make the content fill the client window,
 // i.e. it won't give the impression to content that the number of device
 // screen pixels changes!
 pref("full-screen-api.ignore-widgets", true);
 #endif
 
 pref("media.volume.steps", 10);
 
-#ifdef ENABLE_MARIONETTE
-//Enable/disable marionette server, set listening port
-pref("marionette.defaultPrefs.enabled", true);
-pref("marionette.defaultPrefs.port", 2828);
-#ifndef MOZ_WIDGET_GONK
-// On desktop builds, we need to force the socket to listen on localhost only
-pref("marionette.force-local", true);
-#endif
-#endif
-
 #ifdef MOZ_UPDATER
 // When we're applying updates, we can't let anything hang us on
 // quit+restart.  The user has no recourse.
 pref("shutdown.watchdog.timeoutSecs", 10);
 // Timeout before the update prompt automatically installs the update
 pref("b2g.update.apply-prompt-timeout", 60000); // milliseconds
 // Amount of time to wait after the user is idle before prompting to apply an update
 pref("b2g.update.apply-idle-timeout", 600000); // milliseconds
@@ -812,18 +802,17 @@ pref("disk_space_watcher.enabled", true)
 // SNTP preferences.
 pref("network.sntp.maxRetryCount", 10);
 pref("network.sntp.refreshPeriod", 86400); // In seconds.
 pref("network.sntp.pools", // Servers separated by ';'.
      "0.pool.ntp.org;1.pool.ntp.org;2.pool.ntp.org;3.pool.ntp.org");
 pref("network.sntp.port", 123);
 pref("network.sntp.timeout", 30); // In seconds.
 
-// Allow ADB to run for this many hours before disabling
-// (only applies when marionette is disabled)
+// Allow ADB to run for this many hours before disabling.
 // 0 disables the timer.
 pref("b2g.adb.timeout-hours", 12);
 
 // InputMethod so we can do soft keyboards
 pref("dom.mozInputMethod.enabled", true);
 
 // Absolute path to the devtool unix domain socket file used
 // to communicate with a usb cable via adb forward
--- a/b2g/chrome/content/devtools/adb.js
+++ b/b2g/chrome/content/devtools/adb.js
@@ -161,29 +161,16 @@ var AdbController = {
     let usbFuncActive = this.umsActive || isDebugging;
     usbFuncActive |= (sysUsbConfig.indexOf("rndis") >= 0);
     usbFuncActive |= (sysUsbConfig.indexOf("mtp") >= 0);
 
     let enableAdb = this.remoteDebuggerEnabled &&
       (!(this.lockEnabled && this.locked) || usbFuncActive);
 
     let useDisableAdbTimer = true;
-    try {
-      if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) {
-        // Marionette is enabled. Marionette requires that adb be on (and also
-        // requires that remote debugging be off). The fact that marionette
-        // is enabled also implies that we're doing a non-production build, so
-        // we want adb enabled all of the time.
-        enableAdb = true;
-        useDisableAdbTimer = false;
-      }
-    } catch (e) {
-      // This means that the pref doesn't exist. Which is fine. We just leave
-      // enableAdb alone.
-    }
 
     // Check wakelock to prevent adb from disconnecting when phone is locked
     let lockFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
     lockFile.initWithPath('/sys/power/wake_lock');
     if(lockFile.exists()) {
       let foStream = Cc["@mozilla.org/network/file-input-stream;1"]
             .createInstance(Ci.nsIFileInputStream);
       let coStream = Cc["@mozilla.org/intl/converter-input-stream;1"]
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/debug
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/debug
@@ -7,14 +7,13 @@ ac_add_options --enable-b2g-camera
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
-ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
@@ -8,14 +8,13 @@ ac_add_options --enable-updater
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
 ac_add_options --enable-debug-symbols
 # ac_add_options --enable-profiling
-ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/linux32_gecko/debug
+++ b/b2g/config/mozconfigs/linux32_gecko/debug
@@ -18,17 +18,16 @@ export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Use sccache
 no_sccache=
 
 #B2G options
 ac_add_options --enable-application=b2g
-ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moz-tt
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/macosx64_gecko/debug
+++ b/b2g/config/mozconfigs/macosx64_gecko/debug
@@ -16,17 +16,16 @@ export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 
 #ac_add_options --with-macbundlename-prefix=Firefox
 
 # B2G Stuff
 ac_add_options --enable-application=b2g
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
-ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moz-tt
 
--- a/b2g/config/mozconfigs/win32_gecko/debug
+++ b/b2g/config/mozconfigs/win32_gecko/debug
@@ -12,17 +12,16 @@ ac_add_options --enable-debug
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 # B2G Options
 ac_add_options --enable-application=b2g
-ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moz-tt
 
--- a/b2g/graphene/config/horizon-mozconfigs/linux32/debug
+++ b/b2g/graphene/config/horizon-mozconfigs/linux32/debug
@@ -14,12 +14,11 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 # graphene options
-ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/horizon-mozconfigs/common.override"
--- a/b2g/graphene/config/horizon-mozconfigs/linux64/debug
+++ b/b2g/graphene/config/horizon-mozconfigs/linux64/debug
@@ -14,12 +14,11 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 # graphene options
-ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/horizon-mozconfigs/common.override"
--- a/b2g/graphene/config/horizon-mozconfigs/macosx64/debug
+++ b/b2g/graphene/config/horizon-mozconfigs/macosx64/debug
@@ -11,13 +11,12 @@ ac_add_options --enable-js-diagnostics
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 #ac_add_options --with-macbundlename-prefix=Firefox
 
 # graphene Stuff
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
-ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/horizon-mozconfigs/common.override"
--- a/b2g/graphene/config/horizon-mozconfigs/win32/debug
+++ b/b2g/graphene/config/horizon-mozconfigs/win32/debug
@@ -7,13 +7,11 @@ ac_add_options --enable-debug
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 # graphene Options
-ENABLE_MARIONETTE=1
-
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/linux32/debug
+++ b/b2g/graphene/config/mozconfigs/linux32/debug
@@ -14,12 +14,11 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 # graphene options
-ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/linux64/debug
+++ b/b2g/graphene/config/mozconfigs/linux64/debug
@@ -14,12 +14,11 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 # graphene options
-ENABLE_MARIONETTE=1
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/macosx64/debug
+++ b/b2g/graphene/config/mozconfigs/macosx64/debug
@@ -11,13 +11,12 @@ ac_add_options --enable-js-diagnostics
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 #ac_add_options --with-macbundlename-prefix=Firefox
 
 # graphene Stuff
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
-ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/win32/debug
+++ b/b2g/graphene/config/mozconfigs/win32/debug
@@ -7,13 +7,11 @@ ac_add_options --enable-debug
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 # graphene Options
-ENABLE_MARIONETTE=1
-
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/grapheneconfig/mozconfigs/common.override"
--- a/b2g/graphene/graphene.js
+++ b/b2g/graphene/graphene.js
@@ -42,18 +42,14 @@ pref("app.update.url", "https://aus4.moz
 pref("b2g.nativeWindowGeometry.width", 700);
 pref("b2g.nativeWindowGeometry.height", 600);
 pref("b2g.nativeWindowGeometry.screenX", -1); // center
 pref("b2g.nativeWindowGeometry.screenY", -1); // center
 pref("b2g.nativeWindowGeometry.fullscreen", false);
 
 pref("media.useAudioChannelService", false);
 
-#ifdef ENABLE_MARIONETTE
-pref("b2g.is_mulet", true);
-#endif
-
 // Most DevTools prefs are set from the shared file
 // devtools/client/preferences/devtools.js, but this one is currently set
 // per-app or per-channel.
 // Number of usages of the web console or scratchpad. If this is less than 5,
 // then pasting code into the web console or scratchpad is disabled
 pref("devtools.selfxss.count", 5);
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -36,20 +36,16 @@ ifdef WIN_UCRT_REDIST_DIR
 DEFINES += -DMOZ_PACKAGE_WIN_UCRT_DLLS=1
 endif
 endif
 
 ifdef MOZ_DEBUG
 DEFINES += -DMOZ_DEBUG=1
 endif
 
-ifdef ENABLE_MARIONETTE
-DEFINES += -DENABLE_MARIONETTE=1
-endif
-
 MOZ_PACKAGER_MINIFY=1
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifndef _APPNAME
 _APPNAME = $(MOZ_MACBUNDLE_NAME)
 endif
 ifndef _BINPATH
 _BINPATH = /$(_APPNAME)/Contents/MacOS
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -763,22 +763,16 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/ssltunnel
 #endif
 #endif
 @RESPATH@/chrome/icons/
 @RESPATH@/chrome/chrome@JAREXT@
 @RESPATH@/chrome/chrome.manifest
 @RESPATH@/components/B2GComponents.manifest
 @BINPATH@/@DLL_PREFIX@omxplugin@DLL_SUFFIX@
-#if defined(ENABLE_MARIONETTE) || !defined(MOZ_WIDGET_GONK)
-@RESPATH@/chrome/marionette@JAREXT@
-@RESPATH@/chrome/marionette.manifest
-@RESPATH@/components/marionette.manifest
-@RESPATH@/components/marionette.js
-#endif
 @RESPATH@/components/AlertsService.js
 @RESPATH@/components/ContentPermissionPrompt.js
 @RESPATH@/components/DirectoryProvider.js
 @RESPATH@/components/ProcessGlobal.js
 @RESPATH@/components/OMAContentHandler.js
 @RESPATH@/components/RecoveryService.js
 @RESPATH@/components/MailtoProtocolHandler.js
 @RESPATH@/components/SmsProtocolHandler.js
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -84,18 +84,17 @@ SSE2Check()
   MOZ_UNUSED(write(STDERR_FILENO,
                    sSSE2Message,
                    MOZ_ARRAY_LENGTH(sSSE2Message) - 1));
   // _exit() instead of exit() to avoid running the usual "at exit" code.
   _exit(255);
 }
 #endif
 
-#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID) \
-  && !(defined(XP_LINUX) && defined(MOZ_SANDBOX))
+#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID)
 #define MOZ_BROWSER_CAN_BE_CONTENTPROC
 #include "../../ipc/contentproc/plugin-container.cpp"
 #endif
 
 using namespace mozilla;
 
 #ifdef XP_MACOSX
 #define kOSXResourcesFolder "Resources"
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -749,19 +749,16 @@ pref("gecko.handlerService.schemes.ircs.
 pref("gecko.handlerService.schemes.ircs.0.uriTemplate", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.1.name", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.1.uriTemplate", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.2.name", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties");
 
-// By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
-pref("gecko.handlerService.allowRegisterFromDifferentHost", false);
-
 pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");
 
 pref("browser.EULA.version", 3);
 pref("browser.rights.version", 3);
 pref("browser.rights.3.shown", false);
 
 #ifdef DEBUG
 // Don't show the about:rights notification in debug builds.
@@ -1550,8 +1547,11 @@ pref("browser.crashReports.unsubmittedCh
 pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4);
 pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
 
 #ifdef NIGHTLY_BUILD
 // Enable the (fairly costly) client/server validation on nightly only. The other prefs
 // controlling validation are located in /services/sync/services-sync.js
 pref("services.sync.validation.enabled", true);
 #endif
+
+// Preferences for the form autofill system extension
+pref("browser.formautofill.enabled", false);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -554,28 +554,26 @@ toolbar:not(#TabsToolbar) > #personal-bo
 
 #urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
 #urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
 #urlbar[pageproxystate="valid"] > #urlbar-go-button,
 #urlbar:not([focused="true"]) > #urlbar-go-button {
   visibility: collapse;
 }
 
+#urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container,
+#urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box,
 #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
   visibility: collapse;
 }
 
 #urlbar[pageproxystate="invalid"] > #identity-box {
   pointer-events: none;
 }
 
-#urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box {
-  pointer-events: auto;
-}
-
 #identity-icon-labels {
   max-width: 18em;
 }
 @media (max-width: 700px) {
   #urlbar-container {
     min-width: 45ch;
   }
   #identity-icon-labels {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2363,17 +2363,32 @@ function BrowserPageInfo(documentURL, in
     }
   }
 
   // We didn't find a matching window, so open a new one.
   return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
                     "chrome,toolbar,dialog=no,resizable", args);
 }
 
-function URLBarSetURI(aURI) {
+/**
+ * Sets the URI to display in the location bar.
+ *
+ * @param aURI [optional]
+ *        nsIURI to set. If this is unspecified, the current URI will be used.
+ * @param aOptions [optional]
+ *        An object with the following properties:
+ *        {
+ *          isForLocationChange:
+ *            Set to true to indicate that the function was invoked to respond
+ *            to a location change event, rather than to reset the current URI
+ *            value. This is useful to avoid calling PopupNotifications.jsm
+ *            multiple times.
+ *        }
+ */
+function URLBarSetURI(aURI, aOptions = {}) {
   var value = gBrowser.userTypedValue;
   var valid = false;
 
   if (value == null) {
     let uri = aURI || gBrowser.currentURI;
     // Strip off "wyciwyg://" and passwords for the location bar
     try {
       uri = Services.uriFixup.createExposableURI(uri);
@@ -2396,17 +2411,17 @@ function URLBarSetURI(aURI) {
       }
     }
 
     valid = !isBlankPageURL(uri.spec);
   }
 
   gURLBar.value = value;
   gURLBar.valueIsTyped = !valid;
-  SetPageProxyState(valid ? "valid" : "invalid");
+  SetPageProxyState(valid ? "valid" : "invalid", aOptions);
 }
 
 function losslessDecodeURI(aURI) {
   let scheme = aURI.scheme;
   if (scheme == "moz-action")
     throw new Error("losslessDecodeURI should never get a moz-action URI");
 
   var value = aURI.spec;
@@ -2495,31 +2510,59 @@ function UpdateUrlbarSearchSplitterState
 }
 
 function UpdatePageProxyState()
 {
   if (gURLBar && gURLBar.value != gLastValidURLStr)
     SetPageProxyState("invalid");
 }
 
-function SetPageProxyState(aState)
+/**
+ * Updates the user interface to indicate whether the URI in the location bar is
+ * different than the loaded page, because it's being edited or because a search
+ * result is currently selected and is displayed in the location bar.
+ *
+ * @param aState
+ *        The string "valid" indicates that the security indicators and other
+ *        related user interface elments should be shown because the URI in the
+ *        location bar matches the loaded page. The string "invalid" indicates
+ *        that the URI in the location bar is different than the loaded page.
+ * @param aOptions [optional]
+ *        An object with the following properties:
+ *        {
+ *          isForLocationChange:
+ *            Set to true to indicate that the function was invoked to respond
+ *            to a location change event. This is useful to avoid calling
+ *            PopupNotifications.jsm multiple times.
+ *        }
+ */
+function SetPageProxyState(aState, aOptions = {})
 {
   if (!gURLBar)
     return;
 
   gURLBar.setAttribute("pageproxystate", aState);
 
   // the page proxy state is set to valid via OnLocationChange, which
   // gets called when we switch tabs.
   if (aState == "valid") {
     gLastValidURLStr = gURLBar.value;
     gURLBar.addEventListener("input", UpdatePageProxyState, false);
   } else if (aState == "invalid") {
     gURLBar.removeEventListener("input", UpdatePageProxyState, false);
   }
+
+  // Only need to call anchorVisibilityChange if the PopupNotifications object
+  // for this window has already been initialized (i.e. its getter no
+  // longer exists). If this is the result of a locations change, then we will
+  // already invoke PopupNotifications.locationChange separately.
+  if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get &&
+      !aOptions.isForLocationChange) {
+    PopupNotifications.anchorVisibilityChange();
+  }
 }
 
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
@@ -4503,17 +4546,17 @@ var XULBrowserWindow = {
       if ((location == "about:blank" && checkEmptyPageOrigin()) ||
           location == "") {  // Second condition is for new tabs, otherwise
                              // reload function is enabled until tab is refreshed.
         this.reloadCommand.setAttribute("disabled", "true");
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
-      URLBarSetURI(aLocationURI);
+      URLBarSetURI(aLocationURI, { isForLocationChange: true });
 
       BookmarkingUI.onLocationChange();
 
       gIdentityHandler.onLocationChange();
 
       SocialUI.updateState();
 
       UITour.onLocationChange(location);
@@ -5302,17 +5345,17 @@ var gHomeButton = {
 
   updateTooltip: function(homeButton)
   {
     if (!homeButton)
       homeButton = document.getElementById("home-button");
     if (homeButton) {
       var homePage = this.getHomePage();
       homePage = homePage.replace(/\|/g, ', ');
-      if (homePage.toLowerCase() == "about:home")
+      if (["about:home", "about:newtab"].includes(homePage.toLowerCase()))
         homeButton.setAttribute("tooltiptext", homeButton.getAttribute("aboutHomeOverrideTooltip"));
       else
         homeButton.setAttribute("tooltiptext", homePage);
     }
   },
 
   getHomePage: function()
   {
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -404,24 +404,30 @@ Sanitizer.prototype = {
     },
 
     formdata: {
       clear: Task.async(function* (range) {
         let seenException;
         let refObj = {};
         TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj);
         try {
-          // Clear undo history of all searchBars
+          // Clear undo history of all search bars.
           let windows = Services.wm.getEnumerator("navigator:browser");
           while (windows.hasMoreElements()) {
             let currentWindow = windows.getNext();
             let currentDocument = currentWindow.document;
+
+            // searchBar.textbox may not exist due to the search bar binding
+            // not having been constructed yet if the search bar is in the
+            // overflow or menu panel. It won't have a value or edit history in
+            // that case.
             let searchBar = currentDocument.getElementById("searchbar");
-            if (searchBar)
+            if (searchBar && searchBar.textbox)
               searchBar.textbox.reset();
+
             let tabBrowser = currentWindow.gBrowser;
             if (!tabBrowser) {
               // No tab browser? This means that it's too early during startup (typically,
               // Session Restore hasn't completed yet). Since we don't have find
               // bars at that stage and since Session Restore will not restore
               // find bars further down during startup, we have nothing to clear.
               continue;
             }
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -360,16 +360,18 @@ skip-if = (os == 'linux') || (e10s && de
 [browser_purgehistory_clears_sh.js]
 [browser_PageMetaData_pushstate.js]
 [browser_refreshBlocker.js]
 support-files =
   refresh_header.sjs
   refresh_meta.sjs
 [browser_relatedTabs.js]
 [browser_remoteTroubleshoot.js]
+skip-if = !updater
+reason = depends on UpdateUtils .Locale
 support-files =
   test_remoteTroubleshoot.html
 [browser_remoteWebNavigation_postdata.js]
 [browser_removeTabsToTheEnd.js]
 [browser_restore_isAppTab.js]
 [browser_sanitize-passwordDisabledHosts.js]
 [browser_sanitize-sitepermissions.js]
 [browser_sanitize-timespans.js]
--- a/browser/base/content/test/popupNotifications/browser.ini
+++ b/browser/base/content/test/popupNotifications/browser.ini
@@ -11,10 +11,12 @@ skip-if = (os == "linux" && (debug || as
 [browser_popupNotification_3.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_4.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_5.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_checkbox.js]
 skip-if = (os == "linux" && (debug || asan))
+[browser_popupNotification_no_anchors.js]
+skip-if = (os == "linux" && (debug || asan))
 [browser_reshow_in_background.js]
 skip-if = (os == "linux" && (debug || asan))
--- a/browser/base/content/test/popupNotifications/browser_displayURI.js
+++ b/browser/base/content/test/popupNotifications/browser_displayURI.js
@@ -3,21 +3,17 @@
  * consumers e.g. geolocation.
 */
 
 add_task(function* test_displayURI() {
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: "https://test1.example.com/",
   }, function*(browser) {
-    let popupShownPromise = new Promise((resolve, reject) => {
-      onPopupEvent("popupshown", function() {
-        resolve(this);
-      });
-    });
+    let popupShownPromise = waitForNotificationPanel();
     yield ContentTask.spawn(browser, null, function*() {
       content.navigator.geolocation.getCurrentPosition(function(pos) {
         // Do nothing
       });
     });
     let panel = yield popupShownPromise;
     let notification = panel.children[0];
     let body = document.getAnonymousElementByAttribute(notification,
--- a/browser/base/content/test/popupNotifications/browser_popupNotification.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification.js
@@ -8,17 +8,16 @@ var wrongBrowserNotification;
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 var tests = [
   { id: "Test#1",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       showNotification(this.notifyObj);
     },
@@ -105,17 +104,18 @@ var tests = [
       this.notification.remove();
       ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
     }
   },
   // test opening a notification for a background browser
   // Note: test 4 to 6 share a tab.
   { id: "Test#4",
     run: function* () {
-      let tab = gBrowser.addTab("about:blank");
+      let tab = gBrowser.addTab("http://example.com/");
+      yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
       isnot(gBrowser.selectedTab, tab, "new tab isn't selected");
       wrongBrowserNotificationObject.browser = gBrowser.getBrowserForTab(tab);
       let promiseTopic = promiseTopicObserved("PopupNotifications-backgroundShow");
       wrongBrowserNotification = showNotification(wrongBrowserNotificationObject);
       yield promiseTopic;
       is(PopupNotifications.isPanelOpen, false, "panel isn't open");
       ok(!wrongBrowserNotificationObject.mainActionClicked, "main action wasn't clicked");
       ok(!wrongBrowserNotificationObject.secondaryActionClicked, "secondary action wasn't clicked");
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
@@ -4,17 +4,16 @@
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 var tests = [
   // Test optional params
   { id: "Test#1",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.secondaryActions = undefined;
@@ -53,45 +52,43 @@ var tests = [
          "geo anchor should not be visible after removal");
     }
   },
 
   // Test that persistence allows the notification to persist across reloads
   { id: "Test#3",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.addOptions({
         persistence: 2
       });
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
       this.complete = false;
       yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.org/");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/")
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
       // Next load will remove the notification
       this.complete = true;
       yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.org/");
     },
     onHidden: function(popup) {
       ok(this.complete, "Should only have hidden the notification after 3 page loads");
       ok(this.notifyObj.removedCallbackTriggered, "removal callback triggered");
       gBrowser.removeTab(gBrowser.selectedTab);
       gBrowser.selectedTab = this.oldSelectedTab;
     }
   },
   // Test that a timeout allows the notification to persist across reloads
   { id: "Test#4",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       this.notifyObj = new BasicNotification(this.id);
       // Set a timeout of 10 minutes that should never be hit
       this.notifyObj.addOptions({
         timeout: Date.now() + 600000
       });
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
@@ -110,18 +107,17 @@ var tests = [
       gBrowser.selectedTab = this.oldSelectedTab;
     }
   },
   // Test that setting persistWhileVisible allows a visible notification to
   // persist across location changes
   { id: "Test#5",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.addOptions({
         persistWhileVisible: true
       });
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
       this.complete = false;
@@ -218,33 +214,9 @@ var tests = [
     },
     onHidden: function(popup) {
       ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
       this.notification.remove();
       ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
       window.locationbar.visible = true;
     }
   },
-  // Test that dismissed popupnotifications can be opened on about:blank
-  // (where the rest of the identity block is disabled)
-  { id: "Test#11",
-    run: function() {
-      this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-
-      this.notifyObj = new BasicNotification(this.id);
-      this.notifyObj.anchorID = "geo-notification-icon";
-      this.notifyObj.addOptions({dismissed: true});
-      this.notification = showNotification(this.notifyObj);
-
-      EventUtils.synthesizeMouse(document.getElementById("geo-notification-icon"), 0, 0, {});
-    },
-    onShown: function(popup) {
-      checkPopup(popup, this.notifyObj);
-      dismissNotification(popup);
-    },
-    onHidden: function(popup) {
-      this.notification.remove();
-      gBrowser.removeTab(gBrowser.selectedTab);
-      gBrowser.selectedTab = this.oldSelectedTab;
-    }
-  }
 ];
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_3.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_3.js
@@ -4,17 +4,16 @@
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 var tests = [
   // Test notification is removed when dismissed if removeOnDismissal is true
   { id: "Test#1",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.addOptions({
@@ -61,25 +60,25 @@ var tests = [
       ok(this.notifyObj1.removedCallbackTriggered, "removed callback triggered");
 
       this.notification2.remove();
       ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered");
     }
   },
   // Test that multiple notification icons are removed when switching tabs
   { id: "Test#3",
-    run: function() {
+    run: function* () {
       // show the notification on old tab.
       this.notifyObjOld = new BasicNotification(this.id);
       this.notifyObjOld.anchorID = "default-notification-icon";
       this.notificationOld = showNotification(this.notifyObjOld);
 
       // switch tab
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
 
       // show the notification on new tab.
       this.notifyObjNew = new BasicNotification(this.id);
       this.notifyObjNew.anchorID = "geo-notification-icon";
       this.notificationNew = showNotification(this.notifyObjNew);
     },
     onShown: function(popup) {
       checkPopup(popup, this.notifyObjNew);
@@ -165,20 +164,17 @@ var tests = [
         gBrowser.selectedBrowser.reload();
       });
     }
   },
   // location change in background tab removes notification
   { id: "Test#7",
     run: function* () {
       let oldSelectedTab = gBrowser.selectedTab;
-      let newTab = gBrowser.addTab("about:blank");
-      gBrowser.selectedTab = newTab;
-
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       gBrowser.selectedTab = oldSelectedTab;
       let browser = gBrowser.getBrowserForTab(newTab);
 
       let notifyObj = new BasicNotification(this.id);
       notifyObj.browser = browser;
       notifyObj.options.eventCallback = function(eventName) {
         if (eventName == "removed") {
           ok(true, "Notification removed in background tab after reloading");
@@ -194,19 +190,17 @@ var tests = [
       });
     }
   },
   // Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab
   { id: "Test#8",
     run: function* () {
       yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
       let originalTab = gBrowser.selectedTab;
-      let bgTab = gBrowser.addTab("about:blank");
-      gBrowser.selectedTab = bgTab;
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      let bgTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       let anchor = document.createElement("box");
       anchor.id = "test26-anchor";
       anchor.className = "notification-anchor-icon";
       PopupNotifications.iconBox.appendChild(anchor);
 
       gBrowser.selectedTab = originalTab;
 
       let fgNotifyObj = new BasicNotification(this.id);
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
@@ -4,38 +4,37 @@
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 var tests = [
   // Popup Notifications main actions should catch exceptions from callbacks
   { id: "Test#1",
     run: function() {
-      this.testNotif = new ErrorNotification();
+      this.testNotif = new ErrorNotification(this.id);
       showNotification(this.testNotif);
     },
     onShown: function(popup) {
       checkPopup(popup, this.testNotif);
       triggerMainCommand(popup);
     },
     onHidden: function(popup) {
       ok(this.testNotif.mainActionClicked, "main action has been triggered");
     }
   },
   // Popup Notifications secondary actions should catch exceptions from callbacks
   { id: "Test#2",
     run: function() {
-      this.testNotif = new ErrorNotification();
+      this.testNotif = new ErrorNotification(this.id);
       showNotification(this.testNotif);
     },
     onShown: function(popup) {
       checkPopup(popup, this.testNotif);
       triggerSecondaryCommand(popup, 0);
     },
     onHidden: function(popup) {
       ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered");
@@ -92,75 +91,79 @@ var tests = [
       // which could be a problem if we ever decided to deep-copy.
       checkPopup(popup, this.notifyObj);
       triggerMainCommand(popup);
     },
     onHidden: function() { }
   },
   // Moving a tab to a new window should remove non-swappable notifications.
   { id: "Test#5",
-    run: function() {
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+    run: function* () {
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+
       let notifyObj = new BasicNotification(this.id);
+
+      let shown = waitForNotificationPanel();
       showNotification(notifyObj);
-      let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
-      whenDelayedStartupFinished(win, function() {
-        let anchor = win.document.getElementById("default-notification-icon");
-        win.PopupNotifications._reshowNotifications(anchor);
-        ok(win.PopupNotifications.panel.childNodes.length == 0,
-           "no notification displayed in new window");
-        ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
-        ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered");
-        win.close();
-        goNext();
-      });
+      yield shown;
+
+      let promiseWin = BrowserTestUtils.waitForNewWindow();
+      gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
+      let win = yield promiseWin;
+
+      let anchor = win.document.getElementById("default-notification-icon");
+      win.PopupNotifications._reshowNotifications(anchor);
+      ok(win.PopupNotifications.panel.childNodes.length == 0,
+         "no notification displayed in new window");
+      ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
+      ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered");
+
+      yield BrowserTestUtils.closeWindow(win);
+      yield waitForWindowReadyForPopupNotifications(window);
+
+      goNext();
     }
   },
   // Moving a tab to a new window should preserve swappable notifications.
   { id: "Test#6",
     run: function* () {
-      let originalBrowser = gBrowser.selectedBrowser;
-      let originalWindow = window;
-
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       let notifyObj = new BasicNotification(this.id);
       let originalCallback = notifyObj.options.eventCallback;
       notifyObj.options.eventCallback = function(eventName) {
         originalCallback(eventName);
         return eventName == "swapping";
       };
 
+      let shown = waitForNotificationPanel();
       let notification = showNotification(notifyObj);
-      let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
-      yield whenDelayedStartupFinished(win);
+      yield shown;
+
+      let promiseWin = BrowserTestUtils.waitForNewWindow();
+      gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
+      let win = yield promiseWin;
+      yield waitForWindowReadyForPopupNotifications(win);
 
       yield new Promise(resolve => {
         let callback = notification.options.eventCallback;
         notification.options.eventCallback = function(eventName) {
           callback(eventName);
           if (eventName == "shown") {
             resolve();
           }
         };
         info("Showing the notification again");
         notification.reshow();
       });
 
       checkPopup(win.PopupNotifications.panel, notifyObj);
       ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
+
       yield BrowserTestUtils.closeWindow(win);
-
-      // These are the same checks that PopupNotifications.jsm makes before it
-      // allows a notification to open. Do not go to the next test until we are
-      // sure that its attempt to display a notification will not fail.
-      yield BrowserTestUtils.waitForCondition(() => originalBrowser.docShellIsActive,
-                                              "The browser should be active");
-      let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
-      yield BrowserTestUtils.waitForCondition(() => fm.activeWindow == originalWindow,
-                                              "The window should be active")
+      yield waitForWindowReadyForPopupNotifications(window);
 
       goNext();
     }
   },
   // the main action callback can keep the notification.
   { id: "Test#8",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_5.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_5.js
@@ -4,17 +4,16 @@
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 var gNotification;
 
 var tests = [
   // panel updates should fire the showing and shown callbacks again.
   { id: "Test#1",
     run: function() {
@@ -60,36 +59,39 @@ var tests = [
       checkPopup(popup, this.notifyObj2);
       this.notification1.remove();
       this.notification2.remove();
     },
     onHidden: function(popup) { }
   },
   // The anchor icon should be shown for notifications in background windows.
   { id: "Test#3",
-    run: function() {
+    run: function* () {
       let notifyObj = new BasicNotification(this.id);
       notifyObj.options.dismissed = true;
-      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
-      whenDelayedStartupFinished(win, function() {
-        showNotification(notifyObj);
-        let anchor = document.getElementById("default-notification-icon");
-        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
-        win.close();
-        goNext();
-      });
+
+      let win = yield BrowserTestUtils.openNewBrowserWindow();
+
+      // Open the notification in the original window, now in the background.
+      showNotification(notifyObj);
+      let anchor = document.getElementById("default-notification-icon");
+      is(anchor.getAttribute("showing"), "true", "the anchor is shown");
+
+      yield BrowserTestUtils.closeWindow(win);
+      yield waitForWindowReadyForPopupNotifications(window);
+
+      goNext();
     }
   },
   // Test that persistent doesn't allow the notification to persist after
   // navigation.
   { id: "Test#4",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.addOptions({
         persistent: true
       });
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
       this.complete = false;
@@ -110,18 +112,17 @@ var tests = [
       gBrowser.selectedTab = this.oldSelectedTab;
     }
   },
   // Test that persistent allows the notification to persist until explicitly
   // dismissed.
   { id: "Test#5",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.addOptions({
         persistent: true
       });
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
       this.complete = false;
@@ -147,19 +148,17 @@ var tests = [
   { id: "Test#6a",
     run: function* () {
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.options.persistent = true;
       gNotification = showNotification(this.notifyObj);
     },
     onShown: function* (popup) {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
-      info("Waiting for the new tab to load.");
-      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
     },
     onHidden: function(popup) {
       ok(true, "Should have hidden the notification after tab switch");
       gBrowser.removeTab(gBrowser.selectedTab);
       gBrowser.selectedTab = this.oldSelectedTab;
     }
   },
   // Second part of the previous test that compensates for the limitation in
@@ -174,37 +173,47 @@ var tests = [
       goNext();
     }
   },
   // Test that persistent panels are still open after switching to another
   // window and back.
   { id: "Test#7",
     run: function* () {
       this.oldSelectedTab = gBrowser.selectedTab;
-      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+
+      let shown = waitForNotificationPanel();
       let notifyObj = new BasicNotification(this.id);
       notifyObj.options.persistent = true;
       this.notification = showNotification(notifyObj);
-      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
-      whenDelayedStartupFinished(win, () => {
-        ok(notifyObj.shownCallbackTriggered, "Should have triggered the shown callback");
-        let anchor = win.document.getElementById("default-notification-icon");
-        win.PopupNotifications._reshowNotifications(anchor);
-        ok(win.PopupNotifications.panel.childNodes.length == 0,
-           "no notification displayed in new window");
-        ok(PopupNotifications.isPanelOpen, "Should be still showing the popup in the first window");
-        win.close();
-        let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
-        ok(id.endsWith("Test#7"), "Should have found the notification from Test7");
-        ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the window");
-        this.notification.remove();
-        gBrowser.removeTab(gBrowser.selectedTab);
-        gBrowser.selectedTab = this.oldSelectedTab;
-        goNext();
-      });
+      yield shown;
+
+      ok(notifyObj.shownCallbackTriggered, "Should have triggered the shown callback");
+
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+      let promiseWin = BrowserTestUtils.waitForNewWindow();
+      gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
+      let win = yield promiseWin;
+
+      let anchor = win.document.getElementById("default-notification-icon");
+      win.PopupNotifications._reshowNotifications(anchor);
+      ok(win.PopupNotifications.panel.childNodes.length == 0,
+         "no notification displayed in new window");
+
+      yield BrowserTestUtils.closeWindow(win);
+      yield waitForWindowReadyForPopupNotifications(window);
+
+      let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
+      ok(id.endsWith("Test#7"), "Should have found the notification from Test7");
+      ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the window");
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+
+      goNext();
     }
   },
   // Test that only the first persistent notification is shown on update
   { id: "Test#8",
     run: function() {
       this.notifyObj1 = new BasicNotification(this.id);
       this.notifyObj1.id += "_1";
       this.notifyObj1.anchorID = "default-notification-icon";
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_checkbox.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_checkbox.js
@@ -4,17 +4,16 @@
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
   setup();
-  goNext();
 }
 
 function checkCheckbox(checkbox, label, checked = false, hidden = false) {
   is(checkbox.label, label, "Checkbox should have the correct label");
   is(checkbox.hidden, hidden, "Checkbox should be shown");
   is(checkbox.checked, checked, "Checkbox should be checked by default");
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_no_anchors.js
@@ -0,0 +1,153 @@
+/* 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/. */
+
+function test() {
+  waitForExplicitFinish();
+
+  ok(PopupNotifications, "PopupNotifications object exists");
+  ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+  setup();
+}
+
+var tests = [
+  // Test that popupnotifications are anchored to the identity icon on
+  // about:blank, where anchor icons are hidden.
+  { id: "Test#1",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.anchorID = "geo-notification-icon";
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+      is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+         "geo anchor shouldn't be visible");
+      is(popup.anchorNode.id, "identity-icon",
+         "notification anchored to identity icon");
+      dismissNotification(popup);
+    },
+    onHidden: function(popup) {
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Test that popupnotifications are anchored to the identity icon after
+  // navigation to about:blank.
+  { id: "Test#2",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.anchorID = "geo-notification-icon";
+      this.notifyObj.addOptions({
+        persistence: 1,
+      });
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown: function* (popup) {
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "about:blank");
+
+      checkPopup(popup, this.notifyObj);
+      is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+         "geo anchor shouldn't be visible");
+      is(popup.anchorNode.id, "identity-icon",
+         "notification anchored to identity icon");
+      dismissNotification(popup);
+    },
+    onHidden: function(popup) {
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Test that dismissed popupnotifications cannot be opened on about:blank, but
+  // can be opened after navigation.
+  { id: "Test#3",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.anchorID = "geo-notification-icon";
+      this.notifyObj.addOptions({
+        dismissed: true,
+        persistence: 1,
+      });
+      this.notification = showNotification(this.notifyObj);
+
+      is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+         "geo anchor shouldn't be visible");
+
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+
+      isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
+            "geo anchor should be visible");
+
+      EventUtils.synthesizeMouse(document.getElementById("geo-notification-icon"), 0, 0, {});
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+      dismissNotification(popup);
+    },
+    onHidden: function(popup) {
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Test that popupnotifications are anchored to the identity icon while
+  // editing the URL in the location bar, and restored to their anchors when the
+  // URL is reverted.
+  { id: "Test#4",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+
+      let shownInitially = waitForNotificationPanel();
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.anchorID = "geo-notification-icon";
+      this.notifyObj.addOptions({
+        persistent: true,
+      });
+      this.notification = showNotification(this.notifyObj);
+      yield shownInitially;
+
+      checkPopup(PopupNotifications.panel, this.notifyObj);
+
+      let shownAgain = waitForNotificationPanel();
+      // This will cause the popup to hide and show again.
+      gURLBar.select();
+      EventUtils.synthesizeKey("*", {});
+      // Keep the URL bar empty, so we don't show the awesomebar.
+      EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+      yield shownAgain;
+
+      is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+         "geo anchor shouldn't be visible");
+      is(PopupNotifications.panel.anchorNode.id, "identity-icon",
+         "notification anchored to identity icon");
+
+      let shownLastTime = waitForNotificationPanel();
+      // This will cause the popup to hide and show again.
+      EventUtils.synthesizeKey("VK_ESCAPE", {});
+      yield shownLastTime;
+
+      checkPopup(PopupNotifications.panel, this.notifyObj);
+
+      let hidden = new Promise(resolve => onPopupEvent("popuphidden", resolve));
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+      yield hidden;
+
+      goNext();
+    }
+  },
+];
--- a/browser/base/content/test/popupNotifications/browser_reshow_in_background.js
+++ b/browser/base/content/test/popupNotifications/browser_reshow_in_background.js
@@ -4,18 +4,21 @@
  * Tests that when PopupNotifications for background tabs are reshown, they
  * don't show up in the foreground tab, but only in the background tab that
  * they belong to.
  */
 add_task(function* test_background_notifications_dont_reshow_in_foreground() {
   // Our initial tab will be A. Let's open two more tabs B and C, but keep
   // A selected. Then, we'll trigger a PopupNotification in C, and then make
   // it reshow.
-  let tabB = gBrowser.addTab("about:blank");
-  let tabC = gBrowser.addTab("about:blank");
+  let tabB = gBrowser.addTab("http://example.com/");
+  yield BrowserTestUtils.browserLoaded(tabB.linkedBrowser);
+
+  let tabC = gBrowser.addTab("http://example.com/");
+  yield BrowserTestUtils.browserLoaded(tabC.linkedBrowser);
 
   let seenEvents = [];
 
   let options = {
     dismissed: false,
     eventCallback(popupEvent) {
       seenEvents.push(popupEvent);
     },
--- a/browser/base/content/test/popupNotifications/head.js
+++ b/browser/base/content/test/popupNotifications/head.js
@@ -2,31 +2,16 @@ Components.utils.import("resource://gre/
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 
-function whenDelayedStartupFinished(aWindow, aCallback) {
-  return new Promise(resolve => {
-    info("Waiting for delayed startup to finish");
-    Services.obs.addObserver(function observer(aSubject, aTopic) {
-      if (aWindow == aSubject) {
-        Services.obs.removeObserver(observer, aTopic);
-        if (aCallback) {
-          executeSoon(aCallback);
-        }
-        resolve();
-      }
-    }, "browser-delayed-startup-finished", false);
-  });
-}
-
 /**
  * Allows waiting for an observer notification once.
  *
  * @param topic
  *        Notification topic to observe.
  *
  * @return {Promise}
  * @resolves The array [subject, data] from the observed notification.
@@ -38,16 +23,33 @@ function promiseTopicObserved(topic)
   info("Waiting for observer topic " + topic);
   Services.obs.addObserver(function PTO_observe(obsSubject, obsTopic, obsData) {
     Services.obs.removeObserver(PTO_observe, obsTopic);
     deferred.resolve([obsSubject, obsData]);
   }, topic, false);
   return deferred.promise;
 }
 
+/**
+ * Called after opening a new window or switching windows, this will wait until
+ * we are sure that an attempt to display a notification will not fail.
+ */
+function* waitForWindowReadyForPopupNotifications(win)
+{
+  // These are the same checks that PopupNotifications.jsm makes before it
+  // allows a notification to open.
+  yield BrowserTestUtils.waitForCondition(
+    () => win.gBrowser.selectedBrowser.docShellIsActive,
+    "The browser should be active"
+  );
+  yield BrowserTestUtils.waitForCondition(
+    () => Services.focus.activeWindow == win,
+    "The window should be active"
+  );
+}
 
 /**
  * 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
@@ -65,20 +67,20 @@ function promiseTabLoadEvent(tab, url)
   }
 
   return BrowserTestUtils.browserLoaded(browser, false, url);
 }
 
 const PREF_SECURITY_DELAY_INITIAL = Services.prefs.getIntPref("security.notification_enable_delay");
 
 function setup() {
-  // Disable transitions as they slow the test down and we want to click the
-  // mouse buttons in a predictable location.
-
+  BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/")
+                  .then(goNext);
   registerCleanupFunction(() => {
+    gBrowser.removeTab(gBrowser.selectedTab);
     PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL;
   });
 }
 
 function goNext() {
   executeSoon(() => executeSoon(Task.async(runNextTest)));
 }
 
@@ -168,29 +170,29 @@ function BasicNotification(testId) {
   };
 }
 
 BasicNotification.prototype.addOptions = function(options) {
   for (let [name, value] of Object.entries(options))
     this.options[name] = value;
 };
 
-function ErrorNotification() {
+function ErrorNotification(testId) {
+  BasicNotification.call(this, testId);
   this.mainAction.callback = () => {
     this.mainActionClicked = true;
     throw new Error("Oops!");
   };
   this.secondaryActions[0].callback = () => {
     this.secondaryActionClicked = true;
     throw new Error("Oops!");
   };
 }
 
-ErrorNotification.prototype = new BasicNotification();
-ErrorNotification.prototype.constructor = ErrorNotification;
+ErrorNotification.prototype = BasicNotification.prototype;
 
 function checkPopup(popup, notifyObj) {
   info("Checking notification " + notifyObj.id);
 
   ok(notifyObj.showingCallbackTriggered, "showing callback was triggered");
   ok(notifyObj.shownCallbackTriggered, "shown callback was triggered");
 
   let notifications = popup.childNodes;
@@ -252,16 +254,24 @@ function onPopupEvent(eventName, callbac
     PopupNotifications.panel.removeEventListener(eventName, listener, false);
     gActiveListeners.delete(listener);
     executeSoon(() => callback.call(PopupNotifications.panel));
   }
   gActiveListeners.set(listener, eventName);
   PopupNotifications.panel.addEventListener(eventName, listener, false);
 }
 
+function waitForNotificationPanel() {
+  return new Promise(resolve => {
+    onPopupEvent("popupshown", function() {
+      resolve(this);
+    });
+  });
+}
+
 function triggerMainCommand(popup) {
   let notifications = popup.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   let notification = notifications[0];
   info("Triggering main command for notification " + notification.id);
   EventUtils.synthesizeMouseAtCenter(notification.button, {});
 }
 
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -33,16 +33,17 @@ var gNextRadioGroupID = 0;
 var gMaxLabelLength = 64;
 
 var gMenuBuilder = {
   // When a new contextMenu is opened, this function is called and
   // we populate the |xulMenu| with all the items from extensions
   // to be displayed. We always clear all the items again when
   // popuphidden fires.
   build(contextData) {
+    let firstItem = true;
     let xulMenu = contextData.menu;
     xulMenu.addEventListener("popuphidden", this);
     this.xulMenu = xulMenu;
     for (let [, root] of gRootItems) {
       let rootElement = this.buildElementWithChildren(root, contextData);
       if (!rootElement.firstChild || !rootElement.firstChild.childNodes.length) {
         // If the root has no visible children, there is no reason to show
         // the root menu item itself either.
@@ -67,16 +68,23 @@ var gMenuBuilder = {
         if (rootElement.localName == "menu") {
           rootElement.setAttribute("class", "menu-iconic");
         } else if (rootElement.localName == "menuitem") {
           rootElement.setAttribute("class", "menuitem-iconic");
         }
         rootElement.setAttribute("image", resolvedURL);
       }
 
+      if (firstItem) {
+        firstItem = false;
+        const separator = xulMenu.ownerDocument.createElement("menuseparator");
+        this.itemsToCleanUp.add(separator);
+        xulMenu.append(separator);
+      }
+
       xulMenu.appendChild(rootElement);
       this.itemsToCleanUp.add(rootElement);
     }
   },
 
   // Builds a context menu for browserAction and pageAction buttons.
   buildActionContextMenu(contextData) {
     const {menu} = contextData;
@@ -247,23 +255,18 @@ var gMenuBuilder = {
   itemsToCleanUp: new Set(),
 };
 
 // Called from pageAction or browserAction popup.
 global.actionContextMenu = function(contextData) {
   gMenuBuilder.buildActionContextMenu(contextData);
 };
 
-function contextMenuObserver(subject, topic, data) {
-  subject = subject.wrappedJSObject;
-  gMenuBuilder.build(subject);
-}
-
 function getContexts(contextData) {
-  let contexts = new Set(["all"]);
+  let contexts = new Set();
 
   if (contextData.inFrame) {
     contexts.add("frame");
   }
 
   if (contextData.isTextSelected) {
     contexts.add("selection");
   }
@@ -295,20 +298,26 @@ function getContexts(contextData) {
   if (contextData.onPageAction) {
     contexts.add("page_action");
   }
 
   if (contextData.onBrowserAction) {
     contexts.add("browser_action");
   }
 
-  if (contexts.size == 1) {
+  if (contexts.size === 0) {
     contexts.add("page");
   }
 
+  if (contextData.onTab) {
+    contexts.add("tab");
+  } else {
+    contexts.add("all");
+  }
+
   return contexts;
 }
 
 function MenuItem(extension, createProperties, isRoot = false) {
   this.extension = extension;
   this.children = [];
   this.parent = null;
   this.tabManager = TabManager.for(extension);
@@ -512,33 +521,71 @@ MenuItem.prototype = {
         return false;
       }
     }
 
     return true;
   },
 };
 
+// While any extensions are active, this Tracker registers to observe/listen
+// for contex-menu events from both content and chrome.
+const contextMenuTracker = {
+  register() {
+    Services.obs.addObserver(this, "on-build-contextmenu", false);
+    for (const window of WindowListManager.browserWindows()) {
+      this.onWindowOpen(window);
+    }
+    WindowListManager.addOpenListener(this.onWindowOpen);
+  },
+
+  unregister() {
+    Services.obs.removeObserver(this, "on-build-contextmenu");
+    for (const window of WindowListManager.browserWindows()) {
+      const menu = window.document.getElementById("tabContextMenu");
+      menu.removeEventListener("popupshowing", this);
+    }
+    WindowListManager.removeOpenListener(this.onWindowOpen);
+  },
+
+  observe(subject, topic, data) {
+    subject = subject.wrappedJSObject;
+    gMenuBuilder.build(subject);
+  },
+
+  onWindowOpen(window) {
+    const menu = window.document.getElementById("tabContextMenu");
+    menu.addEventListener("popupshowing", contextMenuTracker);
+  },
+
+  handleEvent(event) {
+    const menu = event.target;
+    if (menu.id === "tabContextMenu") {
+      const trigger = menu.triggerNode;
+      const tab = trigger.localName === "tab" ? trigger : TabManager.activeTab;
+      const pageUrl = tab.linkedBrowser.currentURI.spec;
+      gMenuBuilder.build({menu, tab, pageUrl, onTab: true});
+    }
+  },
+};
+
 var gExtensionCount = 0;
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
   gContextMenuMap.set(extension, new Map());
   if (++gExtensionCount == 1) {
-    Services.obs.addObserver(contextMenuObserver,
-                             "on-build-contextmenu",
-                             false);
+    contextMenuTracker.register();
   }
 });
 
 extensions.on("shutdown", (type, extension) => {
   gContextMenuMap.delete(extension);
   gRootItems.delete(extension);
   if (--gExtensionCount == 0) {
-    Services.obs.removeObserver(contextMenuObserver,
-                                "on-build-contextmenu");
+    contextMenuTracker.unregister();
   }
 });
 /* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerSchemaAPI("contextMenus", "addon_parent", context => {
   let {extension} = context;
   return {
     contextMenus: {
--- a/browser/components/extensions/schemas/context_menus.json
+++ b/browser/components/extensions/schemas/context_menus.json
@@ -26,18 +26,18 @@
         "value": 6,
         "description": "The maximum number of top level extension items that can be added to an extension action context menu. Any items beyond this limit will be ignored."
       }
     },
     "types": [
       {
         "id": "ContextType",
         "type": "string",
-        "enum": ["all", "page", "frame", "selection", "link", "editable", "password", "image", "video", "audio", "launcher", "browser_action", "page_action"],
-        "description": "The different contexts a menu can appear in. Specifying 'all' is equivalent to the combination of all other contexts except for 'launcher'. The 'launcher' context is only supported by apps and is used to add menu items to the context menu that appears when clicking on the app icon in the launcher/taskbar/dock/etc. Different platforms might put limitations on what is actually supported in a launcher context menu."
+        "enum": ["all", "page", "frame", "selection", "link", "editable", "password", "image", "video", "audio", "launcher", "browser_action", "page_action", "tab"],
+        "description": "The different contexts a menu can appear in. Specifying 'all' is equivalent to the combination of all other contexts except for 'tab'."
       },
       {
         "id": "ItemType",
         "type": "string",
         "enum": ["normal", "checkbox", "radio", "separator"],
         "description": "The type of menu item."
       },
       {
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -29,18 +29,18 @@ support-files =
 [browser_ext_browserAction_popup_resize.js]
 [browser_ext_browserAction_simple.js]
 [browser_ext_commands_execute_browser_action.js]
 [browser_ext_commands_execute_page_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
-[browser_ext_contextMenus_actionMenus.js]
 [browser_ext_contextMenus_checkboxes.js]
+[browser_ext_contextMenus_chrome.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 [browser_ext_contextMenus_radioGroups.js]
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_getViews.js]
 [browser_ext_incognito_views.js]
rename from browser/components/extensions/test/browser/browser_ext_contextMenus_actionMenus.js
rename to browser/components/extensions/test/browser/browser_ext_contextMenus_chrome.js
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_actionMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_chrome.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-add_task(function* () {
+add_task(function* test_actionContextMenus() {
   const manifest = {
     page_action: {},
     browser_action: {},
     permissions: ["contextMenus"],
   };
 
   async function background() {
     const contexts = ["page_action", "browser_action"];
@@ -38,17 +38,17 @@ add_task(function* () {
 
   for (const kind of ["page", "browser"]) {
     const menu = yield openActionContextMenu(extension, kind);
     const [submenu, second, , , , last, separator] = menu.children;
 
     is(submenu.tagName, "menu", "Correct submenu type");
     is(submenu.label, "parent", "Correct submenu title");
 
-    const popup = yield openActionSubmenu(submenu);
+    const popup = yield openSubmenu(submenu);
     is(popup, submenu.firstChild, "Correct submenu opened");
     is(popup.children.length, 2, "Correct number of submenu items");
 
     is(second.tagName, "menuitem", "Second menu item type is correct");
     is(second.label, "click 1", "Second menu item title is correct");
 
     is(last.label, "click 5", "Last menu item title is correct");
     is(separator.tagName, "menuseparator", "Separator after last menu item");
@@ -57,8 +57,73 @@ add_task(function* () {
     const {info, tab} = yield extension.awaitMessage("click");
     is(info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
     is(tab.id, tabId, "Click event tab ID is correct");
   }
 
   yield BrowserTestUtils.removeTab(tab);
   yield extension.unload();
 });
+
+add_task(function* test_tabContextMenu() {
+  const first = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["contextMenus"],
+    },
+    async background() {
+      await browser.contextMenus.create({title: "alpha", contexts: ["tab"]});
+      await browser.contextMenus.create({title: "beta", contexts: ["tab"]});
+
+      browser.contextMenus.onClicked.addListener((info, tab) => {
+        browser.test.sendMessage("click", {info, tab});
+      });
+
+      const [tab] = await browser.tabs.query({active: true});
+      browser.test.sendMessage("ready", tab.id);
+    },
+  });
+
+  const second = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["contextMenus"],
+    },
+    async background() {
+      await browser.contextMenus.create({title: "gamma", contexts: ["tab"]});
+      browser.test.sendMessage("ready");
+    },
+  });
+
+  const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+  yield first.startup();
+  yield second.startup();
+
+  const tabId = yield first.awaitMessage("ready");
+  yield second.awaitMessage("ready");
+
+  const menu = yield openTabContextMenu();
+  const [separator, submenu, gamma] = Array.from(menu.children).slice(-3);
+  is(separator.tagName, "menuseparator", "Separator before first extension item");
+
+  is(submenu.tagName, "menu", "Correct submenu type");
+  is(submenu.label, "Generated extension", "Correct submenu title");
+
+  is(gamma.tagName, "menuitem", "Third menu item type is correct");
+  is(gamma.label, "gamma", "Third menu item label is correct");
+
+  const popup = yield openSubmenu(submenu);
+  is(popup, submenu.firstChild, "Correct submenu opened");
+  is(popup.children.length, 2, "Correct number of submenu items");
+
+  const [alpha, beta] = popup.children;
+  is(alpha.tagName, "menuitem", "First menu item type is correct");
+  is(alpha.label, "alpha", "First menu item label is correct");
+  is(beta.tagName, "menuitem", "Second menu item type is correct");
+  is(beta.label, "beta", "Second menu item label is correct");
+
+  yield closeTabContextMenu(beta);
+  const click = yield first.awaitMessage("click");
+  is(click.info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
+  is(click.tab.id, tabId, "Click event tab ID is correct");
+
+  yield BrowserTestUtils.removeTab(tab);
+  yield first.unload();
+  yield second.unload();
+});
--- a/browser/components/extensions/test/browser/browser_ext_webRequest.js
+++ b/browser/components/extensions/test/browser/browser_ext_webRequest.js
@@ -21,31 +21,59 @@ function createHiddenBrowser(url) {
 
       doc.documentElement.appendChild(browser);
       resolve({frame: frame, browser: browser});
     }));
 }
 
 let extension;
 let dummy = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/file_dummy.html";
+let headers = {
+  request: {
+    add: {
+      "X-WebRequest-request": "text",
+      "X-WebRequest-request-binary": "binary",
+    },
+    modify: {
+      "user-agent": "WebRequest",
+    },
+    remove: [
+      "accept-encoding",
+    ],
+  },
+  response: {
+    add: {
+      "X-WebRequest-response": "text",
+      "X-WebRequest-response-binary": "binary",
+    },
+    modify: {
+      "server": "WebRequest",
+      "content-type": "text/html; charset=utf-8",
+    },
+    remove: [
+      "connection",
+    ],
+  },
+};
 
 add_task(function* setup() {
   // SelfSupport has a tendency to fire when running this test alone, without
   // a good way to turn it off we just set the url to ""
   yield SpecialPowers.pushPrefEnv({
     set: [["browser.selfsupport.url", ""]],
   });
   extension = makeExtension();
   yield extension.startup();
 });
 
 add_task(function* test_newWindow() {
   let expect = {
     "file_dummy.html": {
       type: "main_frame",
+      headers,
     },
   };
   // NOTE: When running solo, favicon will be loaded at some point during
   // the tests in this file, so all tests ignore it.  When running with
   // other tests in this directory, favicon gets loaded at some point before
   // we run, and we never see the request, thus it cannot be handled as part
   // of expect above.
   extension.sendMessage("set-expected", {expect, ignore: ["favicon.ico"]});
@@ -58,30 +86,32 @@ add_task(function* test_newWindow() {
   yield BrowserTestUtils.closeWindow(openedWindow);
 });
 
 add_task(function* test_newTab() {
   // again, in this window
   let expect = {
     "file_dummy.html": {
       type: "main_frame",
+      headers,
     },
   };
   extension.sendMessage("set-expected", {expect, ignore: ["favicon.ico"]});
   yield extension.awaitMessage("continue");
   let tab = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, dummy + "?newTab");
 
   yield extension.awaitMessage("done");
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_subframe() {
   let expect = {
     "file_dummy.html": {
       type: "main_frame",
+      headers,
     },
   };
   // test a content subframe attached to hidden window
   extension.sendMessage("set-expected", {expect, ignore: ["favicon.ico"]});
   yield extension.awaitMessage("continue");
   let frameInfo = yield createHiddenBrowser(dummy + "?subframe");
   yield extension.awaitMessage("done");
   // cleanup
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -5,17 +5,18 @@
 /* exported CustomizableUI makeWidgetId focusWindow forceGC
  *          getBrowserActionWidget
  *          clickBrowserAction clickPageAction
  *          getBrowserActionPopup getPageActionPopup
  *          closeBrowserAction closePageAction
  *          promisePopupShown promisePopupHidden
  *          openContextMenu closeContextMenu
  *          openExtensionContextMenu closeExtensionContextMenu
- *          openActionContextMenu openActionSubmenu closeActionContextMenu
+ *          openActionContextMenu openSubmenu closeActionContextMenu
+ *          openTabContextMenu closeTabContextMenu
  *          imageBuffer getListStyleImage getPanelForNode
  *          awaitExtensionPanel awaitPopupResize
  *          promiseContentDimensions alterContent
  */
 
 const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
 const {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
 
@@ -257,48 +258,63 @@ function* openExtensionContextMenu(selec
 
 function* closeExtensionContextMenu(itemToSelect) {
   let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
   EventUtils.synthesizeMouseAtCenter(itemToSelect, {});
   yield popupHiddenPromise;
 }
 
-function* openActionContextMenu(extension, kind, win = window) {
-  const menu = win.document.getElementById("toolbar-context-menu");
-  const id = `${makeWidgetId(extension.id)}-${kind}-action`;
-  const button = win.document.getElementById(id);
-  SetPageProxyState("valid");
-
+function* openChromeContextMenu(menuId, target, win = window) {
+  const node = win.document.querySelector(target);
+  const menu = win.document.getElementById(menuId);
   const shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
-  EventUtils.synthesizeMouseAtCenter(button, {type: "contextmenu"}, win);
+  EventUtils.synthesizeMouseAtCenter(node, {type: "contextmenu"}, win);
   yield shown;
-
   return menu;
 }
 
-function* openActionSubmenu(submenuItem, win = window) {
+function* openSubmenu(submenuItem, win = window) {
   const submenu = submenuItem.firstChild;
   const shown = BrowserTestUtils.waitForEvent(submenu, "popupshown");
   EventUtils.synthesizeMouseAtCenter(submenuItem, {}, win);
   yield shown;
   return submenu;
 }
 
-function closeActionContextMenu(itemToSelect, win = window) {
-  const menu = win.document.getElementById("toolbar-context-menu");
+function closeChromeContextMenu(menuId, itemToSelect, win = window) {
+  const menu = win.document.getElementById(menuId);
   const hidden = BrowserTestUtils.waitForEvent(menu, "popuphidden");
   if (itemToSelect) {
     EventUtils.synthesizeMouseAtCenter(itemToSelect, {}, win);
   } else {
     menu.hidePopup();
   }
   return hidden;
 }
 
+function openActionContextMenu(extension, kind, win = window) {
+  // See comment from clickPageAction below.
+  SetPageProxyState("valid");
+  const id = `#${makeWidgetId(extension.id)}-${kind}-action`;
+  return openChromeContextMenu("toolbar-context-menu", id, win);
+}
+
+function closeActionContextMenu(itemToSelect, win = window) {
+  return closeChromeContextMenu("toolbar-context-menu", itemToSelect, win);
+}
+
+function openTabContextMenu(win = window) {
+  return openChromeContextMenu("tabContextMenu", ".tabbrowser-tab[selected]", win);
+}
+
+function closeTabContextMenu(itemToSelect, win = window) {
+  return closeChromeContextMenu("tabContextMenu", itemToSelect, win);
+}
+
 function getPageActionPopup(extension, win = window) {
   let panelId = makeWidgetId(extension.id) + "-panel";
   return win.document.getElementById(panelId);
 }
 
 function clickPageAction(extension, win = window) {
   // This would normally be set automatically on navigation, and cleared
   // when the user types a value into the URL bar, to show and hide page
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -25,17 +25,16 @@ const TYPE_MAYBE_FEED = "application/vnd
 const TYPE_ANY = "*/*";
 
 const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
 const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
 const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
 const PREF_SELECTED_ACTION = "browser.feeds.handler";
 const PREF_SELECTED_READER = "browser.feeds.handler.default";
 const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external";
-const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost";
 
 const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";
 
 const NS_ERROR_MODULE_DOM = 2152923136;
 const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12;
 
 function WebContentConverter() {
 }
@@ -151,21 +150,18 @@ const Utils = {
     // we may need to revise this once we support more content types
     if (uri.scheme != "http" && uri.scheme != "https") {
       throw this.getSecurityError(
         "Permission denied to add " + uri.spec + " as a content or protocol handler",
         aContentWindow);
     }
 
     // We also reject handlers registered from a different host (see bug 402287)
-    // The pref allows us to test the feature
-    let pb = Services.prefs;
-    if (!pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST) &&
-        (!["http:", "https:"].includes(aContentWindow.location.protocol) ||
-         aContentWindow.location.hostname != uri.host)) {
+    if (!["http:", "https:"].includes(aContentWindow.location.protocol) ||
+        aContentWindow.location.hostname != uri.host) {
       throw this.getSecurityError(
         "Permission denied to add " + uri.spec + " as a content or protocol handler",
         aContentWindow);
     }
 
     // If the uri doesn't contain '%s', it won't be a good handler
     if (uri.spec.indexOf("%s") < 0)
       throw NS_ERROR_DOM_SYNTAX_ERR;
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -1,10 +1,11 @@
 import os
 import shutil
+import time
 
 from marionette_harness import MarionetteTestCase
 
 
 class TestFirefoxRefresh(MarionetteTestCase):
     _username = "marionette-test-login"
     _password = "marionette-test-password"
     _bookmarkURL = "about:mozilla"
@@ -338,31 +339,32 @@ class TestFirefoxRefresh(MarionetteTestC
             else:
                 raise
 
         if self.desktop_backup_path:
             shutil.rmtree(self.desktop_backup_path, ignore_errors=False, onerror=handleRemoveReadonly)
 
         if self.reset_profile_path:
             # Remove ourselves from profiles.ini
-            profileLeafName = os.path.basename(os.path.normpath(self.reset_profile_path))
             self.runCode("""
-              let [salt, name] = arguments[0].split(".");
+              let name = arguments[0];
               let profile = global.profSvc.getProfileByName(name);
               profile.remove(false)
               global.profSvc.flush();
-            """, script_args=[profileLeafName])
+            """, script_args=[self.profileNameToRemove])
             # And delete all the files.
             shutil.rmtree(self.reset_profile_path, ignore_errors=False, onerror=handleRemoveReadonly)
 
     def doReset(self):
+        profileName = "marionette-test-profile-" + str(int(time.time() * 1000))
+        self.profileNameToRemove = profileName
         self.runCode("""
           // Ensure the current (temporary) profile is in profiles.ini:
           let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
-          let profileName = "marionette-test-profile-" + Date.now();
+          let profileName = arguments[1];
           let myProfile = global.profSvc.createProfile(profD, profileName);
           global.profSvc.flush()
 
           // Now add the reset parameters:
           let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
           let allMarionettePrefs = Services.prefs.getChildList("marionette.");
           let prefObj = {};
           for (let pref of allMarionettePrefs) {
@@ -370,17 +372,17 @@ class TestFirefoxRefresh(MarionetteTestC
             let prefVal = global.Preferences.get(pref);
             prefObj[prefSuffix] = prefVal;
           }
           let marionetteInfo = JSON.stringify(prefObj);
           env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", marionetteInfo);
           env.set("MOZ_RESET_PROFILE_RESTART", "1");
           env.set("XRE_PROFILE_PATH", arguments[0]);
           env.set("XRE_PROFILE_NAME", profileName);
-        """, script_args=[self.marionette.instance.profile.profile])
+        """, script_args=[self.marionette.instance.profile.profile, profileName])
 
         profileLeafName = os.path.basename(os.path.normpath(self.marionette.instance.profile.profile))
 
         # Now restart the browser to get it reset:
         self.marionette.restart(clean=False, in_app=True)
         self.setUpScriptData()
 
         # Determine the new profile path (we'll need to remove it when we're done)
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -1,14 +1,15 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper",
                                   "resource:///modules/offlineAppCache.jsm");
 
 this.EXPORTED_SYMBOLS = [
   "SiteDataManager"
 ];
 
@@ -172,10 +173,33 @@ this.SiteDataManager = {
     for (let site of this._sites.values()) {
       this._removePermission(site);
       this._removeQuotaUsage(site);
     }
     Services.cache2.clear();
     Services.cookies.removeAll();
     OfflineAppCacheHelper.clear();
     this.updateSites();
+  },
+
+  getSites() {
+    return Promise.all([this._updateQuotaPromise, this._updateDiskCachePromise])
+                  .then(() => {
+                    let list = [];
+                    for (let [origin, site] of this._sites) {
+                      let cache = null;
+                      let usage = site.quotaUsage;
+                      for (cache of site.appCacheList) {
+                        usage += cache.usage;
+                      }
+                      for (cache of site.diskCacheList) {
+                        usage += cache.dataSize;
+                      }
+                      list.push({
+                        usage,
+                        status: site.status,
+                        uri: NetUtil.newURI(origin)
+                      });
+                    }
+                    return list;
+                  });
   }
 };
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -55,16 +55,18 @@ var gAdvancedPane = {
     this.updateActualCacheSize();
     this.updateActualAppCacheSize();
 
     if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
       Services.obs.addObserver(this, "sitedatamanager:sites-updated", false);
       SiteDataManager.updateSites();
       setEventListener("clearSiteDataButton", "command",
                        gAdvancedPane.clearSiteData);
+      setEventListener("siteDataSettings", "command",
+                       gAdvancedPane.showSiteDataSettings);
     }
 
     setEventListener("layers.acceleration.disabled", "change",
                      gAdvancedPane.updateHardwareAcceleration);
     setEventListener("advancedPrefs", "select",
                      gAdvancedPane.tabSelectionChanged);
     if (AppConstants.MOZ_TELEMETRY_REPORTING) {
       setEventListener("submitHealthReportBox", "command",
@@ -334,16 +336,20 @@ var gAdvancedPane = {
   /**
    * Displays a dialog in which proxy settings may be changed.
    */
   showConnections: function()
   {
     gSubDialog.open("chrome://browser/content/preferences/connection.xul");
   },
 
+  showSiteDataSettings: function() {
+    gSubDialog.open("chrome://browser/content/preferences/siteDataSettings.xul");
+  },
+
   updateTotalSiteDataSize: function() {
     SiteDataManager.getTotalUsage()
       .then(usage => {
         let size = DownloadUtils.convertByteUnits(usage);
         let prefStrBundle = document.getElementById("bundlePreferences");
         let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
         totalSiteDataSizeLabel.textContent = prefStrBundle.getFormattedString("totalSiteDataSize", size);
         let siteDataGroup = document.getElementById("siteDataGroup");
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -333,16 +333,21 @@
       <groupbox id="siteDataGroup" hidden="true">
         <caption><label>&siteData.label;</label></caption>
 
         <hbox align="center">
           <label id="totalSiteDataSize" flex="1"></label>
           <button id="clearSiteDataButton" icon="clear"
                   label="&clearSiteData.label;" accesskey="&clearSiteData.accesskey;"/>
         </hbox>
+        <vbox align="end">
+          <button id="siteDataSettings"
+                  label="&siteDataSettings.label;"
+                  accesskey="&siteDataSettings.accesskey;"/>
+        </vbox>
       </groupbox>
     </tabpanel>
 
     <!-- Update -->
     <tabpanel id="updatePanel" orient="vertical">
 #ifdef MOZ_UPDATER
       <groupbox id="updateApp" align="start">
         <caption><label>&updateApp.label;</label></caption>
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -240,20 +240,27 @@ var gMainPane = {
    *   selected and doesn't change the UI for this preference, the deprecated
    *   option is preserved.
    */
 
   syncFromHomePref: function()
   {
     let homePref = document.getElementById("browser.startup.homepage");
 
-    // If the pref is set to about:home, set the value to "" to show the
-    // placeholder text (about:home title).
-    if (homePref.value.toLowerCase() == "about:home")
+    // If the pref is set to about:home or about:newtab, set the value to ""
+    // to show the placeholder text (about:home title) rather than
+    // exposing those URLs to users.
+    let defaultBranch = Services.prefs.getDefaultBranch("");
+    let defaultValue = defaultBranch.getComplexValue("browser.startup.homepage",
+                                                     Ci.nsIPrefLocalizedString).data;
+    let currentValue = homePref.value.toLowerCase();
+    if (currentValue == "about:home" ||
+        (currentValue == defaultValue && currentValue == "about:newtab")) {
       return "";
+    }
 
     // If the pref is actually "", show about:blank.  The actual home page
     // loading code treats them the same, and we don't want the placeholder text
     // to be shown.
     if (homePref.value == "")
       return "about:blank";
 
     // Otherwise, show the actual pref value.
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 support-files =
   head.js
   privacypane_tests_perwindow.js
   site_data_test.html
 
 [browser_advanced_siteData.js]
 [browser_advanced_update.js]
+skip-if = !updater
 [browser_basic_rebuild_fonts_test.js]
 [browser_bug410900.js]
 [browser_bug705422.js]
 [browser_bug731866.js]
 [browser_bug795764_cachedisabled.js]
 [browser_bug1018066_resetScrollPosition.js]
 [browser_bug1020245_openPreferences_to_paneContent.js]
 [browser_bug1184989_prevent_scrolling_when_preferences_flipped.js]
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -22,10 +22,14 @@ browser.jar:
     content/browser/preferences/permissions.xul
     content/browser/preferences/containers.xul
     content/browser/preferences/containers.js
     content/browser/preferences/permissions.js
     content/browser/preferences/sanitize.xul
     content/browser/preferences/sanitize.js
     content/browser/preferences/selectBookmark.xul
     content/browser/preferences/selectBookmark.js
+    content/browser/preferences/siteDataSettings.xul
+    content/browser/preferences/siteDataSettings.js
+    content/browser/preferences/siteDataSettings.css
+    content/browser/preferences/siteListItem.xml
     content/browser/preferences/translation.xul
     content/browser/preferences/translation.js
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.css
@@ -0,0 +1,15 @@
+/* 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/. */
+
+#sitesList {
+  min-height: 20em;
+}
+
+#sitesList > richlistitem {
+  -moz-binding: url("chrome://browser/content/preferences/siteListItem.xml#siteListItem");
+}
+
+.item-box {
+  padding: 5px 8px;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+const { interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SiteDataManager",
+                                  "resource:///modules/SiteDataManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
+                                  "resource://gre/modules/DownloadUtils.jsm");
+
+"use strict";
+
+let gSiteDataSettings = {
+
+  // Array of meatdata of sites. Each array element is object holding:
+  // - uri: uri of site; instance of nsIURI
+  // - status: persistent-storage permission status
+  // - usage: disk usage which site uses
+  _sites: null,
+
+  _list: null,
+
+  init() {
+    this._list = document.getElementById("sitesList");
+    SiteDataManager.getSites().then(sites => {
+      this._sites = sites;
+      this._sortSites(this._sites, "decending");
+      this._buildSitesList(this._sites);
+    });
+  },
+
+  /**
+   * Sort sites by usages
+   *
+   * @param sites {Array}
+   * @param order {String} indicate to sort in the "decending" or "ascending" order
+   */
+  _sortSites(sites, order) {
+    sites.sort((a, b) => {
+      if (order === "ascending") {
+        return a.usage - b.usage;
+      }
+      return b.usage - a.usage;
+    });
+  },
+
+  _buildSitesList(sites) {
+    // Clear old entries.
+    while (this._list.childNodes.length > 1) {
+      this._list.removeChild(this._list.lastChild);
+    }
+
+    let prefStrBundle = document.getElementById("bundlePreferences");
+    for (let data of sites) {
+      let statusStrId = data.status === Ci.nsIPermissionManager.ALLOW_ACTION ? "important" : "default";
+      let size = DownloadUtils.convertByteUnits(data.usage);
+      let item = document.createElement("richlistitem");
+      item.setAttribute("data-origin", data.uri.spec);
+      item.setAttribute("host", data.uri.host);
+      item.setAttribute("status", prefStrBundle.getString(statusStrId));
+      item.setAttribute("usage", prefStrBundle.getFormattedString("siteUsage", size));
+      this._list.appendChild(item);
+    }
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >
+
+<window id="SiteDataSettingsDialog" windowtype="Browser:SiteDataSettings"
+        class="windowDialog" title="&window.title;"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        style="width: 45em;"
+        onload="gSiteDataSettings.init();"
+        persist="screenX screenY width height">
+
+  <script src="chrome://browser/content/preferences/siteDataSettings.js"/>
+
+  <stringbundle id="bundlePreferences"
+                src="chrome://browser/locale/preferences/preferences.properties"/>
+
+  <vbox flex="1">
+    <description>&settings.description;</description>
+    <separator class="thin"/>
+
+    <richlistbox id="sitesList" orient="vertical" flex="1">
+      <listheader>
+        <treecol flex="4" width="50" label="&hostCol.label;"/>
+        <treecol flex="2" width="50" label="&statusCol.label;"/>
+        <treecol flex="1" width="50" label="&usageCol.label;"/>
+      </listheader>
+    </richlistbox>
+  </vbox>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteListItem.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- import-globals-from siteDataSettings.js -->
+
+<!DOCTYPE overlay [
+  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+  <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd">
+  %brandDTD;
+  %applicationsDTD;
+]>
+
+<bindings id="siteListItemBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+
+  <binding id="siteListItem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <content>
+      <xul:hbox flex="1">
+        <xul:hbox flex="4" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=host">
+          <xul:label flex="1" crop="end" xbl:inherits="value=host"/>
+        </xul:hbox>
+        <xul:hbox flex="2" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=status">
+          <xul:label flex="1" crop="end" xbl:inherits="value=status"/>
+        </xul:hbox>
+        <xul:hbox flex="1" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=usage">
+          <xul:label flex="1" crop="end" xbl:inherits="value=usage"/>
+        </xul:hbox>
+      </xul:hbox>
+    </content>
+  </binding>
+
+</bindings>
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -876,20 +876,18 @@ var SessionStoreInternal = {
             !win.gInitialPages.includes(uri)) {
           browser.userTypedValue = uri;
         }
 
         // If the page has a title, set it.
         if (activePageData) {
           if (activePageData.title) {
             tab.label = activePageData.title;
-            tab.crop = "end";
           } else if (activePageData.url != "about:blank") {
             tab.label = activePageData.url;
-            tab.crop = "center";
           }
         } else if (tab.hasAttribute("customizemode")) {
           win.gCustomizeMode.setTab(tab);
         }
 
         // Restore the tab icon.
         if ("image" in tabData) {
           // Use the serialized contentPrincipal with the new icon load.
--- a/browser/components/tests/browser/browser.ini
+++ b/browser/components/tests/browser/browser.ini
@@ -1,4 +1,6 @@
 [DEFAULT]
 
 [browser_bug538331.js]
+skip-if = !updater
+reason = test depends on update channel
 [browser_contentpermissionprompt.js]
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -155,30 +155,25 @@ function loadJSONAsync(file, options) {
       throw ex;
     }
     return data;
   });
 }
 
 // Returns a promise that is resolved with the AddonInstall for that URL.
 function addonInstallForURL(url, hash) {
-  let deferred = Promise.defer();
-  AddonManager.getInstallForURL(url, install => deferred.resolve(install),
-                                "application/x-xpinstall", hash);
-  return deferred.promise;
+  return AddonManager.getInstallForURL(url, null, "application/x-xpinstall", hash);
 }
 
 // Returns a promise that is resolved with an Array<Addon> of the installed
 // experiment addons.
 function installedExperimentAddons() {
-  let deferred = Promise.defer();
-  AddonManager.getAddonsByTypes(["experiment"], (addons) => {
-    deferred.resolve(addons.filter(a => !a.appDisabled));
+  return AddonManager.getAddonsByTypes(["experiment"]).then(addons => {
+    return addons.filter(a => !a.appDisabled);
   });
-  return deferred.promise;
 }
 
 // Takes an Array<Addon> and returns a promise that is resolved when the
 // addons are uninstalled.
 function uninstallAddons(addons) {
   let ids = new Set(addons.map(addon => addon.id));
   let deferred = Promise.defer();
 
@@ -2020,28 +2015,24 @@ Experiments.ExperimentEntry.prototype = 
    *
    * @return Promise<Addon|null>
    */
   _getAddon: function() {
     if (!this._addonId) {
       return Promise.resolve(null);
     }
 
-    let deferred = Promise.defer();
-
-    AddonManager.getAddonByID(this._addonId, (addon) => {
+    return AddonManager.getAddonByID(this._addonId).then(addon => {
       if (addon && addon.appDisabled) {
         // Don't return PreviousExperiments.
-        addon = null;
+        return null;
       }
 
-      deferred.resolve(addon);
+      return addon;
     });
-
-    return deferred.promise;
   },
 
   _logTermination: function(terminationKind, terminationReason) {
     if (terminationKind === undefined) {
       return;
     }
 
     if (!(terminationKind in TELEMETRY_LOG.TERMINATION)) {
rename from browser/extensions/formautofill/content/FormAutofillParent.jsm
rename to browser/extensions/formautofill/FormAutofillParent.jsm
rename from browser/extensions/formautofill/content/ProfileStorage.jsm
rename to browser/extensions/formautofill/ProfileStorage.jsm
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -1,12 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* exported startup, shutdown, install, uninstall */
 
-function startup() {}
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillParent",
+                                  "resource://formautofill/FormAutofillParent.jsm");
+
+function startup() {
+  // Besides this pref, we'll need dom.forms.autocomplete.experimental enabled
+  // as well to make sure form autocomplete works correctly.
+  if (!Services.prefs.getBoolPref("browser.formautofill.enabled")) {
+    return;
+  }
+
+  FormAutofillParent.init();
+  Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillContent.js", true);
+}
+
 function shutdown() {}
 function install() {}
 function uninstall() {}
rename from browser/extensions/formautofill/content/FormAutofillContent.jsm
rename to browser/extensions/formautofill/content/FormAutofillContent.js
--- a/browser/extensions/formautofill/content/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/content/FormAutofillContent.js
@@ -1,20 +1,21 @@
 /* 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/. */
 
 /*
- * Implements a service used by DOM content to request Form Autofill.
+ * Form Autofill frame script.
  */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+
 /**
  * Handles profile autofill for a DOM Form element.
  * @param {HTMLFormElement} form Form that need to be auto filled
  */
 function FormAutofillHandler(form) {
   this.form = form;
   this.fieldDetails = [];
 }
@@ -125,10 +126,8 @@ FormAutofillHandler.prototype = {
         Cu.reportError("Autocomplete tokens mismatched");
         continue;
       }
 
       fieldDetail.element.setUserInput(field.value);
     }
   },
 };
-
-this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];
--- a/browser/extensions/formautofill/jar.mn
+++ b/browser/extensions/formautofill/jar.mn
@@ -1,7 +1,10 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [features/formautofill@mozilla.org] chrome.jar:
-% resource formautofill %content/
- content/ (content/*)
+% resource formautofill %res/
+  res/ (*.jsm)
+
+% content formautofill %content/
+  content/ (content/*)
--- a/browser/extensions/formautofill/moz.build
+++ b/browser/extensions/formautofill/moz.build
@@ -15,8 +15,11 @@ FINAL_TARGET_PP_FILES.features['formauto
   'install.rdf.in'
 ]
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 JAR_MANIFESTS += ['jar.mn']
+
+with Files('**'):
+    BUG_COMPONENT = ('Toolkit', 'Form Manager')
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -1,40 +1,58 @@
 /**
  * Provides infrastructure for automated formautofill components tests.
  */
 
-/* exported importAutofillModule, getTempFile */
+/* exported getTempFile */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://testing-common/MockDocument.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
                                   "resource://gre/modules/DownloadPaths.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
-// Register the resource path of formautofill
-let resHandler = Services.io.getProtocolHandler("resource")
-                            .QueryInterface(Ci.nsISubstitutingProtocolHandler);
-let dataURI = NetUtil.newURI(do_get_file(".", true));
-resHandler.setSubstitution("formautofill", dataURI);
+// Load our bootstrap extension manifest so we can access our chrome/resource URIs.
+const EXTENSION_ID = "formautofill@mozilla.org";
+let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
+extensionDir.append("browser");
+extensionDir.append("features");
+extensionDir.append(EXTENSION_ID);
+// If the unpacked extension doesn't exist, use the packed version.
+if (!extensionDir.exists()) {
+  extensionDir = extensionDir.parent;
+  extensionDir.append(EXTENSION_ID + ".xpi");
+}
+Components.manager.addBootstrappedManifestLocation(extensionDir);
 
 // While the previous test file should have deleted all the temporary files it
 // used, on Windows these might still be pending deletion on the physical file
 // system.  Thus, start from a new base number every time, to make a collision
 // with a file that is still pending deletion highly unlikely.
 let gFileCounter = Math.floor(Math.random() * 1000000);
 
+function loadFormAutofillContent() {
+  let facGlobal = {};
+  let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+               .getService(Ci.mozIJSSubScriptLoader);
+  loader.loadSubScriptWithOptions("chrome://formautofill/content/FormAutofillContent.js", {
+    target: facGlobal,
+  });
+
+  return facGlobal;
+}
+
 /**
  * Returns a reference to a temporary file, that is guaranteed not to exist, and
  * to have never been created before.
  *
  * @param {string} leafName
  *        Suggested leaf name for the file to be created.
  *
  * @returns {nsIFile} pointing to a non-existent file in a temporary directory.
@@ -59,15 +77,18 @@ function getTempFile(leafName) {
       file.remove(false);
     }
   });
 
   return file;
 }
 
 add_task(function* test_common_initialize() {
+  Services.prefs.setBoolPref("browser.formautofill.enabled", true);
   Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", true);
+  loadFormAutofillContent();
 
   // Clean up after every test.
   do_register_cleanup(() => {
-    Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", false);
+    Services.prefs.clearUserPref("browser.formautofill.enabled");
+    Services.prefs.clearUserPref("dom.forms.autocomplete.experimental");
   });
 });
deleted file mode 100644
--- a/browser/extensions/formautofill/test/unit/tail.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Cleans up the testing environment.
- */
-
-/* global resHandler */
-
-"use strict";
-
-// Unregister the resource path of formautofill.
-resHandler.setSubstitution("formautofill", null);
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -1,15 +1,15 @@
 /*
  * Test for form auto fill content helper fill all inputs function.
  */
 
 "use strict";
 
-let {FormAutofillHandler} = Cu.import("resource://formautofill/FormAutofillContent.jsm");
+let {FormAutofillHandler} = loadFormAutofillContent();
 
 const TESTCASES = [
   {
     description: "Form without autocomplete property",
     document: `<form><input id="given-name"><input id="family-name">
                <input id="street-addr"><input id="city"><input id="country">
                <input id='email'><input id="tel"></form>`,
     fieldDetails: [],
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -1,15 +1,15 @@
 /*
  * Test for form auto fill content helper collectFormFields functions.
  */
 
 "use strict";
 
-let {FormAutofillHandler} = Cu.import("resource://formautofill/FormAutofillContent.jsm");
+let {FormAutofillHandler} = loadFormAutofillContent();
 
 const TESTCASES = [
   {
     description: "Form without autocomplete property",
     document: `<form><input id="given-name"><input id="family-name">
                <input id="street-addr"><input id="city"><input id="country">
                <input id='email'><input id="tel"></form>`,
     returnedFormat: [],
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -1,12 +1,10 @@
 [DEFAULT]
+firefox-appdir = browser
 head = head.js
-tail = tail.js
+tail =
 support-files =
-  ../../content/FormAutofillContent.jsm
-  ../../content/FormAutofillParent.jsm
-  ../../content/ProfileStorage.jsm
 
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_populateFieldValues.js]
 [test_profileStorage.js]
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -56,16 +56,18 @@
 <!ENTITY httpCache.label                 "Cached Web Content">
 
 <!ENTITY offlineStorage2.label           "Offline Web Content and User Data">
 
 <!--  Site Data section manages sites using Storage API and is under Network -->
 <!ENTITY siteData.label                  "Site Data">
 <!ENTITY clearSiteData.label             "Clear All Data">
 <!ENTITY clearSiteData.accesskey         "l">
+<!ENTITY siteDataSettings.label          "Settings…">
+<!ENTITY siteDataSettings.accesskey      "i">
 
 <!-- LOCALIZATION NOTE:
   The entities limitCacheSizeBefore.label and limitCacheSizeAfter.label appear on a single
   line in preferences as follows:
 
   &limitCacheSizeBefore.label [textbox for cache size in MB] &limitCacheSizeAfter.label;
 -->
 <!ENTITY limitCacheSizeBefore.label      "Limit cache to">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -168,16 +168,19 @@ actualAppCacheSize=Your application cach
 #LOCALIZATION NOTE: The next string is for the total usage of site data.
 #   e.g., "The total usage is currently using 200 MB"
 #   %1$S = size
 #   %2$S = unit (MB, KB, etc.)
 totalSiteDataSize=Your stored site data is currently using %1$S %2$S of disk space
 clearSiteDataPromptTitle=Clear all cookies and site data
 clearSiteDataPromptText=Selecting ‘Clear Now’ will clear all cookies and site data stored by Firefox. This may sign you out of websites and remove offline web content.
 clearSiteDataNow=Clear Now
+important=Important
+default=Default
+siteUsage=%1$S %2$S
 
 syncUnlink.title=Do you want to unlink your device?
 syncUnlink.label=This device will no longer be associated with your Sync account. All of your personal data, both on this device and in your Sync account, will remain intact.
 syncUnlinkConfirm.label=Unlink
 
 # LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/preferences/siteDataSettings.dtd
@@ -0,0 +1,9 @@
+<!-- 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/. -->
+
+<!ENTITY     window.title                  "Settings - Site Data">
+<!ENTITY     settings.description          "The following websites asked to store site data in your disk. You can specify which websites are allowed to store site data. Default site data is temporary and could be deleted automatically.">
+<!ENTITY     hostCol.label                 "Site">
+<!ENTITY     statusCol.label               "Status">
+<!ENTITY     usageCol.label                "Storage">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -77,16 +77,17 @@
     locale/browser/preferences/preferences.properties (%chrome/browser/preferences/preferences.properties)
     locale/browser/preferences/containers.properties  (%chrome/browser/preferences/containers.properties)
     locale/browser/preferences/privacy.dtd            (%chrome/browser/preferences/privacy.dtd)
     locale/browser/preferences/security.dtd           (%chrome/browser/preferences/security.dtd)
     locale/browser/preferences/containers.dtd         (%chrome/browser/preferences/containers.dtd)
     locale/browser/preferences/sync.dtd               (%chrome/browser/preferences/sync.dtd)
     locale/browser/preferences/tabs.dtd               (%chrome/browser/preferences/tabs.dtd)
     locale/browser/preferences/search.dtd             (%chrome/browser/preferences/search.dtd)
+    locale/browser/preferences/siteDataSettings.dtd   (%chrome/browser/preferences/siteDataSettings.dtd)
     locale/browser/preferences/translation.dtd        (%chrome/browser/preferences/translation.dtd)
     locale/browser/syncBrand.dtd                (%chrome/browser/syncBrand.dtd)
     locale/browser/syncSetup.dtd                (%chrome/browser/syncSetup.dtd)
     locale/browser/syncSetup.properties         (%chrome/browser/syncSetup.properties)
     locale/browser/syncGenericChange.properties         (%chrome/browser/syncGenericChange.properties)
     locale/browser/syncKey.dtd                  (%chrome/browser/syncKey.dtd)
     locale/browser/syncQuota.dtd                (%chrome/browser/syncQuota.dtd)
     locale/browser/syncQuota.properties         (%chrome/browser/syncQuota.properties)
deleted file mode 100644
index 1ec110be4be728526476fec1ad4d5f1f3d1c8eeb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -26,17 +26,16 @@ browser.jar:
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/privatebrowsing-mask.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar.css
-  skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar@2x.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -393,17 +393,17 @@ if __name__ == "__main__":
             os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
     elif is_windows():
         extra_cflags = []
         extra_cxxflags = []
         # clang-cl would like to figure out what it's supposed to be emulating
         # by looking at an MSVC install, but we don't really have that here.
         # Force things on.
         extra_cflags2 = []
-        extra_cxxflags2 = ['-fms-compatibility-version=19.00.23918', '-Xclang', '-std=c++14']
+        extra_cxxflags2 = ['-fms-compatibility-version=19.00.24213', '-Xclang', '-std=c++14']
 
     build_one_stage(
         [cc] + extra_cflags,
         [cxx] + extra_cxxflags,
         llvm_source_dir, stage1_dir, build_libcxx,
         build_type, assertions, python_path, gcc_dir)
 
     if stages > 1:
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -365,22 +365,22 @@ def check_compiler(compiler, language, t
     if info.language == 'C++':
         if info.type in ('clang', 'gcc') and info.language_version != 201103:
             append_flag('-std=gnu++11')
         # MSVC 2015 headers include C++14 features, but don't guard them
         # with appropriate checks.
         if info.type == 'clang-cl' and info.language_version != 201402:
             append_flag('-std=c++14')
 
-    # We force clang-cl to emulate Visual C++ 2015 Update 2 with fallback to
+    # We force clang-cl to emulate Visual C++ 2015 Update 3 with fallback to
     # cl.exe.
-    if info.type == 'clang-cl' and info.version != '19.00.23918':
+    if info.type == 'clang-cl' and info.version != '19.00.24213':
         # Those flags are direct clang-cl flags that don't need -Xclang, add
         # them directly.
-        flags.append('-fms-compatibility-version=19.00.23918')
+        flags.append('-fms-compatibility-version=19.00.24213')
         flags.append('-fallback')
 
     # Check compiler target
     # --------------------------------------------------------------------
     if not info.cpu or info.cpu != target.cpu:
         if info.type == 'clang':
             append_flag('--target=%s' % target.toolchain)
         elif info.type == 'gcc':
@@ -711,21 +711,21 @@ def compiler(language, host_or_target, c
 
         # If you want to bump the version check here search for
         # __cpp_static_assert above, and see the associated comment.
         if info.type == 'clang' and not info.version:
             raise FatalCheckError(
                 'Only clang/llvm 3.6 or newer is supported.')
 
         if info.type == 'msvc':
-            if info.version < '19.00.23918':
+            if info.version < '19.00.24213':
                 raise FatalCheckError(
                     'This version (%s) of the MSVC compiler is not '
                     'supported.\n'
-                    'You must install Visual C++ 2015 Update 2 or newer in '
+                    'You must install Visual C++ 2015 Update 3 or newer in '
                     'order to build.\n'
                     'See https://developer.mozilla.org/en/'
                     'Windows_Build_Prerequisites' % info.version)
 
         return namespace(
             wrapper=wrapper,
             compiler=compiler,
             flags=flags,
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -26,17 +26,17 @@ const {loader, require} = scopedCuImport
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {TargetFactory} = require("devtools/client/framework/target");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
 let promise = require("promise");
 let defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
 const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
                                          "http://example.com/");
 const URL_ROOT_SSL = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
                                              "https://example.com/");
 
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -50,17 +50,17 @@ loader.lazyRequireGetter(this, "showDoor
   "devtools/client/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "createPerformanceFront",
   "devtools/shared/fronts/performance", true);
 loader.lazyRequireGetter(this, "system",
   "devtools/shared/system");
 loader.lazyRequireGetter(this, "getPreferenceFront",
   "devtools/shared/fronts/preference", true);
 loader.lazyRequireGetter(this, "KeyShortcuts",
-  "devtools/client/shared/key-shortcuts", true);
+  "devtools/client/shared/key-shortcuts");
 loader.lazyRequireGetter(this, "ZoomKeys",
   "devtools/client/shared/zoom-keys");
 loader.lazyRequireGetter(this, "settleAll",
   "devtools/shared/ThreadSafeDevToolsUtils", true);
 loader.lazyRequireGetter(this, "ToolboxButtons",
   "devtools/client/definitions", true);
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
--- a/devtools/client/inspector/breadcrumbs.js
+++ b/devtools/client/inspector/breadcrumbs.js
@@ -11,17 +11,17 @@ const promise = require("promise");
 const {ELLIPSIS} = require("devtools/shared/l10n");
 
 const MAX_LABEL_LENGTH = 40;
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 const SCROLL_REPEAT_MS = 100;
 
 const EventEmitter = require("devtools/shared/event-emitter");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 // Some margin may be required for visible element detection.
 const SCROLL_MARGIN = 1;
 
 /**
  * Component to replicate functionality of XUL arrowscrollbox
  * for breadcrumbs
  *
--- a/devtools/client/inspector/components/box-model.js
+++ b/devtools/client/inspector/components/box-model.js
@@ -833,9 +833,9 @@ BoxModelView.prototype = {
     if (classList.contains("boxmodel-left") ||
         classList.contains("boxmodel-right")) {
       let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT;
       classList.toggle("boxmodel-rotate", force);
     }
   }
 };
 
-exports.BoxModelView = BoxModelView;
+module.exports = BoxModelView;
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -7,31 +7,31 @@
 "use strict";
 
 const ToolDefinitions = require("devtools/client/definitions").Tools;
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Services = require("Services");
-const {OutputParser} = require("devtools/client/shared/output-parser");
+const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
 const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
-const {BoxModelView} = require("devtools/client/inspector/components/box-model");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+const BoxModelView = require("devtools/client/inspector/components/box-model");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const EventEmitter = require("devtools/shared/event-emitter");
-const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 const Services = require("Services");
 
 // Maximum number of selector suggestions shown in the panel.
 const MAX_SUGGESTIONS = 15;
 
 /**
  * Converts any input field into a document search box.
  *
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -8,17 +8,17 @@
 
 "use strict";
 
 var Services = require("Services");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var EventEmitter = require("devtools/shared/event-emitter");
 const {executeSoon} = require("devtools/shared/DevToolsUtils");
-var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+var KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 var {Task} = require("devtools/shared/task");
 const {initCssProperties} = require("devtools/shared/fronts/css-properties");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
@@ -563,17 +563,17 @@ Inspector.prototype = {
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
-      const {LayoutView} = this.browserRequire("devtools/client/inspector/layout/layout");
+      const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -253,9 +253,9 @@ LayoutView.prototype = {
     }
 
     this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
     this.refresh();
   },
 
 };
 
-exports.LayoutView = LayoutView;
+module.exports = LayoutView;
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -9,18 +9,18 @@ const Services = require("Services");
 const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const {PluralForm} = require("devtools/shared/plural-form");
 const {template} = require("devtools/shared/gcli/templater");
-const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
 const {UndoStack} = require("devtools/client/shared/undo");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const MarkupElementContainer = require("devtools/client/inspector/markup/views/element-container");
 const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/read-only-container");
 const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
 const RootContainer = require("devtools/client/inspector/markup/views/root-container");
--- a/devtools/client/inspector/rules/models/element-style.js
+++ b/devtools/client/inspector/rules/models/element-style.js
@@ -2,17 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const promise = require("promise");
-const {Rule} = require("devtools/client/inspector/rules/models/rule");
+const Rule = require("devtools/client/inspector/rules/models/rule");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 /**
  * ElementStyle is responsible for the following:
  *   Keeps track of which properties are overridden.
  *   Maintains a list of Rule objects for a given element.
@@ -404,9 +404,9 @@ UserProperties.prototype = {
     return style.actorID + ":" + name;
   },
 
   clear: function () {
     this.map.clear();
   }
 };
 
-exports.ElementStyle = ElementStyle;
+module.exports = ElementStyle;
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -678,9 +678,9 @@ Rule.prototype = {
       if (!prop.invisible) {
         return true;
       }
     }
     return false;
   }
 };
 
-exports.Rule = Rule;
+module.exports = Rule;
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -7,37 +7,37 @@
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {Tools} = require("devtools/client/definitions");
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const {OutputParser} = require("devtools/client/shared/output-parser");
+const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
-const {ElementStyle} = require("devtools/client/inspector/rules/models/element-style");
-const {Rule} = require("devtools/client/inspector/rules/models/rule");
-const {RuleEditor} = require("devtools/client/inspector/rules/views/rule-editor");
+const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
+const Rule = require("devtools/client/inspector/rules/models/rule");
+const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
   VIEW_NODE_LOCATION_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
 const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
 const {createChild, promiseWarn, throttle} = require("devtools/client/inspector/shared/utils");
 const EventEmitter = require("devtools/shared/event-emitter");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
-const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
 const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
       "devtools.inspector.mdnDocsTooltip.enabled";
 const FILTER_CHANGED_TIMEOUT = 150;
 const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -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/. */
 
 "use strict";
 
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const {Rule} = require("devtools/client/inspector/rules/models/rule");
+const Rule = require("devtools/client/inspector/rules/models/rule");
 const {InplaceEditor, editableField, editableItem} =
       require("devtools/client/shared/inplace-editor");
 const {TextPropertyEditor} =
       require("devtools/client/inspector/rules/views/text-property-editor");
 const {
   createChild,
   blurOnMultipleProperties,
   promiseWarn
@@ -613,9 +613,9 @@ RuleEditor.prototype = {
     if (this.rule.textProps.length > 0) {
       this.rule.textProps[0].editor.nameSpan.click();
     } else {
       this.propertyList.click();
     }
   }
 };
 
-exports.RuleEditor = RuleEditor;
+module.exports = RuleEditor;
--- a/devtools/client/inspector/shared/test/browser_styleinspector_output-parser.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_output-parser.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 // Test expected outputs of the output-parser's parseCssProperty function.
 
 // This is more of a unit test than a mochitest-browser test, but can't be
 // tested with an xpcshell test as the output-parser requires the DOM to work.
 
-const {OutputParser} = require("devtools/client/shared/output-parser");
+const OutputParser = require("devtools/client/shared/output-parser");
 const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const COLOR_CLASS = "color-class";
 const URL_CLASS = "url-class";
 const CUBIC_BEZIER_CLASS = "bezier-class";
 const ANGLE_CLASS = "angle-class";
 
 const TEST_DATA = [
--- a/devtools/client/shared/autocomplete-popup.js
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -81,17 +81,16 @@ function AutocompletePopup(toolboxDoc, o
 
   // Array of raw autocomplete items
   this.items = [];
   // Map of autocompleteItem to HTMLElement
   this.elements = new WeakMap();
 
   this.selectedIndex = -1;
 }
-exports.AutocompletePopup = AutocompletePopup;
 
 AutocompletePopup.prototype = {
   _document: null,
   _tooltip: null,
   _list: null,
 
   onSelect: function (e) {
     if (this.onSelectCallback) {
@@ -587,8 +586,10 @@ AutocompletePopup.prototype = {
 
   /**
    * Used by tests.
    */
   get _window() {
     return this._document.defaultView;
   },
 };
+
+module.exports = AutocompletePopup;
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -99,17 +99,17 @@ define(function (require, exports, modul
       let props = this.getProps(properties, indexes, truncate);
       if (truncate) {
         // There are some undisplayed props. Then display "more...".
         let objectLink = this.props.objectLink || span;
 
         props.push(Caption({
           object: objectLink({
             object: object
-          }, `${object.ownPropertyLength - max} more…`)
+          }, `${propertiesLength - max} more…`)
         }));
       }
 
       return props;
     },
 
     /**
      * Get props ordered by index.
--- a/devtools/client/shared/components/search-box.js
+++ b/devtools/client/shared/components/search-box.js
@@ -2,17 +2,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/. */
 
 /* global window */
 
 "use strict";
 
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 /**
  * A generic search box component for use across devtools
  */
 module.exports = createClass({
   displayName: "SearchBox",
 
   propTypes: {
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip.html
@@ -26,16 +26,17 @@ window.onload = Task.async(function* () 
   try {
     yield testBasic();
     yield testBooleanObject();
     yield testNumberObject();
     yield testStringObject();
     yield testProxy();
     yield testArrayBuffer();
     yield testSharedArrayBuffer();
+    yield testApplicationCache();
 
     // Test property iterator
     yield testMaxProps();
     yield testMoreThanMaxProps();
     yield testUninterestingProps();
     yield testNonEnumerableProps();
 
     // Test that properties are rendered as expected by PropRep
@@ -283,16 +284,53 @@ window.onload = Task.async(function* () 
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+  function testApplicationCache() {
+    // Test object: `window.applicationCache`
+    const testName = "testApplicationCache";
+
+    // Test that correct rep is chosen
+    const gripStub = getGripStub(testName);
+    const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+    is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
+
+    // Test rendering
+    const defaultOutput =
+      "OfflineResourceList { status: 0, onchecking: null, onerror: null, 7 more… }";
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.TINY,
+        expectedOutput: "OfflineResourceList",
+      },
+      {
+        mode: MODE.SHORT,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: MODE.LONG,
+        expectedOutput: "OfflineResourceList { status: 0, onchecking: null, " +
+          "onerror: null, onnoupdate: null, ondownloading: null, onprogress: null, " +
+          "onupdateready: null, oncached: null, onobsolete: null, mozItems: [] }",
+      }
+    ];
+
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
   function testMaxProps() {
     // Test object: `{a: "a", b: "b", c: "c"}`;
     const testName = "testMaxProps";
 
     const defaultOutput = `Object { a: "a", b: "b", c: "c" }`;
 
     const modeTests = [
       {
@@ -873,15 +911,117 @@ window.onload = Task.async(function* () 
                 "getterValue": 5,
                 "getterPrototypeLevel": 1,
                 "enumerable": false,
                 "writable": true
               }
             }
           }
         };
+      case "testApplicationCache":
+        return {
+          "type": "object",
+          "actor": "server2.conn1.child2/obj45",
+          "class": "OfflineResourceList",
+          "ownPropertyLength": 0,
+          "preview": {
+            "kind": "Object",
+            "ownProperties": {},
+            "ownPropertiesLength": 0,
+            "safeGetterValues": {
+              "status": {
+                "getterValue": 0,
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onchecking": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onerror": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onnoupdate": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "ondownloading": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onprogress": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onupdateready": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "oncached": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "onobsolete": {
+                "getterValue": {
+                  "type": "null"
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              },
+              "mozItems": {
+                "getterValue": {
+                  "type": "object",
+                  "actor": "server2.conn1.child2/obj46",
+                  "class": "DOMStringList",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "ArrayLike",
+                    "length": 0
+                  }
+                },
+                "getterPrototypeLevel": 1,
+                "enumerable": true,
+                "writable": true
+              }
+            }
+          }
+        };
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/key-shortcuts.js
+++ b/devtools/client/shared/key-shortcuts.js
@@ -243,9 +243,10 @@ KeyShortcuts.prototype = {
     }
     this.eventEmitter.on(key, listener);
   },
 
   off(key, listener) {
     this.eventEmitter.off(key, listener);
   },
 };
-exports.KeyShortcuts = KeyShortcuts;
+
+module.exports = KeyShortcuts;
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -22,17 +22,17 @@ const CSS_GRID_ENABLED_PREF = "layout.cs
 /**
  * This module is used to process text for output by developer tools. This means
  * linking JS files with the debugger, CSS files with the style editor, JS
  * functions with the debugger, placing color swatches next to colors and
  * adding doorhanger previews where possible (images, angles, lengths,
  * border radius, cubic-bezier etc.).
  *
  * Usage:
- *   const {OutputParser} = require("devtools/client/shared/output-parser");
+ *   const OutputParser = require("devtools/client/shared/output-parser");
  *
  *   let parser = new OutputParser(document, supportsType);
  *
  *   parser.parseCssProperty("color", "red"); // Returns document fragment.
  *
  * @param {Document} document Used to create DOM nodes.
  * @param {Function} supportsTypes - A function that returns a boolean when asked if a css
  *                   property name supports a given css type.
@@ -47,18 +47,16 @@ function OutputParser(document, {support
   this.supportsType = supportsType;
   this.isValidOnClient = isValidOnClient;
   this.colorSwatches = new WeakMap();
   this.angleSwatches = new WeakMap();
   this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
   this._onAngleSwatchMouseDown = this._onAngleSwatchMouseDown.bind(this);
 }
 
-exports.OutputParser = OutputParser;
-
 OutputParser.prototype = {
   /**
    * Parse a CSS property value given a property name.
    *
    * @param  {String} name
    *         CSS Property Name
    * @param  {String} value
    *         CSS Property value
@@ -688,8 +686,10 @@ OutputParser.prototype = {
     };
 
     for (let item in overrides) {
       defaults[item] = overrides[item];
     }
     return defaults;
   }
 };
+
+module.exports = OutputParser;
--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
@@ -1,17 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from helper_inplace_editor.js */
 
 "use strict";
 
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
-const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
 loadHelperScript("helper_inplace_editor.js");
 
 // Test the inplace-editor autocomplete popup for CSS properties suggestions.
 // Using a mocked list of CSS properties to avoid test failures linked to
 // engine changes (new property, removed property, ...).
 
 // format :
 //  [
--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
@@ -1,17 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from helper_inplace_editor.js */
 
 "use strict";
 
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
-const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
 loadHelperScript("helper_inplace_editor.js");
 
 // Test the inplace-editor autocomplete popup for CSS values suggestions.
 // Using a mocked list of CSS properties to avoid test failures linked to
 // engine changes (new property, removed property, ...).
 
 // format :
 //  [
--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
@@ -1,17 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from helper_inplace_editor.js */
 
 "use strict";
 
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
-const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
 loadHelperScript("helper_inplace_editor.js");
 
 const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
   <?xml-stylesheet href="chrome://global/skin/global.css"?>
   <?xml-stylesheet href="resource://devtools/client/themes/common.css"?>
   <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
   <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    title="Tooltip test">
--- a/devtools/client/shared/test/browser_outputparser.js
+++ b/devtools/client/shared/test/browser_outputparser.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {OutputParser} = require("devtools/client/shared/output-parser");
+const OutputParser = require("devtools/client/shared/output-parser");
 const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 add_task(function* () {
   yield addTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
@@ -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/. */
 
 "use strict";
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const TOOLTIP_WIDTH = 418;
 const TOOLTIP_HEIGHT = 308;
 
 /**
  * Tooltip for displaying docs for CSS properties from MDN.
  *
--- a/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 
 /**
  * Base class for all (color, gradient, ...)-swatch based value editors inside
  * tooltips
  *
  * @param {Document} document
  *        The document to attach the SwatchBasedEditorTooltip. This is either the toolbox
--- a/devtools/client/shared/zoom-keys.js
+++ b/devtools/client/shared/zoom-keys.js
@@ -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/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const Services = require("Services");
-const { KeyShortcuts } = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const ZOOM_PREF = "devtools.toolbox.zoomValue";
 const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
--- a/devtools/client/sourceeditor/autocomplete.js
+++ b/devtools/client/sourceeditor/autocomplete.js
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
-const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const CM_TERN_SCRIPTS = [
   "chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
   "chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js"
 ];
 
 const autocompleteMap = new WeakMap();
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -29,17 +29,17 @@ const MAX_VERTICAL_OFFSET = 3;
 const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
 const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
 
 const Services = require("Services");
 const promise = require("promise");
 const events = require("devtools/shared/event-emitter");
 const { PrefObserver } = require("devtools/client/shared/prefs");
 const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/sourceeditor.properties");
 
 const { OS } = Services.appinfo;
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -3,17 +3,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/. */
 
 "use strict";
 
 const {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
-const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const JSOL = require("devtools/client/shared/vendor/jsol");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 // GUID to be used as a separator in compound keys. This must match the same
 // constant in devtools/server/actors/storage.js,
 // devtools/client/storage/test/head.js and
 // devtools/server/tests/browser/head.js
 const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
--- a/devtools/client/themes/fonts.css
+++ b/devtools/client/themes/fonts.css
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #sidebar-panel-fontinspector {
   margin: 0;
   display: flex;
   flex-direction: column;
-  padding-bottom: 20px;
   width: 100%;
   height: 100%;
 }
 
 #sidebar-panel-fontinspector > .devtools-toolbar {
   display: flex;
 }
 
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -12,17 +12,17 @@ const promise = require("promise");
 const Debugger = require("Debugger");
 const Services = require("Services");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
-loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
+loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup");
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
@@ -58,19 +58,16 @@ function getCleanedPacket(key, packet) {
 
     if (res.message) {
       // Clean timeStamp on the message prop.
       res.message.timeStamp = existingPacket.message.timeStamp;
       if (res.message.timer) {
         // Clean timer properties on the message.
         // Those properties are found on console.time and console.timeEnd calls,
         // and those time can vary, which is why we need to clean them.
-        if (res.message.timer.started) {
-          res.message.timer.started = existingPacket.message.timer.started;
-        }
         if (res.message.timer.duration) {
           res.message.timer.duration = existingPacket.message.timer.duration;
         }
       }
 
       if (Array.isArray(res.message.arguments)) {
         // Clean actor ids on each message.arguments item.
         res.message.arguments.forEach((argument, i) => {
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -322,25 +322,25 @@ stubPreparedMessages.set("console.dirxml
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn11.child1/obj31",
       "class": "Window",
       "extensible": true,
       "frozen": false,
       "sealed": false,
-      "ownPropertyLength": 811,
+      "ownPropertyLength": 808,
       "preview": {
         "kind": "ObjectWithURL",
         "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
       }
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159908948,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":811,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[]}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159908948,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":808,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[]}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -958,17 +958,17 @@ stubPackets.set("console.dirxml(window)"
     "arguments": [
       {
         "type": "object",
         "actor": "server1.conn11.child1/obj31",
         "class": "Window",
         "extensible": true,
         "frozen": false,
         "sealed": false,
-        "ownPropertyLength": 811,
+        "ownPropertyLength": 808,
         "preview": {
           "kind": "ObjectWithURL",
           "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
         }
       }
     ],
     "columnNumber": 27,
     "counter": null,
@@ -1042,18 +1042,17 @@ stubPackets.set("console.time('bar')", {
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "time",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159911476,
     "timer": {
-      "name": "bar",
-      "started": 1166.305
+      "name": "bar"
     },
     "workerType": "none",
     "styles": [],
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.timeEnd('bar')", {
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -28,17 +28,17 @@ loader.lazyRequireGetter(this, "Messages
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "system", "devtools/shared/system");
 loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/jsterm", true);
 loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/jsterm", true);
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
-loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts", true);
+loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
 loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
 loader.lazyRequireGetter(this, "WebConsoleConnectionProxy", "devtools/client/webconsole/webconsole-connection-proxy", true);
 
 const {PluralForm} = require("devtools/shared/plural-form");
 const STRINGS_URI = "devtools/client/locales/webconsole.properties";
 var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
--- a/dom/animation/test/mozilla/file_hide_and_show.html
+++ b/dom/animation/test/mozilla/file_hide_and_show.html
@@ -120,14 +120,43 @@ test(function(t) {
   parentElement.style.display = '';
   assert_equals(div.getAnimations().length, 1,
                 'Element which is no longer in display:none subtree has ' +
                 'animations again');
 
   assert_not_equals(div.getAnimations()[0], animation,
                     'Restarted animation is a newly-generated animation');
 
-}, 'Animation which has already finished starts playing when its parent ' +
+}, 'Animation with fill:forwards which has already finished starts playing ' +
+   'when its parent element is shown from "display:none" state');
+
+test(function(t) {
+  var parentElement = addDiv(t);
+  var div = addDiv(t, { style: 'animation: move 100s' });
+  parentElement.appendChild(div);
+  assert_equals(div.getAnimations().length, 1,
+                'display:initial element has animations');
+
+  var animation = div.getAnimations()[0];
+  animation.finish();
+  assert_equals(div.getAnimations().length, 0,
+                'Element does not have finished animations');
+
+  parentElement.style.display = 'none';
+  assert_equals(animation.playState, 'idle',
+                'The animation.playState should be idle');
+  assert_equals(div.getAnimations().length, 0,
+                'Element in display:none subtree has no animations');
+
+  parentElement.style.display = '';
+  assert_equals(div.getAnimations().length, 1,
+                'Element which is no longer in display:none subtree has ' +
+                'animations again');
+
+  assert_not_equals(div.getAnimations()[0], animation,
+                    'Restarted animation is a newly-generated animation');
+
+}, 'CSS Animation which has already finished starts playing when its parent ' +
    'element is shown from "display:none" state');
 
 done();
 </script>
 </body>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -27,19 +27,17 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "mozilla/dom/DeviceStorageAreaListener.h"
-#ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadServiceTest.h"
-#endif
 #include "mozilla/dom/PowerManager.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/FlyWebPublishedServer.h"
 #include "mozilla/dom/FlyWebService.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/Presentation.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
@@ -212,19 +210,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
-#ifdef MOZ_GAMEPAD
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
-#endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
 
 void
 Navigator::Invalidate()
@@ -302,22 +298,20 @@ Navigator::Invalidate()
     mMediaKeySystemAccessManager->Shutdown();
     mMediaKeySystemAccessManager = nullptr;
   }
 
   if (mDeviceStorageAreaListener) {
     mDeviceStorageAreaListener = nullptr;
   }
 
-#ifdef MOZ_GAMEPAD
   if (mGamepadServiceTest) {
     mGamepadServiceTest->Shutdown();
     mGamepadServiceTest = nullptr;
   }
-#endif
 
   mVRGetDisplaysPromises.Clear();
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
@@ -708,17 +702,17 @@ Navigator::GetDoNotTrack(nsAString &aRes
   } else {
     aResult.AssignLiteral("unspecified");
   }
 
   return NS_OK;
 }
 
 bool
-Navigator::JavaEnabled(ErrorResult& aRv)
+Navigator::JavaEnabled(CallerType aCallerType, ErrorResult& aRv)
 {
   Telemetry::AutoTimer<Telemetry::CHECK_JAVA_ENABLED> telemetryTimer;
 
   // Return true if we have a handler for the java mime
   nsAdoptingString javaMIME = Preferences::GetString("plugin.java.mime");
   NS_ENSURE_TRUE(!javaMIME.IsEmpty(), false);
 
   if (!mMimeTypes) {
@@ -726,17 +720,17 @@ Navigator::JavaEnabled(ErrorResult& aRv)
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return false;
     }
     mMimeTypes = new nsMimeTypeArray(mWindow);
   }
 
   RefreshMIMEArray();
 
-  nsMimeType *mimeType = mMimeTypes->NamedItem(javaMIME);
+  nsMimeType *mimeType = mMimeTypes->NamedItem(javaMIME, aCallerType);
 
   return mimeType && mimeType->GetEnabledPlugin();
 }
 
 uint64_t
 Navigator::HardwareConcurrency()
 {
   workers::RuntimeService* rts = workers::RuntimeService::GetOrCreateService();
@@ -1600,17 +1594,16 @@ Navigator::RequestWakeLock(const nsAStri
 
 already_AddRefed<LegacyMozTCPSocket>
 Navigator::MozTCPSocket()
 {
   RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
   return socket.forget();
 }
 
-#ifdef MOZ_GAMEPAD
 void
 Navigator::GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads,
                        ErrorResult& aRv)
 {
   if (!mWindow) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
@@ -1623,17 +1616,16 @@ Navigator::GetGamepads(nsTArray<RefPtr<G
 GamepadServiceTest*
 Navigator::RequestGamepadServiceTest()
 {
   if (!mGamepadServiceTest) {
     mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow);
   }
   return mGamepadServiceTest;
 }
-#endif
 
 already_AddRefed<Promise>
 Navigator::GetVRDisplays(ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -56,20 +56,18 @@ class Permissions;
 namespace battery {
 class BatteryManager;
 } // namespace battery
 
 class Promise;
 
 class DesktopNotificationCenter;
 class MozIdleObserver;
-#ifdef MOZ_GAMEPAD
 class Gamepad;
 class GamepadServiceTest;
-#endif // MOZ_GAMEPAD
 class NavigatorUserMediaSuccessCallback;
 class NavigatorUserMediaErrorCallback;
 class MozGetUserMediaDevicesSuccessCallback;
 
 namespace network {
 class Connection;
 } // namespace network
 
@@ -178,17 +176,17 @@ public:
                 ErrorResult& aRv) const;
   // The XPCOM GetVendor is OK
   // The XPCOM GetVendorSub is OK
   // The XPCOM GetProductSub is OK
   bool CookieEnabled();
   void GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                   ErrorResult& aRv) const;
   PowerManager* GetMozPower(ErrorResult& aRv);
-  bool JavaEnabled(ErrorResult& aRv);
+  bool JavaEnabled(CallerType aCallerType, ErrorResult& aRv);
   uint64_t HardwareConcurrency();
   bool CpuHasSSE2();
   bool TaintEnabled()
   {
     return false;
   }
   void AddIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
@@ -207,20 +205,18 @@ public:
   GetDeviceStorageByNameAndType(const nsAString& aName, const nsAString& aType,
                                 ErrorResult& aRv);
 
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   already_AddRefed<LegacyMozTCPSocket> MozTCPSocket();
   network::Connection* GetConnection(ErrorResult& aRv);
   MediaDevices* GetMediaDevices(ErrorResult& aRv);
 
-#ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
   GamepadServiceTest* RequestGamepadServiceTest();
-#endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
   void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
@@ -306,19 +302,17 @@ private:
 #endif
   RefPtr<MediaDevices> mMediaDevices;
   nsTArray<nsWeakPtr> mDeviceStorageStores;
   RefPtr<time::TimeManager> mTimeManager;
   RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
   RefPtr<Presentation> mPresentation;
-#ifdef MOZ_GAMEPAD
   RefPtr<GamepadServiceTest> mGamepadServiceTest;
-#endif
   nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
   nsTArray<uint32_t> mRequestedVibrationPattern;
   RefPtr<StorageManager> mStorageManager;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6738,16 +6738,18 @@ nsContentUtils::IsPatternMatching(nsAStr
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
 
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext* cx = jsapi.cx();
 
+  MOZ_RELEASE_ASSERT(js::AllowGCBarriers(cx), "IsPatternMatching can enter the JS engine during painting. See bug 1310335.");
+
   // We can use the junk scope here, because we're just using it for
   // regexp evaluation, not actual script execution.
   JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
 
   // The pattern has to match the entire value.
   aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
   aPattern.AppendLiteral(")$");
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -23,16 +23,17 @@
 #include "mozilla/EventForwards.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "nsTArrayForwardDeclare.h"
 #include "Units.h"
 #include "mozilla/dom/AutocompleteInfoBinding.h"
+#include "mozilla/dom/BindingDeclarations.h" // For CallerType
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Logging.h"
 #include "mozilla/NotNull.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
@@ -2077,23 +2078,24 @@ public:
    * Returns true if URL getters should percent decode the value of the segment
    */
   static bool GettersDecodeURLHash()
   {
     return sGettersDecodeURLHash && sEncodeDecodeURLHash;
   }
 
   /*
-   * Returns true if the browser should attempt to prevent content scripts
+   * Returns true if the browser should attempt to prevent the given caller type
    * from collecting distinctive information about the browser that could
    * be used to "fingerprint" and track the user across websites.
    */
-  static bool ResistFingerprinting()
+  static bool ResistFingerprinting(mozilla::dom::CallerType aCallerType)
   {
-    return sPrivacyResistFingerprinting;
+    return aCallerType != mozilla::dom::CallerType::System &&
+           sPrivacyResistFingerprinting;
   }
 
   /**
    * Returns true if the browser should show busy cursor when loading page.
    */
   static bool UseActivityCursor()
   {
     return sUseActivityCursor;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -14,16 +14,17 @@
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 #include "XrayWrapper.h"
 
 #include "xpcpublic.h"
 #include "xpcprivate.h"
+#include "xpc_make_class.h"
 #include "XPCWrapper.h"
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/RegisterBindings.h"
 
 #include "nscore.h"
 #include "nsDOMClassInfo.h"
 #include "nsIDOMClassInfo.h"
@@ -129,16 +130,19 @@ using namespace mozilla::dom;
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
   // nothing
 #endif
 
 #define NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags,              \
                                         _chromeOnly, _allowXBL)               \
   { #_class,                                                                  \
     nullptr,                                                                  \
+    XPC_MAKE_CLASS_OPS(_flags),                                               \
+    XPC_MAKE_CLASS(#_class, _flags,                                           \
+                   &sClassInfoData[eDOMClassInfo_##_class##_id].mClassOps),   \
     _helper::doCreate,                                                        \
     nullptr,                                                                  \
     nullptr,                                                                  \
     nullptr,                                                                  \
     _flags,                                                                   \
     true,                                                                     \
     _chromeOnly,                                                              \
     _allowXBL,                                                                \
@@ -778,16 +782,23 @@ nsDOMClassInfo::GetClassName(char **aCla
 
 // virtual
 uint32_t
 nsDOMClassInfo::GetScriptableFlags()
 {
   return mData->mScriptableFlags;
 }
 
+// virtual
+const js::Class*
+nsDOMClassInfo::GetClass()
+{
+    return &mData->mClass;
+}
+
 NS_IMETHODIMP
 nsDOMClassInfo::PreCreate(nsISupports *nativeObj, JSContext *cx,
                           JSObject *globalObj, JSObject **parentObj)
 {
   *parentObj = globalObj;
   return NS_OK;
 }
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -6,16 +6,17 @@
 
 #ifndef nsDOMClassInfo_h___
 #define nsDOMClassInfo_h___
 
 #include "mozilla/Attributes.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIXPCScriptable.h"
 #include "nsIScriptGlobalObject.h"
+#include "js/Class.h"
 #include "js/Id.h"
 #include "nsIXPConnect.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 struct nsGlobalNameStruct;
@@ -25,18 +26,22 @@ struct nsDOMClassInfoData;
 
 typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
   (nsDOMClassInfoData* aData);
 
 typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
 
 struct nsDOMClassInfoData
 {
+  // XXX: mName is the same as the name gettable from the callback. This
+  // redundancy should be removed eventually.
   const char *mName;
   const char16_t *mNameUTF16;
+  const js::ClassOps mClassOps;
+  const js::Class mClass;
   nsDOMClassInfoConstructorFnc mConstructorFptr;
 
   nsIClassInfo *mCachedClassInfo;
   const nsIID *mProtoChainInterface;
   const nsIID **mInterfaces;
   uint32_t mScriptableFlags : 31; // flags must not use more than 31 bits!
   uint32_t mHasClassInterface : 1;
   bool mChromeOnly : 1;
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2046,27 +2046,27 @@ GK_ATOM(svgClipPathFrame, "SVGClipPathFr
 GK_ATOM(svgDefsFrame, "SVGDefsFrame")
 GK_ATOM(svgFEContainerFrame, "SVGFEContainerFrame")
 GK_ATOM(svgFEImageFrame, "SVGFEImageFrame")
 GK_ATOM(svgFELeafFrame, "SVGFELeafFrame")
 GK_ATOM(svgFEUnstyledLeafFrame, "SVGFEUnstyledLeafFrame")
 GK_ATOM(svgFilterFrame, "SVGFilterFrame")
 GK_ATOM(svgForeignObjectFrame, "SVGForeignObjectFrame")
 GK_ATOM(svgGenericContainerFrame, "SVGGenericContainerFrame")
+GK_ATOM(svgGeometryFrame, "SVGGeometryFrame")
 GK_ATOM(svgGFrame, "SVGGFrame")
 GK_ATOM(svgGradientFrame, "SVGGradientFrame")
 GK_ATOM(svgImageFrame, "SVGImageFrame")
 GK_ATOM(svgInnerSVGFrame, "SVGInnerSVGFrame")
 GK_ATOM(svgLinearGradientFrame, "SVGLinearGradientFrame")
 GK_ATOM(svgMarkerFrame, "SVGMarkerFrame")
 GK_ATOM(svgMarkerAnonChildFrame, "SVGMarkerAnonChildFrame")
 GK_ATOM(svgMaskFrame, "SVGMaskFrame")
 GK_ATOM(svgOuterSVGFrame, "SVGOuterSVGFrame")
 GK_ATOM(svgOuterSVGAnonChildFrame, "SVGOuterSVGAnonChildFrame")
-GK_ATOM(svgPathGeometryFrame, "SVGPathGeometryFrame")
 GK_ATOM(svgPatternFrame, "SVGPatternFrame")
 GK_ATOM(svgRadialGradientFrame, "SVGRadialGradientFrame")
 GK_ATOM(svgStopFrame, "SVGStopFrame")
 GK_ATOM(svgSwitchFrame, "SVGSwitchFrame")
 GK_ATOM(svgTextFrame, "SVGTextFrame")
 GK_ATOM(svgUseFrame, "SVGUseFrame")
 GK_ATOM(svgViewFrame, "SVGViewFrame")
 GK_ATOM(HTMLVideoFrame, "VideoFrame")
@@ -2122,23 +2122,21 @@ GK_ATOM(ratechange, "ratechange")
 GK_ATOM(durationchange, "durationchange")
 GK_ATOM(volumechange, "volumechange")
 GK_ATOM(ondataavailable, "ondataavailable")
 GK_ATOM(onwarning, "onwarning")
 GK_ATOM(onstart, "onstart")
 GK_ATOM(onstop, "onstop")
 GK_ATOM(onphoto, "onphoto")
 GK_ATOM(onactivestatechanged, "onactivestatechanged")
-#ifdef MOZ_GAMEPAD
 GK_ATOM(ongamepadbuttondown, "ongamepadbuttondown")
 GK_ATOM(ongamepadbuttonup, "ongamepadbuttonup")
 GK_ATOM(ongamepadaxismove, "ongamepadaxismove")
 GK_ATOM(ongamepadconnected, "ongamepadconnected")
 GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
-#endif
 
 // Content property names
 GK_ATOM(animationsProperty, "AnimationsProperty")        // FrameAnimations*
 GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations*
 GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
 GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet*
 GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet*
 GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -192,20 +192,18 @@
 #include "mozilla/Logging.h"
 #include "prenv.h"
 #include "prprf.h"
 
 #include "mozilla/dom/IDBFactory.h"
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/Promise.h"
 
-#ifdef MOZ_GAMEPAD
 #include "mozilla/dom/Gamepad.h"
 #include "mozilla/dom/GamepadManager.h"
-#endif
 
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/dom/VREventObserver.h"
 
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 
 #include "mozilla/AddonPathService.h"
@@ -1234,19 +1232,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mIsChrome(false),
     mCleanMessageManager(false),
     mNeedsFocus(true),
     mHasFocus(false),
     mShowFocusRingForContent(false),
     mFocusByKeyOccurred(false),
     mHasGamepad(false),
     mHasVREvents(false),
-#ifdef MOZ_GAMEPAD
     mHasSeenGamepadInput(false),
-#endif
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
     mSuspendDepth(0),
     mFreezeDepth(0),
     mFocusMethod(0),
     mSerial(0),
     mIdleRequestCallbackCounter(1),
 #ifdef DEBUG
@@ -1621,16 +1617,17 @@ nsGlobalWindow::CleanUp()
   mFrames = nullptr;
   mWindowUtils = nullptr;
   mApplicationCache = nullptr;
   mIndexedDB = nullptr;
 
   mConsole = nullptr;
 
   mAudioWorklet = nullptr;
+  mPaintWorklet = nullptr;
 
   mExternal = nullptr;
 
   mMozSelfSupport = nullptr;
 
   mPerformance = nullptr;
 
 #ifdef MOZ_WEBSPEECH
@@ -1804,21 +1801,19 @@ nsGlobalWindow::FreeInnerObjects()
 
   CleanupCachedXBLHandlers(this);
 
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     mAudioContexts[i]->Shutdown();
   }
   mAudioContexts.Clear();
 
-#ifdef MOZ_GAMEPAD
   DisableGamepadUpdates();
   mHasGamepad = false;
   mGamepads.Clear();
-#endif
   DisableVRUpdates();
   mHasVREvents = false;
   mVRDisplays.Clear();
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsISupports
 //*****************************************************************************
@@ -1966,19 +1961,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   }
 
   for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
-#ifdef MOZ_GAMEPAD
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
-#endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
 
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
@@ -1989,16 +1982,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioWorklet)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
 
   tmp->TraverseHostObjectURIs(cb);
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -2044,19 +2038,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
 
-#ifdef MOZ_GAMEPAD
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
-#endif
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
@@ -2067,16 +2059,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
 
   tmp->UnlinkHostObjectURIs();
 
   tmp->DisableIdleCallbackRequests();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -10016,20 +10009,17 @@ void nsGlobalWindow::SetIsBackground(boo
     return;
   }
 
   if (resetTimers) {
     inner->mTimeoutManager->ResetTimersForThrottleReduction();
   }
 
   inner->UnthrottleIdleCallbackRequests();
-
-#ifdef MOZ_GAMEPAD
   inner->SyncGamepadState();
-#endif
 }
 
 void nsGlobalWindow::MaybeUpdateTouchState()
 {
   FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
 
   if (mMayHaveTouchEventListener) {
     nsCOMPtr<nsIObserverService> observerService =
@@ -10044,37 +10034,33 @@ void nsGlobalWindow::MaybeUpdateTouchSta
 }
 
 void
 nsGlobalWindow::EnableGamepadUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mHasGamepad) {
-#ifdef MOZ_GAMEPAD
     RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
     if (gamepadManager) {
       gamepadManager->AddListener(this);
     }
-#endif
   }
 }
 
 void
 nsGlobalWindow::DisableGamepadUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mHasGamepad) {
-#ifdef MOZ_GAMEPAD
     RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
     if (gamepadManager) {
       gamepadManager->RemoveListener(this);
     }
-#endif
   }
 }
 
 void
 nsGlobalWindow::EnableVRUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
 
@@ -12996,18 +12982,16 @@ nsGlobalWindow::AddSizeOfIncludingThis(n
     }
     if (EventListenerManager* elm = et->GetExistingListenerManager()) {
       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
     }
     ++aWindowSizes->mDOMEventTargetsCount;
   }
 }
 
-
-#ifdef MOZ_GAMEPAD
 void
 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   MOZ_ASSERT(IsInnerWindow());
   // Create the index we will present to content based on which indices are
   // already taken, as required by the spec.
   // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
   int index = 0;
@@ -13079,17 +13063,16 @@ nsGlobalWindow::SyncGamepadState()
   MOZ_ASSERT(IsInnerWindow());
   if (mHasSeenGamepadInput) {
     RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
     for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
       gamepadManager->SyncGamepadState(iter.Key(), iter.UserData());
     }
   }
 }
-#endif // MOZ_GAMEPAD
 
 bool
 nsGlobalWindow::UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices)
 {
   FORWARD_TO_INNER(UpdateVRDisplays, (aDevices), false);
 
   VRDisplay::UpdateVRDisplays(mVRDisplays, AsInner());
   aDevices = mVRDisplays;
@@ -14266,17 +14249,35 @@ nsGlobalWindow::GetAudioWorklet(ErrorRes
 
   if (!mAudioWorklet) {
     nsIPrincipal* principal = GetPrincipal();
     if (!principal) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
-    mAudioWorklet = new Worklet(AsInner(), principal);
+    mAudioWorklet = new Worklet(AsInner(), principal, Worklet::eAudioWorklet);
   }
 
   return mAudioWorklet;
 }
 
+Worklet*
+nsGlobalWindow::GetPaintWorklet(ErrorResult& aRv)
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
+  if (!mPaintWorklet) {
+    nsIPrincipal* principal = GetPrincipal();
+    if (!principal) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    mPaintWorklet = new Worklet(AsInner(), principal, Worklet::ePaintWorklet);
+  }
+
+  return mPaintWorklet;
+}
+
 template class nsPIDOMWindow<mozIDOMWindowProxy>;
 template class nsPIDOMWindow<mozIDOMWindow>;
 template class nsPIDOMWindow<nsISupports>;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -724,26 +724,24 @@ public:
   enum SlowScriptResponse {
     ContinueSlowScript = 0,
     ContinueSlowScriptAndKeepNotifying,
     AlwaysContinueSlowScript,
     KillSlowScript
   };
   SlowScriptResponse ShowSlowScriptDialog();
 
-#ifdef MOZ_GAMEPAD
   // Inner windows only.
   void AddGamepad(uint32_t aIndex, mozilla::dom::Gamepad* aGamepad);
   void RemoveGamepad(uint32_t aIndex);
   void GetGamepads(nsTArray<RefPtr<mozilla::dom::Gamepad> >& aGamepads);
   already_AddRefed<mozilla::dom::Gamepad> GetGamepad(uint32_t aIndex);
   void SetHasSeenGamepadInput(bool aHasSeen);
   bool HasSeenGamepadInput();
   void SyncGamepadState();
-#endif
 
   // Inner windows only.
   // Enable/disable updates for gamepad input.
   void EnableGamepadUpdates();
   void DisableGamepadUpdates();
 
   // Inner windows only.
   // Enable/disable updates for VR
@@ -923,16 +921,19 @@ public:
   TokenizeDialogOptions(nsAString& aToken, nsAString::const_iterator& aIter,
                         nsAString::const_iterator aEnd);
   static void
   ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult);
 
   mozilla::dom::Worklet*
   GetAudioWorklet(mozilla::ErrorResult& aRv);
 
+  mozilla::dom::Worklet*
+  GetPaintWorklet(mozilla::ErrorResult& aRv);
+
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
 public:
   void Alert(nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
@@ -1752,21 +1753,19 @@ protected:
 
   // Inner windows only.
   // Indicates whether this window wants gamepad input events
   bool                   mHasGamepad : 1;
 
   // Inner windows only.
   // Indicates whether this window wants VR events
   bool                   mHasVREvents : 1;
-#ifdef MOZ_GAMEPAD
   nsCheapSet<nsUint32HashKey> mGamepadIndexSet;
   nsRefPtrHashtable<nsUint32HashKey, mozilla::dom::Gamepad> mGamepads;
   bool mHasSeenGamepadInput;
-#endif
 
   // 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;
 
   nsCOMPtr<nsIScriptContext>    mContext;
@@ -1796,16 +1795,17 @@ protected:
   nsString                      mStatus;
   nsString                      mDefaultStatus;
   RefPtr<nsGlobalWindowObserver> mObserver; // Inner windows only.
   RefPtr<mozilla::dom::Crypto>  mCrypto;
   RefPtr<mozilla::dom::U2F> mU2F;
   RefPtr<mozilla::dom::cache::CacheStorage> mCacheStorage;
   RefPtr<mozilla::dom::Console> mConsole;
   RefPtr<mozilla::dom::Worklet> mAudioWorklet;
+  RefPtr<mozilla::dom::Worklet> mPaintWorklet;
   // We need to store an nsISupports pointer to this object because the
   // mozilla::dom::External class doesn't exist on b2g and using the type
   // forward declared here means that ~nsGlobalWindow wouldn't compile because
   // it wouldn't see the ~External function's declaration.
   nsCOMPtr<nsISupports>         mExternal;
 
   RefPtr<mozilla::dom::MozSelfSupport> mMozSelfSupport;
 
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -39,16 +39,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsSVGEffects.h"
 
 #include "gfxPrefs.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
@@ -143,16 +144,19 @@ nsImageLoadingContent::Notify(imgIReques
     // We should definitely have a request here
     MOZ_ASSERT(aRequest, "no request?");
 
     NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
                     "Unknown request");
   }
 
   {
+    MOZ_RELEASE_ASSERT(js::AllowGCBarriers(CycleCollectedJSContext::Get()->Context()),
+                       "ImageObservers can be implement in JS, so they should not be called during painting. See bug 1311841");
+
     nsAutoScriptBlocker scriptBlocker;
 
     for (ImageObserver* observer = &mObserverList, *next; observer;
          observer = next) {
       next = observer->mNext;
       if (observer->mObserver) {
         observer->mObserver->Notify(aRequest, aType, aData);
       }
--- a/dom/base/nsMimeTypeArray.cpp
+++ b/dom/base/nsMimeTypeArray.cpp
@@ -37,22 +37,16 @@ nsMimeTypeArray::nsMimeTypeArray(nsPIDOM
   : mWindow(aWindow)
 {
 }
 
 nsMimeTypeArray::~nsMimeTypeArray()
 {
 }
 
-static bool
-ResistFingerprinting() {
-  return !nsContentUtils::ThreadsafeIsCallerChrome() &&
-         nsContentUtils::ResistFingerprinting();
-}
-
 JSObject*
 nsMimeTypeArray::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MimeTypeArrayBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 nsMimeTypeArray::Refresh()
@@ -64,35 +58,36 @@ nsMimeTypeArray::Refresh()
 nsPIDOMWindowInner*
 nsMimeTypeArray::GetParentObject() const
 {
   MOZ_ASSERT(mWindow);
   return mWindow;
 }
 
 nsMimeType*
-nsMimeTypeArray::Item(uint32_t aIndex)
+nsMimeTypeArray::Item(uint32_t aIndex, CallerType aCallerType)
 {
   bool unused;
-  return IndexedGetter(aIndex, unused);
+  return IndexedGetter(aIndex, unused, aCallerType);
 }
 
 nsMimeType*
-nsMimeTypeArray::NamedItem(const nsAString& aName)
+nsMimeTypeArray::NamedItem(const nsAString& aName, CallerType aCallerType)
 {
   bool unused;
-  return NamedGetter(aName, unused);
+  return NamedGetter(aName, unused, aCallerType);
 }
 
 nsMimeType*
-nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound)
+nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound,
+                               CallerType aCallerType)
 {
   aFound = false;
 
-  if (ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     return nullptr;
   }
 
   EnsurePluginMimeTypes();
 
   if (aIndex >= mMimeTypes.Length()) {
     return nullptr;
   }
@@ -112,21 +107,22 @@ FindMimeType(const nsTArray<RefPtr<nsMim
       return mimeType;
     }
   }
 
   return nullptr;
 }
 
 nsMimeType*
-nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
+nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound,
+                             CallerType aCallerType)
 {
   aFound = false;
 
-  if (ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     return nullptr;
   }
 
   EnsurePluginMimeTypes();
 
   nsString lowerName(aName);
   ToLowerCase(lowerName);
 
@@ -139,30 +135,35 @@ nsMimeTypeArray::NamedGetter(const nsASt
   if (hiddenType) {
     nsPluginArray::NotifyHiddenPluginTouched(hiddenType->GetEnabledPlugin());
   }
 
   return nullptr;
 }
 
 uint32_t
-nsMimeTypeArray::Length()
+nsMimeTypeArray::Length(CallerType aCallerType)
 {
-  if (ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     return 0;
   }
 
   EnsurePluginMimeTypes();
 
   return mMimeTypes.Length();
 }
 
 void
-nsMimeTypeArray::GetSupportedNames(nsTArray<nsString>& aRetval)
+nsMimeTypeArray::GetSupportedNames(nsTArray<nsString>& aRetval,
+                                   CallerType aCallerType)
 {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
+    return;
+  }
+
   EnsurePluginMimeTypes();
 
   for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
     aRetval.AppendElement(mMimeTypes[i]->Type());
   }
 }
 
 void
--- a/dom/base/nsMimeTypeArray.h
+++ b/dom/base/nsMimeTypeArray.h
@@ -6,16 +6,17 @@
 
 #ifndef nsMimeTypeArray_h___
 #define nsMimeTypeArray_h___
 
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingDeclarations.h"
 
 class nsMimeType;
 class nsPluginElement;
 
 class nsMimeTypeArray final : public nsISupports,
                               public nsWrapperCache
 {
 public:
@@ -25,22 +26,26 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsMimeTypeArray)
 
   nsPIDOMWindowInner* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Refresh();
 
   // MimeTypeArray WebIDL methods
-  nsMimeType* Item(uint32_t index);
-  nsMimeType* NamedItem(const nsAString& name);
-  nsMimeType* IndexedGetter(uint32_t index, bool &found);
-  nsMimeType* NamedGetter(const nsAString& name, bool &found);
-  uint32_t Length();
-  void GetSupportedNames(nsTArray<nsString>& retval);
+  nsMimeType* Item(uint32_t index, mozilla::dom::CallerType aCallerType);
+  nsMimeType* NamedItem(const nsAString& name,
+                        mozilla::dom::CallerType aCallerType);
+  nsMimeType* IndexedGetter(uint32_t index, bool &found,
+                            mozilla::dom::CallerType aCallerType);
+  nsMimeType* NamedGetter(const nsAString& name, bool &found,
+                          mozilla::dom::CallerType aCallerType);
+  uint32_t Length(mozilla::dom::CallerType aCallerType);
+  void GetSupportedNames(nsTArray<nsString>& retval,
+                         mozilla::dom::CallerType aCallerType);
 
 protected:
   virtual ~nsMimeTypeArray();
 
   void EnsurePluginMimeTypes();
   void Clear();
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -637,21 +637,23 @@ nsObjectLoadingContent::UnbindFromTree(b
     QueueCheckPluginStopEvent();
   } else if (mType != eType_Image) {
     // nsImageLoadingContent handles the image case.
     // Reset state and clear pending events
     /// XXX(johns): The implementation for GenericFrame notes that ideally we
     ///             would keep the docshell around, but trash the frameloader
     UnloadObject();
   }
-  nsIDocument* doc = thisContent->GetComposedDoc();
-  if (doc && doc->IsActive()) {
-    nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc,
-                                                       NS_LITERAL_STRING("PluginRemoved"));
-    NS_DispatchToCurrentThread(ev);
+  if (mType == eType_Plugin) {
+    nsIDocument* doc = thisContent->GetComposedDoc();
+    if (doc && doc->IsActive()) {
+      nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc,
+                                                         NS_LITERAL_STRING("PluginRemoved"));
+      NS_DispatchToCurrentThread(ev);
+    }
   }
 }
 
 nsObjectLoadingContent::nsObjectLoadingContent()
   : mType(eType_Loading)
   , mFallbackType(eFallbackAlternate)
   , mRunID(0)
   , mHasRunID(false)
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -40,22 +40,16 @@ nsPluginArray::Init()
     mozilla::services::GetObserverService();
   if (obsService) {
     obsService->AddObserver(this, "plugin-info-updated", true);
   }
 }
 
 nsPluginArray::~nsPluginArray() = default;
 
-static bool
-ResistFingerprinting() {
-  return !nsContentUtils::ThreadsafeIsCallerChrome() &&
-         nsContentUtils::ResistFingerprinting();
-}
-
 nsPIDOMWindowInner*
 nsPluginArray::GetParentObject() const
 {
   MOZ_ASSERT(mWindow);
   return mWindow;
 }
 
 JSObject*
@@ -127,27 +121,27 @@ nsPluginArray::GetCTPMimeTypes(nsTArray<
   GetPluginMimeTypes(mCTPPlugins, aMimeTypes);
 
   // Alphabetize the enumeration order of non-hidden MIME types to reduce
   // fingerprintable entropy based on plugins' installation file times.
   aMimeTypes.Sort();
 }
 
 nsPluginElement*
-nsPluginArray::Item(uint32_t aIndex)
+nsPluginArray::Item(uint32_t aIndex, CallerType aCallerType)
 {
   bool unused;
-  return IndexedGetter(aIndex, unused);
+  return IndexedGetter(aIndex, unused, aCallerType);
 }
 
 nsPluginElement*
-nsPluginArray::NamedItem(const nsAString& aName)
+nsPluginArray::NamedItem(const nsAString& aName, CallerType aCallerType)
 {
   bool unused;
-  return NamedGetter(aName, unused);
+  return NamedGetter(aName, unused, aCallerType);
 }
 
 void
 nsPluginArray::Refresh(bool aReloadDocuments)
 {
   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
 
   if(!AllowPlugins() || !pluginHost) {
@@ -186,21 +180,21 @@ nsPluginArray::Refresh(bool aReloadDocum
 
   nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
   if (aReloadDocuments && webNav) {
     webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
   }
 }
 
 nsPluginElement*
-nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound)
+nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound, CallerType aCallerType)
 {
   aFound = false;
 
-  if (!AllowPlugins() || ResistFingerprinting()) {
+  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
     return nullptr;
   }
 
   EnsurePlugins();
 
   aFound = aIndex < mPlugins.Length();
 
   if (!aFound) {
@@ -233,21 +227,22 @@ FindPlugin(const nsTArray<RefPtr<nsPlugi
       return plugin;
     }
   }
 
   return nullptr;
 }
 
 nsPluginElement*
-nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
+nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound,
+                           CallerType aCallerType)
 {
   aFound = false;
 
-  if (!AllowPlugins() || ResistFingerprinting()) {
+  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
     return nullptr;
   }
 
   EnsurePlugins();
 
   nsPluginElement* plugin = FindPlugin(mPlugins, aName);
   aFound = (plugin != nullptr);
   if (!aFound) {
@@ -269,33 +264,34 @@ void nsPluginArray::NotifyHiddenPluginTo
   event->SetTarget(doc);
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   bool dummy;
   doc->DispatchEvent(event, &dummy);
 }
 
 uint32_t
-nsPluginArray::Length()
+nsPluginArray::Length(CallerType aCallerType)
 {
-  if (!AllowPlugins() || ResistFingerprinting()) {
+  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
     return 0;
   }
 
   EnsurePlugins();
 
   return mPlugins.Length();
 }
 
 void
-nsPluginArray::GetSupportedNames(nsTArray<nsString>& aRetval)
+nsPluginArray::GetSupportedNames(nsTArray<nsString>& aRetval,
+                                 CallerType aCallerType)
 {
   aRetval.Clear();
 
-  if (!AllowPlugins()) {
+  if (!AllowPlugins() || nsContentUtils::ResistFingerprinting(aCallerType)) {
     return;
   }
 
   for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
     nsAutoString pluginName;
     mPlugins[i]->GetName(pluginName);
 
     aRetval.AppendElement(pluginName);
--- a/dom/base/nsPluginArray.h
+++ b/dom/base/nsPluginArray.h
@@ -7,16 +7,17 @@
 #ifndef nsPluginArray_h___
 #define nsPluginArray_h___
 
 #include "nsTArray.h"
 #include "nsWeakReference.h"
 #include "nsIObserver.h"
 #include "nsWrapperCache.h"
 #include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingDeclarations.h"
 
 class nsPluginElement;
 class nsMimeType;
 class nsIInternalPluginTag;
 
 class nsPluginArray final : public nsIObserver,
                             public nsSupportsWeakReference,
                             public nsWrapperCache
@@ -42,23 +43,27 @@ public:
 
   void GetMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes);
   void GetCTPMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes);
 
   static void NotifyHiddenPluginTouched(nsPluginElement* aElement);
 
   // PluginArray WebIDL methods
 
-  nsPluginElement* Item(uint32_t aIndex);
-  nsPluginElement* NamedItem(const nsAString& aName);
+  nsPluginElement* Item(uint32_t aIndex, mozilla::dom::CallerType aCallerType);
+  nsPluginElement* NamedItem(const nsAString& aName,
+                             mozilla::dom::CallerType aCallerType);
   void Refresh(bool aReloadDocuments);
-  nsPluginElement* IndexedGetter(uint32_t aIndex, bool &aFound);
-  nsPluginElement* NamedGetter(const nsAString& aName, bool &aFound);
-  uint32_t Length();
-  void GetSupportedNames(nsTArray<nsString>& aRetval);
+  nsPluginElement* IndexedGetter(uint32_t aIndex, bool &aFound,
+                                 mozilla::dom::CallerType aCallerType);
+  nsPluginElement* NamedGetter(const nsAString& aName, bool &aFound,
+                               mozilla::dom::CallerType aCallerType);
+  uint32_t Length(mozilla::dom::CallerType aCallerType);
+  void GetSupportedNames(nsTArray<nsString>& aRetval,
+                         mozilla::dom::CallerType aCallerType);
 
 private:
   virtual ~nsPluginArray();
 
   bool AllowPlugins() const;
   void EnsurePlugins();
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -650,17 +650,17 @@ skip-if = (toolkit == 'android') # Andro
 [test_iframe_referrer.html]
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
 [test_Image_constructor.html]
 [test_img_referrer.html]
 [test_innersize_scrollport.html]
 [test_integer_attr_with_leading_zero.html]
 [test_intersectionobservers.html]
-skip-if = (os == "android") # Timing issues
+skip-if = true # Track Bug 1320704
 [test_link_prefetch.html]
 skip-if = !e10s # Track Bug 1281415
 [test_link_stylesheet.html]
 [test_messagemanager_targetchain.html]
 [test_meta_viewport0.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport1.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
--- a/dom/base/test/test_pluginAudioNotification.html
+++ b/dom/base/test/test_pluginAudioNotification.html
@@ -70,25 +70,33 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     iframe.src = "file_pluginAudio.html";
   },
 
   function() {
+    info("=== Mute plugin ===");
     ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
     iframe.contentWindow.toggleMuteState(true);
     ok(iframe.contentWindow.pluginMuted(), "Plugin should be muted");
+    expectedNotification = 'inactive-nonaudible';
+  },
+
+  function() {
+    info("=== unmute plugin ==");
+    ok(iframe.contentWindow.pluginMuted(), "Plugin should be muted");
     iframe.contentWindow.toggleMuteState(false);
     ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
-    runTest();
+    expectedNotification = 'active';
   },
 
   function() {
+    info("=== stop audio ==");
     expectedNotification = 'inactive-pause';
     iframe.contentWindow.stopAudio();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5312,26 +5312,20 @@ def getJSToNativeConversionInfo(type, de
             #    it in the content compartment, we will try to call .then() on
             #    the chrome promise while in the content compartment, which will
             #    throw and we'll just get a rejected Promise.  Note that this is
             #    also the reason why a caller who has a chrome Promise
             #    representing an async operation can't itself convert it to a
             #    content-side Promise (at least not without some serious
             #    gyrations).
             # 3) Promise return value from a callback or callback interface.
-            #    This is in theory a case the spec covers but in practice it
-            #    really doesn't define behavior here because it doesn't define
-            #    what Realm we're in after the callback returns, which is when
-            #    the argument conversion happens.  We will use the current
-            #    compartment, which is the compartment of the callable (which
-            #    may itself be a cross-compartment wrapper itself), which makes
-            #    as much sense as anything else. In practice, such an API would
-            #    once again be providing a Promise to signal completion of an
-            #    operation, which would then not be exposed to anyone other than
-            #    our own implementation code.
+            #    Per spec, this should use the Realm of the callback object.  In
+            #    our case, that's the compartment of the underlying callback,
+            #    not the current compartment (which may be the compartment of
+            #    some cross-compartment wrapper around said callback).
             # 4) Return value from a JS-implemented interface.  In this case we
             #    have a problem.  Our current compartment is the compartment of
             #    the JS implementation.  But if the JS implementation returned
             #    a page-side Promise (which is a totally sane thing to do, and
             #    in fact the right thing to do given that this return value is
             #    going right to content script) then we don't want to
             #    Promise.resolve with our current compartment Promise, because
             #    that will wrap it up in a chrome-side Promise, which is
@@ -5363,16 +5357,24 @@ def getJSToNativeConversionInfo(type, de
                     if (!unwrappedVal) {
                       // A slight lie, but not much of one, for a dead object wrapper.
                       aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
                       return nullptr;
                     }
                     globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
                     """,
                     sourceDescription=sourceDescription)
+            elif isCallbackReturnValue == "Callback":
+                getPromiseGlobal = dedent(
+                    """
+                    // We basically want our entry global here.  Play it safe
+                    // and use GetEntryGlobal() to get it, with whatever
+                    // principal-clamping it ends up doing.
+                    globalObj = GetEntryGlobal()->GetGlobalJSObject();
+                    """)
             else:
                 getPromiseGlobal = ""
 
             templateBody = fill(
                 """
                 { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment,
                   // etc.
 
@@ -6943,16 +6945,23 @@ def needScopeObject(returnType, argument
     isMember should be true if we're dealing with an attribute
     annotated as [StoreInSlot].
     """
     return (considerTypes and not isWrapperCached and
             ((not isMember and typeNeedsScopeObject(returnType, True)) or
              any(typeNeedsScopeObject(a.type) for a in arguments)))
 
 
+def callerTypeGetterForDescriptor(descriptor):
+    if descriptor.interface.isExposedInAnyWorker():
+        systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
+    else:
+        systemCallerGetter = "nsContentUtils::IsSystemCaller"
+    return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter
+
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     needsSubjectPrincipal is a boolean indicating whether the call should
     receive the subject nsIPrincipal as argument.
 
@@ -7025,21 +7034,17 @@ class CGCallGenerator(CGThing):
             else:
                 assert resultOutParam == "ptr"
                 args.append(CGGeneric("&" + resultVar))
 
         if needsSubjectPrincipal:
             args.append(CGGeneric("subjectPrincipal"))
 
         if needsCallerType:
-            if descriptor.interface.isExposedInAnyWorker():
-                systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
-            else:
-                systemCallerGetter = "nsContentUtils::IsSystemCaller"
-            args.append(CGGeneric("%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter))
+            args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
 
         if isFallible:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
@@ -11592,44 +11597,57 @@ class CGDOMJSProxyHandler_ownPropNames(C
                 Argument('JS::Handle<JSObject*>', 'proxy'),
                 Argument('unsigned', 'flags'),
                 Argument('JS::AutoIdVector&', 'props')]
         ClassMethod.__init__(self, "ownPropNames", "bool", args,
                              virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
-        # Per spec, we do indices, then named props, then everything else
+        # Per spec, we do indices, then named props, then everything else.
         if self.descriptor.supportsIndexedProperties():
-            addIndices = dedent("""
-
-                uint32_t length = UnwrapProxy(proxy)->Length();
+            if self.descriptor.lengthNeedsCallerType():
+                callerType = callerTypeGetterForDescriptor(self.descriptor)
+            else:
+                callerType = ""
+            addIndices = fill(
+                """
+
+                uint32_t length = UnwrapProxy(proxy)->Length(${callerType});
                 MOZ_ASSERT(int32_t(length) >= 0);
                 for (int32_t i = 0; i < int32_t(length); ++i) {
                   if (!props.append(INT_TO_JSID(i))) {
                     return false;
                   }
                 }
-                """)
+                """,
+                callerType=callerType)
         else:
             addIndices = ""
 
         if self.descriptor.supportsNamedProperties():
             if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
                 shadow = "!isXray"
             else:
                 shadow = "false"
+
+            if self.descriptor.supportedNamesNeedCallerType():
+                callerType = ", " + callerTypeGetterForDescriptor(self.descriptor)
+            else:
+                callerType = ""
+
             addNames = fill(
                 """
                 nsTArray<nsString> names;
-                UnwrapProxy(proxy)->GetSupportedNames(names);
+                UnwrapProxy(proxy)->GetSupportedNames(names${callerType});
                 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
                   return false;
                 }
                 """,
+                callerType=callerType,
                 shadow=shadow)
             if not self.descriptor.namedPropertiesEnumerable:
                 addNames = CGIfWrapper(CGGeneric(addNames),
                                        "flags & JSITER_HIDDEN").define()
             addNames = "\n" + addNames
         else:
             addNames = ""
 
@@ -11944,24 +11962,29 @@ class CGDOMJSProxyHandler_getElements(Cl
             'jsvalRef': 'temp',
             'jsvalHandle': '&temp',
             'obj': 'proxy',
             'successCode': ("if (!adder->append(cx, temp)) return false;\n"
                             "continue;\n")
         }
         get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
 
+        if self.descriptor.lengthNeedsCallerType():
+            callerType = callerTypeGetterForDescriptor(self.descriptor)
+        else:
+            callerType = ""
+
         return fill(
             """
             JS::Rooted<JS::Value> temp(cx);
             MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
                        "Should not have a XrayWrapper here");
 
             ${nativeType}* self = UnwrapProxy(proxy);
-            uint32_t length = self->Length();
+            uint32_t length = self->Length(${callerType});
             // Compute the end of the indices we'll get ourselves
             uint32_t ourEnd = std::max(begin, std::min(end, length));
 
             for (uint32_t index = begin; index < ourEnd; ++index) {
               $*{get}
             }
 
             if (end > ourEnd) {
@@ -11970,16 +11993,17 @@ class CGDOMJSProxyHandler_getElements(Cl
                 return false;
               }
               return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
             }
 
             return true;
             """,
             nativeType=self.descriptor.nativeType,
+            callerType=callerType,
             get=get)
 
 
 class CGDOMJSProxyHandler_getInstance(ClassMethod):
     def __init__(self):
         ClassMethod.__init__(self, "getInstance", "const DOMProxyHandler*", [], static=True)
 
     def getBody(self):
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -604,19 +604,40 @@ class Descriptor(DescriptorProvider):
             throwsAttr = "GetterThrows" if getter else "SetterThrows"
             throws = member.getExtendedAttribute(throwsAttr)
         maybeAppendInfallibleToAttrs(attrs, throws)
         return attrs
 
     def supportsIndexedProperties(self):
         return self.operations['IndexedGetter'] is not None
 
+    def lengthNeedsCallerType(self):
+        """
+        Determine whether our length getter needs a caller type; this is needed
+        in some indexed-getter proxy algorithms.  The idea is that if our
+        indexed getter needs a caller type, our automatically-generated Length()
+        calls need one too.
+        """
+        assert self.supportsIndexedProperties()
+        indexedGetter = self.operations['IndexedGetter']
+        return indexedGetter.getExtendedAttribute("NeedsCallerType")
+
     def supportsNamedProperties(self):
         return self.operations['NamedGetter'] is not None
 
+    def supportedNamesNeedCallerType(self):
+        """
+        Determine whether our GetSupportedNames call needs a caller type.  The
+        idea is that if your named getter needs a caller type, then so does
+        GetSupportedNames.
+        """
+        assert self.supportsNamedProperties()
+        namedGetter = self.operations['NamedGetter']
+        return namedGetter.getExtendedAttribute("NeedsCallerType")
+
     def hasNonOrdinaryGetPrototypeOf(self):
         return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf")
 
     def needsHeaderInclude(self):
         """
         An interface doesn't need a header file if it is not concrete, not
         pref-controlled, has no prototype object, has no static methods or
         attributes and has no parent.  The parent matters because we assert
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -6393,27 +6393,27 @@ class Parser(Tokenizer):
     def p_NonAnyTypeSequenceType(self, p):
         """
             NonAnyType : SEQUENCE LT Type GT Null
         """
         innerType = p[3]
         type = IDLSequenceType(self.getLocation(p, 1), innerType)
         p[0] = self.handleNullable(type, p[5])
 
-    # Note: Promise<void> is allowed, so we want to parametrize on
-    # ReturnType, not Type.  Also, we want this to end up picking up
-    # the Promise interface for now, hence the games with IDLUnresolvedType.
+    # Note: Promise<void> is allowed, so we want to parametrize on ReturnType,
+    # not Type.  Also, we want this to end up picking up the Promise interface
+    # for now, hence the games with IDLUnresolvedType.  Promise types can't be
+    # null, hence no "Null" in there.
     def p_NonAnyTypePromiseType(self, p):
         """
-            NonAnyType : PROMISE LT ReturnType GT Null
+            NonAnyType : PROMISE LT ReturnType GT
         """
         innerType = p[3]
         promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
-        type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
-        p[0] = self.handleNullable(type, p[5])
+        p[0] = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
 
     def p_NonAnyTypeMozMapType(self, p):
         """
             NonAnyType : MOZMAP LT Type GT Null
         """
         innerType = p[3]
         type = IDLMozMapType(self.getLocation(p, 1), innerType)
         p[0] = self.handleNullable(type, p[5])
--- a/dom/bindings/parser/tests/test_promise.py
+++ b/dom/bindings/parser/tests/test_promise.py
@@ -44,16 +44,46 @@ def WebIDLTest(parser, harness):
         results = parser.finish();
     except:
         threw = True
     harness.ok(threw,
                "Should not allow overloads which have both Promise and "
                "non-Promise return types.")
 
     parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface _Promise {};
+            interface A {
+              Promise<any>? foo();
+            };
+        """)
+        results = parser.finish();
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow nullable Promise return values.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface _Promise {};
+            interface A {
+              void foo(Promise<any>? arg);
+            };
+        """)
+        results = parser.finish();
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow nullable Promise arguments.")
+
+    parser = parser.reset()
     parser.parse("""
         interface _Promise {};
         interface A {
           Promise<any> foo();
           Promise<any> foo(long arg);
         };
     """)
     results = parser.finish();
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -748,23 +748,19 @@ public:
   void PassDateSequence(const Sequence<Date>&);
   void PassDateMozMap(const MozMap<Date>&);
   void PassNullableDateSequence(const Sequence<Nullable<Date> >&);
   Date ReceiveDate();
   Nullable<Date> ReceiveNullableDate();
 
   // Promise types
   void PassPromise(Promise&);
-  void PassNullablePromise(Promise*);
   void PassOptionalPromise(const Optional<OwningNonNull<Promise>>&);
-  void PassOptionalNullablePromise(const Optional<RefPtr<Promise>>&);
-  void PassOptionalNullablePromiseWithDefaultValue(Promise*);
   void PassPromiseSequence(const Sequence<OwningNonNull<Promise>>&);
   void PassPromiseMozMap(const MozMap<RefPtr<Promise>>&);
-  void PassNullablePromiseSequence(const Sequence<RefPtr<Promise>> &);
   Promise* ReceivePromise();
   already_AddRefed<Promise> ReceiveAddrefedPromise();
 
   // binaryNames tests
   void MethodRenamedTo();
   void OtherMethodRenamedTo();
   void MethodRenamedTo(int8_t);
   int8_t AttributeGetterRenamedTo();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -726,22 +726,18 @@ interface TestInterface {
   void passDateSequence(sequence<Date> arg);
   void passNullableDateSequence(sequence<Date?> arg);
   void passDateMozMap(MozMap<Date> arg);
   Date receiveDate();
   Date? receiveNullableDate();
 
   // Promise types
   void passPromise(Promise<any> arg);
-  void passNullablePromise(Promise<any>? arg);
   void passOptionalPromise(optional Promise<any> arg);
-  void passOptionalNullablePromise(optional Promise<any>? arg);
-  void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
   void passPromiseSequence(sequence<Promise<any>> arg);
-  void passNullablePromiseSequence(sequence<Promise<any>?> arg);
   Promise<any> receivePromise();
   Promise<any> receiveAddrefedPromise();
 
   // binaryNames tests
   void methodRenamedFrom();
   [BinaryName="otherMethodRenamedTo"]
   void otherMethodRenamedFrom();
   void methodRenamedFrom(byte argument);
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -561,22 +561,18 @@ interface TestExampleInterface {
   void passDateSequence(sequence<Date> arg);
   void passNullableDateSequence(sequence<Date?> arg);
   void passDateMozMap(MozMap<Date> arg);
   Date receiveDate();
   Date? receiveNullableDate();
 
   // Promise types
   void passPromise(Promise<any> arg);
-  void passNullablePromise(Promise<any>? arg);
   void passOptionalPromise(optional Promise<any> arg);
-  void passOptionalNullablePromise(optional Promise<any>? arg);
-  void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
   void passPromiseSequence(sequence<Promise<any>> arg);
-  void passNullablePromiseSequence(sequence<Promise<any>?> arg);
   Promise<any> receivePromise();
   Promise<any> receiveAddrefedPromise();
 
   // binaryNames tests
   void methodRenamedFrom();
   [BinaryName="otherMethodRenamedTo"]
   void otherMethodRenamedFrom();
   void methodRenamedFrom(byte argument);
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -573,22 +573,18 @@ interface TestJSImplInterface {
   void passDateSequence(sequence<Date> arg);
   void passNullableDateSequence(sequence<Date?> arg);
   void passDateMozMap(MozMap<Date> arg);
   Date receiveDate();
   Date? receiveNullableDate();
 
   // Promise types
   void passPromise(Promise<any> arg);
-  void passNullablePromise(Promise<any>? arg);
   void passOptionalPromise(optional Promise<any> arg);
-  void passOptionalNullablePromise(optional Promise<any>? arg);
-  void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
   void passPromiseSequence(sequence<Promise<any>> arg);
-  void passNullablePromiseSequence(sequence<Promise<any>?> arg);
   Promise<any> receivePromise();
   Promise<any> receiveAddrefedPromise();
 
   // binaryNames tests
   void methodRenamedFrom();
   [BinaryName="otherMethodRenamedTo"]
   void otherMethodRenamedFrom();
   void methodRenamedFrom(byte argument);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1869,19 +1869,20 @@ CanvasRenderingContext2D::GetHeight() co
 {
   return mHeight;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
   bool retainBuffer = false;
-  if (aWidth == mWidth && aHeight == mHeight) {
-    retainBuffer = true;
-  }
+  // See bug 1318283 as to why we are disabling this optimization.
+  // Based on the results of the investigation, this may go away
+  // completely or come back.
+  // retainBuffer = (aWidth == mWidth && aHeight == mHeight);
   ClearTarget(retainBuffer);
 
   // Zero sized surfaces can cause problems.
   mZero = false;
   if (aHeight == 0) {
     aHeight = 1;
     mZero = true;
   }
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -14,30 +14,16 @@
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLTexelConversions.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 namespace webgl {
 
-static bool
-UnpackFormatHasColorAndAlpha(GLenum unpackFormat)
-{
-    switch (unpackFormat) {
-    case LOCAL_GL_LUMINANCE_ALPHA:
-    case LOCAL_GL_RGBA:
-    case LOCAL_GL_SRGB_ALPHA:
-        return true;
-
-    default:
-        return false;
-    }
-}
-
 static WebGLTexelFormat
 FormatForPackingInfo(const PackingInfo& pi)
 {
     switch (pi.type) {
     case LOCAL_GL_UNSIGNED_BYTE:
         switch (pi.format) {
         case LOCAL_GL_RED:
         case LOCAL_GL_LUMINANCE:
@@ -220,181 +206,104 @@ ZeroOn2D(TexImageTarget target, uint32_t
 static uint32_t
 FallbackOnZero(uint32_t val, uint32_t fallback)
 {
     return (val ? val : fallback);
 }
 
 TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target,
                              uint32_t rowLength, uint32_t width, uint32_t height,
-                             uint32_t depth, bool isSrcPremult)
+                             uint32_t depth, bool srcIsPremult)
     : mAlignment(webgl->mPixelStore_UnpackAlignment)
     , mRowLength(rowLength)
-    , mImageHeight(FallbackOnZero(ZeroOn2D(target,
-                                           webgl->mPixelStore_UnpackImageHeight),
+    , mImageHeight(FallbackOnZero(ZeroOn2D(target, webgl->mPixelStore_UnpackImageHeight),
                                   height))
 
     , mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
     , mSkipRows(webgl->mPixelStore_UnpackSkipRows)
     , mSkipImages(ZeroOn2D(target, webgl->mPixelStore_UnpackSkipImages))
 
     , mWidth(width)
     , mHeight(height)
     , mDepth(depth)
 
-    , mIsSrcPremult(isSrcPremult)
+    , mSrcIsPremult(srcIsPremult)
 
     , mNeedsExactUpload(false)
 {
     MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
 }
 
 bool
 TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
-                               const uint8_t* srcBytes, uint32_t srcStride,
-                               uint8_t srcBPP, WebGLTexelFormat srcFormat,
-                               const webgl::DriverUnpackInfo* dstDUI,
-                               const uint8_t** const out_bytes,
+                               const uint32_t rowLength, const uint32_t rowCount,
+                               WebGLTexelFormat srcFormat,
+                               const uint8_t* const srcBegin, const ptrdiff_t srcStride,
+                               WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
+
+                               const uint8_t** const out_begin,
                                UniqueBuffer* const out_anchoredBuffer) const
 {
-    *out_bytes = srcBytes;
+    MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
+    MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
 
-    if (!HasData() || !mWidth || !mHeight || !mDepth)
+    *out_begin = srcBegin;
+
+    if (!rowLength || !rowCount)
         return true;
 
-    //////
-
-    const auto totalSkipRows = mSkipRows + CheckedUint32(mSkipImages) * mImageHeight;
-    const auto offset = mSkipPixels * CheckedUint32(srcBPP) + totalSkipRows * srcStride;
-    if (!offset.isValid()) {
-        webgl->ErrorOutOfMemory("%s: Invalid offset calculation during conversion.",
-                                funcName);
-        return false;
-    }
-    const uint32_t skipBytes = offset.value();
-
-    auto const srcBegin = srcBytes + skipBytes;
-
-    //////
-
+    const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
-    const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
-
-    const auto pi = dstDUI->ToPacking();
-
-    const auto dstBPP = webgl::BytesPerPixel(pi);
-    const auto dstWidthBytes = CheckedUint32(dstBPP) * mWidth;
-    const auto dstRowLengthBytes = CheckedUint32(dstBPP) * mRowLength;
 
-    const auto dstAlignment = mAlignment;
-    const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
-
-    //////
+    if (srcFormat != dstFormat) {
+        webgl->GenerateWarning("%s: Conversion requires pixel reformatting.", funcName);
+    } else if (mSrcIsPremult != dstIsPremult) {
+        webgl->GenerateWarning("%s: Conversion requires change in"
+                               "alpha-premultiplication.",
+                               funcName);
+    } else if (srcOrigin != dstOrigin) {
+        webgl->GenerateWarning("%s: Conversion requires y-flip.", funcName);
+    } else if (srcStride != dstStride) {
+        webgl->GenerateWarning("%s: Conversion requires change in stride.", funcName);
+    } else {
+        return true;
+    }
 
-    const auto dstTotalRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight;
-    const auto dstUsedSizeExceptLastRow = (dstTotalRows - 1) * dstStride;
+    ////
 
-    const auto dstSize = skipBytes + dstUsedSizeExceptLastRow + dstWidthBytes;
-    if (!dstSize.isValid()) {
-        webgl->ErrorOutOfMemory("%s: Invalid dstSize calculation during conversion.",
-                                funcName);
+    const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
+    if (!dstTotalBytes.isValid()) {
+        webgl->ErrorOutOfMemory("%s: Calculation failed.", funcName);
         return false;
     }
 
-    //////
-
-    const auto dstFormat = FormatForPackingInfo(pi);
-
-    bool premultMatches = (mIsSrcPremult == isDstPremult);
-    if (!UnpackFormatHasColorAndAlpha(dstDUI->unpackFormat)) {
-        premultMatches = true;
-    }
-
-    const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches);
-    const bool originsMatch = (srcOrigin == dstOrigin);
-
-    MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP);
-
-    if (!needsPixelConversion &&
-        originsMatch &&
-        srcStride == dstStride.value())
-    {
-        // No conversion needed!
-        return true;
-    }
-
-    //////
-    // We need some sort of conversion, so create the dest buffer.
-
-    *out_anchoredBuffer = calloc(1, dstSize.value());
-    const auto dstBytes = (uint8_t*)out_anchoredBuffer->get();
-
-    if (!dstBytes) {
-        webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.",
-                                funcName);
+    UniqueBuffer dstBuffer = calloc(1, dstTotalBytes.value());
+    if (!dstBuffer.get()) {
+        webgl->ErrorOutOfMemory("%s: Failed to allocate dest buffer.", funcName);
         return false;
     }
-    *out_bytes = dstBytes;
-    const auto dstBegin = dstBytes + skipBytes;
-
-    //////
-    // Row conversion
-
-    if (!needsPixelConversion) {
-        webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.",
-                               funcName);
-
-        const uint8_t* srcRow = srcBegin;
-        uint8_t* dstRow = dstBegin;
-        const auto widthBytes = dstWidthBytes.value();
-        ptrdiff_t dstCopyStride = dstStride.value();
+    const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
 
-        if (!originsMatch) {
-            dstRow += dstUsedSizeExceptLastRow.value();
-            dstCopyStride = -dstCopyStride;
-        }
-
-        for (uint32_t i = 0; i < dstTotalRows.value(); i++) {
-            memcpy(dstRow, srcRow, widthBytes);
-            srcRow += srcStride;
-            dstRow += dstCopyStride;
-        }
-        return true;
-    }
-
-    ////////////
-    // Pixel conversion.
-
-    MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
-    MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
-
-    webgl->GenerateWarning("%s: Incurred CPU pixel conversion, which is very slow.",
-                           funcName);
-
-    //////
+    ////
 
     // And go!:
     bool wasTrivial;
-    if (!ConvertImage(mWidth, dstTotalRows.value(),
-                      srcBegin, srcStride, srcOrigin, srcFormat, mIsSrcPremult,
-                      dstBegin, dstStride.value(), dstOrigin, dstFormat, isDstPremult,
+    if (!ConvertImage(rowLength, rowCount,
+                      srcBegin, srcStride, srcOrigin, srcFormat, mSrcIsPremult,
+                      dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult,
                       &wasTrivial))
     {
         webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName);
         return false;
     }
 
-    if (!wasTrivial) {
-        webgl->GenerateWarning("%s: Chosen format/type incurred an expensive reformat:"
-                               " 0x%04x/0x%04x",
-                               funcName, dstDUI->unpackFormat, dstDUI->unpackType);
-    }
-
+    *out_begin = dstBegin;
+    *out_anchoredBuffer = Move(dstBuffer);
     return true;
 }
 
 static GLenum
 DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
                 const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
                 GLsizei width, GLsizei height, GLsizei depth, const void* data)
 {
@@ -408,18 +317,18 @@ DoTexOrSubImage(bool isSubImage, gl::GLC
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // TexUnpackBytes
 
 TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
                                uint32_t width, uint32_t height, uint32_t depth,
                                bool isClientData, const uint8_t* ptr, size_t availBytes)
     : TexUnpackBlob(webgl, target,
-                    FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width), width,
-                    height, depth, false)
+                    FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width),
+                    width, height, depth, false)
     , mIsClientData(isClientData)
     , mPtr(ptr)
     , mAvailBytes(availBytes)
 { }
 
 bool
 TexUnpackBytes::Validate(WebGLContext* webgl, const char* funcName,
                          const webgl::PackingInfo& pi)
@@ -434,54 +343,86 @@ bool
 TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, GLenum* const out_error) const
 {
     WebGLContext* webgl = tex->mContext;
 
     const auto pi = dui->ToPacking();
-
+    const auto format = FormatForPackingInfo(pi);
     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-    const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
-    const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
-    if (!rowStride.isValid()) {
-        MOZ_CRASH("Should be checked earlier.");
-    }
+
+    const uint8_t* uploadPtr = mPtr;
+    UniqueBuffer tempBuffer;
+
+    do {
+        if (!mIsClientData || !mPtr)
+            break;
+
+        if (!webgl->mPixelStore_FlipY &&
+            !webgl->mPixelStore_PremultiplyAlpha)
+        {
+            break;
+        }
 
-    const auto format = FormatForPackingInfo(pi);
+        if (webgl->mPixelStore_UnpackImageHeight ||
+            webgl->mPixelStore_UnpackSkipImages ||
+            webgl->mPixelStore_UnpackRowLength ||
+            webgl->mPixelStore_UnpackSkipRows ||
+            webgl->mPixelStore_UnpackSkipPixels)
+        {
+            webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult"
+                                         " or y-flip do not support subrect selection.",
+                                         funcName);
+            return false;
+        }
 
-    auto uploadPtr = mPtr;
-    UniqueBuffer tempBuffer;
-    if (mIsClientData &&
-        !ConvertIfNeeded(webgl, funcName, mPtr, rowStride.value(), bytesPerPixel, format,
-                         dui, &uploadPtr, &tempBuffer))
-    {
-        return false;
-    }
+        webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for"
+                               " non-DOM-Element uploads.",
+                               funcName);
+
+        const uint32_t rowLength = mWidth;
+        const uint32_t rowCount = mHeight * mDepth;
+        const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment);
+        if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride,
+                             format, stride, &uploadPtr, &tempBuffer))
+        {
+            return false;
+        }
+    } while (false);
+
+    //////
 
     const auto& gl = webgl->gl;
 
-    //////
-
     bool useParanoidHandling = false;
     if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
         webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte"
                                " count smaller than the row stride can incur extra"
                                " overhead.",
                                funcName);
 
         if (gl->WorkAroundDriverBugs()) {
             useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
         }
     }
 
     if (!useParanoidHandling) {
+        if (webgl->mBoundPixelUnpackBuffer) {
+            gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
+                            webgl->mBoundPixelUnpackBuffer->mGLName);
+        }
+
         *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
                                      zOffset, mWidth, mHeight, mDepth, uploadPtr);
+
+        if (webgl->mBoundPixelUnpackBuffer) {
+            gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+        }
         return true;
     }
 
     //////
 
     MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
 
     if (!isSubImage) {
@@ -515,16 +456,21 @@ TexUnpackBytes::TexOrSubImage(bool isSub
         *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
                                      zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr);
     }
 
     const auto totalSkipRows = CheckedUint32(mSkipImages) * mImageHeight + mSkipRows;
     const auto totalFullRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight - 1;
     const auto tailOffsetRows = totalSkipRows + totalFullRows;
 
+    const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
+    const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
+    if (!rowStride.isValid()) {
+        MOZ_CRASH("Should be checked earlier.");
+    }
     const auto tailOffsetBytes = tailOffsetRows * rowStride;
 
     uploadPtr += tailOffsetBytes.value();
 
     //////
 
     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);   // No stride padding.
     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);  // No padding in general.
@@ -589,18 +535,18 @@ TexUnpackImage::TexOrSubImage(bool isSub
         if (*out_error)
             return true;
     }
 
     do {
         if (mDepth != 1)
             break;
 
-        const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
-        if (mIsSrcPremult != isDstPremult)
+        const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
+        if (mSrcIsPremult != dstIsPremult)
             break;
 
         if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
             break;
 
         if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
             break;
 
@@ -649,17 +595,17 @@ TexUnpackImage::TexOrSubImage(bool isSub
     if (!dataSurf) {
         webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
                                 " blit failed for TexUnpackImage.",
                                 funcName);
         return false;
     }
 
     const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
-                                    mIsSrcPremult);
+                                    mSrcIsPremult);
 
     return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
                                   dui, xOffset, yOffset, zOffset, out_error);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // TexUnpackSurface
@@ -734,67 +680,88 @@ TexUnpackSurface::Validate(WebGLContext*
 
 bool
 TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                                 WebGLTexture* tex, TexImageTarget target, GLint level,
                                 const webgl::DriverUnpackInfo* dstDUI, GLint xOffset,
                                 GLint yOffset, GLint zOffset,
                                 GLenum* const out_error) const
 {
-    WebGLContext* webgl = tex->mContext;
+    const auto& webgl = tex->mContext;
+
+    ////
+
+    const auto& rowLength = mSurf->GetSize().width;
+    const auto& rowCount = mSurf->GetSize().height;
+
+    const auto& dstPI = dstDUI->ToPacking();
+    const auto& dstBPP = webgl::BytesPerPixel(dstPI);
+    const auto dstFormat = FormatForPackingInfo(dstPI);
+
+    ////
 
     WebGLTexelFormat srcFormat;
     uint8_t srcBPP;
     if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
         webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
                                       " WebGLTexelFormat::%u.",
                                       funcName, uint32_t(mSurf->GetFormat()));
         return false;
     }
 
     gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
     if (!map.IsMapped()) {
         webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
         return false;
     }
 
-    const auto srcBytes = map.GetData();
-    const auto srcStride = map.GetStride();
+    const auto& srcBegin = map.GetData();
+    const auto& srcStride = map.GetStride();
 
-    // CPU conversion. (++numCopies)
+    ////
+
+    const auto srcRowLengthBytes = rowLength * srcBPP;
 
-    webgl->GenerateWarning("%s: Incurred CPU-side conversion, which is very slow.",
-                           funcName);
+    const uint8_t maxGLAlignment = 8;
+    uint8_t srcAlignment = 1;
+    for (; srcAlignment <= maxGLAlignment; srcAlignment *= 2) {
+        const auto strideGuess = RoundUpToMultipleOf(srcRowLengthBytes, srcAlignment);
+        if (strideGuess == srcStride)
+            break;
+    }
+    const uint32_t dstAlignment = (srcAlignment > maxGLAlignment) ? 1 : srcAlignment;
 
-    const uint8_t* uploadBytes;
+    const auto dstRowLengthBytes = rowLength * dstBPP;
+    const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
+
+    ////
+
+    const uint8_t* dstBegin = srcBegin;
     UniqueBuffer tempBuffer;
-    if (!ConvertIfNeeded(webgl, funcName, srcBytes, srcStride, srcBPP, srcFormat,
-                         dstDUI, &uploadBytes, &tempBuffer))
+    if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin,
+                         srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer))
     {
         return false;
     }
 
-    //////
+    ////
 
-    gl::GLContext* const gl = webgl->gl;
+    const auto& gl = webgl->gl;
     MOZ_ALWAYS_TRUE( gl->MakeCurrent() );
 
-    const auto curEffectiveRowLength = FallbackOnZero(webgl->mPixelStore_UnpackRowLength,
-                                                      mWidth);
-
-    const bool changeRowLength = (mRowLength != curEffectiveRowLength);
-    if (changeRowLength) {
-        MOZ_ASSERT(webgl->IsWebGL2());
-        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, dstAlignment);
+    if (webgl->IsWebGL2()) {
+        gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
     }
 
     *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dstDUI, xOffset,
-                                 yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes);
+                                 yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin);
 
-    if (changeRowLength) {
+    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
+    if (webgl->IsWebGL2()) {
         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
     }
 
     return true;
 }
 
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -45,33 +45,36 @@ public:
     const uint32_t mRowLength;
     const uint32_t mImageHeight;
     const uint32_t mSkipPixels;
     const uint32_t mSkipRows;
     const uint32_t mSkipImages;
     const uint32_t mWidth;
     const uint32_t mHeight;
     const uint32_t mDepth;
-    const bool mIsSrcPremult;
+
+    const bool mSrcIsPremult;
 
     bool mNeedsExactUpload;
 
 protected:
     TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target, uint32_t rowLength,
                   uint32_t width, uint32_t height, uint32_t depth, bool isSrcPremult);
 
 public:
     virtual ~TexUnpackBlob() { }
 
 protected:
     bool ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
-                         const uint8_t* srcBytes, uint32_t srcStride, uint8_t srcBPP,
+                         const uint32_t rowLength, const uint32_t rowCount,
                          WebGLTexelFormat srcFormat,
-                         const webgl::DriverUnpackInfo* dstDUI,
-                         const uint8_t** const out_bytes,
+                         const uint8_t* const srcBegin, const ptrdiff_t srcStride,
+                         WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
+
+                         const uint8_t** const out_begin,
                          UniqueBuffer* const out_anchoredBuffer) const;
 
 public:
     virtual bool HasData() const { return true; }
 
     virtual bool Validate(WebGLContext* webgl, const char* funcName,
                           const webgl::PackingInfo& pi) = 0;
 
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1044,61 +1044,62 @@ WebGLFramebuffer::ResolvedData::Resolved
     }
     if (parent.mDepthStencilAttachment.IsDefined()) {
         depthBuffer = &parent.mDepthStencilAttachment;
         stencilBuffer = &parent.mDepthStencilAttachment;
     }
 
     ////
 
+    colorDrawBuffers.reserve(parent.mColorDrawBuffers.size());
     texDrawBuffers.reserve(parent.mColorDrawBuffers.size() + 2); // +2 for depth+stencil.
 
     const auto fnCommon = [&](const WebGLFBAttachPoint& attach) {
         if (!attach.IsDefined())
             return false;
 
         hasSampleBuffers |= bool(attach.Samples());
 
         if (attach.Texture()) {
             texDrawBuffers.push_back(&attach);
         }
         return true;
     };
 
     ////
 
-    const auto fnColor = [&](const WebGLFBAttachPoint& attach,
-                             decltype(drawSet)* const out_destSet)
-    {
-        if (!fnCommon(attach))
-            return;
-
-        out_destSet->insert(WebGLFBAttachPoint::Ordered(attach));
-    };
-
     const auto fnDepthStencil = [&](const WebGLFBAttachPoint& attach) {
         if (!fnCommon(attach))
             return;
 
         drawSet.insert(WebGLFBAttachPoint::Ordered(attach));
         readSet.insert(WebGLFBAttachPoint::Ordered(attach));
     };
 
-    ////
-
     fnDepthStencil(parent.mDepthAttachment);
     fnDepthStencil(parent.mStencilAttachment);
     fnDepthStencil(parent.mDepthStencilAttachment);
 
-    for (const auto& attach : parent.mColorDrawBuffers) {
-        fnColor(*attach, &drawSet);
+    ////
+
+    for (const auto& pAttach : parent.mColorDrawBuffers) {
+        const auto& attach = *pAttach;
+        if (!fnCommon(attach))
+            return;
+
+        drawSet.insert(WebGLFBAttachPoint::Ordered(attach));
+        colorDrawBuffers.push_back(&attach);
     }
 
     if (parent.mColorReadBuffer) {
-        fnColor(*(parent.mColorReadBuffer), &readSet);
+        const auto& attach = *parent.mColorReadBuffer;
+        if (!fnCommon(attach))
+            return;
+
+        readSet.insert(WebGLFBAttachPoint::Ordered(attach));
     }
 }
 
 void
 WebGLFramebuffer::RefreshResolvedData()
 {
     if (mResolvedCompleteData) {
         mResolvedCompleteData.reset(new ResolvedData(*this));
@@ -1617,30 +1618,31 @@ WebGLFramebuffer::BlitFramebuffer(WebGLC
             return webgl::ComponentType::Float;
 
         default:
             return format->componentType;
         }
     };
 
     const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
+        MOZ_ASSERT(dstFormat->r || dstFormat->g || dstFormat->b || dstFormat->a);
         dstHasColor = true;
         colorFormatsMatch &= (dstFormat == srcColorFormat);
         colorTypesMatch &= ( fnNarrowComponentType(dstFormat) ==
                              fnNarrowComponentType(srcColorFormat) );
     };
 
     if (dstFB) {
         dstSampleBuffers = dstFB->mResolvedCompleteData->hasSampleBuffers;
 
         dstDepthFormat = fnGetFormat(dstFB->mResolvedCompleteData->depthBuffer);
         dstStencilFormat = fnGetFormat(dstFB->mResolvedCompleteData->stencilBuffer);
 
-        for (const auto& drawBufferEntry : dstFB->mResolvedCompleteData->drawSet) {
-            fnCheckColorFormat(drawBufferEntry.mRef.Format()->format);
+        for (const auto& cur : dstFB->mResolvedCompleteData->colorDrawBuffers) {
+            fnCheckColorFormat(cur->Format()->format);
         }
     } else {
         dstSampleBuffers = bool(gl->Screen()->Samples());
 
         const webgl::FormatInfo* dstColorFormat;
         GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat, &dstStencilFormat);
 
         fnCheckColorFormat(dstColorFormat);
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -168,16 +168,17 @@ protected:
     std::vector<const WebGLFBAttachPoint*> mColorDrawBuffers; // Non-null
     const WebGLFBAttachPoint* mColorReadBuffer; // Null if NONE
 
     ////
 
     struct ResolvedData {
         // BlitFramebuffer
         bool hasSampleBuffers;
+        std::vector<const WebGLFBAttachPoint*> colorDrawBuffers;
         const WebGLFBAttachPoint* depthBuffer;
         const WebGLFBAttachPoint* stencilBuffer;
 
         // IsFeedback
         std::vector<const WebGLFBAttachPoint*> texDrawBuffers; // Non-null
         std::set<WebGLFBAttachPoint::Ordered> drawSet;
         std::set<WebGLFBAttachPoint::Ordered> readSet;
 
--- a/dom/canvas/WebGLTexelConversions.cpp
+++ b/dom/canvas/WebGLTexelConversions.cpp
@@ -394,17 +394,21 @@ ConvertImage(size_t width, size_t height
         //
         // The case where absolutely nothing needs to be done is supposed to have
         // been handled earlier (in TexImage2D_base, etc).
         //
         // So the case we're handling here is when even though no format conversion is
         // needed, we still might have to flip vertically and/or to adjust to a different
         // stride.
 
-        MOZ_ASSERT(shouldYFlip || srcStride != dstStride,
+        // We ignore canSkipPremult for this perf trap, since it's an avoidable perf cliff
+        // under the WebGL API user's control.
+        MOZ_ASSERT((srcPremultiplied != dstPremultiplied ||
+                    shouldYFlip ||
+                    srcStride != dstStride),
                    "Performance trap -- should handle this case earlier to avoid memcpy");
 
         const auto bytesPerPixel = TexelBytesForFormat(srcFormat);
         const size_t bytesPerRow = bytesPerPixel * width;
 
         while (srcItr != srcEnd) {
             memcpy(dstItr, srcItr, bytesPerRow);
             srcItr += srcStride;
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -470,29 +470,30 @@ ValidateTexImage(WebGLContext* webgl, We
 
     *out_imageInfo = &imageInfo;
     return true;
 }
 
 // For *TexImage*
 bool
 WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
-                                            GLint level, uint32_t width, uint32_t height,
-                                            uint32_t depth,
+                                            GLint rawLevel, uint32_t width,
+                                            uint32_t height, uint32_t depth,
                                             WebGLTexture::ImageInfo** const out_imageInfo)
 {
     if (mImmutable) {
         mContext->ErrorInvalidOperation("%s: Specified texture is immutable.", funcName);
         return false;
     }
 
     // Do this early to validate `level`.
     WebGLTexture::ImageInfo* imageInfo;
-    if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo))
+    if (!ValidateTexImage(mContext, this, funcName, target, rawLevel, &imageInfo))
         return false;
+    const uint32_t level(rawLevel);
 
     if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP &&
         width != height)
     {
         mContext->ErrorInvalidValue("%s: Cube map images must be square.", funcName);
         return false;
     }
 
@@ -507,43 +508,54 @@ WebGLTexture::ValidateTexImageSpecificat
      * lower nor higher than MAX_TEXTURE_SIZE.
      *
      * Note that mImplMaxTextureSize must be >= than the advertized MAX_TEXTURE_SIZE.
      * For simplicity, we advertize MAX_TEXTURE_SIZE as mImplMaxTextureSize.
      */
 
     uint32_t maxWidthHeight = 0;
     uint32_t maxDepth = 0;
+    uint32_t maxLevel = 0;
 
     MOZ_ASSERT(level <= 31);
     switch (target.get()) {
     case LOCAL_GL_TEXTURE_2D:
         maxWidthHeight = mContext->mImplMaxTextureSize >> level;
         maxDepth = 1;
+        maxLevel = CeilingLog2(mContext->mImplMaxTextureSize);
         break;
 
     case LOCAL_GL_TEXTURE_3D:
         maxWidthHeight = mContext->mImplMax3DTextureSize >> level;
         maxDepth = maxWidthHeight;
+        maxLevel = CeilingLog2(mContext->mImplMax3DTextureSize);
         break;
 
     case LOCAL_GL_TEXTURE_2D_ARRAY:
         maxWidthHeight = mContext->mImplMaxTextureSize >> level;
         // "The maximum number of layers for two-dimensional array textures (depth)
         //  must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
         maxDepth = mContext->mImplMaxArrayTextureLayers;
+        maxLevel = CeilingLog2(mContext->mImplMaxTextureSize);
         break;
 
     default: // cube maps
         MOZ_ASSERT(IsCubeMap());
         maxWidthHeight = mContext->mImplMaxCubeMapTextureSize >> level;
         maxDepth = 1;
+        maxLevel = CeilingLog2(mContext->mImplMaxCubeMapTextureSize);
         break;
     }
 
+    if (level > maxLevel) {
+        mContext->ErrorInvalidValue("%s: Requested level is not supported for target.",
+                                    funcName);
+        return false;
+    }
+
     if (width > maxWidthHeight ||
         height > maxWidthHeight ||
         depth > maxDepth)
     {
         mContext->ErrorInvalidValue("%s: Requested size at this level is unsupported.",
                                     funcName);
         return false;
     }
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -83,17 +83,16 @@ ConsoleStructuredCloneData
 
 class ConsoleCallData final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ConsoleCallData)
 
   ConsoleCallData()
     : mMethodName(Console::MethodLog)
-    , mPrivate(false)
     , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
     , mStartTimerValue(0)
     , mStartTimerStatus(false)
     , mStopTimerDuration(0)
     , mStopTimerStatus(false)
     , mCountValue(MAX_PAGE_COUNTERS)
     , mIDType(eUnknown)
     , mOuterIDNumber(0)
@@ -196,17 +195,16 @@ public:
   JS::Heap<JSObject*> mGlobal;
 
   // This is a copy of the arguments we received from the DOM bindings. Console
   // object traces them because this ConsoleCallData calls
   // RegisterConsoleCallData() in the Initialize().
   nsTArray<JS::Heap<JS::Value>> mCopiedArguments;
 
   Console::MethodName mMethodName;
-  bool mPrivate;
   int64_t mTimeStamp;
 
   // These values are set in the owning thread and they contain the timestamp of
   // when the new timer has started, the name of it and the status of the
   // creation of it. If status is false, something went wrong. User
   // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
   // monotonicTimer from Performance.now();
   // They will be set on the owning thread and never touched again on that
@@ -565,30 +563,16 @@ private:
   {
     AssertIsOnMainThread();
 
     // The windows have to run in parallel.
     MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
 
     if (aOuterWindow) {
       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
-
-      // Save the principal's OriginAttributes in the console event data
-      // so that we will be able to filter messages by origin attributes.
-      nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aInnerWindow);
-      if (NS_WARN_IF(!sop)) {
-        return;
-      }
-
-      nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
-      if (NS_WARN_IF(!principal)) {
-        return;
-      }
-
-      mCallData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
     } else {
       ConsoleStackEntry frame;
       if (mCallData->mTopStackFrame) {
         frame = *mCallData->mTopStackFrame;
       }
 
       nsString id = frame.mFilename;
       nsString innerID;
@@ -599,25 +583,16 @@ private:
         // Use scope as ID so the webconsole can decide if the message should
         // show up per tab
         id.AssignWithConversion(mWorkerPrivate->WorkerName());
       } else {
         innerID = NS_LITERAL_STRING("Worker");
       }
 
       mCallData->SetIDs(id, innerID);
-
-      // Save the principal's OriginAttributes in the console event data
-      // so that we will be able to filter messages by origin attributes.
-      nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
-      if (NS_WARN_IF(!principal)) {
-        return;
-      }
-
-      mCallData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
     }
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
     ProcessCallData(aCx);
 
     mClonedData.mParent = nullptr;
@@ -1222,42 +1197,55 @@ Console::MethodInternal(JSContext* aCx, 
 
   ClearException ce(aCx);
 
   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
                                        aData, this))) {
     return;
   }
 
+  PrincipalOriginAttributes oa;
+
   if (mWindow) {
-    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
-    if (!webNav) {
-      return;
-    }
-
-    nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
-    MOZ_ASSERT(loadContext);
-
-    loadContext->GetUsePrivateBrowsing(&callData->mPrivate);
-
     // Save the principal's OriginAttributes in the console event data
     // so that we will be able to filter messages by origin attributes.
     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
     if (NS_WARN_IF(!sop)) {
       return;
     }
 
     nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
     if (NS_WARN_IF(!principal)) {
       return;
     }
 
-    callData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
+    oa = BasePrincipal::Cast(principal)->OriginAttributesRef();
+
+#ifdef DEBUG
+    if (!nsContentUtils::IsSystemPrincipal(principal)) {
+      nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
+      if (webNav) {
+        nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+        MOZ_ASSERT(loadContext);
+
+        bool pb;
+        if (NS_SUCCEEDED(loadContext->GetUsePrivateBrowsing(&pb))) {
+          MOZ_ASSERT(pb == !!oa.mPrivateBrowsingId);
+        }
+      }
+    }
+#endif
+  } else {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    oa = workerPrivate->GetOriginAttributes();
   }
 
+  callData->SetOriginAttributes(oa);
+
   JS::StackCapture captureMode = ShouldIncludeStackTrace(aMethodName) ?
     JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH)) :
     JS::StackCapture(JS::FirstSubsumedFrame(aCx));
   nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, mozilla::Move(captureMode));
 
   if (stack) {
     callData->mTopStackFrame.emplace();
     nsresult rv = StackFrameToStackEntry(aCx, stack,
@@ -1544,17 +1532,17 @@ Console::PopulateConsoleNotificationInTh
       CopyUTF8toUTF16(spec, event.mFilename);
     }
   }
 
   event.mLineNumber = frame.mLineNumber;
   event.mColumnNumber = frame.mColumnNumber;
   event.mFunctionName = frame.mFunctionName;
   event.mTimeStamp = aData->mTimeStamp;
-  event.mPrivate = aData->mPrivate;
+  event.mPrivate = !!aData->mOriginAttributes.mPrivateBrowsingId;
 
   switch (aData->mMethodName) {
     case MethodLog:
     case MethodInfo:
     case MethodWarn:
     case MethodError:
     case MethodException:
     case MethodDebug:
@@ -1580,17 +1568,16 @@ Console::PopulateConsoleNotificationInTh
   if (aData->mMethodName == MethodGroup ||
       aData->mMethodName == MethodGroupCollapsed ||
       aData->mMethodName == MethodGroupEnd) {
     ComposeGroupName(aCx, aArguments, event.mGroupName);
   }
 
   else if (aData->mMethodName == MethodTime && !aArguments.IsEmpty()) {
     event.mTimer = CreateStartTimerValue(aCx, aData->mStartTimerLabel,
-                                         aData->mStartTimerValue,
                                          aData->mStartTimerStatus);
   }
 
   else if (aData->mMethodName == MethodTimeEnd && !aArguments.IsEmpty()) {
     event.mTimer = CreateStopTimerValue(aCx, aData->mStopTimerLabel,
                                         aData->mStopTimerDuration,
                                         aData->mStopTimerStatus);
   }
@@ -2007,34 +1994,32 @@ Console::StartTimer(JSContext* aCx, cons
 
   aTimerLabel = label;
   *aTimerValue = aTimestamp;
   return true;
 }
 
 JS::Value
 Console::CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
-                               DOMHighResTimeStamp aTimerValue,
                                bool aTimerStatus) const
 {
   if (!aTimerStatus) {
     RootedDictionary<ConsoleTimerError> error(aCx);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!ToJSValue(aCx, error, &value)) {
       return JS::UndefinedValue();
     }
 
     return value;
   }
 
   RootedDictionary<ConsoleTimerStart> timer(aCx);
 
   timer.mName = aTimerLabel;
-  timer.mStarted = aTimerValue;
 
   JS::Rooted<JS::Value> value(aCx);
   if (!ToJSValue(aCx, timer, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
--- a/dom/console/Console.h
+++ b/dom/console/Console.h
@@ -271,21 +271,19 @@ private:
              DOMHighResTimeStamp* aTimerValue);
 
   // CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as
   // JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError
   // instead. It's called only after the execution StartTimer on the owning
   // thread.
   // * aCx - this is the context that will root the returned value.
   // * aTimerLabel - this label must be what StartTimer received as aTimerLabel.
-  // * aTimerValue - this is what StartTimer received as aTimerValue
   // * aTimerStatus - the return value of StartTimer.
   JS::Value
   CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
-                        DOMHighResTimeStamp aTimerValue,
                         bool aTimerStatus) const;
 
   // StopTimer follows the same pattern as StartTimer: it runs on the
   // owning thread and populates aTimerLabel and aTimerDuration, used by
   // CreateStopTimerValue. It returns false if a JS exception is thrown or if
   // the aName timer doesn't exist in the mTimerRegistry.
   // * aCx - the JSContext rooting aName.
   // * aName - this is (should be) the name of the timer as JS::Value.
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -403,23 +403,21 @@ EventListenerManager::AddEventListenerIn
       nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
       NS_WARNING_ASSERTION(
         !nsContentUtils::IsChromeDoc(d),
         "Please do not use mouseenter/leave events in chrome. "
         "They are slower than mouseover/out!");
 #endif
       window->SetHasMouseEnterLeaveEventListeners();
     }
-#ifdef MOZ_GAMEPAD
   } else if (aEventMessage >= eGamepadEventFirst &&
              aEventMessage <= eGamepadEventLast) {
     if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
       window->SetHasGamepadEventListener();
     }
-#endif
   } else if (aTypeAtom == nsGkAtoms::onkeydown ||
              aTypeAtom == nsGkAtoms::onkeypress ||
              aTypeAtom == nsGkAtoms::onkeyup) {
     if (!aFlags.mInSystemGroup) {
       mMayHaveKeyEventListener = true;
     }
   } else if (aTypeAtom == nsGkAtoms::oncompositionend ||
              aTypeAtom == nsGkAtoms::oncompositionstart ||
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -891,17 +891,16 @@ NON_IDL_EVENT(MozAfterPaint,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(MozScrolledAreaChanged,
               eScrolledAreaChanged,
               EventNameType_None,
               eScrollAreaEventClass)
 
-#ifdef MOZ_GAMEPAD
 NON_IDL_EVENT(gamepadbuttondown,
               eGamepadButtonDown,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepadbuttonup,
               eGamepadButtonUp,
               EventNameType_None,
               eBasicEventClass)
@@ -912,17 +911,16 @@ NON_IDL_EVENT(gamepadaxismove,
 NON_IDL_EVENT(gamepadconnected,
               eGamepadConnected,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepaddisconnected,
               eGamepadDisconnected,
               EventNameType_None,
               eBasicEventClass)
-#endif
 
 // Simple gesture events
 NON_IDL_EVENT(MozSwipeGestureMayStart,
               eSwipeGestureMayStart,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozSwipeGestureStart,
               eSwipeGestureStart,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -494,23 +494,19 @@ IsMessageMouseUserActivity(EventMessage 
          aMessage == eMouseClick ||
          aMessage == eMouseActivate ||
          aMessage == eMouseLongTap;
 }
 
 static bool
 IsMessageGamepadUserActivity(EventMessage aMessage)
 {
-#ifndef MOZ_GAMEPAD
-  return false;
-#else
   return aMessage == eGamepadButtonDown ||
          aMessage == eGamepadButtonUp ||
          aMessage == eGamepadAxisMove;
-#endif
 }
 
 nsresult
 EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
                                   WidgetEvent* aEvent,
                                   nsIFrame* aTargetFrame,
                                   nsIContent* aTargetContent,
                                   nsEventStatus* aStatus)
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -359,18 +359,17 @@ MouseEvent::GetScreenX(int32_t* aScreenX
   NS_ENSURE_ARG_POINTER(aScreenX);
   *aScreenX = ScreenX(CallerType::System);
   return NS_OK;
 }
 
 int32_t
 MouseEvent::ScreenX(CallerType aCallerType)
 {
-  if (aCallerType != CallerType::System &&
-      nsContentUtils::ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     // Sanitize to something sort of like client cooords, but not quite
     // (defaulting to (0,0) instead of our pre-specified client coords).
     return Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint,
                                   CSSIntPoint(0, 0)).x;
   }
 
   return Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint).x;
 }
@@ -381,18 +380,17 @@ MouseEvent::GetScreenY(int32_t* aScreenY
   NS_ENSURE_ARG_POINTER(aScreenY);
   *aScreenY = ScreenY(CallerType::System);
   return NS_OK;
 }
 
 int32_t
 MouseEvent::ScreenY(CallerType aCallerType)
 {
-  if (aCallerType != CallerType::System &&
-      nsContentUtils::ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     // Sanitize to something sort of like client cooords, but not quite
     // (defaulting to (0,0) instead of our pre-specified client coords).
     return Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint,
                                   CSSIntPoint(0, 0)).y;
   }
 
   return Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint).y;
 }
--- a/dom/events/Touch.cpp
+++ b/dom/events/Touch.cpp
@@ -138,29 +138,27 @@ Touch::GetTarget() const
   }
 
   return mTarget;
 }
 
 int32_t
 Touch::ScreenX(CallerType aCallerType) const
 {
-  if (aCallerType != CallerType::System &&
-      nsContentUtils::ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     return ClientX();
   }
 
   return mScreenPoint.x;
 }
 
 int32_t
 Touch::ScreenY(CallerType aCallerType) const
 {
-  if (aCallerType != CallerType::System &&
-      nsContentUtils::ResistFingerprinting()) {
+  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
     return ClientY();
   }
 
   return mScreenPoint.y;
 }
 
 void
 Touch::InitializePoints(nsPresContext* aPresContext, WidgetEvent* aEvent)
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -1,22 +1,29 @@
 [DEFAULT]
 skip-if = os == 'android' # Bug 1312791
 support-files =
   mochitest_support_external.js
   mochitest_support_internal.js
   pointerevent_styles.css
   pointerevent_support.js
 
-[test_pointerevent_attributes_mouse-manual.html]
-  support-files = pointerevent_attributes_mouse-manual.html
+[test_pointerevent_attributes_hoverable_pointers-manual.html]
+  support-files =
+    pointerevent_attributes_hoverable_pointers-manual.html
+    ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
+[test_pointerevent_attributes_nohover_pointers-manual.html]
+  support-files =
+    pointerevent_attributes_nohover_pointers-manual.html
+    ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
 [test_pointerevent_capture_mouse-manual.html]
   support-files = pointerevent_capture_mouse-manual.html
 [test_pointerevent_capture_suppressing_mouse-manual.html]
   support-files = pointerevent_capture_suppressing_mouse-manual.html
+  disabled = should be investigated
 [test_pointerevent_change-touch-action-onpointerdown_touch-manual.html]
   support-files = pointerevent_change-touch-action-onpointerdown_touch-manual.html
   disabled = disabled
 [test_pointerevent_constructor.html]
   support-files = pointerevent_constructor.html
 [test_pointerevent_element_haspointercapture-manual.html]
   support-files = pointerevent_element_haspointercapture-manual.html
 [test_pointerevent_element_haspointercapture_release_pending_capture-manual.html]
@@ -27,77 +34,45 @@ support-files =
   support-files = pointerevent_lostpointercapture_for_disconnected_node-manual.html
 [test_pointerevent_lostpointercapture_is_first-manual.html]
   support-files = pointerevent_lostpointercapture_is_first-manual.html
 [test_pointerevent_multiple_primary_pointers_boundary_events-manual.html]
   support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html
   disabled = should be investigated
 [test_pointerevent_pointercancel_touch-manual.html]
   support-files = pointerevent_pointercancel_touch-manual.html
-[test_pointerevent_pointerdown-manual.html]
-  support-files = pointerevent_pointerdown-manual.html
 [test_pointerevent_pointerenter_does_not_bubble-manual.html]
   support-files = pointerevent_pointerenter_does_not_bubble-manual.html
-[test_pointerevent_pointerenter_nohover-manual.html]
-  support-files = pointerevent_pointerenter_nohover-manual.html
 [test_pointerevent_pointerId_scope-manual.html]
   support-files =
     test_pointerevent_pointerId_scope-manual.html
     ./resources/pointerevent_pointerId_scope-iframe.html
   disabled = should be investigated
-[test_pointerevent_pointerenter-manual.html]
-  support-files = pointerevent_pointerenter-manual.html
 [test_pointerevent_pointerleave_after_pointercancel_touch-manual.html]
   support-files = pointerevent_pointerleave_after_pointercancel_touch-manual.html
-[test_pointerevent_pointerleave_after_pointerup_nohover-manual.html]
-  support-files = pointerevent_pointerleave_after_pointerup_nohover-manual.html
 [test_pointerevent_pointerleave_descendant_over-manual.html]
   support-files = pointerevent_pointerleave_descendant_over-manual.html
 [test_pointerevent_pointerleave_descendants-manual.html]
   support-files = pointerevent_pointerleave_descendants-manual.html
 [test_pointerevent_pointerleave_does_not_bubble-manual.html]
   support-files = pointerevent_pointerleave_does_not_bubble-manual.html
-[test_pointerevent_pointerleave_mouse-manual.html]
-  support-files = pointerevent_pointerleave_mouse-manual.html
 [test_pointerevent_pointerleave_pen-manual.html]
   support-files = pointerevent_pointerleave_pen-manual.html
-[test_pointerevent_pointerleave_touch-manual.html]
-  support-files = pointerevent_pointerleave_touch-manual.html
 [test_pointerevent_pointermove-manual.html]
   support-files = pointerevent_pointermove-manual.html
 [test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html
-[test_pointerevent_pointermove-on-chorded-mouse-button.html]
-  support-files = pointerevent_pointermove-on-chorded-mouse-button.html
-[test_pointerevent_pointermove_pointertype-manual.html]
-  support-files = pointerevent_pointermove_pointertype-manual.html
-[test_pointerevent_pointerout-manual.html]
-  support-files = pointerevent_pointerout-manual.html
+[test_pointerevent_pointermove_on_chorded_mouse_button-manual.html]
+  support-files = pointerevent_pointermove_on_chorded_mouse_button-manual.html
 [test_pointerevent_pointerout_after_pointercancel_touch-manual.html]
   support-files = pointerevent_pointerout_after_pointercancel_touch-manual.html
-[test_pointerevent_pointerout_after_pointerup_nohover-manual.html]
-  support-files = pointerevent_pointerout_after_pointerup_nohover-manual.html
 [test_pointerevent_pointerout_pen-manual.html]
   support-files = pointerevent_pointerout_pen-manual.html
 [test_pointerevent_pointerout_received_once-manual.html]
   support-files = pointerevent_pointerout_received_once-manual.html
-[test_pointerevent_pointerover-manual.html]
-  support-files = pointerevent_pointerover-manual.html
-[test_pointerevent_pointertype_mouse-manual.html]
-  support-files = pointerevent_pointertype_mouse-manual.html
-[test_pointerevent_pointertype_pen-manual.html]
-  support-files = pointerevent_pointertype_pen-manual.html
-[test_pointerevent_pointertype_touch-manual.html]
-  support-files = pointerevent_pointertype_touch-manual.html
-[test_pointerevent_pointerup-manual.html]
-  support-files = pointerevent_pointerup-manual.html
-[test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html]
-  support-files = pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
-[test_pointerevent_pointerup_pointertype-manual.html]
-  support-files = pointerevent_pointerup_pointertype-manual.html
 [test_pointerevent_releasepointercapture_events_to_original_target-manual.html]
   support-files = pointerevent_releasepointercapture_events_to_original_target-manual.html
 [test_pointerevent_releasepointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_releasepointercapture_invalid_pointerid-manual.html
 [test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html]
   support-files = pointerevent_releasepointercapture_onpointercancel_touch-manual.html
 [test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html]
   support-files = pointerevent_releasepointercapture_onpointerup_mouse-manual.html
@@ -111,20 +86,20 @@ support-files =
 [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
 [test_pointerevent_setpointercapture_to_same_element_twice-manual.html]
   support-files = pointerevent_setpointercapture_to_same_element_twice-manual.html
-[test_pointerevent_suppress_compat_events_on_click.html]
-  support-files = pointerevent_suppress_compat_events_on_click.html
-[test_pointerevent_suppress_compat_events_on_drag_mouse.html]
-  support-files = pointerevent_suppress_compat_events_on_drag_mouse.html
+[test_pointerevent_suppress_compat_events_on_click-manual.html]
+  support-files = pointerevent_suppress_compat_events_on_click-manual.html
+[test_pointerevent_suppress_compat_events_on_drag_mouse-manual.html]
+  support-files = pointerevent_suppress_compat_events_on_drag_mouse-manual.html
 [test_touch_action.html]
   support-files =
     ../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js
     ../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
     touch_action_helpers.js
     pointerevent_touch-action-auto-css_touch-manual.html
     pointerevent_touch-action-button-test_touch-manual.html
     pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/pointerevent_attributes_hoverable_pointers-manual.html
@@ -0,0 +1,144 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!-- script src="/resources/testharnessreport.js"></script -->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+            var detected_pointertypes = {};
+            var detected_eventTypes = {};
+            var eventList = ['pointerover', 'pointerenter', 'pointermove', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
+            var expectedPointerId = NaN;
+
+            function resetTestState() {
+                detected_eventTypes = {};
+                document.getElementById("square1").style.visibility = 'visible';
+                document.getElementById('innerFrame').contentDocument.getElementById("square2").style.visibility = 'hidden';
+                expectedPointerId = NaN;
+            }
+            function checkPointerEventAttributes(event, targetBoundingClientRect, testNamePrefix) {
+                if (detected_eventTypes[event.type])
+                    return;
+                var expectedEventType =  eventList[Object.keys(detected_eventTypes).length];
+                detected_eventTypes[event.type] = true;
+                var pointerTestName = testNamePrefix + ' ' + expectedPointerType + ' ' + expectedEventType;
+
+                detected_pointertypes[event.pointerType] = true;
+
+                test(function() {
+                    assert_equals(event.type, expectedEventType, "Event.type should be " + expectedEventType)
+                }, pointerTestName + "'s type should be " + expectedEventType);
+
+                // Test button and buttons
+                if (event.type == 'pointerdown') {
+                    test(function() {
+                        assert_true(event.button == 0, "Button attribute is 0")
+                    }, pointerTestName + "'s button attribute is 0 when left mouse button is pressed.");
+                    test(function() {
+                        assert_true(event.buttons == 1, "Buttons attribute is 1")
+                    }, pointerTestName + "'s buttons attribute is 1 when left mouse button is pressed.");
+                } else if (event.type == 'pointerup') {
+                    test(function() {
+                        assert_true(event.button == 0, "Button attribute is 0")
+                    }, pointerTestName + "'s button attribute is 0 when left mouse button is just released.");
+                    test(function() {
+                        assert_true(event.buttons == 0, "Buttons attribute is 0")
+                    }, pointerTestName + "'s buttons attribute is 0 when left mouse button is just released.");
+                } else {
+                    test(function() {
+                        assert_true(event.button == -1, "Button attribute is -1")
+                    }, pointerTestName + "'s button is -1 when mouse buttons are in released state.");
+                    test(function() {
+                        assert_true(event.buttons == 0, "Buttons attribute is 0")
+                    }, pointerTestName + "'s buttons is 0 when mouse buttons are in released state.");
+                }
+
+                // Test clientX and clientY
+                if (event.type != 'pointerout' && event.type != 'pointerleave' ) {
+                    test(function () {
+                        assert_true(event.clientX >= targetBoundingClientRect.left && event.clientX < targetBoundingClientRect.right && event.clientY >= targetBoundingClientRect.top && event.clientY < targetBoundingClientRect.bottom, "ClientX/Y should be in the boundaries of the box");
+                    }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+                } else {
+                    test(function () {
+                        assert_true(event.clientX < targetBoundingClientRect.left || event.clientX > targetBoundingClientRect.right - 1 || event.clientY < targetBoundingClientRect.top || event.clientY > targetBoundingClientRect.bottom - 1, "ClientX/Y should be out of the boundaries of the box");
+                    }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+                }
+
+                check_PointerEvent(event, testNamePrefix);
+
+                // Test isPrimary value
+                test(function () {
+                    assert_equals(event.isPrimary, true, "isPrimary should be true");
+                }, pointerTestName + ".isPrimary attribute is correct.");
+
+                // Test pointerId value
+                if (isNaN(expectedPointerId)) {
+                    expectedPointerId = event.pointerId;
+                } else {
+                    test(function () {
+                        assert_equals(event.pointerId, expectedPointerId, "pointerId should remain the same for the same active pointer");
+                    }, pointerTestName + ".pointerId should be the same as previous pointer events for this active pointer.");
+                }
+            }
+
+            function run() {
+                var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", HOVERABLE_POINTERS);
+                var square1 = document.getElementById("square1");
+                var rectSquare1 = square1.getBoundingClientRect();
+                var innerFrame = document.getElementById('innerFrame');
+                var square2 = innerFrame.contentDocument.getElementById('square2');
+                var rectSquare2 = square2.getBoundingClientRect();
+
+                eventList.forEach(function(eventName) {
+                    on_event(square1, eventName, function (event) {
+                        if (square1.style.visibility == 'hidden')
+                            return;
+                        checkPointerEventAttributes(event, rectSquare1, "");
+                        if (Object.keys(detected_eventTypes).length == eventList.length) {
+                            square1.style.visibility = 'hidden';
+                            detected_eventTypes = {};
+                            square2.style.visibility = 'visible';
+                            expectedPointerId = NaN;
+                        }
+                    });
+                    on_event(square2, eventName, function (event) {
+                        checkPointerEventAttributes(event, rectSquare2, "Inner frame ");
+                        if (Object.keys(detected_eventTypes).length == eventList.length) {
+                            square2.style.visibility = 'hidden';
+                            test_pointerEvent.done();
+                        }
+                    });
+                });
+            }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events hoverable pointer attributes test</h1>
+        <h2 id="pointerTypeDescription"></h2>
+        <h4>
+            Test Description: This test checks the properties of hoverable pointer events. If you are using hoverable pen don't leave the range of digitizer while doing the instructions.
+            <ol>
+                 <li>Move your pointer over the black square and click on it.</li>
+                 <li>Then move it off the black square so that it disappears.</li>
+                 <li>When red square appears move your pointer over the red square and click on it.</li>
+                 <li>Then move it off the red square.</li>
+            </ol>
+
+            Test passes if the proper behavior of the events is observed.
+        </h4>
+        <div id="square1" class="square"></div>
+        <iframe id="innerFrame" src="resources/pointerevent_attributes_hoverable_pointers-iframe.html"></iframe>
+        <div class="spacer"></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
+
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_attributes_mouse-manual.html
+++ /dev/null
@@ -1,113 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Events properties tests</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script>
-            var detected_pointertypes = {};
-            var detected_eventTypes = {};
-            var test_pointerEvent = async_test("pointerevent attributes");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var square1 = document.getElementById("square1");
-                var rectSquare1 = square1.getBoundingClientRect();
-                var pointerover_event;
-
-                var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
-                eventList.forEach(function(eventName) {
-                    on_event(square1, eventName, function (event) {
-                        if (detected_eventTypes[event.type])
-                            return;
-                        detected_pointertypes[event.pointerType] = true;
-                        test(function () {
-                            assert_equals(event.pointerType, 'mouse', 'pointerType should be mouse');
-                        }, event.type + ".pointerType attribute is correct.");
-
-                        // Test button and buttons
-                        if (event.type == 'pointerdown') {
-                            test(function() {
-                                assert_true(event.button == 0, "If left mouse button is pressed button attribute is 0")
-                            }, event.type + "'s button attribute is 0 when left mouse button is pressed.");
-                            test(function() {
-                                assert_true(event.buttons == 1, "If left mouse button is pressed buttons attribute is 1")
-                            }, event.type + "'s buttons attribute is 1 when left mouse button is pressed.");
-                        } else if (event.type == 'pointerup') {
-                            test(function() {
-                                assert_true(event.button == 0, "If left mouse button is just released button attribute is 0")
-                            }, event.type + "'s button attribute is 0 when left mouse button is just released.");
-                            test(function() {
-                                assert_true(event.buttons == 0, "If left mouse button is just released buttons attribute is 0")
-                            }, event.type + "'s buttons attribute is 0 when left mouse button is just released.");
-                        } else {
-                            test(function() {
-                                assert_true(event.button == -1, "If mouse buttons are released button attribute is -1")
-                            }, event.type + "'s button is -1 when mouse buttons are released.");
-                            test(function() {
-                                assert_true(event.buttons == 0, "If mouse buttons are released buttons attribute is 0")
-                            }, event.type + "'s buttons is 0 when mouse buttons are released.");
-                        }
-
-                        // Test clientX and clientY
-                        if (event.type != 'pointerout' && event.type != 'pointerleave' ) {
-                            test(function () {
-                                assert_true(event.clientX >= rectSquare1.left && event.clientX < rectSquare1.right, "ClientX should be in the boundaries of the black box");
-                            }, event.type + ".clientX attribute is correct.");
-                            test(function () {
-                              assert_true(event.clientY >= rectSquare1.top && event.clientY < rectSquare1.bottom, "ClientY should be in the boundaries of the black box");
-                            }, event.type + ".clientY attribute is correct.");
-                        } else {
-                            test(function () {
-                                assert_true(event.clientX < rectSquare1.left || event.clientX > rectSquare1.right - 1 || event.clientY < rectSquare1.top || event.clientY > rectSquare1.bottom - 1, "ClientX/Y should be out of the boundaries of the black box");
-                            }, event.type + "'s ClientX and ClientY attributes are correct.");
-                        }
-
-                        // Test isPrimary
-                        test(function () {
-                            assert_equals(event.isPrimary, true, "isPrimary should be true");
-                        }, event.type + ".isPrimary attribute is correct.");
-
-                        // Test width and height
-                        test(function () {
-                            assert_equals(event.width, 1, "width of mouse should be 1");
-                        }, event.type + ".width attribute is correct.");
-                        test(function () {
-                            assert_equals(event.height, 1, "height of mouse should be 1");
-                        }, event.type + ".height attribute is correct.");
-
-                        check_PointerEvent(event);
-                        detected_eventTypes[event.type] = true;
-                        if (Object.keys(detected_eventTypes).length == eventList.length)
-                            test_pointerEvent.done();
-                    });
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Events pointerdown tests</h1>
-        <!--
-        <h4>
-            Test Description: This test checks the properties of mouse pointer events. Move your mouse over the black square and click on it. Then move it off the black square.
-        </h4>
-        -->
-        Test passes if the proper behavior of the events is observed.
-        <div id="square1" class="square"></div>
-        <div class="spacer"></div>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
-
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/pointerevent_attributes_nohover_pointers-manual.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!-- script src="/resources/testharnessreport.js"></script -->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+            var detected_pointertypes = {};
+            var detected_eventTypes = {};
+            var eventList = ['pointerover', 'pointerenter', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
+            var expectedPointerId = NaN;
+
+            function resetTestState() {
+                detected_eventTypes = {};
+                document.getElementById("square1").style.visibility = 'visible';
+                document.getElementById('innerFrame').contentDocument.getElementById("square2").style.visibility = 'hidden';
+            }
+            function checkPointerEventAttributes(event, targetBoundingClientRect, testNamePrefix) {
+                if (detected_eventTypes[event.type])
+                    return;
+                var expectedEventType =  eventList[Object.keys(detected_eventTypes).length];
+                detected_eventTypes[event.type] = true;
+                var pointerTestName = testNamePrefix + ' ' + expectedPointerType + ' ' + expectedEventType;
+
+                detected_pointertypes[event.pointerType] = true;
+
+                test(function() {
+                    assert_equals(event.type, expectedEventType, "Event.type should be " + expectedEventType)
+                }, pointerTestName + "'s type should be " + expectedEventType);
+
+                // Test button and buttons
+                test(function() {
+                    assert_true(event.button == 0, "Button attribute is 0")
+                }, pointerTestName + "'s button attribute is 0 when left mouse button is pressed.");
+
+                if (event.type == 'pointerdown' || event.type == 'pointerover' || event.type == 'pointerenter') {
+                    test(function() {
+                        assert_true(event.buttons == 1, "Buttons attribute is 1")
+                    }, pointerTestName + "'s buttons attribute is 1 when left mouse button is pressed.");
+                } else {
+                    test(function() {
+                        assert_true(event.buttons == 0, "Buttons attribute is 0")
+                    }, pointerTestName + "'s buttons is 0 when mouse buttons are in released state.");
+                }
+
+                // Test clientX and clientY
+                test(function () {
+                    assert_true(event.clientX >= targetBoundingClientRect.left && event.clientX < targetBoundingClientRect.right && event.clientY >= targetBoundingClientRect.top && event.clientY < targetBoundingClientRect.bottom, "ClientX/Y should be in the boundaries of the box");
+                }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+
+                check_PointerEvent(event, testNamePrefix);
+
+                // Test isPrimary
+                test(function () {
+                    assert_equals(event.isPrimary, true, "isPrimary should be true");
+                }, pointerTestName + ".isPrimary attribute is correct.");
+
+                // Test pointerId value
+                if (isNaN(expectedPointerId)) {
+                    expectedPointerId = event.pointerId;
+                } else {
+                    test(function () {
+                        assert_equals(event.pointerId, expectedPointerId, "pointerId should remain the same for the same active pointer");
+                    }, pointerTestName + ".pointerId should be the same as previous pointer events for this active pointer.");
+                }
+            }
+
+            function run() {
+                var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", NOHOVER_POINTERS);
+                var square1 = document.getElementById("square1");
+                var rectSquare1 = square1.getBoundingClientRect();
+                var innerFrame = document.getElementById('innerFrame');
+                var square2 = innerFrame.contentDocument.getElementById('square2');
+                var rectSquare2 = square2.getBoundingClientRect();
+
+                eventList.forEach(function(eventName) {
+                    on_event(square1, eventName, function (event) {
+                        if (square1.style.visibility == 'hidden')
+                            return;
+                        checkPointerEventAttributes(event, rectSquare1, "");
+                        if (Object.keys(detected_eventTypes).length == eventList.length) {
+                            square1.style.visibility = 'hidden';
+                            detected_eventTypes = {};
+                            square2.style.visibility = 'visible';
+                            expectedPointerId = NaN;
+                        }
+                    });
+                    on_event(square2, eventName, function (event) {
+                        checkPointerEventAttributes(event, rectSquare2, "Inner frame ");
+                        if (Object.keys(detected_eventTypes).length == eventList.length) {
+                            square2.style.visibility = 'hidden';
+                            test_pointerEvent.done();
+                        }
+                    });
+                });
+            }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events no-hover pointer attributes test</h1>
+        <h2 id="pointerTypeDescription"></h2>
+        <h4>
+            Test Description: This test checks the properties of pointer events that do not support hover.
+            <ol>
+                 <li>Tap the black square.</li>
+                 <li>Then move it off the black square so that it disappears.</li>
+                 <li>When the red square appears tap on that as well.</li>
+            </ol>
+
+            Test passes if the proper behavior of the events is observed.
+        </h4>
+        <div id="square1" class="square"></div>
+        <iframe id="innerFrame" src="resources/pointerevent_attributes_hoverable_pointers-iframe.html"></iframe>
+        <div class="spacer"></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
+
--- a/dom/events/test/pointerevents/pointerevent_capture_suppressing_mouse-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_capture_suppressing_mouse-manual.html
@@ -15,132 +15,145 @@
         <h1>Pointer Events capture test</h1>
         <h4>
             Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
             <ol>
                 <li> Put your mouse over the black rectangle. pointerover and pointerenter should be logged inside of it.</li>
                 <li> Move your mouse out of the black rectangle. pointerout and pointerleave should be logged inside of it</li>
                 <li> Put your mouse over the purple rectangle. pointerover and pointerenter should be logged inside of it.</li>
                 <li> Move your mouse out of the purple rectangle. pointerout and pointerleave should be logged inside of it</li>
-                <li> Press and hold left mouse button over "Set Capture" button. "gotpointercapture" should be logged in the black rectangle</li>
+                <li> Press and hold left mouse button over "Set Capture" button and move mouse a litte inside the button. "gotpointercapture", "pointerover", and "pointerenter" should be logged in the black rectangle</li>
                 <li> Put your mouse over the purple rectangle and then move it out. Nothing should happen</li>
-                <li> Put your mouse over the black rectangle. pointerover and pointerenter should be logged inside of it.</li>
-                <li> Move your mouse out of the black rectangle. pointerout and pointerleave should be logged inside of it</li>
-                <li> Release left mouse button. "lostpointercapture" should be logged in the black rectangle</li>
+                <li> Put your mouse over the black rectangle and then move it out. Nothing should happen.</li>
+                <li> Put your mouse over the purple rectangle and then release left mouse button. "lostpointercapture" should be logged in the black rectangle. Move your mouse in the purple rectangle a little. "pointerout" and "pointerleave" should be logged in the black rectangle. Also "pointerover" and "pointerenter" should be logged in the purple rectangle"</li>
             </ol>
         </h4>
         Test passes if the proper behaviour of the events is observed.
         -->
         <div id="target0"></div>
         <br>
         <div id="target1"></div>
         <br>
         <input type="button" id="btnCapture" value="Set Capture">
         <script type='text/javascript'>
             var isPointerCapture = false;
             var isRelatedTargetValueTested = false;
             var isTargetAuthenticityTested = false;
+            var lostPointerCaptureReceived = false;
             var count = 0;
 
             var detected_pointertypes = {};
             add_completion_callback(showPointerTypes);
 
             var target0 = document.getElementById('target0');
             var target1 = document.getElementById('target1');
             var captureButton = document.getElementById('btnCapture');
 
             var test_gotpointercapture = async_test("gotpointercapture event received");
             var test_lostpointercapture = async_test("lostpointercapture event received");
 
             var test_pointerover_no_capture = async_test("pointerover event without capture received");
             var test_pointerover_capture = async_test("pointerover event with capture received");
 
             var test_pointerout_no_capture = async_test("pointerout event without capture received");
-            var test_pointerout_capture = async_test("pointerout event with capture received");
+            var test_pointerout_after_capture = async_test("pointerout event after lostpointercapture received");
 
             var test_pointerenter_no_capture = async_test("pointerenter event without capture received");
             var test_pointerenter_capture = async_test("pointerenter event with capture received");
 
             var test_pointerleave_no_capture = async_test("pointerleave event without capture received");
-            var test_pointerleave_capture = async_test("pointerleave event with capture received");
+            var test_pointerleave_after_capture = async_test("pointerleave event after lostpointercapture received");
 
             window.onload = function() {
                 on_event(captureButton, 'pointerdown', function(e) {
                     if(isPointerCapture == false) {
                         sPointerCapture(e);
                         isPointerCapture = true;
                     }
                 });
 
                 on_event(target0, 'gotpointercapture', function(e) {
                     test_gotpointercapture.done();
                     log("gotpointercapture", target0);
                 });
 
                 on_event(target0, 'lostpointercapture', function(e) {
                     isPointerCapture = false;
+                    lostPointerCaptureReceived = true;
                     test_lostpointercapture.done();
                     log("lostpointercapture", target0);
                 });
 
                 run();
             }
 
             function run() {
                 on_event(target0, "pointerover", function (event) {
                     detected_pointertypes[ event.pointerType ] = true;
                     log("pointerover", target0);
                     if(isPointerCapture) {
                         test_pointerover_capture.done();
                         if (!isRelatedTargetValueTested) {
                             test(function() {
-                                assert_true(event.relatedTarget==null, "relatedTarget is null when the capture is set")
-                            }, "relatedTarget is null when the capture is set. relatedTarget is " + event.relatedTarget);
+                                assert_not_equals(event.relatedTarget, null, "relatedTarget should behave the same as when the capture is not set")
+                            }, "relatedTarget is not null for boundary events even when the capture is set.");
                             isRelatedTargetValueTested = true;
                         }
                         var hitTest = document.elementFromPoint(event.clientX, event.clientY);
                         if(event.target !== hitTest && !isTargetAuthenticityTested) {
                             test(function () {
-                                assert_unreached("pointerover for this target shouldn't trigger events on capture target");
-                            }, "pointerover should only trigger over the black rectangle");
+                                assert_not_equals(event.target, hitTest, "pointerover should be fired on capture target even if the pointer it not over the capture target");
+                            }, "pointerover should trigger the black rectangle even when pointer is not over black rectangle.");
                             isTargetAuthenticityTested = true;
                         }
                     }
                     else {
                         test_pointerover_no_capture.done();
                     }
                 });
 
                 on_event(target0, "pointerout", function (event) {
                     log("pointerout", target0);
                     if(isPointerCapture) {
-                        test_pointerout_capture.done();
+                        test(function() {
+                            assert_unreached("pointerout shouldn't be sent to captured node as all the events are targeted at the capturing node");
+                        }, "pointerout shouldn't be sent to captured node as all the events are targeted at the capturing node.");
                     }
                     else {
-                        test_pointerout_no_capture.done();
+                        if (lostPointerCaptureReceived) {
+                            test_pointerout_after_capture.done();
+                        } else {
+                            test_pointerout_no_capture.done();
+                        }
                     }
                 });
 
                 on_event(target0, "pointerenter", function (event) {
                     log("pointerenter", target0);
                     if(isPointerCapture) {
                         test_pointerenter_capture.done();
                     }
                     else {
                         test_pointerenter_no_capture.done();
                     }
                 });
 
                 on_event(target0, "pointerleave", function (event) {
                     log("pointerleave", target0);
                     if(isPointerCapture) {
-                        test_pointerleave_capture.done();
+                        test(function() {
+                            assert_unreached("pointerleave shouldn't be sent to captured node as all the events are targeted at the capturing node");
+                        }, "pointerleave shouldn't be sent to captured node as all the events are targeted at the capturing node.");
                     }
                     else {
-                        test_pointerleave_no_capture.done();
+                        if (lostPointerCaptureReceived) {
+                            test_pointerleave_after_capture.done();
+                        } else {
+                            test_pointerleave_no_capture.done();
+                        }
                     }
                 });
 
                 // fail if capture is set but event is received for the non-captured target
                 on_event(target1, "pointerover", function (event) {
                     log("pointerover", target1);
                     if(isPointerCapture == true) {
                         test(function() {
@@ -178,9 +191,9 @@
             }
         </script>
         <h1>Pointer Events Capture Test</h1>
         <div id="complete-notice">
             <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
         </div>
         <div id="log"></div>
     </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/events/test/pointerevents/pointerevent_constructor.html
+++ b/dom/events/test/pointerevents/pointerevent_constructor.html
@@ -26,26 +26,30 @@
             var testPointerId = 42;
             var testPointerType = 'pen';
             var testClientX = 300;
             var testClientY = 500;
             var testWidth = 3;
             var testHeight = 5;
             var testTiltX = -45;
             var testTiltY = 30;
+            var testButton = 0;
+            var testButtons = 1;
             var testPressure = 0.4;
             var testIsPrimary = true;
 
             on_event(target0, "pointerover", this.step_func(function(event) {
                 detected_pointertypes[ event.pointerType ] = true;
                 generate_tests(assert_equals, [
                     ["custom bubbles", event.bubbles, testBubbles],
                     ["custom cancelable", event.cancelable, testCancelable],
                     ["custom pointerId", event.pointerId, testPointerId],
                     ["custom pointerType", event.pointerType, testPointerType],
+                    ["custom button", event.button, testButton],
+                    ["custom buttons", event.buttons, testButtons],
                     ["custom width", event.width, testWidth],
                     ["custom height", event.height, testHeight],
                     ["custom clientX", event.clientX, testClientX],
                     ["custom clientY", event.clientY, testClientY],
                     ["custom tiltX", event.tiltX, testTiltX],
                     ["custom tiltY", event.tiltY, testTiltY],
                     ["custom isPrimary", event.isPrimary, testIsPrimary]
                 ]);
@@ -76,16 +80,18 @@
                 pointerId: testPointerId,
                 pointerType: testPointerType,
                 width: testWidth,
                 height: testHeight,
                 clientX: testClientX,
                 clientY: testClientY,
                 tiltX: testTiltX,
                 tiltY: testTiltY,
+                button: testButton,
+                buttons: testButtons,
                 pressure: testPressure,
                 isPrimary: testIsPrimary
                 });
                 // A PointerEvent created with a PointerEvent constructor must have all its attributes set to the corresponding values provided to the constructor.
                 // For attributes where values are not provided to the constructor, the corresponding default values must be used.
                 // TA: 12.1
                 target0.dispatchEvent(pointerEventCustom);
                 var pointerEventDefault = new PointerEvent("pointerout");
--- a/dom/events/test/pointerevents/pointerevent_element_haspointercapture-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_element_haspointercapture-manual.html
@@ -56,30 +56,37 @@
                                       "after target0.releasePointerCapture, target1.hasPointerCapture should be false");
                     });
                     target0.setPointerCapture(e.pointerId);
                     set_capture_to_target0 = true;
                     test_pointerEvent.step(function () {
                         assert_equals(target0.hasPointerCapture(e.pointerId), true,
                                       "after target0.setPointerCapture, target0.hasPointerCapture should be true");
                     });
+                    // If the element.hasPointerCapture is false element.releasePointerCapture does nothing
+                    target1.releasePointerCapture(e.pointerId);
+                    test_pointerEvent.step(function () {
+                        assert_equals(target0.hasPointerCapture(e.pointerId), true,
+                                      "while target1.hasPointerCapture is false, target1.releasePointerCapture should not change hasPointerCapture");
+                    });
                 });
 
                 for (var i = 0; i < listening_events.length; i++) {
                     on_event(target0, listening_events[i], function (e) {
                         test_pointerEvent.step(function () {
                             assert_equals(target0.hasPointerCapture(e.pointerId), set_capture_to_target0,
                                           "Received " + e.type + " target0.hasPointerCapture should be " + set_capture_to_target0);
                         });
                     });
                 }
 
                 on_event(target0, "pointerup", function (e) {
-                    // Immediately after firing the pointerup or pointercancel events, a user agent must run the steps
-                    // as if the releasePointerCapture() method has been called
+                    // Immediately after firing the pointerup or pointercancel events, a user agent must clear
+                    // the pending pointer capture target override for the pointerId, and then run
+                    // "Process Pending Pointer Capture" steps to fire lostpointercapture if necessary.
                     test_pointerEvent.step(function () {
                         assert_equals(target0.hasPointerCapture(e.pointerId), true,
                                       "pointerup target0.hasPointerCapture should be true");
                     });
                     set_capture_to_target0 = false;
                 });
 
                 on_event(target0, "lostpointercapture", function (e) {
@@ -115,9 +122,9 @@
         -->
         <div id="target0" touch-action:none></div>
         <div id="target1" touch-action:none></div>
         <div id="complete-notice">
             <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
         </div>
         <div id="log"></div>
     </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/events/test/pointerevents/pointerevent_lostpointercapture_is_first-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_lostpointercapture_is_first-manual.html
@@ -13,29 +13,32 @@
     <body onload="run()">
         <h1>Pointer Events capture test - lostpointercapture order</h1>
         <!--
         <h4>
             Test Description:
             This test checks if lostpointercapture is handled asynchronously and prior to all subsequent events.
             Complete the following actions:
             <ol>
-                <li>Press and hold left mouse button over "Set Capture" button. "gotpointercapture" should be logged inside of the black rectangle
+                <li>Press and hold left mouse button over "Set Capture" button and move a little. "gotpointercapture" should be logged inside of the black rectangle
                 <li>"lostpointercapture" should be logged inside of the black rectangle after pointerup
             </ol>
         </h4>
         Test passes if lostpointercapture is dispatched after releasing the mouse button and before any additional pointer events.
         -->
         <div id="target0" style="background:black; color:white"></div>
         <br>
         <input type="button" id="btnCapture" value="Set Capture">
         <script type='text/javascript'>
             var detected_pointertypes = {};
             var detected_pointerEvents = new Array();
             var pointerdown_event = null;
+            var firstPointermoveReceived = false; // To handle the first pointermove right after gotpointercapture which gotpointercapture was sent right before it.
+            var firstPointeroverReceived = false; // To handle the first pointerover right after gotpointercapture which gotpointercapture was sent right before it.
+            var firstPointerenterReceived = false; // To handle the first pointeenter right after gotpointercapture which gotpointercapture was sent right before it.
 
             var test_pointerEvent = async_test("lostpointercapture is dispatched prior to subsequent events"); // set up test harness
 
             var isPointerCapture = false;
             var count=0;
 
             var testStarted = false;
             var eventRcvd = false;
@@ -95,24 +98,35 @@
                             }
                             test_pointerEvent.step(function () {
                                 assert_false(eventRcvd, "no other events should be received before lostpointercapture." + eventsRcvd_str);
                                 assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerID is same for pointerdown and lostpointercapture");
                             });
                             test_pointerEvent.done(); // complete test
                         }
                         else {
-                            if (testStarted && pointerdown_event != null && pointerdown_event.pointerId === event.pointerId && event.type != "pointerup") {
-                                detected_pointerEvents.push(event.type);
-                                eventRcvd = true;
+                            if (testStarted && pointerdown_event != null && pointerdown_event.pointerId === event.pointerId) {
+                                if (event.type == "pointermove" && !firstPointermoveReceived) {
+                                  firstPointermoveReceived = true;
+                                }
+                                else if (event.type == "pointerenter" && !firstPointerenterReceived) {
+                                  firstPointerenterReceived = true;
+                                }
+                                else if (event.type == "pointerover" && !firstPointeroverReceived) {
+                                  firstPointeroverReceived = true;
+                                }
+                                else if (event.type != "pointerup") {
+                                    detected_pointerEvents.push(event.type);
+                                    eventRcvd = true;
+                                }
                             }
                         }
                     });
                 }
             }
         </script>
         <h1>Pointer Events Capture Test</h1>
         <div id="complete-notice">
             <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
         </div>
         <div id="log"></div>
     </body>
-</html>
\ No newline at end of file
+</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerdown-manual.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!doctype html>
-<html>
-    <!--
-Test cases for Pointer Events v1 spec
-This document references Test Assertions (abbrev TA below) written by Cathy Chan
-http://www.w3.org/wiki/PointerEvents/TestAssertions
--->
-    <head>
-        <title>Pointer Events pointerdown tests</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script>
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerdown event received");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-
-            function run() {
-                var target0 = document.getElementById("target0");
-                var pointerover_event;
-
-                on_event(target0, "pointerover", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    pointerover_event = event;
-                    check_PointerEvent(event);
-                });
-
-                on_event(target0, "pointerdown", function (event) {
-                    check_PointerEvent(event);
-
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.pointerType, pointerover_event.pointerType, "pointerType is same for pointerover and pointerdown");
-                        assert_equals(event.isPrimary, pointerover_event.isPrimary, "isPrimary is same for pointerover and pointerdown");
-                    });
-
-                    test_pointerEvent.done();
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Events pointerdown tests</h1>
-        <div id="target0">
-            Start with your pointing device outside of this box, then click here.
-        </div>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerenter-manual.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: Dispatch pointerenter. </title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="When a pointing device is moved into the hit test boundaries of an element or one of its descendants, the pointerenter event must be dispatched."/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerenter event"); // set up test harness
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerenter", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    check_PointerEvent(event);
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.type, "pointerenter", "pointer event received: " + event.type);
-                    });
-                    test_pointerEvent.done(); // complete test
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointerenter</h1>
-        <h4>
-            Test Description:
-            When a pointing device is moved into the hit test boundaries of an element or one of its descendants, the pointerenter event must be dispatched.
-        </h4>
-        <div id="target0">
-            Use the mouse or pen to move over this box.
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerenter_nohover-manual.html
+++ /dev/null
@@ -1,77 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: Dispatch pointerenter. (nohover)</title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="When a pointing device that does not support hover is moved into the hit test boundaries of an element
-or one of its descendants as a result of a pointerdown event, the pointerenter event must be dispatched. "/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerenter event"); // set up test harness
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            var test_pointerEnter;
-            var f_pointerenter_rcvd = false;
-            var pointerenter_event;
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerdown", function (event) {
-                    if(event.pointerType == 'touch') {
-                        check_PointerEvent(event);
-                        test_pointerEvent.step(function () {
-                            assert_equals(event.type, "pointerdown", "pointer event received: " + event.type);
-                            assert_true(f_pointerenter_rcvd, "pointerenter event should have been received before pointerdown");
-                            assert_equals(event.pointerType, pointerenter_event.pointerType, "pointerType is same for pointerenter and pointerdown");
-                            assert_equals(event.isPrimary, pointerenter_event.isPrimary, "isPrimary is same for pointerenter and pointerdown");
-                        });
-                        test_pointerEvent.done(); // complete test
-                    }
-                });
-
-                on_event(target0, "pointerenter", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    if(event.pointerType == 'touch') {
-                        pointerenter_event = event;
-                        check_PointerEvent(event);
-                        test_pointerEvent.step(function () {
-                            assert_equals(event.type, "pointerenter", "pointer event received: " + event.type);
-                        });
-                        f_pointerenter_rcvd = true;
-                    }
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointerenter (nohover)</h1>
-        <!--
-        <h4>
-            Test Description:
-            When a pointing device that does not support hover is moved into the hit test boundaries of an element or one of its
-            descendants as a result of a pointerdown event, the pointerenter event must be dispatched.
-        </h4>
-        <br />
-        -->
-        <div id="target0">
-            Tap here.
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerleave_after_pointerup_nohover-manual.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>pointerleave after pointerup</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-    </head>
-    <body onload="run()">
-        <h2>pointerleave after pointerup</h2>
-        <h4>Test Description: This test checks if pointerleave event triggers for devices that don't support hover. Tap the black rectangle. </h4>
-        <p>Note: this test is only for devices that do not support hover.</p>
-        <div id="target0"></div>
-        <script>
-            var test_pointerleave = async_test("pointerleave event received");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            var eventTested = false;
-            var isPointerupReceived = false;
-            var pointerup_event = null;
-            var detected_pointertypes = {};
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerup", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    pointerup_event = event;
-                });
-
-                // For input devices that do not support hover, a pointerleave event must follow the pointerup event.
-                // TA: 3.6
-                on_event(target0, "pointerleave", function (event) {
-                    if(event.pointerType == 'touch') {
-                        if(pointerup_event != null) {
-                            if(eventTested == false) {
-                                eventTested = true;
-                                test_pointerleave.step(function() {
-                                    assert_equals(event.pointerType, pointerup_event.pointerType, "pointerType is same for pointerup and pointerleave");
-                                    assert_equals(event.isPrimary, pointerup_event.isPrimary, "isPrimary is same for pointerup and pointerleave");
-                                });
-                                test_pointerleave.done();
-                            }
-                        }
-                        else {
-                            test_pointerleave.step(function() {
-                                assert_unreached("pointerleave received before pointerup");
-                            }, "pointerleave received before pointerup");
-                        }
-                    }
-                });
-            }
-
-        </script>
-        <h1>Pointer Events pointerleave tests</h1>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerleave_mouse-manual.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: Dispatch pointerleave (mouse). </title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="When a pointing device that has continuous position (such as a mouse) leaves the hit test boundaries of an element, the pointerleave event must be dispatched."/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerleave event"); // set up test harness;
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerleave", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    check_PointerEvent(event);
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.pointerType, "mouse", "Test should be run using a mouse as input.");
-                        assert_equals(event.type, "pointerleave", "The " + event.type + " event was received.");
-                    });
-                    test_pointerEvent.done(); // complete test
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointerleave (mouse)</h1>
-        <!--
-        <h4>
-            Test Description:
-            When a pointing device that has continuous position (such as a mouse) leaves the hit test boundaries of an element, the pointerleave event must be dispatched.
-        </h4>
-        <br />
-        -->
-        <div id="target0">
-            Use a mouse to move over then out of this element
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerleave_touch-manual.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: Dispatch pointerleave (touch). </title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="When a pointing device that does not support hover (such as a finger) leaves the hit test boundaries as a result of a pointerup event, the pointerleave event must be dispatched. "/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerleave event");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerleave", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    check_PointerEvent(event);
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.pointerType, "touch", "Test should be run using touch input");
-                        assert_equals(event.type, "pointerleave", "The " + event.type + " event received");
-                    });
-                    test_pointerEvent.done(); // complete test
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointerleave (touch)</h1>
-        <h4>
-            Test Description:
-            When a pointing device that does not support hover (such as a finger) leaves the hit test boundaries as a result of a pointerup event, the pointerleave event must be dispatched.
-        </h4>
-        <br />
-        <div id="target0">
-            Use touch to tap on this box.
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
rename from dom/events/test/pointerevents/pointerevent_pointermove-on-chorded-mouse-button.html
rename to dom/events/test/pointerevents/pointerevent_pointermove_on_chorded_mouse_button-manual.html
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointermove_pointertype-manual.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>pointerType conservation</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
-    </head>
-    <body onload="run()">
-        <!--
-        <h1>pointerType conservation</h1>
-        <h4>Test Description: This test checks if pointerType attribute defined properly.</h4>
-        <div id="instructions">
-            Press and move a mouse button, touch contact or pen contact on the black rectangle. Only use one device per test run.
-        </div>
-        <p>Note: This test may be run with different pointer devices, however only one device should be used per test run.
-        <p>
-        -->
-        <div id="target0"></div>
-        <script>
-            var eventTested = false;
-            var pointerTypeGot = false;
-            var pointerdown_event;
-            var detected_pointertypes = {};
-
-            setup({ explicit_done: true });
-
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerover", function(event) {
-                    detected_pointertypes[ event.pointerType ] = true;
-                });
-
-                // The pointerType attribute of a pointermove event must have the same value as the pointerType attribute of the last pointerdown event with the same pointerId attribute.
-                // TA: 5.1
-                on_event(target0, "pointerdown", function (event) {
-                    pointerdown_event = event;
-                    pointerTypeGot = true;
-                });
-
-                on_event(target0, "pointermove", function (event) {
-                    if(pointerTypeGot == true) {
-                        if(!eventTested) {
-                            test(function() {
-                                assert_equals(event.pointerId, pointerdown_event.pointerId, "pointer IDs are equal: ");
-                                assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType of pointermove event matches pointerdown event: ");
-                            }, "pointerType is dispatched properly");
-                        }
-                        done();
-                    }
-                });
-            }
-        </script>
-        <h1>Pointer Events pointerType conservation tests</h1>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerout-manual.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>pointerout</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-    </head>
-    <body onload="run()">
-        <h2>pointerout</h2>
-        <h4>Test Description: This test checks if pointerout event triggers. Put your mouse over the black rectangle and then move it out of the rectangle boundaries. If you are using touchscreen tap the black rectangle. </h4>
-        <div id="target0" style="background: black"></div>
-        <script>
-            var test_pointerEvent = async_test("pointerout event received");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            var eventTested = false;
-            var detected_pointertypes = {};
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerout", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    if (eventTested == false) {
-                        eventTested = true;
-                        check_PointerEvent(event);
-                        test_pointerEvent.done();
-                    }
-                });
-            }
-        </script>
-        <h1>Pointer Events pointerout tests</h1>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerout_after_pointerup_nohover-manual.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>pointerout</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-    </head>
-    <body onload="run()">
-        <h2>pointerout</h2>
-        <h4>Test Description: This test checks if pointerout event triggers for devices that don't support hover. Tap the black rectangle. </h4>
-        <p>Note: this test is only for devices that do not support hover.</p>
-        <div id="target0"></div>
-        <script>
-            var test_pointerout = async_test("pointerout event received");
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            var eventTested = false;
-            var pointerup_event = null;
-            var detected_pointertypes = {};
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerup", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    pointerup_event = event;
-                });
-
-                // For input devices that do not support hover, a pointerout event must follow the pointerup event.
-                // TA: 3.6
-                on_event(target0, "pointerout", function (event) {
-                    if(event.pointerType == 'touch') {
-                        if(pointerup_event != null) {
-                            if(eventTested == false) {
-                                test_pointerout.step(function() {
-                                    assert_equals(event.pointerType, pointerup_event.pointerType, "pointerType is same for pointerup and pointerout");
-                                    assert_equals(event.isPrimary, pointerup_event.isPrimary, "isPrimary is same for pointerup and pointerout");
-                                });
-                                eventTested = true;
-                                test_pointerout.done();
-                            }
-                        }
-                        else {
-                            test_pointerout.step(function() {
-                                assert_true(false,
-                                "pointerup received before pointerout");
-                            }, "pointerup received before pointerout");
-                        }
-                    }
-                });
-            }
-
-        </script>
-        <h1>Pointer Events pointerout tests</h1>
-        <div id="complete-notice">
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerover-manual.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: Dispatch pointerover. </title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="When a pointing device is moved into the hit test boundaries of an element, the pointerover event must be dispatched. "/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerover is dispatched"); // set up test harness;
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerover", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    test_pointerEvent.step(function () {
-                        check_PointerEvent(event);
-                        assert_equals(event.type, "pointerover", "Pointer Event received");
-                    });
-                    test_pointerEvent.done(); // complete test
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointerover.</h1>
-        <h4>Test Description:
-            When a pointing device is moved into the hit test boundaries of an element, the pointerover event must be dispatched.
-        </h4>
-        <br />
-        <div id="target0">
-            Use mouse, touch or pen to hover or contact this element..
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-            <p>Refresh the page to run the tests again with a different pointer type.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointertype_mouse-manual.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: If a pointer event is initiated by a mouse device, then the pointerType must be "mouse"</title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="If a pointer event is initiated by a mouse device, then the pointerType must be 'mouse'."/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointer event have pointerType as mouse"); // set up test harness
-            var eventTested = false;
-
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function eventHandler(event) {
-                detected_pointertypes[event.pointerType] = true;
-                if(!eventTested && event.type == "pointerover") {
-                    check_PointerEvent(event);
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.pointerType, "mouse", "Verify event.pointerType is 'mouse'.");
-                    });
-                    eventTested = true;
-                }
-                if (event.type == "pointerup") {
-                    test_pointerEvent.done(); // complete test
-                }
-            }
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                // listen for all events.
-                for (var i = 0; i < All_Pointer_Events.length; i++) {
-                    on_event(target0, All_Pointer_Events[i], eventHandler);
-                }
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: Dispatch pointer events with pointerType equal to "mouse"</h1>
-        <!--
-        <h4>Test Description:
-            If a pointer event is initiated by a mouse device, then the pointerType must be 'mouse'.
-        </h4>
-        <br />
-        -->
-        <div id="target0">
-            Using the mouse, click this element.
-        </div>
-        <div id="complete-notice">
-            <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
-            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
-        </div>
-        <div id="log"></div>
-    </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointertype_pen-manual.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: If a pointer event is initiated by a pen device, then the pointerType must be "pen"</title>
-        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-        <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
-        <meta name="assert" content="If a pointer event is initiated by a pen device, then the pointerType must be 'pen'."/>
-        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
-        <!-- /resources/testharness.js -->
-        <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
-        <!-- Additional helper script for common checks across event types -->
-        <script type="text/javascript" src="pointerevent_support.js"></script>
-        <script type="text/javascript" src="mochitest_support_internal.js"></script>
-        <script type="text/javascript">
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointer event has pointerType as pen"); // set up test harness
-            var eventTested = false;
-            // showPointerTypes is defined in pointerevent_support.js
-            // Requirements: the callback function will reference the test_pointerEvent object and
-            // will fail unless the async_test is created with the var name "test_pointerEvent".
-            add_completion_callback(showPointerTypes);
-
-            function eventHandler(event) {
-                detected_pointertypes[event.pointerType] = true;
-                if(!eventTested) {
-                    check_PointerEvent(event);
-                    test_pointerEvent.step(function () {
-                        assert_equals(event.pointerType, "pen", "Verify event.pointerType is 'pen'.");
-                    });
-                    eventTested = true;
-                }
-                if (event.type == "pointerup") {
-                    test_pointerEvent.done(); // complete test
-                }
-            }
-
-            function run() {
-                var target0 = document.getElementById("target0");
-                // listen for all events.
-                for (var i = 0; i