Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 17 Dec 2016 14:00:48 -0800
changeset 450776 5745bab28ff5e85128c774b56b4cc27c2afe2e1b
parent 450775 881ef8c0e2322c6117aeaee8f07fbf4d0f22cd95 (current diff)
parent 450706 ebcba38b2a825191b0f148bb65c436ff3bb78f8b (diff)
child 450777 461cde474a975489d9b1e411a0fab6c3e4e53bec
child 450783 067773698619552a6836ef373bd124c59b69c957
child 450791 a2ebad7c8162cc2fda22f104bf498159244a7f7c
child 450862 e25a580eb7e8df965ed269293c37717f75c18636
child 451184 d11b3d124532770b9a9532c19d904b45e70b9dbf
child 458111 b8957416bbd474d6e820d3d263f8be98267cab71
push id38944
push userbmo:leftysolara@gmail.com
push dateSun, 18 Dec 2016 16:01:15 +0000
reviewersmerge
milestone53.0a1
Merge m-i to m-c, a=merge MozReview-Commit-ID: 3TWj1elRxgV
browser/app/profile/firefox.js
browser/base/content/browser.js
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/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
--- 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.
--- 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);
--- 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/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/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/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/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/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/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -708,17 +708,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 +726,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();
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -178,17 +178,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);
--- 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/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1621,16 +1621,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
@@ -1989,16 +1990,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
 
@@ -2067,16 +2069,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
@@ -14266,17 +14269,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
@@ -923,16 +923,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);
@@ -1796,16 +1799,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/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/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6945,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.
 
@@ -7027,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([])
 
@@ -11594,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 = ""
 
@@ -11946,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) {
@@ -11972,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/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/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 < 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 "pen"</h1>
-        <!--
-        <h4>Test Description:
-            If a pointer event is initiated by a pen device, then the pointerType must be 'pen'.
-        </h4>
-        <br />
-        -->
-        <div id="target0">
-            Using pen, 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>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointertype_touch-manual.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: If a pointer event is initiated by a touch device, then the pointerType must be "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="If a pointer event is initiated by a touch device, then the pointerType must be 'touch'."/>
-        <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 touch"); // 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, "touch", "Verify event.pointerType is 'touch'.");
-                    });
-                    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 "touch"</h1>
-        <!--
-        <h4>Test Description:
-            If a pointer event is initiated by a touch device, then the pointerType must be 'touch'.
-        </h4>
-        <br />
-        -->
-        <div id="target0">
-            Using touch, 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>
\ No newline at end of file
deleted file mode 100644
--- a/dom/events/test/pointerevents/pointerevent_pointerup-manual.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>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()">
-        <h1>pointerup</h1>
-        <h4>Test Description: This test checks if pointerup event triggers. Press mouse left button and release it over the black rectangle or tap it if you are using a touchscreen.</h4>
-        <div id="target0" style="background:black"></div>
-        <script>
-            var detected_pointertypes = {};
-            var test_pointerEvent = async_test("pointerup 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");
-
-                on_event(target0, "pointerup", function (event) {
-                    detected_pointertypes[event.pointerType] = true;
-                    test_pointerEvent.step(function () {
-                        check_PointerEvent(event);
-                        assert_equals(event.type, "pointerup", "Pointer Event received.");
-                    });
-                    test_pointerEvent.done();
-                });
-            }
-        </script>
-        <h1>Pointer Events pointerup 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_pointerup_isprimary_same_as_pointerdown-manual.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Event: pointerup has same isPrimary as last pointerdown with the same pointerId</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="The isPrimary attribute of a pointerup event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute."/>
-        <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("pointerup has same isPrimary as last pointerdown"); // 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 pointerup_event = null;
-            var pointerdown_event = null;
-
-            function run() {
-                var target0 = document.getElementById("target0");
-
-                on_event(target0, "pointerup", function (event) {
-                    if (pointerdown_event != null) {
-                        test_pointerEvent.step(function () {
-                            assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerup.pointerId should be the same as pointerdown.pointerId.");
-                            assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointerup.isPrimary should be the same as pointerdown.isPrimary.");
-                        });
-                        pointerup_event = event;
-                        test_pointerEvent.done(); // complete test
-                    }
-                });
-
-                on_event(target0, "pointerdown", function (event) {
-                    pointerdown_event = event;
-                    detected_pointertypes[event.pointerType] = true;
-                });
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Event: pointerup has the same isPrimary as last pointerdown with the same pointerId</h1>
-        <!--
-        <h4>Test Description:
-            The isPrimary attribute of a pointerup event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute.
-        </h4>
-        <div id="instructions">
-            Press and release a mouse button, touch contact or pen contact on this element. Only use one device per test run.
-        </div>
-        -->
-        <div id="target0" style="touch-action: none;">
-        </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_pointerup_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 release 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 pointerup event must have the same value as the pointerType attribute of the last pointerdown event with the same pointerId attribute.
-                // TA: 3.1
-                on_event(target0, "pointerdown", function (event) {
-                    pointerdown_event = event;
-                    pointerTypeGot = true;
-                });
-
-                on_event(target0, "pointerup", 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 pointerup 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
--- a/dom/events/test/pointerevents/pointerevent_releasepointercapture_events_to_original_target-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_releasepointercapture_events_to_original_target-manual.html
@@ -9,80 +9,95 @@
         <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("lostpointercapture: subsequent events to target.");  // set up test harness
-            var suppressedEventsFail = 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);
 
             var captured_event;
+            var test_done = false;
+            var overEnterEventsFail = false;
+            var outLeaveEventsFail = false;
             var f_gotPointerCapture = false;
             var f_lostPointerCapture = false;
 
             function listenerEventHandler(event) {
+                if (test_done)
+                    return;
                 detected_pointertypes[event.pointerType] = true;
                 if (event.type == "gotpointercapture") {
                     f_gotPointerCapture = true;
                     check_PointerEvent(event);
                 }
                 else if (event.type == "lostpointercapture") {
                     f_lostPointerCapture = true;
                     f_gotPointerCapture = false;
                     check_PointerEvent(event);
                 }
-                else if(event.type == "pointerover" || event.type == "pointerenter" || event.type == "pointerout" || event.type == "pointerleave") {
-                    if(!suppressedEventsFail) {
+                else if(event.type == "pointerover" || event.type == "pointerenter") {
+                    if(!overEnterEventsFail) {
                         test(function() {
-                            assert_true(false, "Suppressed events were received");
-                        }, "Suppressed events were received");
-                        suppressedEventsFail = true;
+                            assert_true(f_gotPointerCapture, "pointerover/enter should not be received when the target doesn't have capture and pointer is not over it.");
+                        }, "pointerover/enter should not be received when the target doesn't have capture and pointer is not over it.");
+                        overEnterEventsFail = true;
+                    }
+                }
+                else if(event.type == "pointerout" || event.type == "pointerleave") {
+                    if(!outLeaveEventsFail) {
+                        test(function() {
+                            assert_true(f_lostPointerCapture, "pointerout/leave should not be received unless the target just lost the capture.");
+                        }, "pointerout/leave should not be received unless the target just lost the capture.");
+                        outLeaveEventsFail = true;
                     }
                 }
                 else if (event.pointerId == captured_event.pointerId) {
                     if (f_gotPointerCapture && event.type == "pointermove") {
                         // on first event received for capture, release capture
                         listener.releasePointerCapture(event.pointerId);
                     }
                     else {
                         // if any other events are received after releaseCapture, then the test fails
-                        test_pointerEvent.step(function () {
-                            assert_true(false, event.target.id + "-" + event.type + " should be handled by target element handler");
-                        });
+                        test(function () {
+                            assert_unreached(event.target.id + "-" + event.type + " should be handled by target element handler");
+                        }, "No other events should be recieved by capturing node after release");
                     }
                 }
             }
 
             function targetEventHandler(event) {
+                if (test_done)
+                    return;
                 if (f_gotPointerCapture) {
                     if(event.type != "pointerout" && event.type != "pointerleave") {
-                        test_pointerEvent.step(function () {
-                            assert_true(false, "The Target element should not have received any events while capture is active. Event recieved:" + event.type + ".  ");
-                        });
+                        test(function () {
+                            assert_unreached("The Target element should not have received any events while capture is active. Event recieved:" + event.type + ".  ");
+                        }, "The target element should not receive any events while capture is active");
                     }
                 }
 
                 if (event.type == "pointerdown") {
                     // pointerdown event received will be used to capture events.
                     listener.setPointerCapture(event.pointerId);
                     captured_event = event;
                 }
 
                 if (f_lostPointerCapture) {
                     test_pointerEvent.step(function () {
                         assert_equals(event.pointerId, captured_event.pointerId, "pointerID is same for event captured and after release");
                     });
                     if (event.type == "pointerup") {
                         test_pointerEvent.done(); // complete test
+                        test_done = true;
                     }
                 }
             }
 
             function run() {
                 var listener = document.getElementById("listener");
                 var target0 = document.getElementById("target0");
                 target0.style["touchAction"] = "none";
@@ -111,9 +126,9 @@
         </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
+</html>
--- a/dom/events/test/pointerevents/pointerevent_setpointercapture_relatedtarget-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_setpointercapture_relatedtarget-manual.html
@@ -10,19 +10,19 @@
         <script src="mochitest_support_internal.js"></script>
     </head>
     <body>
         <!--
         <h1>Pointer Events Capture Test - capture and relatedTarget</h1>
         <h4>
             Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
             <ol>
-                <li> Put your mouse over the lower rectangle. pointerover should be received for the purple rectangle
+                <li> Put your mouse over the purple rectangle. pointerover should be received for the purple rectangle
                 <li> Press and hold left mouse button over "Set Capture" button
-                <li> Put your mouse over the upper rectangle. pointerover should be received for the black rectangle
+                <li> Move your mouse. pointerover should be received for the black rectangle
                 <li> Release left mouse button to complete the test.
             </ol>
         </h4>
         Test passes if the proper behavior of the events is observed.
         -->
         <div id="target0" style="background:black; color:white"></div>
         <br>
         <div id="target1" style="background:purple; color:white"></div>
@@ -62,25 +62,24 @@
                     log("lostpointercapture", document.getElementById('target0'));
                     isPointerCapture = false;
                 });
 
                 run();
             }
 
             function run() {
-                // After invoking the setPointerCapture method on an element, subsequent pointer events for the specified pointer must be targeted at that element.
-                // Additionally, the relatedTarget property of all such pointer events must be set to null.
-                // TA: 13.3
+                // After invoking the setPointerCapture method on an element, subsequent pointer events for the specified pointer must be targeted at that element
+                // and boundary events should be sent accordingly and relatedTarget should behave normally.
                 on_event(target0, "pointerover", function (event) {
                     log("pointerover", document.getElementById('target0'));
                     if(isPointerCapture && isPointeroverGot) {
                         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 not be null even when the capture is set")
+                        }, "relatedTarget should not be null even when the capture is set.");
                         done();
                     }
                 });
 
                 on_event(target1, "pointerover", function (event) {
                     detected_pointertypes[ event.pointerType ] = true;
                     if(!isPointeroverGot) {
                         test(function() {
@@ -95,9 +94,9 @@
         <h1>Pointer Events Capture Test</h1>
         <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
+</html>
--- a/dom/events/test/pointerevents/pointerevent_styles.css
+++ b/dom/events/test/pointerevents/pointerevent_styles.css
@@ -1,16 +1,31 @@
+#innerFrame {
+position: absolute;
+top: 300px;
+left: 200px;
+height: 100px;
+width: 100px;
+}
+
 .spacer {
 height: 100px;
 }
 
 #square1 {
+top: 330px;
+left: 150px;
 background: black;
-top: 150px;
-left: 100px;
+}
+
+#square2 {
+top: 50px;
+left: 30px;
+visibility: hidden;
+background: red;
 }
 
 .square {
 height: 20px;
 width: 20px;
 position: absolute;
 padding: 0px;
 }
@@ -26,16 +41,20 @@ overflow-x: auto;
 #target1 {
 background: purple;
 color: white;
 white-space: nowrap;
 overflow-y: auto;
 overflow-x: auto;
 }
 
+#scrollTarget {
+  background: darkblue;
+}
+
 .touchActionNone {
 touch-action: none;
 }
 
 #innerframe {
 width: 90%;
 margin: 10px;
 margin-left: 10%;
--- a/dom/events/test/pointerevents/pointerevent_support.js
+++ b/dom/events/test/pointerevents/pointerevent_support.js
@@ -7,20 +7,31 @@ var All_Pointer_Events = [
         "pointerout",
         "pointerenter",
         "pointerleave",
         "gotpointercapture",
         "lostpointercapture"];
 
 // Check for conformance to PointerEvent interface
 // TA: 1.1, 1.2, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13
-function check_PointerEvent(event) {
-    var pointerTestName = event.pointerType + ' ' + event.type;
+function check_PointerEvent(event, testNamePrefix) {
+    if (testNamePrefix === undefined)
+        testNamePrefix = "";
+
+    // Use expectedPointerType if set otherwise just use the incoming event pointerType in the test name.
+    var pointerTestName = testNamePrefix + ' ' + (expectedPointerType == null ? event.pointerType : expectedPointerType) + ' ' + event.type;
+
+    if (expectedPointerType != null) {
+        test(function () {
+            assert_equals(event.pointerType, expectedPointerType, "pointerType should be the same as the requested device.");
+        }, pointerTestName + " event pointerType is correct.");
+    }
+
     test(function () {
-        assert_true(event instanceof PointerEvent, "event is a PointerEvent event");
+        assert_true(event instanceof event.target.ownerDocument.defaultView.PointerEvent, "event is a PointerEvent event");
     }, pointerTestName + " event is a PointerEvent event");
 
 
     // Check attributes for conformance to WebIDL:
     // * attribute exists
     // * has proper type
     // * if the attribute is "readonly", it cannot be changed
     // TA: 1.1, 1.2
@@ -74,32 +85,36 @@ function check_PointerEvent(event) {
 
     // Check the pressure value
     // TA: 1.6, 1.7, 1.8
     test(function () {
         // TA: 1.6
         assert_greater_than_equal(event.pressure, 0, "pressure is greater than or equal to 0");
         assert_less_than_equal(event.pressure, 1, "pressure is less than or equal to 1");
 
+        if (event.type === "pointerup") {
+            assert_equals(event.pressure, 0, "pressure is 0 during pointerup");
+        }
 
         // TA: 1.7, 1.8
         if (event.pointerType === "mouse") {
             if (event.buttons === 0) {
                 assert_equals(event.pressure, 0, "pressure is 0 for mouse with no buttons pressed");
             } else {
                 assert_equals(event.pressure, 0.5, "pressure is 0.5 for mouse with a button pressed");
             }
         }
     }, pointerTestName + ".pressure value is valid");
 
-
     // Check mouse-specific properties
     if (event.pointerType === "mouse") {
         // TA: 1.9, 1.10, 1.13
         test(function () {
+            assert_equals(event.width, 1, "width of mouse should be 1");
+            assert_equals(event.height, 1, "height of mouse should be 1");
             assert_equals(event.tiltX, 0, event.type + ".tiltX is 0 for mouse");
             assert_equals(event.tiltY, 0, event.type + ".tiltY is 0 for mouse");
             assert_true(event.isPrimary, event.type + ".isPrimary is true for mouse");
         }, pointerTestName + " properties for pointerType = mouse");
         // Check properties for pointers other than mouse
     }
 }
 
@@ -178,8 +193,52 @@ function sPointerCapture(e) {
 function rPointerCapture(e) {
     try {
         captureButton.value = 'Set Capture';
         target0.releasePointerCapture(e.pointerId);
     }
     catch(e) {
     }
 }
+
+var globalPointerEventTest = null;
+var expectedPointerType = null;
+const HOVERABLE_POINTERS = ['mouse', 'pen'];
+const NOHOVER_POINTERS = ['touch'];
+
+function MultiPointerTypeTest(testName, types) {
+    this.testName = testName;
+    this.types = types;
+    this.currentTypeIndex = 0;
+    this.currentTest = null;
+    this.createNextTest();
+}
+
+MultiPointerTypeTest.prototype.skip = function() {
+    var prevTest = this.currentTest;
+    this.createNextTest();
+    prevTest.timeout();
+}
+
+MultiPointerTypeTest.prototype.done = function() {
+    var prevTest = this.currentTest;
+    this.createNextTest();
+    if (prevTest != null)
+        prevTest.done();
+}
+
+MultiPointerTypeTest.prototype.createNextTest = function() {
+    if (this.currentTypeIndex < this.types.length) {
+        var pointerTypeDescription = document.getElementById('pointerTypeDescription');
+        document.getElementById('pointerTypeDescription').innerHTML = "Follow the test instructions with <span style='color: red'>" + this.types[this.currentTypeIndex] + "</span>. If you don't have the device <a href='javascript:;' onclick='globalPointerEventTest.skip()'>skip it</a>.";
+        this.currentTest = async_test(this.types[this.currentTypeIndex] + ' ' + this.testName);
+        expectedPointerType = this.types[this.currentTypeIndex];
+        this.currentTypeIndex++;
+    } else {
+        document.getElementById('pointerTypeDescription').innerHTML = "";
+    }
+    resetTestState();
+}
+
+
+function setup_pointerevent_test(testName, supportedPointerTypes) {
+   return globalPointerEventTest = new MultiPointerTypeTest(testName, supportedPointerTypes);
+}
rename from dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_click.html
rename to dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_click-manual.html
rename from dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
rename to dom/events/test/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse-manual.html
--- a/dom/events/test/pointerevents/pointerevent_touch-action-button-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-button-test_touch-manual.html
@@ -101,9 +101,9 @@
             }
         </script>
         <h1>touch-action: none</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_touch-action-inherit_child-auto-child-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
@@ -14,21 +14,21 @@
             }
             .scroller > div div {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning.</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning.</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
             <div>
-                <div>
+                <div id="scrollTarget">
                     <p>
                         Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                         nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                         Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                         lobortis nisl ut aliquip ex ea commodo consequat.
                     </p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-none_touch-manual.html
@@ -11,20 +11,20 @@
         <style>
             .scroller > div {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
-            <div>
+            <div id="scrollTarget">
                 <p>
                     Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                     nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                     Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                     lobortis nisl ut aliquip ex ea commodo consequat.
                 </p>
                 <p>Lorem ipsum dolor sit amet...</p>
                 <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
@@ -14,21 +14,21 @@
             }
             .scroller > div div {
             touch-action: pan-x;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
             <div>
-                <div>
+                <div id="scrollTarget">
                     <p>
                         Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                         nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                         Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                         lobortis nisl ut aliquip ex ea commodo consequat.
                     </p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
@@ -14,21 +14,21 @@
             }
             .scroller > div div {
             touch-action: pan-y;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
             <div>
-                <div>
+                <div id="scrollTarget">
                     <p>
                         Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                         nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                         Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                         lobortis nisl ut aliquip ex ea commodo consequat.
                     </p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
@@ -10,21 +10,21 @@
         <style>
             #divParent {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll text DOWN. Wait for description update. Expected: pan enabled</h4>
+        <h4 id="desc">Test Description: Try to scroll text DOWN inside blue rectangle. Wait for description update. Expected: pan enabled</h4>
         <p>Note: this test is for touch-devices only</p>
         <div id="divParent">
             <div class="scroller" id="target0">
-                <div>
+                <div id="scrollTarget">
                     <p>
                         Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                         nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                         Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                         lobortis nisl ut aliquip ex ea commodo consequat.
                     </p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_parent-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_parent-none_touch-manual.html
@@ -11,20 +11,20 @@
         <style>
             .scroller {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
-            <div>
+            <div id="scrollTarget">
                 <p>
                     Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                     nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                     Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                     lobortis nisl ut aliquip ex ea commodo consequat.
                 </p>
                 <p>Lorem ipsum dolor sit amet...</p>
                 <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
@@ -14,21 +14,21 @@
             }
             .scroller > div div {
             touch-action: pan-y;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
-        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
+        <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
         <p>Note: this test is for touch-devices only</p>
         <div class="scroller" id="target0">
             <div>
-                <div>
+                <div id="scrollTarget">
                     Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
                     nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
                     Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
                     lobortis nisl ut aliquip ex ea commodo consequat.
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
                     <p>Lorem ipsum dolor sit amet...</p>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html
@@ -114,9 +114,9 @@
             }
         </script>
         <h1>touch-action: none</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_touch-action-verification.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-verification.html
@@ -9,93 +9,83 @@
         pan-y: The user agent MAY consider touches that begin on the element only for the purposes of vertically scrolling the element's nearest ancestor with vertically scrollable content.
         manipulation: The user agent MAY consider touches that begin on the element only for the purposes of scrolling and continuous zooming. Any additional behaviors supported by auto are out of scope for this specification.">
         <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>
         <style>
-            #target0 {
-            width: 700px;
-            height: 20px;
-            touch-action: auto;
-            }
-            #target1 {
-            width: 700px;
-            height: 20px;
-            touch-action: none;
-            background: black;
-            margin-top: 5px;
-            touch-action: pan-x;
-            }
-            #target2 {
-            width: 700px;
-            height: 20px;
-            touch-action: none;
-            background: black;
-            margin-top: 5px;
-            touch-action: pan-y;
-            }
-            #target3 {
-            width: 700px;
-            height: 20px;
-            touch-action: none;
-            background: black;
-            margin-top: 5px;
-            touch-action: none;
-            }
-            #target4 {
-            width: 700px;
-            height: 20px;
-            touch-action: none;
-            background: black;
-            margin-top: 5px;
-            touch-action: manipulation;
-            }
+          /*
+          Give some rules below something to override in order to test
+          that they really are being parsed
+          */
+          .defnone {
+          touch-action: none;
+          }
         </style>
     </head>
     <body onload="run()">
         <h2>Pointer Events touch-action attribute support</h2>
-        <h4 id="desc">Test Description: Test will automatically check behaviour of following values: 'auto', 'pan-x', 'pan-y', ' none', 'manipulation'</h4>
-        <div id="target0"></div>
-        <div id="target1"></div>
-        <div id="target2"></div>
-        <div id="target3"></div>
-        <div id="target4"></div>
+        <h4 id="desc">Test Description: Test will automatically check parsing behaviour of various touch-action combinations.</h4>
         <script type='text/javascript'>
             var detected_pointertypes = {};
 
             setup({ explicit_done: true });
 
             function run() {
-                var target0 = document.getElementById('target0');
-                var target1 = document.getElementById('target1');
-                var target2 = document.getElementById('target2');
-                var target3 = document.getElementById('target3');
-                var target4 = document.getElementById('target4');
-
+                var tests = document.querySelectorAll('.test');
                 //TA 15.20
-                test(function() {
-                        assert_true(getComputedStyle(target0).touchAction == 'auto', "'auto' is set properly");
-                    }, "'auto' is set properly");
-                test(function() {
-                        assert_true(getComputedStyle(target1).touchAction == 'pan-x', "'pan-x' is corrected properly");
-                    }, "'pan-x' is corrected properly");
-                test(function() {
-                    assert_true(getComputedStyle(target2).touchAction == 'pan-y', "'pan-y' is set properly");
-                    }, "'pan-y' is set properly");
-                test(function() {
-                    assert_true(getComputedStyle(target3).touchAction == 'none', "'none' is set properly");
-                    }, "'none' is set properly");
-                test(function() {
-                    assert_true(getComputedStyle(target4).touchAction == 'manipulation', "'manipulation' is set properly");
-                    }, "'manipulation' is set properly");
+                for (var i = 0; i < tests.length; i++) {
+                    test(function() {
+                      var style = window.getComputedStyle(tests[i]);
+                      assert_equals(tests[i].attributes.expected.value, style.touchAction);
+                    }, tests[i].id);
+                }
                 done();
             }
         </script>
         <h1>touch-action: basic verification</h1>
         <div id="complete-notice">
             <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
         </div>
         <div id="log"></div>
+        <div class="test" id="default" expected="auto"></div>
+        <div class="test defnone" id="stylesheet-none" expected="none"></div>
+        <div class="test defnone" id="explicit-auto" style="touch-action: auto;" expected="auto"></div>
+        <div class="test" id="explicit-pan-x" style="touch-action: pan-x;" expected="pan-x"></div>
+        <div class="test" id="explicit-pan-left" style="touch-action: pan-left;" expected="pan-left"></div>
+        <div class="test" id="explicit-pan-right" style="touch-action: pan-right;" expected="pan-right"></div>
+        <div class="test" id="explicit-pan-y" style="touch-action: pan-y;" expected="pan-y"></div>
+        <div class="test" id="explicit-pan-up" style="touch-action: pan-up;" expected="pan-up"></div>
+        <div class="test" id="explicit-pan-down" style="touch-action: pan-down;" expected="pan-down"></div>
+        <div class="test" id="explicit-pan-x-pan-y" style="touch-action: pan-x pan-y;" expected="pan-x pan-y"></div>
+        <div class="test" id="explicit-pan-y-pan-x" style="touch-action: pan-y pan-x;" expected="pan-x pan-y"></div>
+        <div class="test" id="explicit-pan-left-pan-up" style="touch-action: pan-left pan-up;" expected="pan-left pan-up"></div>
+        <div class="test" id="explicit-pan-left-pan-down" style="touch-action: pan-left pan-down;" expected="pan-left pan-down"></div>
+        <div class="test" id="explicit-pan-right-pan-up" style="touch-action: pan-right pan-up;" expected="pan-right pan-up"></div>
+        <div class="test" id="explicit-pan-right-pan-down" style="touch-action: pan-right pan-down;" expected="pan-right pan-down"></div>
+        <div class="test" id="explicit-pan-up-pan-left" style="touch-action: pan-up pan-left;" expected="pan-left pan-up"></div>
+        <div class="test" id="explicit-pan-up-pan-right" style="touch-action: pan-up pan-right;" expected="pan-right pan-up"></div>
+        <div class="test" id="explicit-pan-down-pan-left" style="touch-action: pan-down pan-left;" expected="pan-left pan-down"></div>
+        <div class="test" id="explicit-pan-down-pan-right" style="touch-action: pan-down pan-right;" expected="pan-right pan-down"></div>
+        <div class="test" id="explicit-manipulation" style="touch-action: manipulation;" expected="manipulation"></div>
+        <div class="test" id="explicit-none" style="touch-action: none;" expected="none"></div>
+        <div class="test" id="explicit-invalid-1" style="touch-action: bogus;" expected="auto"></div>
+        <div class="test defnone" id="explicit-invalid-2" style="touch-action: auto pan-x;" expected="none"></div>
+        <div class="test" id="explicit-invalid-3" style="touch-action: pan-y none;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-4" style="touch-action: pan-x pan-x;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-5" style="touch-action: manipulation pan-x;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-6" style="touch-action: pan-x pan-left;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-7" style="touch-action: auto pan-left;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-8" style="touch-action: none pan-left;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-9" style="touch-action: pan-x pan-right;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-10" style="touch-action: pan-y pan-up;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-11" style="touch-action: pan-y pan-down;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-12" style="touch-action: pan-left pan-right;" expected="auto"></div>
+        <div class="test" id="explicit-invalid-13" style="touch-action: pan-up pan-down;" expected="auto"></div>
+        <div style="touch-action: none;">
+          <div class="test" id="not-inherited" expected="auto"></div>
+          <div class="test" id="inherit" style="touch-action: inherit;" expected="none"></div>
+        </div>
+        <div class="test defnone" id="initial" style="touch-action: initial;" expected="auto"></div>
     </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/events/test/pointerevents/readme.md
+++ b/dom/events/test/pointerevents/readme.md
@@ -1,5 +1,9 @@
 Directory for Pointer Events Tests
 
-All tests were got from official repository:
+Latest Editor's Draft: https://w3c.github.io/pointerevents/
+
+Latest W3C Technical Report: http://www.w3.org/TR/pointerevents/
 
-https://github.com/w3c/web-platform-tests/tree/master/pointerevents
+Discussion forum for tests: http://lists.w3.org/Archives/Public/public-test-infra/
+
+Test Assertion table: https://www.w3.org/wiki/PointerEvents/TestAssertions
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/resources/pointerevent_attributes_hoverable_pointers-iframe.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+    <head>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+    </head>
+    <body>
+        <div id="square2" class="square"></div>
+    </body>
+</html>
--- a/dom/events/test/pointerevents/resources/pointerevent_pointerId_scope-iframe.html
+++ b/dom/events/test/pointerevents/resources/pointerevent_pointerId_scope-iframe.html
@@ -7,18 +7,16 @@ http://www.w3.org/wiki/PointerEvents/Tes
 -->
     <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>
             function run() {
                 var target1 = document.getElementById("target1");
-                var pointerover_event;
-                var ponterId = null;
 
                 var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
 
                 eventList.forEach(function(eventName) {
                     target1.addEventListener(eventName, function (event) {
                         var pass_data = {
                             'pointerId' : event.pointerId,
                             'type' : event.type,
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointerevent_attributes_hoverable_pointers-manual.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test pointerevent attributes for hoverable pointers</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        ok(true, "debug0");
+        runTestInNewWindow("pointerevent_attributes_hoverable_pointers-manual.html");
+      }
+      function executeTest(int_win) {
+        let iframeWin = int_win.document.getElementById("innerFrame").contentWindow;
+        // synthesize mouse events with input source = mouse
+        sendMouseEvent(int_win, "square1", "mousemove", {button:-1});
+        sendMouseEvent(int_win, "square1", "mousedown");
+        sendMouseEvent(int_win, "square1", "mouseup");
+        sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+                                                         offsetX:-1,
+                                                         offsetY:-1});
+        sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1});
+        sendMouseEvent(iframeWin, "square2", "mousedown");
+        sendMouseEvent(iframeWin, "square2", "mouseup");
+        sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+                                                           offsetX:-1,
+                                                           offsetY:-1});
+        // synthesize mouse events with input source = pen
+        let inputPen = MouseEvent.MOZ_SOURCE_PEN;
+        sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+                                                         inputSource: inputPen});
+        sendMouseEvent(int_win, "square1", "mousedown", {inputSource:inputPen});
+        sendMouseEvent(int_win, "square1", "mouseup", {inputSource:inputPen});
+        sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+                                                         offsetX:-1,
+                                                         offsetY:-1,
+                                                         inputSource:inputPen});
+        sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+                                                           inputSource:inputPen});
+        sendMouseEvent(iframeWin, "square2", "mousedown", {inputSource:inputPen});
+        sendMouseEvent(iframeWin, "square2", "mouseup", {inputSource:inputPen});
+        sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+                                                           offsetX:-1,
+                                                           offsetY:-1,
+                                                           inputSource:inputPen});
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_attributes_mouse-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "square1", "mousemove");
-        sendMouseEvent(int_win, "square1", "mousedown");
-        sendMouseEvent(int_win, "square1", "mouseup");
-        sendMouseEvent(int_win, "square1", "mousemove");
-        sendMouseEvent(int_win, "square1", "mousemove", {offsetX: -1, offSetY: -1});
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
-
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointerevent_attributes_nohover_pointers-manual.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test pointerevent attributes for non-hoverable pointers</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        runTestInNewWindow("pointerevent_attributes_nohover_pointers-manual.html");
+      }
+      function executeTest(int_win) {
+        sendTouchEvent(int_win, "square1", "touchstart");
+        sendTouchEvent(int_win, "square1", "touchend");
+        let iframe = int_win.document.getElementById("innerFrame");
+        sendTouchEvent(iframe.contentWindow, "square2", "touchstart");
+        sendTouchEvent(iframe.contentWindow, "square2", "touchend");
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerdown-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "log",     "mousemove");
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerenter-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerenter_nohover-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerenter_nohover-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "log",     "mousemove");
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointerup_nohover-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerleave_after_pointerup_nohover-manual.html");
-      }
-      function executeTest(int_win) {
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_mouse-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerleave_mouse-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousemove");
-        sendMouseEvent(int_win, "log",     "mousemove");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_touch-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerleave_touch-manual.html");
-      }
-      function executeTest(int_win) {
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
rename from dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html
rename to dom/events/test/pointerevents/test_pointerevent_pointermove_on_chorded_mouse_button-manual.html
--- a/dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html
+++ b/dom/events/test/pointerevents/test_pointerevent_pointermove_on_chorded_mouse_button-manual.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <meta name="author" content="Maksim Lebedev" />
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <script type="text/javascript" src="mochitest_support_external.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
-        runTestInNewWindow("pointerevent_pointermove-on-chorded-mouse-button.html");
+        runTestInNewWindow("pointerevent_pointermove_on_chorded_mouse_button-manual.html");
       }
 
       function executeTest(int_win) {
         sendMouseEvent(int_win, "target0", "mousemove");
         sendMouseEvent(int_win, "target0", "mousedown", {button: MouseEventHelper.BUTTON_LEFT});
         sendMouseEvent(int_win, "target0", "mousemove");
         sendMouseEvent(int_win, "target0", "mousedown", {button: MouseEventHelper.BUTTON_MIDDLE});
         sendMouseEvent(int_win, "target0", "mousemove");
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointermove_pointertype-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mousemove");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerout-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerout-manual.html");
-      }
-      function executeTest(int_win) {
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointerup_nohover-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerout_after_pointerup_nohover-manual.html");
-      }
-      function executeTest(int_win) {
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerover-manual.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerover-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousemove");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointertype_mouse-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointertype_pen-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointertype_pen-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-        sendMouseEvent(int_win, "target0", "mouseup",   {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointertype_touch-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointertype_touch-manual.html");
-      }
-      function executeTest(int_win) {
-        sendTouchEvent(int_win, "target0", "touchstart");
-        sendTouchEvent(int_win, "target0", "touchend");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerup-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerup-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_pointerup_pointertype-manual.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        runTestInNewWindow("pointerevent_pointerup_pointertype-manual.html");
-      }
-      function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown");
-        sendMouseEvent(int_win, "target0", "mouseup");
-      }
-    </script>
-  </head>
-  <body>
-  </body>
-</html>
rename from dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html
rename to dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click-manual.html
--- a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html
+++ b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click-manual.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <meta name="author" content="Maksim Lebedev" />
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <script type="text/javascript" src="mochitest_support_external.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
-        runTestInNewWindow("pointerevent_suppress_compat_events_on_click.html");
+        runTestInNewWindow("pointerevent_suppress_compat_events_on_click-manual.html");
       }
       function executeTest(int_win) {
         sendMouseEvent(int_win, "target0", "mousedown");
         sendMouseEvent(int_win, "target0", "mouseup");
         sendMouseEvent(int_win, "target1", "mousedown");
         sendMouseEvent(int_win, "target1", "mouseup");
         sendMouseEvent(int_win, "done",    "mousedown");
         sendMouseEvent(int_win, "done",    "mouseup");
rename from dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html
rename to dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse-manual.html
--- a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html
+++ b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse-manual.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <meta name="author" content="Maksim Lebedev" />
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <script type="text/javascript" src="mochitest_support_external.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
-        runTestInNewWindow("pointerevent_suppress_compat_events_on_drag_mouse.html");
+        runTestInNewWindow("pointerevent_suppress_compat_events_on_drag_mouse-manual.html");
       }
       function executeTest(int_win) {
         sendMouseEvent(int_win, "target0", "mousedown");
         sendMouseEvent(int_win, "target0", "mousemove");
         sendMouseEvent(int_win, "target0", "mouseup");
         sendMouseEvent(int_win, "target1", "mousedown");
         sendMouseEvent(int_win, "target1", "mousemove");
         sendMouseEvent(int_win, "target1", "mouseup");
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -121,20 +121,26 @@ protected:
   virtual nsISupports* GetAsISupports() = 0;
 
   virtual void DispatchBufferFullEvent() = 0;
 
   virtual TimeStamp CreationTimeStamp() const = 0;
 
   virtual DOMHighResTimeStamp CreationTime() const = 0;
 
-  virtual bool IsPerformanceTimingAttribute(const nsAString& aName) = 0;
+  virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
+  {
+    return false;
+  }
 
   virtual DOMHighResTimeStamp
-  GetPerformanceTimingFromString(const nsAString& aTimingName) = 0;
+  GetPerformanceTimingFromString(const nsAString& aTimingName)
+  {
+    return 0;
+  }
 
   bool IsResourceEntryLimitReached() const
   {
     return mResourceEntries.Length() >= mResourceTimingBufferSize;
   }
 
   void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
   void TimingNotification(PerformanceEntry* aEntry, const nsACString& aOwner,
--- a/dom/performance/PerformanceWorker.cpp
+++ b/dom/performance/PerformanceWorker.cpp
@@ -18,39 +18,16 @@ PerformanceWorker::PerformanceWorker(Wor
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 PerformanceWorker::~PerformanceWorker()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
-// To be removed once bug 1124165 lands
-bool
-PerformanceWorker::IsPerformanceTimingAttribute(const nsAString& aName)
-{
-  // In workers we just support navigationStart.
-  return aName.EqualsASCII("navigationStart");
-}
-
-DOMHighResTimeStamp
-PerformanceWorker::GetPerformanceTimingFromString(const nsAString& aProperty)
-{
-  if (!IsPerformanceTimingAttribute(aProperty)) {
-    return 0;
-  }
-
-  if (aProperty.EqualsLiteral("navigationStart")) {
-    return mWorkerPrivate->CreationTime();
-  }
-
-  MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
-  return 0;
-}
-
 void
 PerformanceWorker::InsertUserEntry(PerformanceEntry* aEntry)
 {
   if (mWorkerPrivate->PerformanceLoggingEnabled()) {
     nsAutoCString uri;
     nsCOMPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
     if (!scriptURI || NS_FAILED(scriptURI->GetHost(uri))) {
       // If we have no URI, just put in "none".
--- a/dom/performance/PerformanceWorker.h
+++ b/dom/performance/PerformanceWorker.h
@@ -66,21 +66,16 @@ protected:
 
   nsISupports* GetAsISupports() override
   {
     return nullptr;
   }
 
   void InsertUserEntry(PerformanceEntry* aEntry) override;
 
-  bool IsPerformanceTimingAttribute(const nsAString& aName) override;
-
-  DOMHighResTimeStamp
-  GetPerformanceTimingFromString(const nsAString& aTimingName) override;
-
   void DispatchBufferFullEvent() override
   {
     MOZ_CRASH("This should not be called on workers.");
   }
 
 private:
   workers::WorkerPrivate* mWorkerPrivate;
 };
--- a/dom/performance/tests/test_performance_user_timing.js
+++ b/dom/performance/tests/test_performance_user_timing.js
@@ -175,25 +175,28 @@ var steps = [
         is(measures_type.length, 1, "Number of measures by type should be 1");
         var measure = measures_type[0];
         is(measure.startTime, last_mark.startTime, "Measure start time should be the start time of the latest 'test' mark");
         // Tolerance testing to avoid oranges, since we're doing double math across two different languages.
         ok(measure.duration - (test_mark.startTime - last_mark.startTime) < .00001,
            "Measure duration ( " + measure.duration + ") should be difference between two marks");
     },
     function() {
-        ok(true, "Running measure addition with no start/end time test");
-        performance.measure("test", "navigationStart");
-        var measures = performance.getEntriesByType("measure");
-        is(measures.length, 1, "number of measures should be 1");
-        var measure = measures[0];
-        is(measure.name, "test", "measure name should be 'test'");
-        is(measure.entryType, "measure", "measure type should be 'measure'");
-        is(measure.startTime, 0, "measure start time should be zero");
-        ok(measure.duration >= 0, "measure duration should not be negative");
+        // We don't have navigationStart in workers.
+        if ("window" in self) {
+          ok(true, "Running measure addition with no start/end time test");
+          performance.measure("test", "navigationStart");
+          var measures = performance.getEntriesByType("measure");
+          is(measures.length, 1, "number of measures should be 1");
+          var measure = measures[0];
+          is(measure.name, "test", "measure name should be 'test'");
+          is(measure.entryType, "measure", "measure type should be 'measure'");
+          is(measure.startTime, 0, "measure start time should be zero");
+          ok(measure.duration >= 0, "measure duration should not be negative");
+        }
     },
     // Test all measure removal
     function () {
         ok(true, "Running all measure removal test");
         performance.measure("test");
         performance.measure("test2");
         var measures = performance.getEntriesByType("measure");
         is(measures.length, 2, "measure entries should be length 2");
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -370,17 +370,16 @@ function testConsoleTime(aMessageObject)
   is(messageWindow, gWindow, "found correct window by window ID");
 
   is(aMessageObject.level, gLevel, "expected level received");
 
   is(aMessageObject.filename, gArgs[0].filename, "filename matches");
   is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches");
   is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches");
   is(aMessageObject.timer.name, gArgs[0].timer.name, "timer.name matches");
-  ok(aMessageObject.timer.started, "timer.started exists");
 
   gArgs[0].arguments.forEach(function (a, i) {
     is(aMessageObject.arguments[i], a, "correct arg " + i);
   });
 
   resolve();
 }
 
--- a/dom/webidl/AudioWorkletGlobalScope.webidl
+++ b/dom/webidl/AudioWorkletGlobalScope.webidl
@@ -1,13 +1,13 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
- * https://www.w3.org/TR/worklets-1/#fakeworkletglobalscope
+ * https://webaudio.github.io/web-audio-api/#AudioWorkletGlobalScope
  */
 
 [Global=(Worklet,AudioWorklet),Exposed=AudioWorklet]
 interface AudioWorkletGlobalScope : WorkletGlobalScope {
     void registerProcessor (DOMString name, VoidFunction processorCtor);
 };
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -82,17 +82,16 @@ dictionary ConsoleStackEntry {
   unsigned long columnNumber = 0;
   DOMString functionName = "";
   unsigned long language = 0;
   DOMString? asyncCause;
 };
 
 dictionary ConsoleTimerStart {
   DOMString name = "";
-  double started = 0;
 };
 
 dictionary ConsoleTimerEnd {
   DOMString name = "";
   double duration = 0;
 };
 
 dictionary ConsoleTimerError {
--- a/dom/webidl/Function.webidl
+++ b/dom/webidl/Function.webidl
@@ -7,8 +7,10 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#functiocn
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 callback Function = any(any... arguments);
+
+callback VoidFunction = void ();
--- a/dom/webidl/MimeTypeArray.webidl
+++ b/dom/webidl/MimeTypeArray.webidl
@@ -1,13 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [LegacyUnenumerableNamedProperties]
 interface MimeTypeArray {
+  [NeedsCallerType]
   readonly attribute unsigned long length;
 
+  [NeedsCallerType]
   getter MimeType? item(unsigned long index);
+  [NeedsCallerType]
   getter MimeType? namedItem(DOMString name);
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -187,17 +187,17 @@ partial interface Navigator {
   // WebKit/Blink/Trident/Presto support this.
   readonly attribute boolean cookieEnabled;
   [Throws, Constant, Cached, NeedsCallerType]
   readonly attribute DOMString buildID;
   [Throws, ChromeOnly, UnsafeInPrerendering]
   readonly attribute MozPowerManager mozPower;
 
   // WebKit/Blink/Trident/Presto support this.
-  [Throws]
+  [Throws, NeedsCallerType]
   boolean javaEnabled();
 
   /**
    * Navigator requests to add an idle observer to the existing window.
    */
   [Throws, ChromeOnly]
   void addIdleObserver(MozIdleObserver aIdleObserver);
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PaintWorkletGlobalScope.webidl
@@ -0,0 +1,13 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.css-houdini.org/css-paint-api-1/#paintworkletglobalscope
+ */
+
+[Global=(Worklet,PaintWorklet),Exposed=PaintWorklet]
+interface PaintWorkletGlobalScope : WorkletGlobalScope {
+    void registerPaint(DOMString name, VoidFunction paintCtor);
+};
--- a/dom/webidl/PluginArray.webidl
+++ b/dom/webidl/PluginArray.webidl
@@ -1,15 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [LegacyUnenumerableNamedProperties]
 interface PluginArray {
+  [NeedsCallerType]
   readonly attribute unsigned long length;
 
+  [NeedsCallerType]
   getter Plugin? item(unsigned long index);
+  [NeedsCallerType]
   getter Plugin? namedItem(DOMString name);
 
   void refresh(optional boolean reloadDocuments = false);
 };
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -4,17 +4,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://w3c.github.io/webrtc-pc/#interface-definition
  */
 
 callback RTCSessionDescriptionCallback = void (RTCSessionDescriptionInit description);
 callback RTCPeerConnectionErrorCallback = void (DOMError error);
-callback VoidFunction = void ();
 callback RTCStatsCallback = void (RTCStatsReport report);
 
 enum RTCSignalingState {
     "stable",
     "have-local-offer",
     "have-remote-offer",
     "have-local-pranswer",
     "have-remote-pranswer",
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -10,16 +10,18 @@
  * http://dev.w3.org/csswg/cssom/
  * http://dev.w3.org/csswg/cssom-view/
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  * https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
  * https://w3c.github.io/requestidlecallback/
+ * https://webaudio.github.io/web-audio-api/#widl-Window-audioWorklet
+ * https://drafts.css-houdini.org/css-paint-api-1/#dom-window-paintworklet
  */
 
 interface ApplicationCache;
 interface IID;
 interface nsIBrowserDOMWindow;
 interface nsIMessageBroadcaster;
 interface nsIDOMCrypto;
 typedef any Transferable;
@@ -475,21 +477,28 @@ partial interface Window {
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplayconnect;
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaydisconnect;
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaypresentchange;
 };
 
+// https://webaudio.github.io/web-audio-api/#widl-Window-audioWorklet
 partial interface Window {
   [Pref="dom.audioWorklet.enabled", Throws, SameObject]
   readonly attribute Worklet audioWorklet;
 };
 
+// https://drafts.css-houdini.org/css-paint-api-1/#dom-window-paintworklet
+partial interface Window {
+    [Pref="dom.paintWorklet.enabled", Throws, SameObject]
+    readonly attribute Worklet paintWorklet;
+};
+
 Window implements ChromeWindow;
 Window implements WindowOrWorkerGlobalScope;
 
 partial interface Window {
   [Throws, Pref="dom.requestIdleCallback.enabled"]
   unsigned long requestIdleCallback(IdleRequestCallback callback,
                                     optional IdleRequestOptions options);
   [Pref="dom.requestIdleCallback.enabled"]
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -336,16 +336,17 @@ WEBIDL_FILES = [
     'NotifyPaintEvent.webidl',
     'OfflineAudioCompletionEvent.webidl',
     'OfflineAudioContext.webidl',
     'OfflineResourceList.webidl',
     'OffscreenCanvas.webidl',
     'OscillatorNode.webidl',
     'PaintRequest.webidl',
     'PaintRequestList.webidl',
+    'PaintWorkletGlobalScope.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
     'Performance.webidl',
     'PerformanceEntry.webidl',
     'PerformanceMark.webidl',
     'PerformanceMeasure.webidl',
     'PerformanceNavigation.webidl',
     'PerformanceObserver.webidl',
--- a/dom/worklet/AudioWorkletGlobalScope.cpp
+++ b/dom/worklet/AudioWorkletGlobalScope.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 
 #include "AudioWorkletGlobalScope.h"
 #include "mozilla/dom/AudioWorkletGlobalScopeBinding.h"
-#include "mozilla/dom/RTCPeerConnectionBinding.h" // For VoidFunction
+#include "mozilla/dom/FunctionBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 AudioWorkletGlobalScope::AudioWorkletGlobalScope(nsPIDOMWindowInner* aWindow)
   : WorkletGlobalScope(aWindow)
 {
 }
new file mode 100644
--- /dev/null
+++ b/dom/worklet/PaintWorkletGlobalScope.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#include "PaintWorkletGlobalScope.h"
+#include "mozilla/dom/PaintWorkletGlobalScopeBinding.h"
+#include "mozilla/dom/FunctionBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+PaintWorkletGlobalScope::PaintWorkletGlobalScope(nsPIDOMWindowInner* aWindow)
+  : WorkletGlobalScope(aWindow)
+{
+}
+
+PaintWorkletGlobalScope::~PaintWorkletGlobalScope()
+{
+}
+
+bool
+PaintWorkletGlobalScope::WrapGlobalObject(JSContext* aCx,
+                                          nsIPrincipal* aPrincipal,
+                                          JS::MutableHandle<JSObject*> aReflector)
+{
+  JS::CompartmentOptions options;
+  return PaintWorkletGlobalScopeBinding::Wrap(aCx, this, this,
+                                              options,
+                                              nsJSPrincipals::get(aPrincipal),
+                                              true, aReflector);
+}
+
+void
+PaintWorkletGlobalScope::RegisterPaint(const nsAString& aType,
+                                       VoidFunction& aProcessorCtor)
+{
+  // Nothing to do here, yet.
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/worklet/PaintWorkletGlobalScope.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_PaintWorkletGlobalScope_h
+#define mozilla_dom_PaintWorkletGlobalScope_h
+
+#include "mozilla/dom/WorkletGlobalScope.h"
+
+namespace mozilla {
+namespace dom {
+
+class VoidFunction;
+
+class PaintWorkletGlobalScope final : public WorkletGlobalScope
+{
+public:
+  explicit PaintWorkletGlobalScope(nsPIDOMWindowInner* aWindow);
+
+  bool
+  WrapGlobalObject(JSContext* aCx, nsIPrincipal* aPrincipal,
+                   JS::MutableHandle<JSObject*> aReflector) override;
+
+  void
+  RegisterPaint(const nsAString& aType, VoidFunction& aProcessorCtor);
+
+private:
+  ~PaintWorkletGlobalScope();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PaintWorkletGlobalScope_h
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 
 #include "Worklet.h"
-#include "WorkletGlobalScope.h"
+#include "AudioWorkletGlobalScope.h"
+#include "PaintWorkletGlobalScope.h"
+
 #include "mozilla/dom/WorkletBinding.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/RegisterWorkletBindings.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIThreadRetargetableRequest.h"
@@ -317,19 +319,21 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Wo
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worklet)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
+Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+                 WorkletType aWorkletType)
   : mWindow(aWindow)
   , mPrincipal(aPrincipal)
+  , mWorkletType(aWorkletType)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aPrincipal);
 
 #ifdef RELEASE_OR_BETA
   MOZ_CRASH("This code should not go to release/beta yet!");
 #endif
 }
@@ -348,18 +352,24 @@ Worklet::Import(const nsAString& aModule
 {
   return WorkletFetchHandler::Fetch(this, aModuleURL, aRv);
 }
 
 WorkletGlobalScope*
 Worklet::GetOrCreateGlobalScope(JSContext* aCx)
 {
   if (!mScope) {
-    // So far we don't have any other global scope to implement.
-    mScope = new AudioWorkletGlobalScope(mWindow);
+    switch (mWorkletType) {
+      case eAudioWorklet:
+        mScope = new AudioWorkletGlobalScope(mWindow);
+        break;
+      case ePaintWorklet:
+        mScope = new PaintWorkletGlobalScope(mWindow);
+        break;
+    }
 
     JS::Rooted<JSObject*> global(aCx);
     NS_ENSURE_TRUE(mScope->WrapGlobalObject(aCx, mPrincipal, &global), nullptr);
 
     JSAutoCompartment ac(aCx, global);
 
     // Init Web IDL bindings
     if (!RegisterWorkletBindings(aCx, global)) {
--- a/dom/worklet/Worklet.h
+++ b/dom/worklet/Worklet.h
@@ -25,17 +25,23 @@ class WorkletFetchHandler;
 
 class Worklet final : public nsISupports
                     , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Worklet)
 
-  Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
+  enum WorkletType {
+    eAudioWorklet,
+    ePaintWorklet,
+  };
+
+  Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+          WorkletType aWorkletType);
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -53,16 +59,18 @@ private:
   GetImportFetchHandler(const nsACString& aURI);
 
   void
   AddImportFetchHandler(const nsACString& aURI, WorkletFetchHandler* aHandler);
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
+  WorkletType mWorkletType;
+
   RefPtr<WorkletGlobalScope> mScope;
   nsRefPtrHashtable<nsCStringHashKey, WorkletFetchHandler> mImportHandlers;
 
   friend class WorkletFetchHandler;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/worklet/moz.build
+++ b/dom/worklet/moz.build
@@ -1,22 +1,24 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'AudioWorkletGlobalScope.h',
+    'PaintWorkletGlobalScope.h',
     'Worklet.h',
     'WorkletGlobalScope.h',
 ]
 
 UNIFIED_SOURCES += [
     'AudioWorkletGlobalScope.cpp',
+    'PaintWorkletGlobalScope.cpp',
     'Worklet.cpp',
     'WorkletGlobalScope.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/js/xpconnect/src',
 ]
 
--- a/dom/worklet/tests/common.js
+++ b/dom/worklet/tests/common.js
@@ -1,14 +1,23 @@
-function loadTest(file) {
-  var iframe = document.createElement('iframe');
-  iframe.src = file;
+window.onload = function() {
+  // We are the parent. Let's load the test.
+  if (parent == this || location.search.indexOf("worklet_iframe") == -1) {
+    SimpleTest.waitForExplicitFinish();
 
-  document.body.appendChild(iframe);
-}
+    configureTest().then(() => {
+      var iframe = document.createElement('iframe');
+      iframe.src = location.href + "?worklet_iframe";
+      document.body.appendChild(iframe);
+    });
 
-function setupTest() {
+    return;
+  }
+
+  // Here we are in the iframe.
   window.SimpleTest = parent.SimpleTest;
   window.is = parent.is;
   window.isnot = parent.isnot;
   window.ok = parent.ok;
   window.info = parent.info;
+
+  runTestInIframe();
 }
deleted file mode 100644
--- a/dom/worklet/tests/file_audioWorklet.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for AudioWorklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-audioWorklet.import("worklet_audioWorklet.js")
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/worklet/tests/file_basic.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Worklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-ok(!!audioWorklet, "audioWorklet exists");
-
-// First loading
-audioWorklet.import("common.js")
-.then(() => {
-  ok(true, "Import should load a resource.");
-})
-
-// Second loading - same file
-.then(() => {
-  return audioWorklet.import("common.js")
-})
-.then(() => {
-  ok(true, "Import should load a resource.");
-})
-
-// 3rd loading - a network error
-.then(() => {
-  return audioWorklet.import("404.js");
-})
-.then(() => {
-  ok(false, "The loading should fail.");
-}, () => {
-  ok(true, "The loading should fail.");
-})
-
-// 4th loading - a network error
-.then(() => {
-  return audioWorklet.import("404.js");
-})
-.then(() => {
-  ok(false, "The loading should fail.");
-}, () => {
-  ok(true, "The loading should fail.");
-})
-
-// done
-.then(() => {
-  SimpleTest.finish();
-});
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/worklet/tests/file_console.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Worklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-audioWorklet.import("worklet_console.js");
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/worklet/tests/file_dump.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Worklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-audioWorklet.import("worklet_dump.js")
-.then(() => {
-  ok(true, "All good!");
-  SimpleTest.finish();
-});
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/worklet/tests/file_exception.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Exceptions in Worklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-// This loading should fail
-audioWorklet.import("404.js")
-.then(() => {
-  ok(false, "We should not be called!");
-}, () => {
-  ok(true, "The script thrown but we are still here.");
-})
-
-// This should throw from JS
-.then(() => {
-  return audioWorklet.import("worklet_exception.js")
-})
-.then(() => {
-  ok(false, "We should not be called!");
-}, () => {
-  ok(true, "The script thrown but we are still here.");
-})
-
-.then(() => {
-  SimpleTest.finish();
-});
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/worklet/tests/file_import_with_cache.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Worklet</title>
-  <script type="application/javascript" src="common.js"></script>
-</head>
-<body>
-
-<script type="application/javascript">
-
-setupTest();
-
-function loading() {
-  audioWorklet.import("server_import_with_cache.sjs")
-  .then(() => {
-    ok(true, "Import should load a resource.");
-  }, () => {
-    ok(false, "Import should load a resource.");
-  })
-  .then(() => {
-    done();
-  });
-}
-
-var count = 0;
-const MAX = 10;
-
-function done() {
-  if (++count == MAX) {
-    SimpleTest.finish();
-  }
-}
-
-for (var i = 0; i < MAX; ++i) {
-  loading();
-}
-
-</script>
-</body>
-</html>
--- a/dom/worklet/tests/mochitest.ini
+++ b/dom/worklet/tests/mochitest.ini
@@ -1,17 +1,18 @@
 [DEFAULT]
 skip-if = release_or_beta
 support-files =
   common.js
 
 [test_basic.html]
-support-files=file_basic.html
 [test_console.html]
-support-files=file_console.html worklet_console.js
+support-files=worklet_console.js
 [test_import_with_cache.html]
-support-files=file_import_with_cache.html server_import_with_cache.sjs
+support-files=server_import_with_cache.sjs
 [test_dump.html]
-support-files=file_dump.html worklet_dump.js
+support-files=worklet_dump.js
 [test_audioWorklet.html]
-support-files=file_audioWorklet.html worklet_audioWorklet.js
+support-files=worklet_audioWorklet.js
 [test_exception.html]
-support-files=file_exception.html worklet_exception.js
+support-files=worklet_exception.js
+[test_paintWorklet.html]
+support-files=worklet_paintWorklet.js
--- a/dom/worklet/tests/test_audioWorklet.html
+++ b/dom/worklet/tests/test_audioWorklet.html
@@ -5,38 +5,42 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-function consoleListener() {
-  SpecialPowers.addObserver(this, "console-api-log-event", false);
-}
+function configureTest() {
+  function consoleListener() {
+    SpecialPowers.addObserver(this, "console-api-log-event", false);
+  }
 
-consoleListener.prototype  = {
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic == "console-api-log-event") {
-      var obj = aSubject.wrappedJSObject;
-      if (obj.arguments[0] == "So far so good") {
-        ok(true, "Message received \\o/");
+  consoleListener.prototype  = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "console-api-log-event") {
+        var obj = aSubject.wrappedJSObject;
+        if (obj.arguments[0] == "So far so good") {
+          ok(true, "Message received \\o/");
 
-        SpecialPowers.removeObserver(this, "console-api-log-event");
-        SimpleTest.finish();
-        return;
+          SpecialPowers.removeObserver(this, "console-api-log-event");
+          SimpleTest.finish();
+          return;
+        }
       }
     }
   }
+
+  var cl = new consoleListener();
+
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
 }
 
-var cl = new consoleListener();
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_audioWorklet.html"); });
-
+// This function is called into an iframe.
+function runTestInIframe() {
+  audioWorklet.import("worklet_audioWorklet.js")
+}
 </script>
 </body>
 </html>
--- a/dom/worklet/tests/test_basic.html
+++ b/dom/worklet/tests/test_basic.html
@@ -5,17 +5,61 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_basic.html"); });
+function configureTest() {
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
+}
+
+// This function is called into an iframe.
+function runTestInIframe() {
+  ok(!!audioWorklet, "audioWorklet exists");
+
+  // First loading
+  audioWorklet.import("common.js")
+  .then(() => {
+    ok(true, "Import should load a resource.");
+  })
+
+  // Second loading - same file
+  .then(() => {
+    return audioWorklet.import("common.js")
+  })
+  .then(() => {
+    ok(true, "Import should load a resource.");
+  })
+
+  // 3rd loading - a network error
+  .then(() => {
+    return audioWorklet.import("404.js");
+  })
+  .then(() => {
+    ok(false, "The loading should fail.");
+  }, () => {
+    ok(true, "The loading should fail.");
+  })
+
+  // 4th loading - a network error
+  .then(() => {
+    return audioWorklet.import("404.js");
+  })
+  .then(() => {
+    ok(false, "The loading should fail.");
+  }, () => {
+    ok(true, "The loading should fail.");
+  })
+
+  // done
+  .then(() => {
+    SimpleTest.finish();
+  });
+}
 
 </script>
 </body>
 </html>
--- a/dom/worklet/tests/test_console.html
+++ b/dom/worklet/tests/test_console.html
@@ -5,38 +5,43 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-function consoleListener() {
-  SpecialPowers.addObserver(this, "console-api-log-event", false);
-}
+function configureTest() {
+  function consoleListener() {
+    SpecialPowers.addObserver(this, "console-api-log-event", false);
+  }
 
-consoleListener.prototype  = {
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic == "console-api-log-event") {
-      var obj = aSubject.wrappedJSObject;
-      if (obj.arguments[0] == "Hello world from a worklet") {
-        ok(true, "Message received \\o/");
+  consoleListener.prototype  = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "console-api-log-event") {
+        var obj = aSubject.wrappedJSObject;
+        if (obj.arguments[0] == "Hello world from a worklet") {
+          ok(true, "Message received \\o/");
 
-        SpecialPowers.removeObserver(this, "console-api-log-event");
-        SimpleTest.finish();
-        return;
+          SpecialPowers.removeObserver(this, "console-api-log-event");
+          SimpleTest.finish();
+          return;
+        }
       }
     }
   }
+
+  var cl = new consoleListener();
+
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
 }
 
-var cl = new consoleListener();
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_console.html"); });
+// This function is called into an iframe.
+function runTestInIframe() {
+  audioWorklet.import("worklet_console.js");
+}
 
 </script>
 </body>
 </html>
--- a/dom/worklet/tests/test_dump.html
+++ b/dom/worklet/tests/test_dump.html
@@ -5,17 +5,25 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_dump.html"); });
+function configureTest() {
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
+}
 
+// This function is called into an iframe.
+function runTestInIframe() {
+  audioWorklet.import("worklet_dump.js")
+  .then(() => {
+    ok(true, "All good!");
+    SimpleTest.finish();
+  });
+}
 </script>
 </body>
 </html>
--- a/dom/worklet/tests/test_exception.html
+++ b/dom/worklet/tests/test_exception.html
@@ -5,17 +5,42 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_exception.html"); });
+function configureTest() {
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
+}
+
+// This function is called into an iframe.
+function runTestInIframe() {
+  // This loading should fail
+  audioWorklet.import("404.js")
+  .then(() => {
+    ok(false, "We should not be called!");
+  }, () => {
+    ok(true, "The script thrown but we are still here.");
+  })
+
+  // This should throw from JS
+  .then(() => {
+    return audioWorklet.import("worklet_exception.js")
+  })
+  .then(() => {
+    ok(false, "We should not be called!");
+  }, () => {
+    ok(true, "The script thrown but we are still here.");
+  })
+
+  .then(() => {
+    SimpleTest.finish();
+  });
+}
 
 </script>
 </body>
 </html>
--- a/dom/worklet/tests/test_import_with_cache.html
+++ b/dom/worklet/tests/test_import_with_cache.html
@@ -5,17 +5,44 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="common.js"></script>
 </head>
 <body>
 
 <script type="application/javascript">
 
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  {"set": [["dom.audioWorklet.enabled", true],
-           ["dom.worklet.enabled", true]]},
-  function() { loadTest("file_import_with_cache.html"); });
+function configureTest() {
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.audioWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
+}
 
+// This function is called into an iframe.
+function runTestInIframe() {
+  function loading() {
+    audioWorklet.import("server_import_with_cache.sjs")
+    .then(() => {
+      ok(true, "Import should load a resource.");
+    }, () => {
+      ok(false, "Import should load a resource.");
+    })
+    .then(() => {
+      done();
+    });
+  }
+
+  var count = 0;
+  const MAX = 10;
+
+  function done() {
+    if (++count == MAX) {
+      SimpleTest.finish();
+    }
+  }
+
+  for (var i = 0; i < MAX; ++i) {
+    loading();
+  }
+}
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_paintWorklet.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for PaintWorklet</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+function configureTest() {
+  function consoleListener() {
+    SpecialPowers.addObserver(this, "console-api-log-event", false);
+  }
+
+  consoleListener.prototype  = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "console-api-log-event") {
+        var obj = aSubject.wrappedJSObject;
+        if (obj.arguments[0] == "So far so good") {
+          ok(true, "Message received \\o/");
+
+          SpecialPowers.removeObserver(this, "console-api-log-event");
+          SimpleTest.finish();
+          return;
+        }
+      }
+    }
+  }
+
+  var cl = new consoleListener();
+
+  return SpecialPowers.pushPrefEnv(
+    {"set": [["dom.paintWorklet.enabled", true],
+             ["dom.worklet.enabled", true]]});
+}
+
+// This function is called into an iframe.
+function runTestInIframe() {
+  paintWorklet.import("worklet_paintWorklet.js")
+}
+</script>
+</body>
+</html>
--- a/dom/worklet/tests/worklet_audioWorklet.js
+++ b/dom/worklet/tests/worklet_audioWorklet.js
@@ -1,3 +1,3 @@
 // This should work for real... at some point.
 registerProcessor("sure!", () => {});
-console.log("So far so good");
+console.log(this instanceof AudioWorkletGlobalScope ? "So far so good" : "error");
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/worklet_paintWorklet.js
@@ -0,0 +1,3 @@
+// This should work for real... at some point.
+registerPaint("sure!", () => {});
+console.log(this instanceof PaintWorkletGlobalScope ? "So far so good" : "error");
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -217,31 +217,29 @@ if CONFIG['MOZ_TREE_FREETYPE']:
 if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
     CFLAGS += [
         '-Wno-enum-compare',
         '-Wno-int-to-pointer-cast',
         '-Wno-sign-compare',
         '-Wno-type-limits',
         '-Wno-missing-field-initializers',
         '-Wno-conversion',
-        '-Wno-unused-but-set-variable',
+        '-Wno-unused',
     ]
+
 if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
     CFLAGS += [
+        '-Wno-absolute-value',
+        '-Wno-deprecated-register',
         '-Wno-incompatible-pointer-types',
+        '-Wno-macro-redefined',
         '-Wno-tautological-compare',
         '-Wno-tautological-constant-out-of-range-compare',
         '-Wno-error=uninitialized',
     ]
-if CONFIG['CLANG_CL']:
-    CFLAGS += [
-        '-Wno-deprecated-register',
-        '-Wno-macro-redefined',
-        '-Wno-unused-variable',
-    ]
 
 # See bug 386897.
 if CONFIG['GNU_CC'] and CONFIG['OS_TARGET'] == 'Android' and CONFIG['MOZ_OPTIMIZE']:
     CFLAGS += ['-O2']
     CXXFLAGS += ['-O2']
 
 if CONFIG['MOZ_X11']:
     CFLAGS += CONFIG['XCFLAGS']
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -74,19 +74,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gtk3',
     'uikit',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
-    # These are the usual dwrite default rendering param values
-    DEFINES['SK_GAMMA_EXPONENT'] = 1.8
-    DEFINES['SK_GAMMA_CONTRAST'] = 0.5
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -684,19 +684,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gtk3',
     'uikit',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
-    # These are the usual dwrite default rendering param values
-    DEFINES['SK_GAMMA_EXPONENT'] = 1.8
-    DEFINES['SK_GAMMA_CONTRAST'] = 0.5
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
@@ -245,16 +245,20 @@ SkStreamAsset* DWriteFontTypeface::onOpe
     return new SkDWriteFontFileStream(fontFileStream.get());
 }
 
 SkScalerContext* DWriteFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
                                                            const SkDescriptor* desc) const {
     return new SkScalerContext_DW(const_cast<DWriteFontTypeface*>(this), effects, desc);
 }
 
+#ifdef MOZ_SKIA
+IDWriteRenderingParams* GetDwriteRenderingParams(bool aGDI);
+#endif
+
 void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const {
     if (rec->fFlags & SkScalerContext::kLCD_Vertical_Flag) {
         rec->fMaskFormat = SkMask::kA8_Format;
     }
 
     unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag |
                                   SkScalerContext::kDevKernText_Flag |
                                   SkScalerContext::kForceAutohinting_Flag |
@@ -274,16 +278,26 @@ void DWriteFontTypeface::onFilterRec(SkS
         if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) {
             float gamma = defaultRenderingParams->GetGamma();
             rec->setDeviceGamma(gamma);
             rec->setPaintGamma(gamma);
 
             rec->setContrast(defaultRenderingParams->GetEnhancedContrast());
         }
     }
+#elif defined(MOZ_SKIA)
+    IDWriteRenderingParams* params = GetDwriteRenderingParams(ForceGDI());
+    SkASSERT(params);
+    rec->setContrast(params->GetEnhancedContrast());
+
+    // GDI gamma should be 2.3
+    // See the LUT gamma values comment for GDI fonts.
+    float gamma = ForceGDI() ? 2.3f : params->GetGamma();
+    rec->setDeviceGamma(gamma);
+    rec->setPaintGamma(gamma);
 #endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 //PDF Support
 
 // Construct Glyph to Unicode table.
 // Unicode code points that require conjugate pairs in utf16 are not
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
@@ -92,17 +92,17 @@ public:
                                       IDWriteFont* font,
                                       IDWriteFontFamily* fontFamily,
                                       IDWriteFontFileLoader* fontFileLoader = nullptr,
                                       IDWriteFontCollectionLoader* fontCollectionLoader = nullptr) {
         return new DWriteFontTypeface(get_style(font), factory, fontFace, font, fontFamily,
                                       fontFileLoader, fontCollectionLoader);
     }
 
-    bool ForceGDI() { return fForceGDI; }
+    bool ForceGDI() const { return fForceGDI; }
 
 protected:
     void weak_dispose() const override {
         if (fDWriteFontCollectionLoader.get()) {
             HRV(fFactory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
         }
         if (fDWriteFontFileLoader.get()) {
             HRV(fFactory->UnregisterFontFileLoader(fDWriteFontFileLoader.get()));
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -76,16 +76,24 @@
 #include "D3D11Checks.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
 
+IDWriteRenderingParams* GetDwriteRenderingParams(bool aGDI)
+{
+  gfxWindowsPlatform::TextRenderingMode mode = aGDI ?
+    gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC :
+    gfxWindowsPlatform::TEXT_RENDERING_NORMAL;
+  return gfxWindowsPlatform::GetPlatform()->GetRenderingParams(mode);
+}
+
 DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
 {
   mDC = nullptr;
   if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
     cairo_t* ctx = static_cast<cairo_t*>
       (aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
     if (ctx) {
       cairo_surface_t* surf = cairo_get_group_target(ctx);
--- a/intl/uconv/nsTextToSubURI.cpp
+++ b/intl/uconv/nsTextToSubURI.cpp
@@ -20,17 +20,17 @@ using mozilla::dom::EncodingUtils;
 // the same as the default "network.IDN.blacklist_chars" value.
 static const char16_t sNetworkIDNBlacklistChars[] =
 {
 /*0x0020,*/
           0x00A0, 0x00BC, 0x00BD, 0x00BE, 0x01C3, 0x02D0, 0x0337,
   0x0338, 0x0589, 0x05C3, 0x05F4, 0x0609, 0x060A, 0x066A, 0x06D4,
   0x0701, 0x0702, 0x0703, 0x0704, 0x115F, 0x1160, 0x1735, 0x2000,
   0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008,
-  0x2009, 0x200A, 0x200B, 0x200E, 0x200F, 0x2024, 0x2027, 0x2028,
+  0x2009, 0x200A, 0x200B, 0x200E, 0x200F, 0x2010, 0x2024, 0x2027, 0x2028,
   0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x202F, 0x2039,
   0x203A, 0x2041, 0x2044, 0x2052, 0x205F, 0x2153, 0x2154, 0x2155,
   0x2156, 0x2157, 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D,
   0x215E, 0x215F, 0x2215, 0x2236, 0x23AE, 0x2571, 0x29F6, 0x29F8,
   0x2AFB, 0x2AFD, 0x2FF0, 0x2FF1, 0x2FF2, 0x2FF3, 0x2FF4, 0x2FF5,
   0x2FF6, 0x2FF7, 0x2FF8, 0x2FF9, 0x2FFA, 0x2FFB, /*0x3000,*/ 0x3002,
   0x3014, 0x3015, 0x3033, 0x3164, 0x321D, 0x321E, 0x33AE, 0x33AF,
   0x33C6, 0x33DF, 0xA789, 0xFE14, 0xFE15, 0xFE3F, 0xFE5D, 0xFE5E,
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -10,26 +10,30 @@
 %{C++
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 #include "js/TypeDecls.h"
 
 struct JSFreeOp;
+namespace js {
+struct Class;
+}
 %}
 
 interface nsIXPConnectWrappedNative;
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(JS::Value);
 [ptr] native JSFreeOpPtr(JSFreeOp);
 [ref] native JSCallArgsRef(const JS::CallArgs);
 [ref] native JSAutoIdVector(JS::AutoIdVector);
+[ptr] native jsClassPtr(const js::Class);
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
  * to *_retval unless they want to return PR_FALSE.
  */
 [uuid(19b70b26-7c3f-437f-a04a-2a8f9e28b617)]
@@ -68,16 +72,17 @@ interface nsIXPCScriptable : nsISupports
 
     // The high order bit is RESERVED for consumers of these flags. 
     // No implementor of this interface should ever return flags 
     // with this bit set.
     const uint32_t RESERVED                         = 1 << 31;
 
     readonly attribute string   className;
     [notxpcom,nostdcall] uint32_t getScriptableFlags();
+    [notxpcom,nostdcall] jsClassPtr getClass();
 
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
     boolean getProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
--- a/js/xpconnect/public/moz.build
+++ b/js/xpconnect/public/moz.build
@@ -2,11 +2,12 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'nsAXPCNativeCallContext.h',
     'nsTArrayHelpers.h',
+    'xpc_make_class.h',
     'xpc_map_end.h',
 ]
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/public/xpc_make_class.h
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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/. */
+
+#ifndef xpc_make_class_h
+#define xpc_make_class_h
+
+// This file should be used to create js::Class instances for nsIXPCScriptable
+// instances. This includes any file that uses xpc_map_end.h.
+
+#include "xpcpublic.h"
+#include "mozilla/dom/DOMJSClass.h"
+
+bool
+XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, JS::HandleObject obj,
+                                  JS::HandleId id, JS::HandleValue v);
+bool
+XPC_WN_CannotModifyPropertyStub(JSContext* cx, JS::HandleObject obj,
+                                JS::HandleId id, JS::HandleValue v);
+
+bool
+XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, JS::HandleObject obj,
+                                        JS::HandleId id,
+                                        JS::ObjectOpResult& result);
+bool
+XPC_WN_CantDeletePropertyStub(JSContext* cx, JS::HandleObject obj,
+                              JS::HandleId id, JS::ObjectOpResult& result);
+
+bool
+XPC_WN_Helper_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                          JS::MutableHandleValue vp);
+
+bool
+XPC_WN_Helper_SetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                          JS::MutableHandleValue vp,
+                          JS::ObjectOpResult& result);
+bool
+XPC_WN_MaybeResolvingSetPropertyStub(JSContext* cx, JS::HandleObject obj,
+                                     JS::HandleId id, JS::MutableHandleValue vp,
+                                     JS::ObjectOpResult& result);
+bool
+XPC_WN_CannotModifySetPropertyStub(JSContext* cx, JS::HandleObject obj,
+                                   JS::HandleId id, JS::MutableHandleValue vp,
+                                   JS::ObjectOpResult& result);
+
+bool
+XPC_WN_Helper_Enumerate(JSContext* cx, JS::HandleObject obj);
+bool
+XPC_WN_Shared_Enumerate(JSContext* cx, JS::HandleObject obj);
+
+bool
+XPC_WN_Helper_Resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      bool* resolvedp);
+
+void
+XPC_WN_Helper_Finalize(js::FreeOp* fop, JSObject* obj);
+void
+XPC_WN_NoHelper_Finalize(js::FreeOp* fop, JSObject* obj);
+
+bool
+XPC_WN_Helper_Call(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+XPC_WN_Helper_HasInstance(JSContext* cx, JS::HandleObject obj,
+                          JS::MutableHandleValue valp, bool* bp);
+
+bool
+XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, JS::Value* vp);
+
+void
+XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj);
+
+extern const js::ClassExtension XPC_WN_JSClassExtension;
+
+extern const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate;
+
+#define XPC_MAKE_CLASS_OPS(_flags) { \
+    /* addProperty */ \
+    ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY) \
+    ? nullptr \
+    : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+      ? XPC_WN_MaybeResolvingPropertyStub \
+      : XPC_WN_CannotModifyPropertyStub, \
+    \
+    /* delProperty */ \
+    ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY) \
+    ? nullptr \
+    : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+      ? XPC_WN_MaybeResolvingDeletePropertyStub \
+      : XPC_WN_CantDeletePropertyStub, \
+    \
+    /* getProperty */ \
+    ((_flags) & nsIXPCScriptable::WANT_GETPROPERTY) \
+    ? XPC_WN_Helper_GetProperty \
+    : nullptr, \
+    \
+    /* setProperty */ \
+    ((_flags) & nsIXPCScriptable::WANT_SETPROPERTY) \
+    ? XPC_WN_Helper_SetProperty \
+    : ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY) \
+      ? nullptr \
+      : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+        ? XPC_WN_MaybeResolvingSetPropertyStub \
+        : XPC_WN_CannotModifySetPropertyStub, \
+    \
+    /* enumerate */ \
+    ((_flags) & nsIXPCScriptable::WANT_NEWENUMERATE) \
+    ? nullptr /* We will use oOps->enumerate set below in this case */ \
+    : ((_flags) & nsIXPCScriptable::WANT_ENUMERATE) \
+      ? XPC_WN_Helper_Enumerate \
+      : XPC_WN_Shared_Enumerate, \
+    \
+    /* resolve */ \
+    /* We have to figure out resolve strategy at call time */ \
+    XPC_WN_Helper_Resolve, \
+    \
+    /* mayResolve */ \
+    nullptr, \
+    \
+    /* finalize */ \
+    ((_flags) & nsIXPCScriptable::WANT_FINALIZE) \
+    ? XPC_WN_Helper_Finalize \
+    : XPC_WN_NoHelper_Finalize, \
+    \
+    /* call */ \
+    ((_flags) & nsIXPCScriptable::WANT_CALL) \
+    ? XPC_WN_Helper_Call \
+    : nullptr, \
+    \
+    /* hasInstance */ \
+    ((_flags) & nsIXPCScriptable::WANT_HASINSTANCE) \
+    ? XPC_WN_Helper_HasInstance \
+    : nullptr, \
+    \
+    /* construct */ \
+    ((_flags) & nsIXPCScriptable::WANT_CONSTRUCT) \
+    ? XPC_WN_Helper_Construct \
+    : nullptr, \
+    \
+    /* trace */ \
+    ((_flags) & nsIXPCScriptable::IS_GLOBAL_OBJECT) \
+    ? JS_GlobalObjectTraceHook \
+    : XPCWrappedNative_Trace, \
+}
+
+#define XPC_MAKE_CLASS(_name, _flags, _classOps) { \
+    /* name */ \
+    _name, \
+    \
+    /* flags */ \
+    XPC_WRAPPER_FLAGS | \
+    JSCLASS_PRIVATE_IS_NSISUPPORTS | \
+    JSCLASS_IS_WRAPPED_NATIVE | \
+    (((_flags) & nsIXPCScriptable::IS_GLOBAL_OBJECT) \
+     ? XPCONNECT_GLOBAL_FLAGS \
+     : 0), \
+    \
+    /* cOps */ \
+    _classOps, \
+    \
+    /* spec */ \
+    nullptr, \
+    \
+    /* ext */ \
+    &XPC_WN_JSClassExtension, \
+    \
+    /* oOps */ \
+    ((_flags) & nsIXPCScriptable::WANT_NEWENUMERATE) \
+    ? &XPC_WN_ObjectOpsWithEnumerate \
+    : nullptr, \
+}
+
+#endif
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -1,14 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* 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/. */
 
+// If you include this file you must also include xpc_make_class.h at the top
+// of the file doing the including.
 
 #ifndef XPC_MAP_CLASSNAME
 #error "Must #define XPC_MAP_CLASSNAME before #including xpc_map_end.h"
 #endif
 
 #ifndef XPC_MAP_QUOTED_CLASSNAME
 #error "Must #define XPC_MAP_QUOTED_CLASSNAME before #including xpc_map_end.h"
 #endif
@@ -62,16 +64,28 @@ XPC_MAP_CLASSNAME::GetScriptableFlags()
     nsIXPCScriptable::WANT_HASINSTANCE |
 #endif
 #ifdef XPC_MAP_FLAGS
     XPC_MAP_FLAGS |
 #endif
     0;
 }
 
+// virtual
+const js::Class*
+XPC_MAP_CLASSNAME::GetClass()
+{
+    static const js::ClassOps classOps =
+        XPC_MAKE_CLASS_OPS(GetScriptableFlags());
+    static const js::Class klass =
+        XPC_MAKE_CLASS(XPC_MAP_QUOTED_CLASSNAME, GetScriptableFlags(),
+                       &classOps);
+    return &klass;
+}
+
 /**************************************************************/
 
 #ifndef XPC_MAP_WANT_PRECREATE
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * cx, JSObject * globalObj, JSObject * *parentObj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_GETPROPERTY
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -18,16 +18,17 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsNullPrincipal.h"
 #include "nsPrincipal.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
+#include "xpc_make_class.h"
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "Crypto.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/CSSBinding.h"
 #include "mozilla/dom/DirectoryBinding.h"
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* 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/. */
 
 /* The "Components" xpcom objects for JavaScript. */
 
 #include "xpcprivate.h"
+#include "xpc_make_class.h"
 #include "xpcIJSModuleLoader.h"
 #include "XPCJSWeakReference.h"
 #include "WrapperFactory.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "jsfriendapi.h"
--- a/js/xpconnect/src/XPCForwards.h
+++ b/js/xpconnect/src/XPCForwards.h
@@ -23,32 +23,30 @@ class nsXPCWrappedJSClass;
 
 class XPCNativeMember;
 class XPCNativeInterface;
 class XPCNativeSet;
 
 class XPCWrappedNative;
 class XPCWrappedNativeProto;
 class XPCWrappedNativeTearOff;
-class XPCNativeScriptableShared;
 class XPCNativeScriptableInfo;
 class XPCNativeScriptableCreateInfo;
 
 class XPCTraceableVariant;
 class XPCJSObjectHolder;
 
 class JSObject2WrappedJSMap;
 class Native2WrappedNativeMap;
 class IID2WrappedJSClassMap;
 class IID2NativeInterfaceMap;
 class ClassInfo2NativeSetMap;
 class ClassInfo2WrappedNativeProtoMap;
 class NativeSetMap;
 class IID2ThisTranslatorMap;
-class XPCNativeScriptableSharedMap;
 class XPCWrappedNativeProtoMap;
 class JSObject2JSObjectMap;
 
 class nsXPCComponents;
 class nsXPCComponents_Interfaces;
 class nsXPCComponents_InterfacesByID;
 class nsXPCComponents_Classes;
 class nsXPCComponents_ClassesByID;
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1547,19 +1547,16 @@ XPCJSContext::~XPCJSContext()
     mClassInfo2NativeSetMap = nullptr;
 
     delete mNativeSetMap;
     mNativeSetMap = nullptr;
 
     delete mThisTranslatorMap;
     mThisTranslatorMap = nullptr;
 
-    delete mNativeScriptableSharedMap;
-    mNativeScriptableSharedMap = nullptr;
-
     delete mDyingWrappedNativeProtoMap;
     mDyingWrappedNativeProtoMap = nullptr;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     // Tell the profiler that the context is gone
     if (PseudoStack* stack = mozilla_get_pseudo_stack())
         stack->sampleContext(nullptr);
 #endif
@@ -3239,17 +3236,16 @@ XPCJSContext::XPCJSContext()
<