Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 26 Jul 2016 12:00:39 +0200
changeset 346673 ef96936825b7c426ac04480ecf46605452409500
parent 346672 4f2b848f4e5d64706920b21fd9a885822ad2803f (current diff)
parent 346670 ff1ef8ec0fd800bf6856c1572c3b1610c45e9b6a (diff)
child 346674 81e206ee8ba75828af38658f5485cacdea04a921
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
netwerk/test/TestStandardURL.cpp
taskcluster/scripts/tester/test-linux.sh
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1037,17 +1037,16 @@ toolbarpaletteitem[place="palette"] > #d
 #main-window[inFullscreen][inDOMFullscreen] chatbar,
 #main-window[inFullscreen][inDOMFullscreen] #social-sidebar-box,
 #main-window[inFullscreen][inDOMFullscreen] #social-sidebar-splitter {
   display: none;
 }
 
 /* Combobox dropdown renderer */
 #ContentSelectDropdown > menupopup {
-  max-height: 350px;
   /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
    * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
   direction: ltr;
 }
 
 /* Indent options in optgroups */
 .contentSelectDropdown-ingroup .menu-iconic-text {
   padding-inline-start: 2em;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7447,17 +7447,17 @@ var gRemoteTabsUI = {
  *        If no suitable window is found, a new one will be opened.
  * @param aOpenParams
  *        If switching to this URI results in us opening a tab, aOpenParams
  *        will be the parameter object that gets passed to openUILinkIn. Please
  *        see the documentation for openUILinkIn to see what parameters can be
  *        passed via this object.
  *        This object also allows:
  *        - 'ignoreFragment' property to be set to true to exclude fragment-portion
- *        matching when comparing URIs.
+ *        matching when comparing URIs. Fragment will be replaced.
  *        - 'ignoreQueryString' property to be set to true to exclude query string
  *        matching when comparing URIs.
  *        - 'replaceQueryString' property to be set to true to exclude query string
  *        matching when comparing URIs and overwrite the initial query string with
  *        the one from the new URI.
  * @return True if an existing tab was found, false otherwise
  */
 function switchToTabHavingURI(aURI, aOpenNew, aOpenParams={}) {
@@ -7483,41 +7483,51 @@ function switchToTabHavingURI(aURI, aOpe
     // are private and they are not in permanent private browsing mode
     if (!kPrivateBrowsingWhitelist.has(aURI.spec) &&
         (PrivateBrowsingUtils.isWindowPrivate(window) ||
          PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
         !PrivateBrowsingUtils.permanentPrivateBrowsing) {
       return false;
     }
 
+    //Remove the query string, fragment, both, or neither from a given url.
+    function cleanURL(url, removeQuery, removeFragment) {
+      let ret = url;
+      if (removeFragment) {
+        ret = ret.split("#")[0];
+        if (removeQuery) {
+          // This removes a query, if present before the fragment.
+          ret = ret.split("?")[0];
+        }
+      } else if (removeQuery) {
+        // This is needed in case there is a fragment after the query.
+        let fragment = ret.split("#")[1];
+        ret = ret.split("?")[0].concat(
+          (fragment != undefined) ? "#".concat(fragment) : "");
+      }
+      return ret;
+    }
+
+    // Need to handle nsSimpleURIs here too (e.g. about:...), which don't
+    // work correctly with URL objects - so treat them as strings
+    let requestedCompare = cleanURL(
+        aURI.spec, ignoreQueryString || replaceQueryString, ignoreFragment);
     let browsers = aWindow.gBrowser.browsers;
     for (let i = 0; i < browsers.length; i++) {
       let browser = browsers[i];
-      if (ignoreFragment ? browser.currentURI.equalsExceptRef(aURI) :
-                           browser.currentURI.equals(aURI)) {
-        // Focus the matching window & tab
+      let browserCompare = cleanURL(
+          browser.currentURI.spec, ignoreQueryString || replaceQueryString, ignoreFragment);
+      if (requestedCompare == browserCompare) {
         aWindow.focus();
-        if (ignoreFragment) {
-          let spec = aURI.spec;
-          browser.loadURI(spec);
+        if (ignoreFragment || replaceQueryString) {
+          browser.loadURI(aURI.spec);
         }
         aWindow.gBrowser.tabContainer.selectedIndex = i;
         return true;
       }
-      if (ignoreQueryString || replaceQueryString) {
-        if (browser.currentURI.spec.split("?")[0] == aURI.spec.split("?")[0]) {
-          // Focus the matching window & tab
-          aWindow.focus();
-          if (replaceQueryString) {
-            browser.loadURI(aURI.spec);
-          }
-          aWindow.gBrowser.tabContainer.selectedIndex = i;
-          return true;
-        }
-      }
     }
     return false;
   }
 
   // This can be passed either nsIURI or a string.
   if (!(aURI instanceof Ci.nsIURI))
     aURI = Services.io.newURI(aURI, null, null);
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -218,18 +218,19 @@ nsContextMenu.prototype = {
     this.showItem("context-video-saveimage", this.onVideo);
     this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
     this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
     // Send media URL (but not for canvas, since it's a big data: URL)
     this.showItem("context-sendimage", this.onImage);
     this.showItem("context-sendvideo", this.onVideo);
     this.showItem("context-castvideo", this.onVideo);
     this.showItem("context-sendaudio", this.onAudio);
-    this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL);
-    this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL);
+    let mediaIsBlob = this.mediaURL.startsWith("blob:");
+    this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL || mediaIsBlob);
+    this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL || mediaIsBlob);
     let shouldShowCast = Services.prefs.getBoolPref("browser.casting.enabled");
     // getServicesForVideo alone would be sufficient here (it depends on
     // SimpleServiceDiscovery.services), but SimpleServiceDiscovery is guaranteed
     // to be already loaded, since we load it on startup in nsBrowserGlue,
     // and CastingApps isn't, so check SimpleServiceDiscovery.services first
     // to avoid needing to load CastingApps.jsm if we don't need to.
     shouldShowCast = shouldShowCast && this.mediaURL &&
                      SimpleServiceDiscovery.services.length > 0 &&
@@ -377,17 +378,17 @@ nsContextMenu.prototype = {
     let pageShare = shareEnabled && !(this.isContentSelected ||
                             this.onTextInput || this.onLink || this.onImage ||
                             this.onVideo || this.onAudio || this.onCanvas);
     this.showItem("context-sharepage", pageShare);
     this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
     this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
     this.showItem("context-shareimage", shareEnabled && this.onImage);
     this.showItem("context-sharevideo", shareEnabled && this.onVideo);
-    this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL);
+    this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:"));
   },
 
   initSpellingItems: function() {
     var canSpell = InlineSpellCheckerUI.canSpellCheck &&
                    !InlineSpellCheckerUI.initialSpellCheckPending &&
                    this.canSpellCheck;
     var onMisspelling = InlineSpellCheckerUI.overMisspelling;
     var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
@@ -1636,17 +1637,20 @@ nsContextMenu.prototype = {
   },
 
   // Kept for addon compat
   linkText: function() {
     return this.linkTextStr;
   },
 
   isMediaURLReusable: function(aURL) {
-    return !/^(?:blob|mediasource):/.test(aURL);
+    if (aURL.startsWith("blob:")) {
+      return URL.isValidURL(aURL);
+    }
+    return true;
   },
 
   toString: function () {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -363,16 +363,17 @@ add_task(function* test_large_popup() {
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let doc = content.document;
     let select = doc.getElementById("one");
     for (var i = 0; i < 180; i++) {
       select.add(new content.Option("Test" + i));
     }
 
+    select.options[60].selected = true;
     select.focus();
   });
 
   let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
   let browserRect = tab.linkedBrowser.getBoundingClientRect();
 
   let positions = [
     "margin-top: 300px;",
@@ -383,16 +384,26 @@ add_task(function* test_large_popup() {
   let position;
   while (true) {
     yield openSelectPopup(selectPopup, false);
 
     let rect = selectPopup.getBoundingClientRect();
     ok(rect.top >= browserRect.top, "Popup top position in within browser area");
     ok(rect.bottom <= browserRect.bottom, "Popup bottom position in within browser area");
 
+    // Don't check the scroll position for the last step as the popup will be cut off.
+    if (positions.length == 1) {
+      let cs = window.getComputedStyle(selectPopup);
+      let bpBottom = parseFloat(cs.paddingBottom) + parseFloat(cs.borderBottomWidth);
+
+      is(selectPopup.childNodes[60].getBoundingClientRect().bottom,
+         selectPopup.getBoundingClientRect().bottom - bpBottom,
+         "Popup scroll at correct position " + bpBottom);
+    }
+
     yield hideSelectPopup(selectPopup, false);
 
     position = positions.shift();
     if (!position) {
       break;
     }
 
     let contentPainted = BrowserTestUtils.contentPainted(tab.linkedBrowser);
--- a/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
+++ b/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
@@ -1,13 +1,13 @@
 /* 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/. */
 
-add_task(function test_ignoreFragment() {
+add_task(function *test_ignoreFragment() {
   let tabRefAboutHome = gBrowser.addTab("about:home#1");
   yield promiseTabLoaded(tabRefAboutHome);
   let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
   yield promiseTabLoaded(tabRefAboutMozilla);
 
   gBrowser.selectedTab = tabRefAboutMozilla;
   let numTabsAtStart = gBrowser.tabs.length;
 
@@ -30,34 +30,34 @@ add_task(function test_ignoreFragment() 
   yield promiseWaitForCondition(function() {
     return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home";
   });
   is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash");
   switchTab("about:about", false, { ignoreFragment: true });
   cleanupTestTabs();
 });
 
-add_task(function test_ignoreQueryString() {
+add_task(function* test_ignoreQueryString() {
   let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
   yield promiseTabLoaded(tabRefAboutHome);
   let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
   yield promiseTabLoaded(tabRefAboutMozilla);
   gBrowser.selectedTab = tabRefAboutMozilla;
 
   switchTab("about:home?hello=firefox", true);
   switchTab("about:home?hello=firefoxos", false);
   // Remove the last opened tab to test ignoreQueryString option.
   gBrowser.removeCurrentTab();
   switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true });
   is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
   is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string");
   cleanupTestTabs();
 });
 
-add_task(function test_replaceQueryString() {
+add_task(function* test_replaceQueryString() {
   let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
   yield promiseTabLoaded(tabRefAboutHome);
   let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
   yield promiseTabLoaded(tabRefAboutMozilla);
   gBrowser.selectedTab = tabRefAboutMozilla;
 
   switchTab("about:home", false);
   switchTab("about:home?hello=firefox", true);
@@ -67,16 +67,48 @@ add_task(function test_replaceQueryStrin
   switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true });
   is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
   // Wait for the tab to load the new URI spec.
   yield promiseTabLoaded(tabRefAboutHome);
   is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec");
   cleanupTestTabs();
 });
 
+add_task(function* test_replaceQueryStringAndFragment() {
+  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox#aaa");
+  yield promiseTabLoaded(tabRefAboutHome);
+  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla?hello=firefoxos#aaa");
+  yield promiseTabLoaded(tabRefAboutMozilla);
+  gBrowser.selectedTab = tabRefAboutMozilla;
+
+  switchTab("about:home", false);
+  gBrowser.removeCurrentTab();
+  switchTab("about:home?hello=firefox#aaa", true);
+  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
+  switchTab("about:mozilla?hello=firefox#bbb", true, { replaceQueryString: true, ignoreFragment: true });
+  is(tabRefAboutMozilla, gBrowser.selectedTab, "Selected tab should be the initial about:mozilla tab");
+  switchTab("about:home?hello=firefoxos#bbb", true, { ignoreQueryString: true, ignoreFragment: true });
+  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
+  cleanupTestTabs();
+});
+
+add_task(function* test_ignoreQueryStringIgnoresFragment() {
+  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox#aaa");
+  yield promiseTabLoaded(tabRefAboutHome);
+  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla?hello=firefoxos#aaa");
+  yield promiseTabLoaded(tabRefAboutMozilla);
+  gBrowser.selectedTab = tabRefAboutMozilla;
+
+  switchTab("about:home?hello=firefox#bbb", false, { ignoreQueryString: true });
+  gBrowser.removeCurrentTab();
+  switchTab("about:home?hello=firefoxos#aaa", true, { ignoreQueryString: true });
+  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
+  cleanupTestTabs();
+});
+
 // Begin helpers
 
 function cleanupTestTabs() {
   while (gBrowser.tabs.length > 1)
     gBrowser.removeCurrentTab();
 }
 
 function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) {
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -622,17 +622,17 @@ function openPreferences(paneID, extraAr
     let supportsStringPrefURL = Cc["@mozilla.org/supports-string;1"]
                                   .createInstance(Ci.nsISupportsString);
     supportsStringPrefURL.data = preferencesURL;
     windowArguments.AppendElement(supportsStringPrefURL);
 
     win = Services.ww.openWindow(null, Services.prefs.getCharPref("browser.chromeURL"),
                                  "_blank", "chrome,dialog=no,all", windowArguments);
   } else {
-    newLoad = !win.switchToTabHavingURI(preferencesURL, true, {ignoreFragment: true});
+    newLoad = !win.switchToTabHavingURI(preferencesURL, true, { ignoreFragment: true, replaceQueryString: true });
     browser = win.gBrowser.selectedBrowser;
   }
 
   if (newLoad) {
     Services.obs.addObserver(function advancedPaneLoadedObs(prefWin, topic, data) {
       if (!browser) {
         browser = win.gBrowser.selectedBrowser;
       }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -135,22 +135,34 @@ toolbarbutton.bookmark-item:not(.subview
   margin: 0;
   padding: 2px 3px;
 }
 
 toolbarbutton.bookmark-item:not(.subviewbutton):not(:hover):not(:active):not([open]) {
   color: inherit;
 }
 
+toolbarbutton.bookmark-item:not(.subviewbutton) {
+  -moz-appearance: none;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  transition-property: background-color, border-color;
+  transition-duration: 150ms;
+}
+
+toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([open]) {
+  background-color: var(--toolbarbutton-hover-background);
+  border-color: var(--toolbarbutton-hover-bordercolor);
+}
+
 toolbarbutton.bookmark-item:not(.subviewbutton):hover:active,
 toolbarbutton.bookmark-item[open="true"] {
-  padding-top: 3px;
-  padding-bottom: 1px;
-  padding-inline-start: 4px;
-  padding-inline-end: 2px;
+  background: var(--toolbarbutton-active-background);
+  box-shadow: var(--toolbarbutton-active-boxshadow);
+  border-color: var(--toolbarbutton-active-bordercolor);
 }
 
 .bookmark-item > .toolbarbutton-icon,
 #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
@@ -1665,17 +1677,17 @@ menuitem:hover > hbox > .alltabs-endimag
   list-style-image: url("chrome://global/skin/icons/loading.png");
   margin-inline-end: 4px;
 }
 
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
 }
 
-toolbar[brighttext] toolbarbutton.chevron:not(:hover):not([open="true"]) {
+toolbar[brighttext] toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
 }
 
 toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 toolbarbutton.chevron > .toolbarbutton-text,
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -459,24 +459,30 @@
 }
 
 /* ::::: bookmark buttons ::::: */
 
 toolbarbutton.bookmark-item:not(.subviewbutton),
 #personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
   margin: 0;
   padding: 2px 3px;
+  -moz-appearance: none;
+  border: 1px solid transparent;
 }
 
-toolbarbutton.bookmark-item:not([disabled="true"]):not(.subviewbutton):hover:active,
+toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]) {
+  border-color: var(--toolbarbutton-hover-bordercolor);
+  background: var(--toolbarbutton-hover-background);
+}
+
+toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]),
 toolbarbutton.bookmark-item[open="true"] {
-  padding-top: 3px;
-  padding-bottom: 1px;
-  padding-inline-start: 4px;
-  padding-inline-end: 2px;
+  border-color: var(--toolbarbutton-active-bordercolor);
+  box-shadow: var(--toolbarbutton-active-boxshadow);
+  background: var(--toolbarbutton-active-background);
 }
 
 .bookmark-item > .toolbarbutton-icon,
 #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
--- a/devtools/client/themes/images/filter.svg
+++ b/devtools/client/themes/images/filter.svg
@@ -1,7 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#aaa">
-  <path fill-opacity=".3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3 0-.4-1.7-2.7-1.7-2.7H13s-1.8 2-1.8 2.7c0 .3-2.1-.1-2.1.3v6.1H7s-.4-4.1-.4-6.1z"/>
-  <path d="M2 2v2.3L4.7 9H6v5.4l2.1 1 1.8-.9V9h1.3L14 4.3V2H2zm11 2l-2.2 4H9v5.8l-.9.4-1.1-.5V8H5.2L3 4V3h10v1z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#0b0b0b">
+  <style>
+    /* Use a fill that's visible on both light and dark themes for filter inputs */
+    #filterinput:target + #icon {
+      fill: #aaa;
+    }
+  </style>
+  <g id="filterinput"/>
+  <g id="icon">
+    <path fill-opacity=".3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3 0-.4-1.7-2.7-1.7-2.7H13s-1.8 2-1.8 2.7c0 .3-2.1-.1-2.1.3v6.1H7s-.4-4.1-.4-6.1z"/>
+    <path d="M2 2v2.3L4.7 9H6v5.4l2.1 1 1.8-.9V9h1.3L14 4.3V2H2zm11 2l-2.2 4H9v5.8l-.9.4-1.1-.5V8H5.2L3 4V3h10v1z"/>
+  </g>
 </svg>
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -359,17 +359,17 @@
   font-size: inherit;
 }
 
 .devtools-searchinput {
   background-image: var(--magnifying-glass-image);
 }
 
 .devtools-filterinput {
-  background-image: var(--filter-image);
+  background-image: url(images/filter.svg#filterinput);
 }
 
 .devtools-searchinput:-moz-locale-dir(rtl),
 .devtools-searchinput:dir(rtl),
 .devtools-filterinput:-moz-locale-dir(rtl),
 .devtools-filterinput:dir(rtl) {
   background-position: calc(100% - 8px) center;
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/CustomElementsRegistry.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/dom/CustomElementsRegistry.h"
+#include "mozilla/dom/CustomElementsRegistryBinding.h"
+#include "mozilla/dom/WebComponentsBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+
+// Only needed for refcounted objects.
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CustomElementsRegistry, mWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementsRegistry)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementsRegistry)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/* static */ bool
+CustomElementsRegistry::IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject)
+{
+  JS::Rooted<JSObject*> obj(aCx, aObject);
+  if (Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
+      nsDocument::IsWebComponentsEnabled(aCx, obj)) {
+    return true;
+  }
+
+  return false;
+}
+
+/* static */ already_AddRefed<CustomElementsRegistry>
+CustomElementsRegistry::Create(nsPIDOMWindowInner* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  if (!aWindow->GetDocShell()) {
+    return nullptr;
+  }
+
+  RefPtr<CustomElementsRegistry> customElementsRegistry =
+    new CustomElementsRegistry(aWindow);
+  return customElementsRegistry.forget();
+}
+
+CustomElementsRegistry::CustomElementsRegistry(nsPIDOMWindowInner* aWindow)
+ : mWindow(aWindow)
+{
+}
+
+CustomElementsRegistry::~CustomElementsRegistry()
+{
+}
+
+JSObject*
+CustomElementsRegistry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return CustomElementsRegistryBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports* CustomElementsRegistry::GetParentObject() const
+{
+  return mWindow;
+}
+
+void CustomElementsRegistry::Define(const nsAString& aName,
+                                    Function& aFunctionConstructor,
+                                    const ElementDefinitionOptions& aOptions,
+                                    ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275835
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+void
+CustomElementsRegistry::Get(JSContext* aCx, const nsAString& aName,
+                            JS::MutableHandle<JS::Value> aRetVal,
+                            ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275838
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+already_AddRefed<Promise>
+CustomElementsRegistry::WhenDefined(const nsAString& name, ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275839
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
+                                                 nsIAtom* aType,
+                                                 nsIAtom* aLocalName,
+                                                 LifecycleCallbacks* aCallbacks,
+                                                 uint32_t aNamespaceID,
+                                                 uint32_t aDocOrder)
+  : mPrototype(aPrototype),
+    mType(aType),
+    mLocalName(aLocalName),
+    mCallbacks(aCallbacks),
+    mNamespaceID(aNamespaceID),
+    mDocOrder(aDocOrder)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/CustomElementsRegistry.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_CustomElementsRegistry_h
+#define mozilla_dom_CustomElementsRegistry_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/FunctionBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+struct ElementDefinitionOptions;
+struct LifecycleCallbacks;
+class Function;
+class Promise;
+
+class CustomElementHashKey : public PLDHashEntryHdr
+{
+public:
+  typedef CustomElementHashKey *KeyType;
+  typedef const CustomElementHashKey *KeyTypePointer;
+
+  CustomElementHashKey(int32_t aNamespaceID, nsIAtom *aAtom)
+    : mNamespaceID(aNamespaceID),
+      mAtom(aAtom)
+  {}
+  explicit CustomElementHashKey(const CustomElementHashKey* aKey)
+    : mNamespaceID(aKey->mNamespaceID),
+      mAtom(aKey->mAtom)
+  {}
+  ~CustomElementHashKey()
+  {}
+
+  KeyType GetKey() const { return const_cast<KeyType>(this); }
+  bool KeyEquals(const KeyTypePointer aKey) const
+  {
+    MOZ_ASSERT(mNamespaceID != kNameSpaceID_Unknown,
+               "This equals method is not transitive, nor symmetric. "
+               "A key with a namespace of kNamespaceID_Unknown should "
+               "not be stored in a hashtable.");
+    return (kNameSpaceID_Unknown == aKey->mNamespaceID ||
+            mNamespaceID == aKey->mNamespaceID) &&
+           aKey->mAtom == mAtom;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+  static PLDHashNumber HashKey(const KeyTypePointer aKey)
+  {
+    return aKey->mAtom->hash();
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+private:
+  int32_t mNamespaceID;
+  nsCOMPtr<nsIAtom> mAtom;
+};
+
+// The required information for a custom element as defined in:
+// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
+struct CustomElementDefinition
+{
+  CustomElementDefinition(JSObject* aPrototype,
+                          nsIAtom* aType,
+                          nsIAtom* aLocalName,
+                          mozilla::dom::LifecycleCallbacks* aCallbacks,
+                          uint32_t aNamespaceID,
+                          uint32_t aDocOrder);
+
+  // The prototype to use for new custom elements of this type.
+  JS::Heap<JSObject *> mPrototype;
+
+  // The type (name) for this custom element.
+  nsCOMPtr<nsIAtom> mType;
+
+  // The localname to (e.g. <button is=type> -- this would be button).
+  nsCOMPtr<nsIAtom> mLocalName;
+
+  // The lifecycle callbacks to call for this custom element.
+  nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
+
+  // Whether we're currently calling the created callback for a custom element
+  // of this type.
+  bool mElementIsBeingCreated;
+
+  // Element namespace.
+  int32_t mNamespaceID;
+
+  // The document custom element order.
+  uint32_t mDocOrder;
+};
+
+class CustomElementsRegistry final : public nsISupports,
+                                     public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementsRegistry)
+
+public:
+  static bool IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject);
+  static already_AddRefed<CustomElementsRegistry> Create(nsPIDOMWindowInner* aWindow);
+  already_AddRefed<nsIDocument> GetOwnerDocument() const;
+
+private:
+  explicit CustomElementsRegistry(nsPIDOMWindowInner* aWindow);
+  ~CustomElementsRegistry();
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+
+public:
+  nsISupports* GetParentObject() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  void Define(const nsAString& aName, Function& aFunctionConstructor,
+              const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
+
+  void Get(JSContext* cx, const nsAString& name,
+           JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+  already_AddRefed<Promise> WhenDefined(const nsAString& name, ErrorResult& aRv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif // mozilla_dom_CustomElementsRegistry_h
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -555,17 +555,17 @@ File::Constructor(const GlobalObject& aG
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   Blob& aData,
                   const ChromeFilePropertyBag& aBag,
                   ErrorResult& aRv)
 {
   if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
-    aRv.Throw(NS_ERROR_FAILURE);
+    aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("Argument 1 of File.constructor"));
     return nullptr;
   }
 
   RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(EmptyString());
   impl->InitializeChromeFile(aData, aBag, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -151,16 +151,17 @@ EXPORTS.mozilla.dom += [
     'Attr.h',
     'BarProps.h',
     'BlobSet.h',
     'BodyUtil.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
+    'CustomElementsRegistry.h',
     'DirectionalityUtils.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMMatrix.h',
@@ -211,16 +212,17 @@ UNIFIED_SOURCES += [
     'BarProps.cpp',
     'BlobSet.cpp',
     'BodyUtil.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
+    'CustomElementsRegistry.cpp',
     'DirectionalityUtils.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -25,16 +25,17 @@
 #include "nsFrame.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "ClientLayerManager.h"
 #include "nsQueryObject.h"
 #ifdef MOZ_FMP4
 #include "MP4Decoder.h"
 #endif
+#include "CubebUtils.h"
 
 #include "nsIScrollableFrame.h"
 
 #include "nsContentUtils.h"
 
 #include "nsIFrame.h"
 #include "nsIWidget.h"
 #include "nsCharsetSource.h"
@@ -2322,16 +2323,23 @@ nsDOMWindowUtils::GetSupportsHardwareH26
   }
   promise->MaybeResolve(NS_LITERAL_STRING("No; Compiled without MP4 support."));
   aPromise.setObject(*promise->PromiseObj());
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetCurrentAudioBackend(nsAString& aBackend)
+{
+  CubebUtils::GetCurrentBackend(aBackend);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::StartFrameTimeRecording(uint32_t *startIndex)
 {
   NS_ENSURE_ARG_POINTER(startIndex);
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -464,31 +464,16 @@ CustomElementCallback::CustomElementCall
                                              CustomElementData* aOwnerData)
   : mThisObject(aThisObject),
     mCallback(aCallback),
     mType(aCallbackType),
     mOwnerData(aOwnerData)
 {
 }
 
-CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
-                                                 nsIAtom* aType,
-                                                 nsIAtom* aLocalName,
-                                                 LifecycleCallbacks* aCallbacks,
-                                                 uint32_t aNamespaceID,
-                                                 uint32_t aDocOrder)
-  : mPrototype(aPrototype),
-    mType(aType),
-    mLocalName(aLocalName),
-    mCallbacks(aCallbacks),
-    mNamespaceID(aNamespaceID),
-    mDocOrder(aDocOrder)
-{
-}
-
 CustomElementData::CustomElementData(nsIAtom* aType)
   : mType(aType),
     mCurrentCallback(-1),
     mElementIsBeingCreated(false),
     mCreatedCallbackInvoked(true),
     mAssociatedMicroTask(-1)
 {
 }
@@ -4763,17 +4748,18 @@ nsDocument::SetScriptGlobalObject(nsIScr
 #endif
         bool allowDNSPrefetch;
         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
         mAllowDNSPrefetch = allowDNSPrefetch;
       }
     }
 
     MaybeRescheduleAnimationFrameNotifications();
-    if (Preferences::GetBool("dom.webcomponents.enabled")) {
+    if (Preferences::GetBool("dom.webcomponents.enabled") ||
+        Preferences::GetBool("dom.webcomponents.customelements.enabled")) {
       mRegistry = new Registry();
     }
   }
 
   // Remember the pointer to our window (or lack there of), to avoid
   // having to QI every time it's asked for.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
   mWindow = window;
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -63,16 +63,17 @@
 #include "mozilla/dom/StyleSheetList.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMXPathEvaluator.h"
 #include "jsfriendapi.h"
 #include "ImportManager.h"
 #include "mozilla/LinkedList.h"
+#include "CustomElementsRegistry.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsDOMStyleSheetSetList;
@@ -127,17 +128,17 @@ public:
 } // namespace dom
 } // namespace mozilla
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
- * 
+ *
  * We also store the document.all result list here. This is mainly so that
  * when all elements with the given ID are removed and we remove
  * the ID's nsIdentifierMapEntry, the document.all result is released too.
  * Perhaps the document.all results should have their own hashtable
  * in nsHTMLDocument.
  */
 class nsIdentifierMapEntry : public nsStringHashKey
 {
@@ -258,58 +259,16 @@ private:
   nsTArray<Element*> mIdContentList;
   RefPtr<nsBaseContentList> mNameContentList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
   RefPtr<Element> mImageElement;
 };
 
 namespace mozilla {
 namespace dom {
-
-class CustomElementHashKey : public PLDHashEntryHdr
-{
-public:
-  typedef CustomElementHashKey *KeyType;
-  typedef const CustomElementHashKey *KeyTypePointer;
-
-  CustomElementHashKey(int32_t aNamespaceID, nsIAtom *aAtom)
-    : mNamespaceID(aNamespaceID),
-      mAtom(aAtom)
-  {}
-  explicit CustomElementHashKey(const CustomElementHashKey* aKey)
-    : mNamespaceID(aKey->mNamespaceID),
-      mAtom(aKey->mAtom)
-  {}
-  ~CustomElementHashKey()
-  {}
-
-  KeyType GetKey() const { return const_cast<KeyType>(this); }
-  bool KeyEquals(const KeyTypePointer aKey) const
-  {
-    MOZ_ASSERT(mNamespaceID != kNameSpaceID_Unknown,
-               "This equals method is not transitive, nor symmetric. "
-               "A key with a namespace of kNamespaceID_Unknown should "
-               "not be stored in a hashtable.");
-    return (kNameSpaceID_Unknown == aKey->mNamespaceID ||
-            mNamespaceID == aKey->mNamespaceID) &&
-           aKey->mAtom == mAtom;
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
-  static PLDHashNumber HashKey(const KeyTypePointer aKey)
-  {
-    return aKey->mAtom->hash();
-  }
-  enum { ALLOW_MEMMOVE = true };
-
-private:
-  int32_t mNamespaceID;
-  nsCOMPtr<nsIAtom> mAtom;
-};
-
 struct LifecycleCallbackArgs
 {
   nsString name;
   nsString oldValue;
   nsString newValue;
 };
 
 struct CustomElementData;
@@ -371,50 +330,16 @@ struct CustomElementData
 
   // Empties the callback queue.
   void RunCallbackQueue();
 
 private:
   virtual ~CustomElementData() {}
 };
 
-// The required information for a custom element as defined in:
-// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
-struct CustomElementDefinition
-{
-  CustomElementDefinition(JSObject* aPrototype,
-                          nsIAtom* aType,
-                          nsIAtom* aLocalName,
-                          mozilla::dom::LifecycleCallbacks* aCallbacks,
-                          uint32_t aNamespaceID,
-                          uint32_t aDocOrder);
-
-  // The prototype to use for new custom elements of this type.
-  JS::Heap<JSObject *> mPrototype;
-
-  // The type (name) for this custom element.
-  nsCOMPtr<nsIAtom> mType;
-
-  // The localname to (e.g. <button is=type> -- this would be button).
-  nsCOMPtr<nsIAtom> mLocalName;
-
-  // The lifecycle callbacks to call for this custom element.
-  nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
-
-  // Whether we're currently calling the created callback for a custom element
-  // of this type.
-  bool mElementIsBeingCreated;
-
-  // Element namespace.
-  int32_t mNamespaceID;
-
-  // The document custom element order.
-  uint32_t mDocOrder;
-};
-
 class Registry : public nsISupports
 {
 public:
   friend class ::nsDocument;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Registry)
 
@@ -611,17 +536,17 @@ protected:
     // that the notificationCallbacks on a loadgroup aren't the docshell itself
     // but a shim that holds a weak reference to the docshell.
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
 
     // Use shims for interfaces that docshell implements directly so that we
     // don't hand out references to the docshell.  The shims should all allow
     // getInterface back on us, but other than that each one should only
     // implement one interface.
-    
+
     // XXXbz I wish we could just derive the _allcaps thing from _i
 #define DECL_SHIM(_i, _allcaps)                                              \
     class _i##Shim final : public nsIInterfaceRequestor,                     \
                            public _i                                         \
     {                                                                        \
       ~_i##Shim() {}                                                         \
     public:                                                                  \
       _i##Shim(nsIInterfaceRequestor* aIfreq, _i* aRealPtr)                  \
@@ -650,17 +575,17 @@ protected:
    * Add an ExternalResource for aURI.  aViewer and aLoadGroup might be null
    * when this is called if the URI didn't result in an XML document.  This
    * function makes sure to remove the pending load for aURI, if any, from our
    * hashtable, and to notify its observers, if any.
    */
   nsresult AddExternalResource(nsIURI* aURI, nsIContentViewer* aViewer,
                                nsILoadGroup* aLoadGroup,
                                nsIDocument* aDisplayDocument);
-  
+
   nsClassHashtable<nsURIHashKey, ExternalResource> mMap;
   nsRefPtrHashtable<nsURIHashKey, PendingLoad> mPendingLoads;
   bool mHaveShutDown;
 };
 
 // Base class for our document implementations.
 //
 // Note that this class *implements* nsIDOMXMLDocument, but it's not
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1560,16 +1560,17 @@ nsGlobalWindow::CleanUp()
   mMenubar = nullptr;
   mToolbar = nullptr;
   mLocationbar = nullptr;
   mPersonalbar = nullptr;
   mStatusbar = nullptr;
   mScrollbars = nullptr;
   mLocation = nullptr;
   mHistory = nullptr;
+  mCustomElements = nullptr;
   mFrames = nullptr;
   mWindowUtils = nullptr;
   mApplicationCache = nullptr;
   mIndexedDB = nullptr;
 
   mConsole = nullptr;
 
   mExternal = nullptr;
@@ -1690,16 +1691,17 @@ nsGlobalWindow::FreeInnerObjects()
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nullptr;
   }
 
   mLocation = nullptr;
   mHistory = nullptr;
+  mCustomElements = nullptr;
 
   if (mNavigator) {
     mNavigator->OnNavigation();
     mNavigator->Invalidate();
     mNavigator = nullptr;
   }
 
   if (mScreen) {
@@ -1872,16 +1874,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
        timeout;
        timeout = timeout->getNext()) {
     cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
@@ -1945,16 +1948,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   }
 
   if (tmp->mListenerManager) {
     tmp->mListenerManager->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
   if (tmp->mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
@@ -3827,16 +3831,27 @@ nsGlobalWindow::GetHistory(ErrorResult& 
 
   if (!mHistory) {
     mHistory = new nsHistory(AsInner());
   }
 
   return mHistory;
 }
 
+CustomElementsRegistry*
+nsGlobalWindow::CustomElements()
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+  if (!mCustomElements) {
+      mCustomElements = CustomElementsRegistry::Create(AsInner());
+  }
+
+  return mCustomElements;
+}
+
 Performance*
 nsPIDOMWindowInner::GetPerformance()
 {
   MOZ_ASSERT(IsInnerWindow());
   CreatePerformanceObjectIfNeeded();
   return mPerformance;
 }
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -101,16 +101,17 @@ class nsWindowSizes;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
+class CustomElementsRegistry;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint32_t;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
@@ -200,17 +201,16 @@ public:
   mozilla::TimeDuration mTimeRemaining;
 
   // Principal with which to execute
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   // stack depth at which timeout is firing
   uint32_t mFiringDepth;
 
-  // 
   uint32_t mNestingLevel;
 
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupControlState mPopupState;
 
   // The language-specific information about the callback.
   nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
@@ -873,16 +873,17 @@ public:
   }
   void GetNameOuter(nsAString& aName);
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetNameOuter(const nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
   nsLocation* GetLocation(mozilla::ErrorResult& aError);
   nsIDOMLocation* GetLocation() override;
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
+  mozilla::dom::CustomElementsRegistry* CustomElements() override;
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatusOuter(nsAString& aStatus);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
@@ -1833,16 +1834,17 @@ protected:
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   // This is a dummy timeout at the moment; if that ever changes, the logic in
   // ResetTimersForNonBackgroundWindow needs to change.
   nsTimeout*                    mTimeoutInsertionPoint;
   uint32_t                      mTimeoutPublicIdCounter;
   uint32_t                      mTimeoutFiringDepth;
   RefPtr<nsLocation>          mLocation;
   RefPtr<nsHistory>           mHistory;
+  RefPtr<mozilla::dom::CustomElementsRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
   typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
   uint32_t mTimeoutsSuspendDepth;
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -571,16 +571,22 @@ nsHostObjectProtocolHandler::RemoveDataE
     return;
   }
 
   gDataTable->Clear();
   delete gDataTable;
   gDataTable = nullptr;
 }
 
+/* static */ bool
+nsHostObjectProtocolHandler::HasDataEntry(const nsACString& aUri)
+{
+  return !!GetDataInfo(aUri);
+}
+
 nsresult
 nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
                                                nsIPrincipal* aPrincipal,
                                                nsACString& aUri)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -70,16 +70,18 @@ public:
                                mozilla::dom::BlobImpl* aBlobImpl);
 
   static void RemoveDataEntry(const nsACString& aUri,
                               bool aBroadcastToOTherProcesses = true);
 
   // This is for IPC only.
   static void RemoveDataEntries();
 
+  static bool HasDataEntry(const nsACString& aUri);
+
   static nsIPrincipal* GetDataEntryPrincipal(const nsACString& aUri);
   static void Traverse(const nsACString& aUri, nsCycleCollectionTraversalCallback& aCallback);
 
   static bool
   GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
                        mozilla::dom::ContentParent* aCP);
 
 protected:
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -39,16 +39,17 @@ struct nsTimeout;
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
+class CustomElementsRegistry;
 } // namespace dom
 namespace gfx {
 class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
@@ -91,17 +92,17 @@ class nsPIDOMWindow : public T
 {
 public:
   nsPIDOMWindowInner* AsInner();
   const nsPIDOMWindowInner* AsInner() const;
   nsPIDOMWindowOuter* AsOuter();
   const nsPIDOMWindowOuter* AsOuter() const;
 
   virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
-
+  virtual mozilla::dom::CustomElementsRegistry* CustomElements() = 0;
   // Outer windows only.
   virtual void ActivateOrDeactivate(bool aActivate) = 0;
 
   // this is called GetTopWindowRoot to avoid conflicts with nsIDOMWindow::GetWindowRoot
   /**
    * |top| gets the root of the window hierarchy.
    *
    * This function does not cross chrome-content boundaries, so if this
@@ -316,17 +317,17 @@ public:
   /**
    * Call this to check whether some node (this window, its document,
    * or content in that document) has a paint event listener.
    */
   bool HasPaintEventListeners()
   {
     return mMayHavePaintEventListener;
   }
-  
+
   /**
    * Call this to indicate that some node (this window, its document,
    * or content in that document) has a touch event listener.
    */
   void SetHasTouchEventListeners()
   {
     if (!mMayHaveTouchEventListener) {
       mMayHaveTouchEventListener = true;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -82,25 +82,53 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTION_0(nsScriptLoadRequest)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptLoadRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoadRequest)
 
 nsScriptLoadRequest::~nsScriptLoadRequest()
 {
   js_free(mScriptTextBuf);
+
+  // We should always clean up any off-thread script parsing resources.
+  MOZ_ASSERT(!mOffThreadToken);
+
+  // But play it safe in release builds and try to clean them up here
+  // as a fail safe.
+  MaybeCancelOffThreadScript();
 }
 
 void
 nsScriptLoadRequest::SetReady()
 {
   MOZ_ASSERT(mProgress != Progress::Ready);
   mProgress = Progress::Ready;
 }
 
+void
+nsScriptLoadRequest::Cancel()
+{
+  MaybeCancelOffThreadScript();
+  mIsCanceled = true;
+}
+
+void
+nsScriptLoadRequest::MaybeCancelOffThreadScript()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mOffThreadToken) {
+    return;
+  }
+
+  JSContext* cx = JS_GetContext(xpc::GetJSRuntime());
+  JS::CancelOffThreadScript(cx, mOffThreadToken);
+  mOffThreadToken = nullptr;
+}
+
 //////////////////////////////////////////////////////////////
 // nsModuleLoadRequest
 //////////////////////////////////////////////////////////////
 
 // A load request for a module, created for every top level module script and
 // every module import.  Load request can share an nsModuleScript if there are
 // multiple imports of the same module.
 
@@ -1824,19 +1852,17 @@ nsScriptLoader::ProcessRequest(nsScriptL
   }
 
   if (aRequest->mOffThreadToken) {
     // The request was parsed off-main-thread, but the result of the off
     // thread parse was not actually needed to process the request
     // (disappearing window, some other error, ...). Finish the
     // request to avoid leaks in the JS engine.
     MOZ_ASSERT(!aRequest->IsModuleRequest());
-    JSContext* cx = JS_GetContext(xpc::GetJSRuntime());
-    JS::CancelOffThreadScript(cx, aRequest->mOffThreadToken);
-    aRequest->mOffThreadToken = nullptr;
+    aRequest->MaybeCancelOffThreadScript();
   }
 
   // Free any source data.
   free(aRequest->mScriptTextBuf);
   aRequest->mScriptTextBuf = nullptr;
   aRequest->mScriptTextLength = 0;
 
   return rv;
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -113,20 +113,17 @@ public:
     mElement->ScriptEvaluated(aResult, mElement, mIsInline);
   }
 
   bool IsPreload()
   {
     return mElement == nullptr;
   }
 
-  virtual void Cancel()
-  {
-    mIsCanceled = true;
-  }
+  virtual void Cancel();
 
   bool IsCanceled() const
   {
     return mIsCanceled;
   }
 
   virtual void SetReady();
 
@@ -147,16 +144,18 @@ public:
   bool IsLoading() const {
     return mProgress == Progress::Loading;
   }
   bool InCompilingStage() const {
     return mProgress == Progress::Compiling ||
            (IsReadyToRun() && mWasCompiledOMT);
   }
 
+  void MaybeCancelOffThreadScript();
+
   using super::getNext;
   using super::isInList;
 
   const nsScriptKind mKind;
   nsCOMPtr<nsIScriptElement> mElement;
   Progress mProgress;     // Are we still waiting for a load to complete?
   bool mIsInline;         // Is the script inline or loaded?
   bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
--- a/dom/canvas/CanvasUtils.h
+++ b/dom/canvas/CanvasUtils.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _CANVASUTILS_H_
 #define _CANVASUTILS_H_
 
+#include "CanvasRenderingContextHelper.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "jsapi.h"
 #include "mozilla/FloatingPoint.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -2,21 +2,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TexUnpackBlob.h"
 
 #include "GLBlitHelper.h"
 #include "GLContext.h"
-#include "GLDefs.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/RefPtr.h"
 #include "nsLayoutUtils.h"
+#include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLTexelConversions.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 namespace webgl {
 
 static bool
@@ -127,47 +127,57 @@ FormatForPackingInfo(const PackingInfo& 
     }
 
     return WebGLTexelFormat::FormatNotSupportingAnyConversion;
 }
 
 ////////////////////
 
 static uint32_t
+ZeroOn2D(TexImageTarget target, uint32_t val)
+{
+    return (IsTarget3D(target) ? val : 0);
+}
+
+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)
     : mAlignment(webgl->mPixelStore_UnpackAlignment)
     , mRowLength(rowLength)
-    , mImageHeight(FallbackOnZero(webgl->mPixelStore_UnpackImageHeight, height))
+    , mImageHeight(FallbackOnZero(ZeroOn2D(target,
+                                           webgl->mPixelStore_UnpackImageHeight),
+                                  height))
 
     , mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
     , mSkipRows(webgl->mPixelStore_UnpackSkipRows)
-    , mSkipImages(IsTarget3D(target) ? webgl->mPixelStore_UnpackSkipImages : 0)
+    , mSkipImages(ZeroOn2D(target, webgl->mPixelStore_UnpackSkipImages))
 
     , mWidth(width)
     , mHeight(height)
     , mDepth(depth)
 
     , mIsSrcPremult(isSrcPremult)
+
+    , mNeedsExactUpload(false)
 {
     MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
 }
 
 bool
 TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
-                               const void* srcBytes, uint32_t srcStride, uint8_t srcBPP,
-                               WebGLTexelFormat srcFormat,
+                               const uint8_t* srcBytes, uint32_t srcStride,
+                               uint8_t srcBPP, WebGLTexelFormat srcFormat,
                                const webgl::DriverUnpackInfo* dstDUI,
-                               const void** const out_bytes,
+                               const uint8_t** const out_bytes,
                                UniqueBuffer* const out_anchoredBuffer) const
 {
     *out_bytes = srcBytes;
 
     if (!HasData() || !mWidth || !mHeight || !mDepth)
         return true;
 
     //////
@@ -176,17 +186,17 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
     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 = (const uint8_t*)srcBytes + skipBytes;
+    auto const srcBegin = srcBytes + skipBytes;
 
     //////
 
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
     const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
 
@@ -232,23 +242,25 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
         // No conversion needed!
         return true;
     }
 
     //////
     // We need some sort of conversion, so create the dest buffer.
 
     *out_anchoredBuffer = calloc(1, dstSize.value());
-    *out_bytes = out_anchoredBuffer->get();
-    if (!*out_bytes) {
+    const auto dstBytes = (uint8_t*)out_anchoredBuffer->get();
+
+    if (!dstBytes) {
         webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.",
                                 funcName);
         return false;
     }
-    const auto dstBegin = (uint8_t*)(*out_bytes) + skipBytes;
+    *out_bytes = dstBytes;
+    const auto dstBegin = dstBytes + skipBytes;
 
     //////
     // Row conversion
 
     if (!needsPixelConversion) {
         webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.",
                                funcName);
 
@@ -314,21 +326,22 @@ DoTexOrSubImage(bool isSubImage, gl::GLC
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // TexUnpackBytes
 
 TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
                                uint32_t width, uint32_t height, uint32_t depth,
-                               const void* bytes)
+                               bool isClientData, const uint8_t* ptr)
     : TexUnpackBlob(webgl, target,
                     FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width), width,
                     height, depth, false)
-    , mBytes(bytes)
+    , mIsClientData(isClientData)
+    , mPtr(ptr)
 { }
 
 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
 {
@@ -340,41 +353,127 @@ TexUnpackBytes::TexOrSubImage(bool isSub
     const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
     if (!rowStride.isValid()) {
         MOZ_CRASH("Should be checked earlier.");
     }
 
     const auto format = FormatForPackingInfo(pi);
 
-    const void* uploadBytes;
+    auto uploadPtr = mPtr;
     UniqueBuffer tempBuffer;
-    if (!ConvertIfNeeded(webgl, funcName, mBytes, rowStride.value(), bytesPerPixel,
-                         format, dui, &uploadBytes, &tempBuffer))
+    if (mIsClientData &&
+        !ConvertIfNeeded(webgl, funcName, mPtr, rowStride.value(), bytesPerPixel, format,
+                         dui, &uploadPtr, &tempBuffer))
     {
         return false;
     }
 
-    *out_error = DoTexOrSubImage(isSubImage, webgl->gl, target, level, dui, xOffset,
-                                 yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes);
+    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) {
+        *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
+                                     zOffset, mWidth, mHeight, mDepth, uploadPtr);
+        return true;
+    }
+
+    //////
+
+    MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
+
+    if (!isSubImage) {
+        // Alloc first to catch OOMs.
+        gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+        *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
+                                     zOffset, mWidth, mHeight, mDepth, nullptr);
+        gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
+                        webgl->mBoundPixelUnpackBuffer->mGLName);
+        if (*out_error)
+            return false;
+    }
+
+    //////
+
+    // Make our sometimes-implicit values explicit. Also this keeps them constant when we
+    // ask for height=mHeight-1 and such.
+    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight);
+
+    if (mDepth > 1) {
+        *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
+                                     zOffset, mWidth, mHeight, mDepth-1, uploadPtr);
+    }
+
+    // Skip the images we uploaded.
+    gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages + mDepth - 1);
+
+    if (mHeight > 1) {
+        *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 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.
+    gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images,
+    gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0);   // or rows.
+                                                      // Keep skipping pixels though!
+
+    *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset,
+                                 yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1,
+                                 uploadPtr);
+
+    // Reset all our modified state.
+    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages);
+    gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows);
+
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // TexUnpackImage
 
 TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, TexImageTarget target,
                                uint32_t width, uint32_t height, uint32_t depth,
                                layers::Image* image, bool isAlphaPremult)
     : TexUnpackBlob(webgl, target, image->GetSize().width, width, height, depth,
                     isAlphaPremult)
     , mImage(image)
 { }
 
+TexUnpackImage::~TexUnpackImage()
+{ }
+
 bool
 TexUnpackImage::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
 {
     MOZ_ASSERT_IF(needsRespec, !isSubImage);
 
@@ -435,19 +534,19 @@ TexUnpackImage::TexOrSubImage(bool isSub
         *out_error = 0;
         return true;
     } while (false);
 
     webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
                            " upload.",
                            funcName);
 
-    const RefPtr<SourceSurface> surf = mImage->GetAsSourceSurface();
+    const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
 
-    RefPtr<DataSourceSurface> dataSurf;
+    RefPtr<gfx::DataSourceSurface> dataSurf;
     if (surf) {
         // WARNING: OSX can lose our MakeCurrent here.
         dataSurf = surf->GetDataSurface();
     }
     if (!dataSurf) {
         webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
                                 " blit failed for TexUnpackImage.",
                                 funcName);
@@ -550,17 +649,17 @@ TexUnpackSurface::TexOrSubImage(bool isS
     const auto srcBytes = map.GetData();
     const auto srcStride = map.GetStride();
 
     // CPU conversion. (++numCopies)
 
     webgl->GenerateWarning("%s: Incurred CPU-side conversion, which is very slow.",
                            funcName);
 
-    const void* uploadBytes;
+    const uint8_t* uploadBytes;
     UniqueBuffer tempBuffer;
     if (!ConvertIfNeeded(webgl, funcName, srcBytes, srcStride, srcBPP, srcFormat,
                          dstDUI, &uploadBytes, &tempBuffer))
     {
         return false;
     }
 
     //////
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -2,44 +2,37 @@
 /* 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 TEX_UNPACK_BLOB_H_
 #define TEX_UNPACK_BLOB_H_
 
 #include "GLContextTypes.h"
-#include "GLTypes.h"
+#include "mozilla/RefPtr.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTypes.h"
 
 
-template <class T>
-class RefPtr;
-
 namespace mozilla {
 
 class UniqueBuffer;
 class WebGLContext;
 class WebGLTexture;
 
 namespace dom {
 class Element;
 class HTMLCanvasElement;
 class HTMLVideoElement;
 } // namespace dom
 
 namespace gfx {
 class DataSourceSurface;
 } // namespace gfx
 
-namespace gl {
-class GLContext;
-} // namespace gl
-
 namespace layers {
 class Image;
 class ImageContainer;
 } // namespace layers
 
 namespace webgl {
 
 struct PackingInfo;
@@ -54,49 +47,53 @@ public:
     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;
 
+    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 void* srcBytes,
-                         uint32_t srcStride, uint8_t srcBPP, WebGLTexelFormat srcFormat,
+    bool ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
+                         const uint8_t* srcBytes, uint32_t srcStride, uint8_t srcBPP,
+                         WebGLTexelFormat srcFormat,
                          const webgl::DriverUnpackInfo* dstDUI,
-                         const void** const out_bytes,
+                         const uint8_t** const out_bytes,
                          UniqueBuffer* const out_anchoredBuffer) const;
 
 public:
     virtual bool HasData() const { return true; }
 
     virtual bool 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 = 0;
 };
 
 class TexUnpackBytes final : public TexUnpackBlob
 {
 public:
-    const void* const mBytes;
+    const bool mIsClientData;
+    const uint8_t* const mPtr;
 
     TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
-                   uint32_t height, uint32_t depth, const void* bytes);
+                   uint32_t height, uint32_t depth, bool isClientData, const uint8_t* ptr);
 
-    virtual bool HasData() const override { return bool(mBytes); }
+    virtual bool HasData() const override { return !mIsClientData || bool(mPtr); }
 
     virtual bool 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 override;
 };
 
@@ -104,16 +101,18 @@ class TexUnpackImage final : public TexU
 {
 public:
     const RefPtr<layers::Image> mImage;
 
     TexUnpackImage(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                    uint32_t height, uint32_t depth, layers::Image* image,
                    bool isAlphaPremult);
 
+    ~TexUnpackImage(); // Prevent needing to define layers::Image in the header.
+
     virtual bool 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 override;
 };
 
 class TexUnpackSurface final : public TexUnpackBlob
@@ -121,18 +120,18 @@ class TexUnpackSurface final : public Te
 public:
     const RefPtr<gfx::DataSourceSurface> mSurf;
 
     TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                      uint32_t height, uint32_t depth, gfx::DataSourceSurface* surf,
                      bool isAlphaPremult);
 
     virtual bool 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 override;
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
+                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
+                               GLint yOffset, GLint zOffset,
+                               GLenum* const out_error) const override;
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // TEX_UNPACK_BLOB_H_
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -28,17 +28,19 @@ public:
     virtual bool IsWebGL2() const override {
         return false;
     }
 
     // nsWrapperCache
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 private:
-    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
+    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
+                                           uint32_t* alignment,
+                                           const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
--- a/dom/canvas/WebGL1ContextUniforms.cpp
+++ b/dom/canvas/WebGL1ContextUniforms.cpp
@@ -3,17 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL1Context.h"
 
 namespace mozilla {
 
 bool
-WebGL1Context::ValidateAttribPointerType(bool /*integerMode*/, GLenum type, GLsizei* out_alignment, const char* info)
+WebGL1Context::ValidateAttribPointerType(bool /*integerMode*/, GLenum type,
+                                         uint32_t* out_alignment, const char* info)
 {
     MOZ_ASSERT(out_alignment);
     if (!out_alignment)
         return false;
 
     switch (type) {
     case LOCAL_GL_BYTE:
     case LOCAL_GL_UNSIGNED_BYTE:
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -117,34 +117,92 @@ public:
 protected:
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLenum unpackFormat, GLenum unpackType,
                        dom::Element* elem, ErrorResult* const out_rv);
 public:
     template<class T>
     inline void
     TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
-                  GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_rv)
+                  GLenum unpackFormat, GLenum unpackType, T& any, ErrorResult& out_rv)
     {
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, unpackFormat, unpackType,
-                      &elem, &out_rv);
+                      &any, &out_rv);
     }
 
     void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                            GLint zOffset, GLint x, GLint y, GLsizei width,
                            GLsizei height);
     void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLsizei depth,
                               GLint border,
                               const dom::ArrayBufferView& data);
     void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLint zOffset, GLsizei width, GLsizei height,
                                  GLsizei depth, GLenum sizedUnpackFormat,
                                  const dom::ArrayBufferView& data);
 
+    ////////////////
+    // Texture PBOs
+
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
+                    GLenum unpackType, WebGLsizeiptr offset, ErrorResult&);
+
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLsizei width, GLsizei height, GLenum unpackFormat,
+                       GLenum unpackType, WebGLsizeiptr offset, ErrorResult&);
+
+    void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
+                    GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
+                    GLenum unpackType, WebGLsizeiptr offset);
+
+    void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLenum unpackFormat, GLenum unpackType, WebGLsizeiptr offset,
+                       ErrorResult&);
+
+    ////////////////
+    // WebGL1 overloads
+
+    void
+    TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat, GLsizei width,
+               GLsizei height, GLint border, GLenum unpackFormat, GLenum unpackType,
+               const dom::Nullable<dom::ArrayBufferView>& pixels, ErrorResult& out_rv)
+    {
+        WebGLContext::TexImage2D(texImageTarget, level, internalFormat, width, height,
+                                 border, unpackFormat, unpackType, pixels, out_rv);
+    }
+
+    template<typename T>
+    void
+    TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+               GLenum unpackFormat, GLenum unpackType, T& any, ErrorResult& out_rv)
+    {
+        WebGLContext::TexImage2D(texImageTarget, level, internalFormat, unpackFormat,
+                                 unpackType, any, out_rv);
+    }
+
+    void
+    TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                  GLsizei width, GLsizei height, GLenum unpackFormat, GLenum unpackType,
+                  const dom::Nullable<dom::ArrayBufferView>& pixels, ErrorResult& out_rv)
+    {
+        WebGLContext::TexSubImage2D(texImageTarget, level, xOffset, yOffset, width,
+                                    height, unpackFormat, unpackType, pixels, out_rv);
+    }
+
+    template<typename T>
+    inline void
+    TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                  GLenum unpackFormat, GLenum unpackType, T& any, ErrorResult& out_rv)
+    {
+        WebGLContext::TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat,
+                                    unpackType, any, out_rv);
+    }
 
     // -------------------------------------------------------------------------
     // Programs and shaders - WebGL2ContextPrograms.cpp
     GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
@@ -359,17 +417,19 @@ private:
     CreateFormatUsage(gl::GLContext* gl) const override;
 
     virtual bool IsTexParamValid(GLenum pname) const override;
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
     // CreateVertexArrayImpl is assumed to be infallible.
     virtual WebGLVertexArray* CreateVertexArrayImpl() override;
-    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
+    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
+                                           uint32_t* alignment,
+                                           const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -15,26 +15,21 @@ bool
 WebGL2Context::ValidateBufferTarget(GLenum target, const char* funcName)
 {
     switch (target) {
     case LOCAL_GL_ARRAY_BUFFER:
     case LOCAL_GL_COPY_READ_BUFFER:
     case LOCAL_GL_COPY_WRITE_BUFFER:
     case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
     case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
     case LOCAL_GL_UNIFORM_BUFFER:
         return true;
 
-    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
-        ErrorInvalidOperation("%s: PBOs are still under development, and are currently"
-                              " disabled.",
-                              funcName);
-        return false;
-
     default:
         ErrorInvalidEnumInfo(funcName, target);
         return false;
     }
 }
 
 bool
 WebGL2Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
--- a/dom/canvas/WebGL2ContextMRTs.cpp
+++ b/dom/canvas/WebGL2ContextMRTs.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
+
 #include "GLContext.h"
+#include "WebGLFramebuffer.h"
 
 namespace mozilla {
 
 bool WebGL2Context::ValidateClearBuffer(const char* info, GLenum buffer, GLint drawbuffer, size_t elemCount)
 {
     size_t requiredElements = -1;
     GLint maxDrawbuffer = -1;
     switch (buffer) {
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -197,16 +197,126 @@ WebGL2Context::CopyTexSubImage3D(GLenum 
     {
         return;
     }
 
     tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width,
                          height);
 }
 
+////////////////////
+
+void
+WebGL2Context::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                          GLsizei width, GLsizei height, GLint border,
+                          GLenum unpackFormat, GLenum unpackType, WebGLsizeiptr offset,
+                          ErrorResult&)
+{
+    const char funcName[] = "texImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
+    {
+        return;
+    }
+
+    const bool isSubImage = false;
+    const GLint xOffset = 0;
+    const GLint yOffset = 0;
+    const GLint zOffset = 0;
+    const GLsizei depth = 1;
+    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
+                       yOffset, zOffset, width, height, depth, border, unpackFormat,
+                       unpackType, offset);
+}
+
+void
+WebGL2Context::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                             GLint yOffset, GLsizei width, GLsizei height,
+                             GLenum unpackFormat, GLenum unpackType, WebGLsizeiptr offset,
+                             ErrorResult&)
+{
+    const char funcName[] = "texSubImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
+    {
+        return;
+    }
+
+    const bool isSubImage = true;
+    const GLenum internalFormat = 0;
+    const GLint zOffset = 0;
+    const GLsizei depth = 1;
+    const GLint border = 0;
+    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
+                       yOffset, zOffset, width, height, depth, border, unpackFormat,
+                       unpackType, offset);
+}
+
+//////////
+
+void
+WebGL2Context::TexImage3D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                          GLsizei width, GLsizei height, GLsizei depth, GLint border,
+                          GLenum unpackFormat, GLenum unpackType, WebGLsizeiptr offset)
+{
+    const char funcName[] = "texImage3D";
+    const uint8_t funcDims = 3;
+
+    TexImageTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
+    {
+        return;
+    }
+
+    const bool isSubImage = false;
+    const GLint xOffset = 0;
+    const GLint yOffset = 0;
+    const GLint zOffset = 0;
+    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
+                       yOffset, zOffset, width, height, depth, border, unpackFormat,
+                       unpackType, offset);
+}
+
+void
+WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                             GLint yOffset, GLint zOffset, GLsizei width, GLsizei height,
+                             GLsizei depth, GLenum unpackFormat, GLenum unpackType,
+                             WebGLsizeiptr offset, ErrorResult&)
+{
+    const char funcName[] = "texSubImage3D";
+    const uint8_t funcDims = 3;
+
+    TexImageTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
+    {
+        return;
+    }
+
+    const bool isSubImage = true;
+    const GLenum internalFormat = 0;
+    const GLint border = 0;
+    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
+                       yOffset, zOffset, width, height, depth, border, unpackFormat,
+                       unpackType, offset);
+}
+
+////////////////////
+
 /*virtual*/ bool
 WebGL2Context::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
     case LOCAL_GL_TEXTURE_BASE_LEVEL:
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
     case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
--- a/dom/canvas/WebGL2ContextVertices.cpp
+++ b/dom/canvas/WebGL2ContextVertices.cpp
@@ -11,17 +11,17 @@
 #include "WebGLVertexAttribData.h"
 
 #include "mozilla/Casting.h"
 
 namespace mozilla {
 
 bool
 WebGL2Context::ValidateAttribPointerType(bool integerMode, GLenum type,
-                                         GLsizei* out_alignment, const char* info)
+                                         uint32_t* out_alignment, const char* info)
 {
   MOZ_ASSERT(out_alignment);
 
   switch (type) {
   case LOCAL_GL_BYTE:
   case LOCAL_GL_UNSIGNED_BYTE:
     *out_alignment = 1;
     return true;
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -27,17 +27,17 @@ class WebGLBuffer final
 public:
 
     enum class Kind {
         Undefined,
         ElementArray,
         OtherData
     };
 
-    explicit WebGLBuffer(WebGLContext* webgl, GLuint buf);
+    WebGLBuffer(WebGLContext* webgl, GLuint buf);
 
     void BindTo(GLenum target);
     Kind Content() const { return mContent; }
 
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -891,17 +891,17 @@ public:
         TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
                       &elem, &out_error);
     }
 
     //////
     // WebGLTextureUpload.cpp
 public:
     bool ValidateUnpackPixels(const char* funcName, uint32_t fullRows,
-                              uint32_t tailPixels, const webgl::TexUnpackBlob* blob);
+                              uint32_t tailPixels, webgl::TexUnpackBlob* blob);
 
 protected:
     bool ValidateTexImageSpecification(const char* funcName, uint8_t funcDims,
                                        GLenum texImageTarget, GLint level,
                                        GLsizei width, GLsizei height, GLsizei depth,
                                        GLint border,
                                        TexImageTarget* const out_target,
                                        WebGLTexture** const out_texture,
@@ -909,18 +909,18 @@ protected:
     bool ValidateTexImageSelection(const char* funcName, uint8_t funcDims,
                                    GLenum texImageTarget, GLint level, GLint xOffset,
                                    GLint yOffset, GLint zOffset, GLsizei width,
                                    GLsizei height, GLsizei depth,
                                    TexImageTarget* const out_target,
                                    WebGLTexture** const out_texture,
                                    WebGLTexture::ImageInfo** const out_imageInfo);
 
-    bool ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type,
-                            webgl::PackingInfo* const out);
+    bool ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
+                            GLenum type, webgl::PackingInfo* const out);
 
 // -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                              GLsizei primcount);
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
@@ -1306,17 +1306,17 @@ private:
     template<class ObjectType>
     bool ValidateObjectAssumeNonNull(const char* info, ObjectType* object);
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual WebGLVertexArray* CreateVertexArrayImpl();
 
-    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) = 0;
+    virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info);
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) = 0;
     virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
 
 protected:
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -3,16 +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/. */
 
 #include "WebGLContext.h"
 
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/UniquePtrExtensions.h"
+#include "nsPrintfCString.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 
 #include "GLContext.h"
+#include "GLScreenBuffer.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Preferences.h"
 #include "nsString.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
--- a/dom/canvas/WebGLContextUnchecked.h
+++ b/dom/canvas/WebGLContextUnchecked.h
@@ -2,27 +2,23 @@
 /* vim: set ts=8 sts=4 et sw=4 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 WEBGLCONTEXTUNCHECKED_H
 #define WEBGLCONTEXTUNCHECKED_H
 
-#include "GLDefs.h"
 #include "mozilla/RefPtr.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLBuffer;
 class WebGLSampler;
-namespace gl {
-    class GLContext;
-} // namespace gl
 
 class WebGLContextUnchecked
 {
 public:
     explicit WebGLContextUnchecked(gl::GLContext* gl);
 
     // -------------------------------------------------------------------------
     // Buffer Objects
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -10,16 +10,17 @@
 #include "CanvasUtils.h"
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "jsfriendapi.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
+#include "nsPrintfCString.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLSampler.h"
 #include "WebGLShader.h"
@@ -566,17 +567,17 @@ WebGLContext::ValidateAttribPointer(bool
                                     WebGLintptr byteOffset, const char* info)
 {
     WebGLBuffer* buffer = mBoundArrayBuffer;
     if (!buffer) {
         ErrorInvalidOperation("%s: must have valid GL_ARRAY_BUFFER binding", info);
         return false;
     }
 
-    GLsizei requiredAlignment = 0;
+    uint32_t requiredAlignment = 0;
     if (!ValidateAttribPointerType(integerMode, type, &requiredAlignment, info))
         return false;
 
     // requiredAlignment should always be a power of two
     MOZ_ASSERT(IsPowerOfTwo(requiredAlignment));
     GLsizei requiredAlignmentMask = requiredAlignment - 1;
 
     if (size < 1 || size > 4) {
--- a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
--- a/dom/canvas/WebGLExtensionCompressedTextureES3.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureES3.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
--- a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
--- a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
--- a/dom/canvas/WebGLExtensionDepthTexture.cpp
+++ b/dom/canvas/WebGLExtensionDepthTexture.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionDepthTexture::WebGLExtensionDepthTexture(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -1,23 +1,23 @@
 /* -*- 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 "WebGLExtensions.h"
 
+#include "gfxPrefs.h"
+#include "GLContext.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/dom/BindingUtils.h"
-#include "GLContext.h"
 #include "WebGLContext.h"
 #include "WebGLTimerQuery.h"
-#include "gfxPrefs.h"
 
 namespace mozilla {
 
 WebGLExtensionDisjointTimerQuery::WebGLExtensionDisjointTimerQuery(WebGLContext* webgl)
   : WebGLExtensionBase(webgl)
   , mActiveQuery(nullptr)
 {
   MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
--- a/dom/canvas/WebGLExtensionElementIndexUint.cpp
+++ b/dom/canvas/WebGLExtensionElementIndexUint.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionElementIndexUint::WebGLExtensionElementIndexUint(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
--- a/dom/canvas/WebGLExtensionShaderTextureLod.cpp
+++ b/dom/canvas/WebGLExtensionShaderTextureLod.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionShaderTextureLod::WebGLExtensionShaderTextureLod(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
--- a/dom/canvas/WebGLExtensionStandardDerivatives.cpp
+++ b/dom/canvas/WebGLExtensionStandardDerivatives.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionStandardDerivatives::WebGLExtensionStandardDerivatives(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
--- a/dom/canvas/WebGLExtensionTextureFilterAnisotropic.cpp
+++ b/dom/canvas/WebGLExtensionTextureFilterAnisotropic.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionTextureFilterAnisotropic::WebGLExtensionTextureFilterAnisotropic(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
--- a/dom/canvas/WebGLExtensionTextureFloatLinear.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloatLinear.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
 WebGLExtensionTextureFloatLinear::WebGLExtensionTextureFloatLinear(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
--- a/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
 WebGLExtensionTextureHalfFloatLinear::WebGLExtensionTextureHalfFloatLinear(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -2,19 +2,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/. */
 
 #ifndef WEBGL_EXTENSIONS_H_
 #define WEBGL_EXTENSIONS_H_
 
 #include "mozilla/AlreadyAddRefed.h"
-#include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
-
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 namespace dom {
 template<typename T>
 class Sequence;
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -1,17 +1,19 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLFormats.h"
 
 #include "gfxPrefs.h"
+#include "GLContext.h"
 #include "GLDefs.h"
+#include "mozilla/gfx/Logging.h"
 #include "mozilla/StaticMutex.h"
 
 #ifdef FOO
 #error FOO is already defined! We use FOO() macros to keep things succinct in this file.
 #endif
 
 namespace mozilla {
 namespace webgl {
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_FORMATS_H_
 #define WEBGL_FORMATS_H_
 
 #include <map>
 #include <set>
 
-#include "GLTypes.h"
 #include "mozilla/UniquePtr.h"
+#include "WebGLTypes.h"
 
 namespace mozilla {
 namespace webgl {
 
 typedef uint8_t EffectiveFormatValueT;
 
 enum class EffectiveFormat : EffectiveFormatValueT {
     // GLES 3.0.4, p128-129, "Required Texture Formats"
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -1,38 +1,37 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_FRAMEBUFFER_H_
 #define WEBGL_FRAMEBUFFER_H_
 
+#include <vector>
+
 #include "mozilla/LinkedList.h"
 #include "mozilla/WeakPtr.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLObjectModel.h"
+#include "WebGLRenderbuffer.h"
 #include "WebGLStrongTypes.h"
-#include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
+#include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLFramebuffer;
 class WebGLRenderbuffer;
 class WebGLTexture;
 
 template<typename T>
 class PlacementArray;
 
-namespace gl {
-    class GLContext;
-} // namespace gl
-
 class WebGLFBAttachPoint
 {
 public:
     WebGLFramebuffer* const mFB;
     const GLenum mAttachmentPoint;
 private:
     WebGLRefPtr<WebGLTexture> mTexturePtr;
     WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -12,19 +12,19 @@
 #include <vector>
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
+#include "WebGLContext.h"
 #include "WebGLObjectModel.h"
 
-
 namespace mozilla {
 class ErrorResult;
 class WebGLActiveInfo;
 class WebGLProgram;
 class WebGLShader;
 class WebGLUniformLocation;
 
 namespace dom {
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -4,30 +4,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_SAMPLER_H_
 #define WEBGL_SAMPLER_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
+#include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 class WebGLSampler final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
     friend class WebGLTexture;
 
 public:
-    explicit WebGLSampler(WebGLContext* webgl, GLuint sampler);
+    WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
     const GLuint mGLName;
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_SHADER_H_
 #define WEBGL_SHADER_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -38,16 +38,22 @@ ChooseValidatorCompileOptions(const ShBu
                   SH_OBJECT_CODE |
                   SH_LIMIT_CALL_STACK_DEPTH |
                   SH_INIT_GL_POSITION;
 
     if (resources.MaxExpressionComplexity > 0) {
         options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
     }
 
+    // Sampler arrays indexed with non-constant expressions are forbidden in
+    // GLSL 1.30 and later.
+    // ESSL 3 requires constant-integral-expressions for this as well.
+    // Just do it universally.
+    options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
+
     if (gfxPrefs::WebGLAllANGLEOptions()) {
         return options |
                SH_VALIDATE_LOOP_INDEXING |
                SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
                SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
                SH_EMULATE_BUILT_IN_FUNCTIONS |
                SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                SH_UNFOLD_SHORT_CIRCUIT |
@@ -73,21 +79,16 @@ ChooseValidatorCompileOptions(const ShBu
             options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
         }
 
         // Work around bug 735560
         if (gl->Vendor() == gl::GLVendor::Intel) {
             options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
         }
 
-        // Work around bug 636926
-        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
-            options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
-        }
-
         // Work around that Mac drivers handle struct scopes incorrectly.
         options |= SH_REGENERATE_STRUCT_NAMES;
     }
 #endif
 
     return options;
 }
 
@@ -124,23 +125,18 @@ ShaderOutput(gl::GLContext* gl)
 }
 
 webgl::ShaderValidator*
 WebGLContext::CreateShaderValidator(GLenum shaderType) const
 {
     if (mBypassShaderValidation)
         return nullptr;
 
-    ShShaderSpec spec = IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC;
-    ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT
-                                                 : SH_GLSL_COMPATIBILITY_OUTPUT;
-
-    // If we're using WebGL2 we want a more specific version of GLSL
-    if (IsWebGL2())
-        outputLanguage = ShaderOutput(gl);
+    const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
+    const auto outputLanguage = ShaderOutput(gl);
 
     ShBuiltInResources resources;
     memset(&resources, 0, sizeof(resources));
     ShInitBuiltInResources(&resources);
 
     resources.HashFunction = webgl::IdentifierHashFunc;
 
     resources.MaxVertexAttribs = mGLMaxVertexAttribs;
@@ -248,17 +244,18 @@ bool
 ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const
 {
     if (!prev) {
         nsPrintfCString error("Passed in NULL prev ShaderValidator.");
         *out_log = error;
         return false;
     }
 
-    if (ShGetShaderVersion(prev->mHandle) != ShGetShaderVersion(mHandle)) {
+    const auto shaderVersion = ShGetShaderVersion(mHandle);
+    if (ShGetShaderVersion(prev->mHandle) != shaderVersion) {
         nsPrintfCString error("Vertex shader version %d does not match"
                               " fragment shader version %d.",
                               ShGetShaderVersion(prev->mHandle),
                               ShGetShaderVersion(mHandle));
         *out_log = error;
         return false;
     }
 
@@ -314,17 +311,17 @@ ShaderValidator::CanLinkTo(const ShaderV
 
             bool definedInVertShader = false;
             bool staticVertUse = false;
 
             for (const auto& vertVarying : *vertVaryings) {
                 if (vertVarying.name != fragVarying.name)
                     continue;
 
-                if (!vertVarying.isSameVaryingAtLinkTime(fragVarying)) {
+                if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, shaderVersion)) {
                     nsPrintfCString error("Varying `%s`is not linkable between"
                                           " attached shaders.",
                                           fragVarying.name.c_str());
                     *out_log = error;
                     return false;
                 }
 
                 definedInVertShader = true;
@@ -350,17 +347,18 @@ ShaderValidator::CanLinkTo(const ShaderV
                                                  staticUseVaryingList.size()))
         {
             *out_log = "Statically used varyings do not fit within packing limits. (see"
                        " GLSL ES Specification 1.0.17, p111)";
             return false;
         }
     }
 
-    {
+    if (shaderVersion == 100) {
+        // Enforce ESSL1 invariant linking rules.
         bool isInvariant_Position = false;
         bool isInvariant_PointSize = false;
         bool isInvariant_FragCoord = false;
         bool isInvariant_PointCoord = false;
 
         for (const auto& varying : *vertVaryings) {
             if (varying.name == "gl_Position") {
                 isInvariant_Position = varying.isInvariant;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/unused.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
+#include "WebGLSampler.h"
 #include "WebGLTexelConversions.h"
 
 namespace mozilla {
 
 /*static*/ const WebGLTexture::ImageInfo WebGLTexture::ImageInfo::kUndefined;
 
 ////////////////////////////////////////
 
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -3,38 +3,49 @@
  * 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 WEBGL_TEXTURE_H_
 #define WEBGL_TEXTURE_H_
 
 #include <algorithm>
 #include <map>
+#include <set>
+#include <vector>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
+#include "WebGLTypes.h"
 
 namespace mozilla {
 class ErrorResult;
 class WebGLContext;
 
 namespace dom {
 class Element;
+class HTMLVideoElement;
 class ImageData;
 class ArrayBufferViewOrSharedArrayBufferView;
 } // namespace dom
 
+namespace layers {
+class Image;
+} // namespace layers
+
 namespace webgl {
+struct DriverUnpackInfo;
+struct FormatUsageInfo;
+struct PackingInfo;
 class TexUnpackBlob;
 } // namespace webgl
 
 
 bool
 DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t dims,
                           const char* funcName);
 
@@ -185,17 +196,17 @@ public:
 
     ImageInfo mImageInfoArr[kMaxLevelCount * kMaxFaceCount];
 
     ////////////////////////////////////
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
 
-    explicit WebGLTexture(WebGLContext* webgl, GLuint tex);
+    WebGLTexture(WebGLContext* webgl, GLuint tex);
 
     void Delete();
 
     bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
     TexTarget Target() const { return mTarget; }
 
     WebGLContext* GetParentObject() const {
         return mContext;
@@ -232,16 +243,22 @@ public:
                        GLint zOffset, GLenum unpackFormat, GLenum unpackType,
                        dom::ImageData* imageData);
 
     void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                        GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLenum unpackFormat, GLenum unpackType,
                        dom::Element* elem, ErrorResult* const out_error);
 
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLint border, GLenum unpackFormat, GLenum unpackType,
+                       WebGLsizeiptr offset);
+
 protected:
     void TexOrSubImageBlob(bool isSubImage, const char* funcName, TexImageTarget target,
                            GLint level, GLenum internalFormat, GLint xOffset,
                            GLint yOffset, GLint zOffset,
                            const webgl::PackingInfo& pi,
                            const webgl::TexUnpackBlob* blob);
 
     bool ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -3,16 +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/. */
 
 #include "WebGLTexture.h"
 
 #include <algorithm>
 
 #include "CanvasUtils.h"
+#include "gfxPrefs.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/unused.h"
@@ -122,128 +123,98 @@ DoesJSTypeMatchUnpackType(GLenum unpackT
 
     default:
         return false;
     }
 }
 
 bool
 WebGLContext::ValidateUnpackPixels(const char* funcName, uint32_t fullRows,
-                                   uint32_t tailPixels, const webgl::TexUnpackBlob* blob)
+                                   uint32_t tailPixels, webgl::TexUnpackBlob* blob)
 {
-    const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
-    const auto usedRowsPerImage = CheckedUint32(blob->mSkipRows) + blob->mHeight;
-    const auto usedImages = CheckedUint32(blob->mSkipImages) + blob->mDepth;
+    auto skipPixels = CheckedUint32(blob->mSkipPixels);
+    skipPixels += CheckedUint32(blob->mSkipRows);
 
-    if (!usedPixelsPerRow.isValid() ||
-        !usedRowsPerImage.isValid() ||
-        !usedImages.isValid())
-    {
-        ErrorOutOfMemory("%s: Invalid calculation for e.g. UNPACK_SKIP_PIXELS + width.",
-                         funcName);
+    const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
+    if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > blob->mRowLength) {
+        ErrorInvalidOperation("%s: UNPACK_SKIP_PIXELS + height > UNPACK_ROW_LENGTH.",
+                              funcName);
+        return false;
+    }
+
+    if (blob->mHeight > blob->mImageHeight) {
+        ErrorInvalidOperation("%s: height > UNPACK_IMAGE_HEIGHT.", funcName);
         return false;
     }
 
     //////
 
-    if (usedPixelsPerRow.value() > blob->mRowLength ||
-        usedRowsPerImage.value() > blob->mImageHeight)
-    {
-        ErrorInvalidOperation("%s: UNPACK_ROW_LENGTH or UNPACK_IMAGE_HEIGHT too small.",
-                              funcName);
-        return false;
-    }
+    // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
+    auto skipFullRows = CheckedUint32(blob->mSkipImages) * blob->mImageHeight;
+    skipFullRows += blob->mSkipRows;
 
-    //////
+    MOZ_ASSERT(blob->mDepth >= 1);
+    MOZ_ASSERT(blob->mHeight >= 1);
+    auto usedFullRows = CheckedUint32(blob->mDepth - 1) * blob->mImageHeight;
+    usedFullRows += blob->mHeight - 1; // Full rows in the final image, excluding the tail.
 
-    auto fullRowsNeeded = (usedImages - 1) * blob->mImageHeight;
-    fullRowsNeeded += usedRowsPerImage - 1;
-
+    const auto fullRowsNeeded = skipFullRows + usedFullRows;
     if (!fullRowsNeeded.isValid()) {
         ErrorOutOfMemory("%s: Invalid calculation for required row count.",
                          funcName);
         return false;
     }
 
     if (fullRows > fullRowsNeeded.value())
         return true;
 
-    if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value())
+    if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) {
+        blob->mNeedsExactUpload = true;
         return true;
+    }
 
     ErrorInvalidOperation("%s: Desired upload requires more data than is available: (%u"
                           " rows plus %u pixels needed, %u rows plus %u pixels"
                           " available)",
                           funcName, fullRowsNeeded.value(), usedPixelsPerRow.value(),
                           fullRows, tailPixels);
     return false;
 }
 
-static UniquePtr<webgl::TexUnpackBlob>
-BlobFromView(WebGLContext* webgl, const char* funcName, TexImageTarget target,
-             uint32_t width, uint32_t height, uint32_t depth,
-             const webgl::PackingInfo& pi,
-             const dom::Nullable<dom::ArrayBufferView>& maybeView)
+static bool
+ValidateUnpackBytes(WebGLContext* webgl, const char* funcName, uint32_t width,
+                    uint32_t height, uint32_t depth, const webgl::PackingInfo& pi,
+                    uint32_t byteCount, webgl::TexUnpackBlob* blob)
 {
-    const uint8_t* bytes = nullptr;
-    uint32_t byteCount = 0;
-
-    if (!maybeView.IsNull()) {
-        const auto& view = maybeView.Value();
+    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
+    const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
+    const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
 
-        const auto jsType = JS_GetArrayBufferViewType(view.Obj());
-        if (!DoesJSTypeMatchUnpackType(pi.type, jsType)) {
-            webgl->ErrorInvalidOperation("%s: `pixels` must be compatible with `type`.",
-                                         funcName);
-            return nullptr;
-        }
-
-        if (width && height && depth) {
-            view.ComputeLengthAndData();
-
-            bytes = view.DataAllowShared();
-            byteCount = view.LengthAllowShared();
-        }
+    const auto fullRows = byteCount / rowStride;
+    if (!fullRows.isValid()) {
+        webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
+        return false;
     }
 
-    UniquePtr<webgl::TexUnpackBlob> blob(new webgl::TexUnpackBytes(webgl, target, width,
-                                                                   height, depth, bytes));
-
-    //////
-
-    if (bytes) {
-        const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-        const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
-        const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
+    const auto bodyBytes = fullRows.value() * rowStride.value();
+    const auto tailPixels = (byteCount - bodyBytes) / bytesPerPixel;
 
-        const auto fullRows = byteCount / rowStride;
-        if (!fullRows.isValid()) {
-            webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
-            return nullptr;
-        }
-
-        const auto bodyBytes = fullRows.value() * rowStride.value();
-        const auto tailPixels = (byteCount - bodyBytes) / bytesPerPixel;
-
-        if (!webgl->ValidateUnpackPixels(funcName, fullRows.value(), tailPixels,
-                                         blob.get()))
-        {
-            return nullptr;
-        }
-    }
-
-    //////
-
-    return Move(blob);
+    return webgl->ValidateUnpackPixels(funcName, fullRows.value(), tailPixels, blob);
 }
 
 bool
-WebGLContext::ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type,
-                                 webgl::PackingInfo* const out)
+WebGLContext::ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
+                                 GLenum type, webgl::PackingInfo* const out)
 {
+    if (usePBOs != bool(mBoundPixelUnpackBuffer)) {
+        ErrorInvalidOperation("%s: PACK_BUFFER must be %s.", funcName,
+                              (usePBOs ? "non-null" : "null"));
+        return false;
+    }
+
     if (!mFormatUsage->AreUnpackEnumsValid(format, type)) {
         ErrorInvalidEnum("%s: Invalid unpack format/type: 0x%04x/0x%04x", funcName,
                          format, type);
         return false;
     }
 
     out->format = format;
     out->type = type;
@@ -260,27 +231,106 @@ WebGLTexture::TexOrSubImage(bool isSubIm
 {
     uint32_t width, height, depth;
     if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border,
                          &width, &height, &depth))
     {
         return;
     }
 
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
-    const auto blob = BlobFromView(mContext, funcName, target, width, height, depth, pi,
-                                   maybeView);
-    if (!blob)
+    ////
+
+    const uint8_t* bytes = nullptr;
+    uint32_t byteCount = 0;
+
+    if (!maybeView.IsNull()) {
+        const auto& view = maybeView.Value();
+
+        const auto jsType = JS_GetArrayBufferViewType(view.Obj());
+        if (!DoesJSTypeMatchUnpackType(pi.type, jsType)) {
+            mContext->ErrorInvalidOperation("%s: `pixels` not compatible with `type`.",
+                                            funcName);
+            return;
+        }
+
+        if (width && height && depth) {
+            view.ComputeLengthAndData();
+
+            bytes = view.DataAllowShared();
+            byteCount = view.LengthAllowShared();
+        }
+    }
+
+    const bool isClientData = true;
+    webgl::TexUnpackBytes blob(mContext, target, width, height, depth, isClientData,
+                               bytes);
+
+    if (bytes &&
+        !ValidateUnpackBytes(mContext, funcName, width, height, depth, pi, byteCount,
+                             &blob))
+    {
+        return;
+    }
+
+    TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
+                      yOffset, zOffset, pi, &blob);
+}
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLsizei rawWidth,
+                            GLsizei rawHeight, GLsizei rawDepth, GLint border,
+                            GLenum unpackFormat, GLenum unpackType,
+                            WebGLsizeiptr offset)
+{
+    uint32_t width, height, depth;
+    if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border,
+                         &width, &height, &depth))
+    {
+        return;
+    }
+
+    const bool usePBOs = true;
+    webgl::PackingInfo pi;
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
+    ////
+
+    if (offset < 0) {
+        mContext->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
+        return;
+    }
+
+    const bool isClientData = false;
+    const auto ptr = (const uint8_t*)offset;
+    webgl::TexUnpackBytes blob(mContext, target, width, height, depth, isClientData, ptr);
+
+    const auto& packBuffer = mContext->mBoundPixelUnpackBuffer;
+    const auto bufferByteCount = packBuffer->ByteLength();
+
+    uint32_t byteCount = 0;
+    if (bufferByteCount >= offset) {
+        byteCount = bufferByteCount - offset;
+    }
+
+    if (!ValidateUnpackBytes(mContext, funcName, width, height, depth, pi, byteCount,
+                             &blob))
+    {
+        return;
+    }
+
     TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
-                      yOffset, zOffset, pi, blob.get());
+                      yOffset, zOffset, pi, &blob);
 }
 
 ////////////////////////////////////////
 // ImageData
 
 static already_AddRefed<gfx::DataSourceSurface>
 FromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType,
               dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr)
@@ -314,18 +364,19 @@ FromImageData(WebGLContext* webgl, const
 }
 
 void
 WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                             GLint level, GLenum internalFormat, GLint xOffset,
                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
                             GLenum unpackType, dom::ImageData* imageData)
 {
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
     if (!imageData) {
         // Spec says to generate an INVALID_VALUE error
         mContext->ErrorInvalidValue("%s: Null ImageData.", funcName);
         return;
     }
 
@@ -341,18 +392,18 @@ WebGLTexture::TexOrSubImage(bool isSubIm
     if (!surf)
         return;
 
     // WhatWG "HTML Living Standard" (30 October 2015):
     // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
     //  non-premultiplied alpha values."
     const bool isAlphaPremult = false;
 
-    const webgl::TexUnpackSurface blob(mContext, target, width, height, depth, surf,
-                                       isAlphaPremult);
+    webgl::TexUnpackSurface blob(mContext, target, width, height, depth, surf,
+                                 isAlphaPremult);
 
     const uint32_t fullRows = imageData->Height();
     const uint32_t tailPixels = 0;
     if (!mContext->ValidateUnpackPixels(funcName, fullRows, tailPixels, &blob))
         return;
 
     TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
                       yOffset, zOffset, pi, &blob);
@@ -363,18 +414,19 @@ WebGLTexture::TexOrSubImage(bool isSubIm
 
 void
 WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                             GLint level, GLenum internalFormat, GLint xOffset,
                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
                             GLenum unpackType, dom::Element* elem,
                             ErrorResult* const out_error)
 {
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
     //////
 
     uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
                      nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
 
     if (mContext->mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
@@ -410,17 +462,19 @@ WebGLTexture::TexOrSubImage(bool isSubIm
     //////
 
     // Eventually, these will be args.
     const uint32_t width = elemWidth;
     const uint32_t height = elemHeight;
     const uint32_t depth = 1;
 
     if (!layersImage && !dataSurf) {
-        const webgl::TexUnpackBytes blob(mContext, target, width, height, depth, nullptr);
+        const bool isClientData = true;
+        const webgl::TexUnpackBytes blob(mContext, target, width, height, depth,
+                                         isClientData, nullptr);
         TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
                           yOffset, zOffset, pi, &blob);
         return;
     }
 
     //////
 
     // While it's counter-intuitive, the shape of the SFEResult API means that we should
@@ -448,17 +502,17 @@ WebGLTexture::TexOrSubImage(bool isSubIm
                                   funcName);
         out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
         return;
     }
 
     //////
     // Ok, we're good!
 
-    UniquePtr<const webgl::TexUnpackBlob> blob;
+    UniquePtr<webgl::TexUnpackBlob> blob;
     const bool isAlphaPremult = sfer.mIsPremultiplied;
 
     if (layersImage) {
         blob.reset(new webgl::TexUnpackImage(mContext, target, width, height, depth,
                                              layersImage, isAlphaPremult));
     } else {
         MOZ_ASSERT(dataSurf);
         blob.reset(new webgl::TexUnpackSurface(mContext, target, width, height, depth,
--- a/dom/canvas/WebGLTimerQuery.h
+++ b/dom/canvas/WebGLTimerQuery.h
@@ -35,17 +35,17 @@ public:
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
   const GLenum mGLName;
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
 
 private:
-  explicit WebGLTimerQuery(WebGLContext* webgl, GLuint name);
+  WebGLTimerQuery(WebGLContext* webgl, GLuint name);
   ~WebGLTimerQuery();
 
   GLenum mTarget;
   bool mCanBeAvailable;
 
   friend class WebGLExtensionDisjointTimerQuery;
 };
 
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -17,17 +17,17 @@ class WebGLTransformFeedback final
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGL2Context;
 
 public:
-    explicit WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
+    WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 
     void Delete();
     WebGLContext* GetParentObject() const;
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     const GLuint mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -11,16 +11,19 @@
 
 // Manual reflection of WebIDL typedefs that are different from their
 // OpenGL counterparts.
 typedef int64_t WebGLsizeiptr;
 typedef int64_t WebGLintptr;
 typedef bool WebGLboolean;
 
 namespace mozilla {
+namespace gl {
+class GLContext; // This is going to be needed a lot.
+} // namespace gl
 
 /*
  * WebGLTextureFakeBlackStatus is an enum to track what needs to use a dummy 1x1 black
  * texture, which we refer to as a 'fake black' texture.
  *
  * There are two things that can cause us to use such 'fake black' textures:
  *
  *   (1) OpenGL ES rules on sampling incomplete textures specify that they
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -18,16 +18,17 @@ struct JSContext;
 
 namespace mozilla {
 class WebGLActiveInfo;
 class WebGLContext;
 class WebGLProgram;
 
 namespace webgl {
 struct LinkedProgramInfo;
+struct UniformInfo;
 } // namespace webgl
 
 class WebGLUniformLocation final
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLUniformLocation)
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4459,25 +4459,22 @@ skip-if = (os == 'win') || (os == 'andro
 skip-if = (os == 'win') || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__attribs__gl-vertexattribipointer-offsets.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__attribs__gl-vertexattribipointer.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__bound-buffer-size-change-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__buffer-copying-contents.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__buffer-copying-restrictions.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__buffer-overflow-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__buffer-type-restrictions.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__getBufferSubData.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__buffers__uniform-buffers.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__context__constants-and-properties-2.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance2__context__context-attributes-depth-stencil-antialias-obeyed.html]
@@ -5834,17 +5831,16 @@ skip-if = (os == 'android' || os == 'lin
 [generated/test_2_conformance__textures__misc__tex-image-canvas-corruption.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__tex-image-webgl.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__tex-image-with-invalid-data.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__tex-sub-image-2d.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__textures__misc__texparameter-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -128,40 +128,32 @@ fail-if = (os == 'win') || (os == 'mac')
 # application crashed [@ mozilla::gl::GLContext::AfterGLCall]
 skip-if = (os == 'android') || (os == 'win')
 
 [generated/test_2_conformance__textures__misc__cube-incomplete-fbo.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'win')
 [generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html]
 fail-if = (os == 'mac') || (os == 'win')
-[generated/test_2_conformance__textures__misc__tex-image-with-invalid-data.html]
-fail-if = (os == 'mac') || (os == 'win')
-[generated/test_2_conformance2__buffers__buffer-type-restrictions.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__rendering__draw-buffers.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win')
-[generated/test_2_conformance2__buffers__buffer-copying-restrictions.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__glsl3__forbidden-operators.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__rendering__negative-one-index.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
-[generated/test_2_conformance2__buffers__buffer-copying-contents.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__reading__read-pixels-pack-parameters.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
 fail-if = (os == 'android') || (os == 'linux')
--- a/dom/html/HTMLTableElement.h
+++ b/dom/html/HTMLTableElement.h
@@ -27,22 +27,21 @@ public:
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   HTMLTableCaptionElement* GetCaption() const
   {
     return static_cast<HTMLTableCaptionElement*>(GetChild(nsGkAtoms::caption));
   }
-  void SetCaption(HTMLTableCaptionElement* aCaption)
+  void SetCaption(HTMLTableCaptionElement* aCaption, ErrorResult& aError)
   {
     DeleteCaption();
     if (aCaption) {
-      mozilla::ErrorResult rv;
-      nsINode::AppendChild(*aCaption, rv);
+      nsINode::AppendChild(*aCaption, aError);
     }
   }
 
   void DeleteTFoot();
 
   already_AddRefed<nsGenericHTMLElement> CreateCaption();
 
   void DeleteCaption();
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1282894.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+    var table = document.createElement("table");
+    var cap = document.createElement("caption");
+    cap.appendChild(table)
+    table.caption = cap;
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -71,8 +71,9 @@ load 903106.html
 load 916322-1.html
 load 916322-2.html
 load 1032654.html
 pref(dom.image.srcset.enabled,true) load 1141260.html
 load 1228876.html
 load 1230110.html
 load 1237633.html
 load 1281972-1.html
+load 1282894.html
\ No newline at end of file
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1396,16 +1396,21 @@ interface nsIDOMWindowUtils : nsISupport
    * Returns a Promise that will be resolved with a string once the capabilities
    * of the h264 decoder have been determined.
    * Success does not mean that all h264 video decoding will be done
    * in hardware.
    */
   readonly attribute jsval supportsHardwareH264Decoding;
 
   /**
+   * Returns the current audio backend as a free-form string.
+   */
+  readonly attribute AString currentAudioBackend;
+
+  /**
    * Record (and return) frame-intervals for frames which were presented
    *   between calling StartFrameTimeRecording and StopFrameTimeRecording.
    *
    * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
    *     (elements were overwritten since Start), result is considered invalid and hence empty.
    * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
    *     Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
    * - Note: the first frame-interval may be longer than expected because last frame
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2005,21 +2005,21 @@ TabChild::RecvRealTouchEvent(const Widge
 
   WidgetTouchEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
 
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
       mPuppetWidget->GetDefaultScale());
 
   if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) {
+    nsCOMPtr<nsIDocument> document = GetDocument();
     if (gfxPrefs::TouchActionEnabled()) {
       APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(mPuppetWidget,
-          localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback);
+          document, localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback);
     }
-    nsCOMPtr<nsIDocument> document = GetDocument();
     APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document,
         localEvent, aGuid, aInputBlockId);
   }
 
   // Dispatch event to content (potentially a long-running operation)
   nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
 
   if (!AsyncPanZoomEnabled()) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2113,16 +2113,19 @@ TabParent::RecvReplyKeyEvent(const Widge
   // Here we convert the WidgetEvent that we received to an nsIDOMEvent
   // to be able to dispatch it to the <browser> element as the target element.
   nsIDocument* doc = mFrameElement->OwnerDoc();
   nsIPresShell* presShell = doc->GetShell();
   NS_ENSURE_TRUE(presShell, true);
   nsPresContext* presContext = presShell->GetPresContext();
   NS_ENSURE_TRUE(presContext, true);
 
+  AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(),
+                                                      &localEvent, doc);
+
   EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
   return true;
 }
 
 bool
 TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
 {
   NS_ENSURE_TRUE(mFrameElement, true);
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -291,10 +291,20 @@ cubeb_stream_type ConvertChannelToCubebT
       return CUBEB_STREAM_TYPE_SYSTEM_ENFORCED;
     default:
       NS_ERROR("The value of AudioChannel is invalid");
       return CUBEB_STREAM_TYPE_MAX;
   }
 }
 #endif
 
+void GetCurrentBackend(nsAString& aBackend)
+{
+  const char* backend = cubeb_get_backend_id(GetCubebContext());
+  if (!backend) {
+    aBackend.AssignLiteral("unknown");
+    return;
+  }
+  aBackend.AssignASCII(backend);
+}
+
 } // namespace CubebUtils
 } // namespace mozilla
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -41,13 +41,14 @@ cubeb* GetCubebContext();
 cubeb* GetCubebContextUnlocked();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebLatency();
 bool CubebLatencyPrefSet();
 #if defined(__ANDROID__) && defined(MOZ_B2G)
 cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
 #endif
+void GetCurrentBackend(nsAString& aBackend);
 
 } // namespace CubebUtils
 } // namespace mozilla
 
 #endif // CubebUtils_h_
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -186,16 +186,23 @@ static const GUID DXVA2_Intel_ModeH264_E
 
 // This tests if a DXVA video decoder can be created for the given media type/resolution.
 // It uses the same decoder device (DXVA2_ModeH264_E - DXVA2_ModeH264_VLD_NoFGT) as the H264
 // decoder MFT provided by windows (CLSID_CMSH264DecoderMFT) uses, so we can use it to determine
 // if the MFT will use software fallback or not.
 bool
 D3D9DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  gfx::D3D9VideoCrashGuard crashGuard;
+  if (crashGuard.Crashed()) {
+    NS_WARNING("DXVA2D3D9 crash detected");
+    return false;
+  }
+
   DXVA2_VideoDesc desc;
   HRESULT hr = ConvertMFTypeToDXVAType(aType, &desc);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   // AMD cards with UVD3 or earlier perform poorly trying to decode 1080p60 in hardware,
   // so use software instead. Pick 45 as an arbitrary upper bound for the framerate we can
   // handle.
   if (mIsAMDPreUVD4 &&
@@ -537,16 +544,23 @@ private:
   uint32_t mHeight;
   UINT mDeviceManagerToken;
   bool mIsAMDPreUVD4;
 };
 
 bool
 D3D11DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  gfx::D3D11VideoCrashGuard crashGuard;
+  if (crashGuard.Crashed()) {
+    NS_WARNING("DXVA2D3D9 crash detected");
+    return false;
+  }
+
   RefPtr<ID3D11VideoDevice> videoDevice;
   HRESULT hr = mDevice->QueryInterface(static_cast<ID3D11VideoDevice**>(getter_AddRefs(videoDevice)));
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   D3D11_VIDEO_DECODER_DESC desc;
   desc.Guid = mDecoderGUID;
 
   UINT32 width = 0;
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -494,41 +494,76 @@ WMFVideoMFTManager::Input(MediaRawData* 
   NS_ENSURE_TRUE(SUCCEEDED(hr) && mLastInput != nullptr, hr);
 
   mLastDuration = aSample->mDuration;
 
   // Forward sample data to the decoder.
   return mDecoder->Input(mLastInput);
 }
 
+class SupportsConfigEvent : public Runnable {
+public:
+  SupportsConfigEvent(DXVA2Manager* aDXVA2Manager, IMFMediaType* aMediaType, float aFramerate)
+    : mDXVA2Manager(aDXVA2Manager)
+    , mMediaType(aMediaType)
+    , mFramerate(aFramerate)
+    , mSupportsConfig(false)
+  {}
+
+  NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+    mSupportsConfig = mDXVA2Manager->SupportsConfig(mMediaType, mFramerate);
+    return NS_OK;
+  }
+  DXVA2Manager* mDXVA2Manager;
+  IMFMediaType* mMediaType;
+  float mFramerate;
+  bool mSupportsConfig;
+};
+
 // The MFTransform we use for decoding h264 video will silently fall
 // back to software decoding (even if we've negotiated DXVA) if the GPU
 // doesn't support decoding the given resolution. It will then upload
 // the software decoded frames into d3d textures to preserve behaviour.
 //
 // Unfortunately this seems to cause corruption (see bug 1193547) and is
 // slow because the upload is done into a non-shareable texture and requires
 // us to copy it.
 //
 // This code tests if the given resolution can be supported directly on the GPU,
 // and makes sure we only ask the MFT for DXVA if it can be supported properly.
+//
+// Ideally we'd know the framerate during initialization and would also ensure
+// that new decoders are created if the resolution changes. Then we could move
+// this check into Init and consolidate the main thread blocking code.
 bool
 WMFVideoMFTManager::CanUseDXVA(IMFMediaType* aType)
 {
   MOZ_ASSERT(mDXVA2Manager);
   // SupportsConfig only checks for valid h264 decoders currently.
   if (mStreamType != H264) {
     return true;
   }
 
   // Assume the current samples duration is representative for the
   // entire video.
   float framerate = 1000000.0 / mLastDuration;
 
-  return mDXVA2Manager->SupportsConfig(aType, framerate);
+  // The supports config check must be done on the main thread since we have
+  // a crash guard protecting it.
+  RefPtr<SupportsConfigEvent> event =
+    new SupportsConfigEvent(mDXVA2Manager, aType, framerate);
+
+  if (NS_IsMainThread()) {
+    event->Run();
+  } else {
+    NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+  }
+
+  return event->mSupportsConfig;
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
 {
   RefPtr<IMFMediaType> mediaType;
   HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
--- a/dom/security/ContentVerifier.cpp
+++ b/dom/security/ContentVerifier.cpp
@@ -42,17 +42,17 @@ ContentVerifier::Init(const nsACString& 
 
   // Keep references to the request and context. We need them in FinishSignature
   // and the ContextCreated callback.
   mContentRequest = aRequest;
   mContentContext = aContext;
 
   rv = mVerifier->CreateContextWithoutCertChain(
     this, aContentSignatureHeader,
-    NS_LITERAL_CSTRING("remote-newtab-signer.mozilla.org"));
+    NS_LITERAL_CSTRING("remotenewtab.content-signature.mozilla.org"));
   if (NS_FAILED(rv)){
     mVerifier = nullptr;
   }
   return rv;
 }
 
 /**
  * Implement nsIStreamListener
--- a/dom/security/test/contentverifier/goodChain.pem
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -1,22 +1,22 @@
 -----BEGIN CERTIFICATE-----
-MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
+MIICUzCCAT2gAwIBAgIUNy0IWlDRDL53zwvj1lq0GCpIe2EwCwYJKoZIhvcNAQEL
 MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
 MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
 IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
 nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
-dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
-ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
-feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
-OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
-+q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
-RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
-5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
-27HBxIpd4tYniYrtfg==
+dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90
+ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B
+AQsDggEBAIeB4WKghknsrow+lj3qzDiHrPBc9AMlb4aZvS6yzazmXr80rXxnsKkb
+ZV1PW/cU6xXH5srWHpfJwypvvYS74btNtuacjKVH2AJdua4482WQIi9gCkXIufRx
+2nSS6pYgYZ4vD+yG8v+3SCChOCXnLjRaN9WxMi8tldbOW9pH44O3vrSSL70pQ2Ph
+8ncUbUbCNNtYhtOe2Z4XT9Cswmfkf4OIQ3gy9eYK2ySEUWP+lHs9KnnNXrLcA/ae
+cSUdI00i3C3OS9yldeyNHzVb8mSsZ5d1WkJrkf/hnXWGrMHRTtlJlG7t7cN8S0Oi
+tQoinJyxrZ+zabFIyl/euDc+Y/dijOU=
 -----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
 MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
 MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
 MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
 AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
 nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
 wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -374,16 +374,18 @@ var interfaceNamesInGlobalScope =
     "CSSSupportsRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CSSTransition", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSValue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSValueList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "CustomElementsRegistry",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "CustomEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DataChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DataErrorEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DataTransfer",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/url/URL.cpp
+++ b/dom/url/URL.cpp
@@ -100,16 +100,20 @@ public:
   CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
                   const objectURLOptions& aOptions, nsAString& aResult,
                   ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
                   ErrorResult& aRv);
 
+  static bool
+  IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+             ErrorResult& aRv);
+
   URLMainThread(nsISupports* aParent, already_AddRefed<nsIURI> aURI)
     : URL(aParent)
     , mURI(aURI)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   virtual void
@@ -289,22 +293,30 @@ URLMainThread::RevokeObjectURL(const Glo
   nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal.Get());
 
   NS_LossyConvertUTF16toASCII asciiurl(aURL);
 
   nsIPrincipal* urlPrincipal =
     nsHostObjectProtocolHandler::GetDataEntryPrincipal(asciiurl);
 
   if (urlPrincipal && principal->Subsumes(urlPrincipal)) {
-    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
     global->UnregisterHostObjectURI(asciiurl);
     nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
   }
 }
 
+/* static */ bool
+URLMainThread::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+                          ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_LossyConvertUTF16toASCII asciiurl(aURL);
+  return nsHostObjectProtocolHandler::HasDataEntry(asciiurl);
+}
+
 void
 URLMainThread::GetHref(nsAString& aHref, ErrorResult& aRv) const
 {
   aHref.Truncate();
 
   nsAutoCString href;
   nsresult rv = mURI->GetSpec(href);
   if (NS_SUCCEEDED(rv)) {
@@ -663,16 +675,20 @@ public:
   CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
                   const mozilla::dom::objectURLOptions& aOptions,
                   nsAString& aResult, mozilla::ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl,
                   ErrorResult& aRv);
 
+  static bool
+  IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+             ErrorResult& aRv);
+
   URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy);
 
   virtual void
   GetHref(nsAString& aHref, ErrorResult& aRv) const override;
 
   virtual void
   SetHref(const nsAString& aHref, ErrorResult& aRv) override;
 
@@ -892,16 +908,50 @@ public:
         global->UnregisterHostObjectURI(url);
       }
     }
 
     return true;
   }
 };
 
+// This class checks if an URL is valid on the main thread.
+class IsValidURLRunnable : public WorkerMainThreadRunnable
+{
+private:
+  const nsString mURL;
+  bool mValid;
+
+public:
+  IsValidURLRunnable(WorkerPrivate* aWorkerPrivate,
+                     const nsAString& aURL)
+  : WorkerMainThreadRunnable(aWorkerPrivate,
+                             NS_LITERAL_CSTRING("URL :: IsValidURL"))
+  , mURL(aURL)
+  , mValid(false)
+  {}
+
+  bool
+  MainThreadRun()
+  {
+    AssertIsOnMainThread();
+
+    NS_ConvertUTF16toUTF8 url(mURL);
+    mValid = nsHostObjectProtocolHandler::HasDataEntry(url);
+
+    return true;
+  }
+
+  bool
+  IsValidURL() const
+  {
+    return mValid;
+  }
+};
+
 // This class creates a URL object on the main thread.
 class ConstructorRunnable : public WorkerMainThreadRunnable
 {
 private:
   const nsString mURL;
 
   nsString mBase; // IsVoid() if we have no base URI string.
   RefPtr<URLProxy> mBaseProxy;
@@ -1302,16 +1352,34 @@ URLWorker::RevokeObjectURL(const GlobalO
   if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) {
     WorkerGlobalScope* scope = workerPrivate->GlobalScope();
     MOZ_ASSERT(scope);
 
     scope->UnregisterHostObjectURI(NS_ConvertUTF16toUTF8(aUrl));
   }
 }
 
+/* static */ bool
+URLWorker::IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+                      ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+  RefPtr<IsValidURLRunnable> runnable =
+    new IsValidURLRunnable(workerPrivate, aUrl);
+
+  runnable->Dispatch(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return false;
+  }
+
+  return runnable->IsValidURL();
+}
+
 URLWorker::URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy)
   : URL(nullptr)
   , mWorkerPrivate(aWorkerPrivate)
   , mURLProxy(aURLProxy)
 {}
 
 URLWorker::~URLWorker()
 {
@@ -1694,16 +1762,26 @@ URL::RevokeObjectURL(const GlobalObject&
 {
   if (NS_IsMainThread()) {
     URLMainThread::RevokeObjectURL(aGlobal, aURL, aRv);
   } else {
     URLWorker::RevokeObjectURL(aGlobal, aURL, aRv);
   }
 }
 
+bool
+URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+                ErrorResult& aRv)
+{
+  if (NS_IsMainThread()) {
+    return URLMainThread::IsValidURL(aGlobal, aURL, aRv);
+  }
+  return URLWorker::IsValidURL(aGlobal, aURL, aRv);
+}
+
 URLSearchParams*
 URL::SearchParams()
 {
   CreateSearchParamsIfNeeded();
   return mSearchParams;
 }
 
 bool IsChromeURI(nsIURI* aURI)
--- a/dom/url/URL.h
+++ b/dom/url/URL.h
@@ -76,16 +76,20 @@ public:
   CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
                   const objectURLOptions& aOptions, nsAString& aResult,
                   ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
                   ErrorResult& aRv);
 
+  static bool
+  IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+             ErrorResult& aRv);
+
   virtual void
   GetHref(nsAString& aHref, ErrorResult& aRv) const = 0;
 
   virtual void
   SetHref(const nsAString& aHref, ErrorResult& aRv) = 0;
 
   virtual void
   GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const = 0;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CustomElementsRegistry.webidl
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://html.spec.whatwg.org/#dom-window-customelements
+[Func="CustomElementsRegistry::IsCustomElementsEnabled"]
+interface CustomElementsRegistry {
+  [Throws]
+  void define(DOMString name, Function functionConstructor,
+              optional ElementDefinitionOptions options);
+  [Throws]
+  any get(DOMString name);
+  [Throws]
+  Promise<void> whenDefined(DOMString name);
+};
+
+dictionary ElementDefinitionOptions {
+  DOMString extends;
+};
+
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -256,17 +256,18 @@ partial interface Document {
 // http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html#extensions-to-the-document-interface
 partial interface Document {
     readonly attribute Element? mozPointerLockElement;
     void mozExitPointerLock ();
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
-    [Throws, Func="nsDocument::IsWebComponentsEnabled"]
+    // this is deprecated from CustomElements v0
+    [Throws, Func="CustomElementsRegistry::IsCustomElementsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
--- a/dom/webidl/HTMLTableElement.webidl
+++ b/dom/webidl/HTMLTableElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © 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.
  */
 
 interface HTMLTableElement : HTMLElement {
+           [SetterThrows]
            attribute HTMLTableCaptionElement? caption;
   HTMLElement createCaption();
   void deleteCaption();
            [SetterThrows]
            attribute HTMLTableSectionElement? tHead;
   HTMLElement createTHead();
   void deleteTHead();
            [SetterThrows]
--- a/dom/webidl/URL.webidl
+++ b/dom/webidl/URL.webidl
@@ -51,16 +51,18 @@ interface URL {
 
 partial interface URL {
   [Throws]
   static DOMString? createObjectURL(Blob blob, optional objectURLOptions options);
   [Throws]
   static DOMString? createObjectURL(MediaStream stream, optional objectURLOptions options);
   [Throws]
   static void revokeObjectURL(DOMString url);
+  [ChromeOnly, Throws]
+  static boolean isValidURL(DOMString url);
 };
 
 dictionary objectURLOptions
 {
 /* boolean autoRevoke = true; */ /* not supported yet */
 };
 
 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -343,55 +343,127 @@ interface WebGL2RenderingContext : WebGL
 
     void readBuffer(GLenum src);
 
     /* Renderbuffer objects */
     [Throws]
     any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname);
     void renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
 
+    ////////////////////////////////////
+
     /* Texture objects */
     void texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
     void texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
                       GLsizei depth);
 
+    //////
+
     void texImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum format,
                     GLenum type, ArrayBufferView? pixels);
+
+    //////
+
     [Throws] // Can't actually throw.
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
-                       GLenum format, GLenum type,
-                       ArrayBufferView? pixels);
+                       GLenum format, GLenum type, ArrayBufferView? pixels);
     [Throws] // Can't actually throw.
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLint zoffset, GLenum format, GLenum type, ImageData? data);
     [Throws]
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLint zoffset, GLenum format, GLenum type, HTMLImageElement image);
     [Throws]
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLint zoffset, GLenum format, GLenum type,
                        HTMLCanvasElement canvas);
     [Throws]
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLint zoffset, GLenum format, GLenum type, HTMLVideoElement video);
 
+    //////
+
     void copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                            GLint zoffset, GLint x, GLint y, GLsizei width,
                            GLsizei height);
 
     void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei depth, GLint border,
                               ArrayBufferView data);
     void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                                  GLint zoffset, GLsizei width, GLsizei height,
                                  GLsizei depth, GLenum format,
                                  ArrayBufferView data);
 
+    ////////////////
+    // Texture from PBO
+
+    [Throws] // Can't actually throw.
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+                    GLsizei height, GLint border, GLenum format, GLenum type,
+                    GLintptr offset);
+
+    [Throws] // Can't actually throw.
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLsizei width, GLsizei height, GLenum format, GLenum type,
+                       GLintptr offset);
+
+    void texImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+                    GLsizei height, GLsizei depth, GLint border, GLenum format,
+                    GLenum type, GLintptr offset);
+
+    [Throws] // Can't actually throw.
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLenum format, GLenum type, GLintptr offset);
+
+    ////////////////
+    // WebGL 1 overloads
+
+    // Overloads must share [Throws].
+    [Throws] // Can't throw.
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+                    GLsizei height, GLint border, GLenum format, GLenum type,
+                    ArrayBufferView? pixels);
+    [Throws] // Can't throw.
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format,
+                    GLenum type, ImageData? pixels);
+    [Throws] // May throw DOMException
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format,
+                    GLenum type, HTMLImageElement image);
+    [Throws] // May throw DOMException
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format,
+                    GLenum type, HTMLCanvasElement canvas);
+    [Throws] // May throw DOMException
+    void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format,
+                    GLenum type, HTMLVideoElement video);
+
+    //////
+
+    [Throws] // Can't throw.
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLsizei width, GLsizei height, GLenum format, GLenum type,
+                       ArrayBufferView? pixels);
+    [Throws] // Can't throw.
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLenum format, GLenum type, ImageData? pixels);
+    [Throws]  // May throw DOMException
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLenum format, GLenum type, HTMLImageElement image);
+    [Throws] // May throw DOMException
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLenum format, GLenum type, HTMLCanvasElement canvas);
+    [Throws] // May throw DOMException
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLenum format, GLenum type, HTMLVideoElement video);
+
+    ////////////////////////////////////
+
     /* Programs and shaders */
     [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram? program, DOMString name);
 
     /* Uniforms and attributes */
     void uniform1ui(WebGLUniformLocation? location, GLuint v0);
     void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
     void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
     void uniform4ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -31,16 +31,18 @@ typedef any Transferable;
    CrossOriginReadable] readonly attribute Window window;
   [Replaceable, Constant, StoreInSlot,
    CrossOriginReadable] readonly attribute Window self;
   [Unforgeable, StoreInSlot, Pure] readonly attribute Document? document;
   [Throws] attribute DOMString name;
   [PutForwards=href, Unforgeable, Throws,
    CrossOriginReadable, CrossOriginWritable] readonly attribute Location? location;
   [Throws] readonly attribute History history;
+  [Func="CustomElementsRegistry::IsCustomElementsEnabled"]
+  readonly attribute CustomElementsRegistry customElements;
   [Replaceable, Throws] readonly attribute BarProp locationbar;
   [Replaceable, Throws] readonly attribute BarProp menubar;
   [Replaceable, Throws] readonly attribute BarProp personalbar;
   [Replaceable, Throws] readonly attribute BarProp scrollbars;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
   [Throws, CrossOriginCallable, UnsafeInPrerendering] void close();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -102,16 +102,17 @@ WEBIDL_FILES = [
     'CSSPrimitiveValue.webidl',
     'CSSPseudoElement.webidl',
     'CSSRuleList.webidl',
     'CSSStyleDeclaration.webidl',
     'CSSStyleSheet.webidl',
     'CSSTransition.webidl',
     'CSSValue.webidl',
     'CSSValueList.webidl',
+    'CustomElementsRegistry.webidl',
     'DataContainerEvent.webidl',
     'DataTransfer.webidl',
     'DataTransferItem.webidl',
     'DataTransferItemList.webidl',
     'DecoderDoctorNotification.webidl',
     'DedicatedWorkerGlobalScope.webidl',
     'DelayNode.webidl',
     'DesktopNotification.webidl',
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -97,16 +97,83 @@ static void
 ReleaseTemporarySurface(void* aPixels, void* aContext)
 {
   DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
   if (surf) {
     surf->Release();
   }
 }
 
+#ifdef DEBUG
+static bool
+VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+  if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+    return true;
+  }
+  // We should've initialized the data to be opaque already
+  // On debug builds, verify that this is actually true.
+  int height = aSize.height;
+  int width = aSize.width;
+
+  for (int row = 0; row < height; ++row) {
+    for (int column = 0; column < width; column += 4) {
+#ifdef IS_BIG_ENDIAN
+      MOZ_ASSERT(aData[column] == 0xFF);
+#else
+      MOZ_ASSERT(aData[column + 3] == 0xFF);
+#endif
+    }
+    aData += aStride;
+  }
+
+  return true;
+}
+
+// Since checking every pixel is expensive, this only checks the four corners and center
+// of a surface that their alpha value is 0xFF.
+static bool
+VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+  if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+    return true;
+  }
+
+  int height = aSize.height;
+  int width = aSize.width;
+  const int pixelSize = 4;
+  const int strideDiff = aStride - (width * pixelSize);
+  MOZ_ASSERT(width * pixelSize <= aStride);
+
+#ifdef IS_BIG_ENDIAN
+  const int alphaOffset = 0;
+#else
+  const int alphaOffset = 3;
+#endif
+
+  const int topLeft = alphaOffset;
+  const int topRight = width * pixelSize + alphaOffset - pixelSize;
+  const int bottomRight = aStride * height - strideDiff + alphaOffset - pixelSize;
+  const int bottomLeft = aStride * height - aStride + alphaOffset;
+
+  // Lastly the center pixel
+  int middleRowHeight = height / 2;
+  int middleRowWidth = (width / 2) * pixelSize;
+  const int middle = aStride * middleRowHeight + middleRowWidth + alphaOffset;
+
+  MOZ_ASSERT(aData[topLeft] == 0xFF);
+  MOZ_ASSERT(aData[topRight] == 0xFF);
+  MOZ_ASSERT(aData[bottomRight] == 0xFF);
+  MOZ_ASSERT(aData[bottomLeft] == 0xFF);
+  MOZ_ASSERT(aData[middle] == 0xFF);
+
+  return true;
+}
+#endif
+
 static SkBitmap
 GetBitmapForSurface(SourceSurface* aSurface)
 {
   SkBitmap bitmap;
 
   if (!aSurface) {
     gfxDebug() << "Creating empty Skia bitmap from null SourceSurface";
     return bitmap;
@@ -124,16 +191,19 @@ GetBitmapForSurface(SourceSurface* aSurf
   }
 
   if (!bitmap.installPixels(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
                             surf->GetData(), surf->Stride(), nullptr,
                             ReleaseTemporarySurface, surf)) {
     gfxDebug() << "Failed installing pixels on Skia bitmap for temporary surface";
   }
 
+  // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque white.
+  MOZ_ASSERT(VerifyRGBXCorners(surf->GetData(), surf->GetSize(),
+                               surf->Stride(), surf->GetFormat()));
   return bitmap;
 }
 
 DrawTargetSkia::DrawTargetSkia()
   : mSnapshot(nullptr)
 #ifdef MOZ_WIDGET_COCOA
   , mCG(nullptr)
   , mColorSpace(nullptr)
@@ -1329,17 +1399,20 @@ DrawTargetSkia::OptimizeSourceSurface(So
     RefPtr<SourceSurface> surface(aSurface);
     return surface.forget();
   }
 
   // If we're not using skia-gl then drawing doesn't require any
   // uploading, so any data surface is fine. Call GetDataSurface
   // to trigger any required readback so that it only happens
   // once.
-  return aSurface->GetDataSurface();
+  RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
+  MOZ_ASSERT(VerifyRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
+                              dataSurface->Stride(), dataSurface->GetFormat()));
+  return dataSurface.forget();
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
 #if USE_SKIA_GPU
   if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
     // Wrap the OpenGL texture id in a Skia texture handle.
@@ -1410,17 +1483,18 @@ DrawTargetSkia::Init(const IntSize &aSiz
   int stride = (BytesPerPixel(aFormat)*aSize.width + (4-1)) & -4;
 
   SkBitmap bitmap;
   bitmap.setInfo(MakeSkiaImageInfo(aSize, aFormat), stride);
   if (!bitmap.tryAllocPixels()) {
     return false;
   }
 
-  bitmap.eraseColor(SK_ColorTRANSPARENT);
+  SkColor clearColor = (aFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+  bitmap.eraseColor(clearColor);
 
   mCanvas.adopt(new SkCanvas(bitmap));
   mSize = aSize;
 
   mFormat = aFormat;
   return true;
 }
 
@@ -1472,40 +1546,16 @@ DrawTargetSkia::InitWithGrContext(GrCont
 
   mCanvas = gpuSurface->getCanvas();
 
   return true;
 }
 
 #endif
 
-#ifdef DEBUG
-bool
-VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
-{
-  // We should've initialized the data to be opaque already
-  // On debug builds, verify that this is actually true.
-  int height = aSize.height;
-  int width = aSize.width;
-
-  for (int row = 0; row < height; ++row) {
-    for (int column = 0; column < width; column += 4) {
-#ifdef IS_BIG_ENDIAN
-      MOZ_ASSERT(aData[column] == 0xFF);
-#else
-      MOZ_ASSERT(aData[column + 3] == 0xFF);
-#endif
-    }
-    aData += aStride;
-  }
-
-  return true;
-}
-#endif
-
 void
 DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized)
 {
   MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) ||
               aUninitialized || VerifyRGBXFormat(aData, aSize, aStride, aFormat));
 
   SkBitmap bitmap;
   bitmap.setInfo(MakeSkiaImageInfo(aSize, aFormat), aStride);
@@ -1553,17 +1603,18 @@ DrawTargetSkia::CreatePathBuilder(FillRu
 }
 
 void
 DrawTargetSkia::ClearRect(const Rect &aRect)
 {
   MarkChanged();
   mCanvas->save();
   mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, true);
-  mCanvas->clear(SK_ColorTRANSPARENT);
+  SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+  mCanvas->clear(clearColor);
   mCanvas->restore();
 }
 
 void
 DrawTargetSkia::PushClip(const Path *aPath)
 {
   if (aPath->GetBackendType() != BackendType::SKIA) {
     return;
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -822,18 +822,22 @@ Factory::CreateDataSourceSurface(const I
                                  SurfaceFormat aFormat,
                                  bool aZero)
 {
   if (!AllowedSurfaceSize(aSize)) {
     gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DSS) " << aSize;
     return nullptr;
   }
 
+  // Skia doesn't support RGBX, so memset RGBX to 0xFF
+  bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+  uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
   RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
-  if (newSurf->Init(aSize, aFormat, aZero)) {
+  if (newSurf->Init(aSize, aFormat, clearSurface, clearValue)) {
     return newSurf.forget();
   }
 
   gfxWarning() << "CreateDataSourceSurface failed in init";
   return nullptr;
 }
 
 already_AddRefed<DataSourceSurface>
@@ -842,18 +846,22 @@ Factory::CreateDataSourceSurfaceWithStri
                                            int32_t aStride,
                                            bool aZero)
 {
   if (aStride < aSize.width * BytesPerPixel(aFormat)) {
     gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat;
     return nullptr;
   }
 
+  // Skia doesn't support RGBX, so memset RGBX to 0xFF
+  bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+  uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
   RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
-  if (newSurf->InitWithStride(aSize, aFormat, aStride, aZero)) {
+  if (newSurf->Init(aSize, aFormat, clearSurface, clearValue, aStride)) {
     return newSurf.forget();
   }
 
   gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero;
   return nullptr;
 }
 
 static uint16_t
--- a/gfx/2d/SourceSurfaceRawData.cpp
+++ b/gfx/2d/SourceSurfaceRawData.cpp
@@ -44,50 +44,36 @@ SourceSurfaceRawData::GuaranteePersistan
 
   memcpy(mRawData, oldData, mStride * mSize.height);
   mOwnData = true;
 }
 
 bool
 SourceSurfaceAlignedRawData::Init(const IntSize &aSize,
                                   SurfaceFormat aFormat,
-                                  bool aZero)
+                                  bool aClearMem,
+                                  uint8_t aClearValue,
+                                  int32_t aStride)
 {
   mFormat = aFormat;
-  mStride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
+  mStride = aStride ? aStride : GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
 
   size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
   if (bufLen > 0) {
+    bool zeroMem = aClearMem && !aClearValue;
     static_assert(sizeof(decltype(mArray[0])) == 1,
                   "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
-    mArray.Realloc(/* actually an object count */ bufLen, aZero);
-    mSize = aSize;
-  } else {
-    mArray.Dealloc();
-    mSize.SizeTo(0, 0);
-  }
-
-  return mArray != nullptr;
-}
 
-bool
-SourceSurfaceAlignedRawData::InitWithStride(const IntSize &aSize,
-                                            SurfaceFormat aFormat,
-                                            int32_t aStride,
-                                            bool aZero)
-{
-  mFormat = aFormat;
-  mStride = aStride;
+    // AlignedArray uses cmalloc to zero mem for a fast path.
+    mArray.Realloc(/* actually an object count */ bufLen, zeroMem);
+    mSize = aSize;
 
-  size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
-  if (bufLen > 0) {
-    static_assert(sizeof(decltype(mArray[0])) == 1,
-                  "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
-    mArray.Realloc(/* actually an object count */ bufLen, aZero);
-    mSize = aSize;
+    if (mArray && aClearMem && aClearValue) {
+      memset(mArray, aClearValue, mStride * aSize.height);
+    }
   } else {
     mArray.Dealloc();
     mSize.SizeTo(0, 0);
   }
 
   return mArray != nullptr;
 }
 
--- a/gfx/2d/SourceSurfaceRawData.h
+++ b/gfx/2d/SourceSurfaceRawData.h
@@ -108,17 +108,17 @@ public:
     , mFormat(SurfaceFormat::UNKNOWN)
     , mMapCount(0)
   {}
   ~SourceSurfaceAlignedRawData()
   {
     MOZ_ASSERT(mMapCount == 0);
   }
 
-  virtual uint8_t *GetData() override { return mArray; }
+  virtual uint8_t* GetData() override { return mArray; }
   virtual int32_t Stride() override { return mStride; }
 
   virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
   virtual IntSize GetSize() const override { return mSize; }
   virtual SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool Map(MapType, MappedSurface *aMappedSurface) override
   {
@@ -137,21 +137,19 @@ public:
     MOZ_ASSERT(mMapCount >= 0);
   }
 
 private:
   friend class Factory;
 
   bool Init(const IntSize &aSize,
             SurfaceFormat aFormat,
-            bool aZero);
-  bool InitWithStride(const IntSize &aSize,
-                      SurfaceFormat aFormat,
-                      int32_t aStride,
-                      bool aZero);
+            bool aClearMem,
+            uint8_t aClearValue,
+            int32_t aStride = 0);
 
   AlignedArray<uint8_t> mArray;
   int32_t mStride;
   SurfaceFormat mFormat;
   IntSize mSize;
   Atomic<int32_t> mMapCount;
 };
 
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -2,60 +2,28 @@
 /* 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 "GLUploadHelpers.h"
 
 #include "GLContext.h"
 #include "mozilla/gfx/2D.h"
+#include "gfxUtils.h"
 #include "mozilla/gfx/Tools.h"  // For BytesPerPixel
 #include "nsRegion.h"
 #include "GfxTexturesReporter.h"
 #include "mozilla/gfx/Logging.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace gl {
 
-/* These two techniques are suggested by "Bit Twiddling Hacks"
- */
-
-/**
- * Returns true if |aNumber| is a power of two
- * 0 is incorreclty considered a power of two
- */
-static bool
-IsPowerOfTwo(int aNumber)
-{
-    return (aNumber & (aNumber - 1)) == 0;
-}
-
-/**
- * Returns the first integer greater than |aNumber| which is a power of two
- * Undefined for |aNumber| < 0
- */
-static int
-NextPowerOfTwo(int aNumber)
-{
-#if defined(__arm__)
-    return 1 << (32 - __builtin_clz(aNumber - 1));
-#else
-    --aNumber;
-    aNumber |= aNumber >> 1;
-    aNumber |= aNumber >> 2;
-    aNumber |= aNumber >> 4;
-    aNumber |= aNumber >> 8;
-    aNumber |= aNumber >> 16;
-    return ++aNumber;
-#endif
-}
-
 static unsigned int
 DataOffset(const IntPoint& aPoint, int32_t aStride, SurfaceFormat aFormat)
 {
   unsigned int data = aPoint.y * aStride;
   data += aPoint.x * BytesPerPixel(aFormat);
   return data;
 }
 
@@ -280,25 +248,26 @@ TexImage2DHelper(GLContext* gl,
                  GLint pixelsize, GLint border, GLenum format,
                  GLenum type, const GLvoid* pixels)
 {
     if (gl->IsGLES()) {
 
         NS_ASSERTION(format == (GLenum)internalformat,
                     "format and internalformat not the same for glTexImage2D on GLES2");
 
+        MOZ_ASSERT(width >= 0 && height >= 0);
         if (!CanUploadNonPowerOfTwo(gl)
             && (stride != width * pixelsize
-            || !IsPowerOfTwo(width)
-            || !IsPowerOfTwo(height))) {
+            || !IsPowerOfTwo((uint32_t)width)
+            || !IsPowerOfTwo((uint32_t)height))) {
 
             // Pad out texture width and height to the next power of two
             // as we don't support/want non power of two texture uploads
-            GLsizei paddedWidth = NextPowerOfTwo(width);
-            GLsizei paddedHeight = NextPowerOfTwo(height);
+            GLsizei paddedWidth = RoundUpPow2((uint32_t)width);
+            GLsizei paddedHeight = RoundUpPow2((uint32_t)height);
 
             GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize];
 
             // Pad out texture data to be in a POT sized buffer for uploading to
             // a POT sized texture
             CopyAndPadTextureData(pixels, paddedPixels, width, height,
                                   paddedWidth, paddedHeight, stride, pixelsize);
 
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -27,17 +27,17 @@ GPUChild::Init()
   // Build a list of prefs the GPU process will need. Note that because we
   // limit the GPU process to prefs contained in gfxPrefs, we can simplify
   // the message in two ways: one, we only need to send its index in gfxPrefs
   // rather than its name, and two, we only need to send prefs that don't
   // have their default value.
   nsTArray<GfxPrefSetting> prefs;
   for (auto pref : gfxPrefs::all()) {
     if (pref->HasDefaultValue()) {
-      return;
+      continue;
     }
 
     GfxPrefValue value;
     pref->GetCachedValue(&value);
     prefs.AppendElement(GfxPrefSetting(pref->Index(), value));
   }
 
   SendInit(prefs);
--- a/gfx/layers/D3D11ShareHandleImage.cpp
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -79,17 +79,17 @@ D3D11ShareHandleImage::GetAsSourceSurfac
   device->GetImmediateContext(getter_AddRefs(context));
   if (!context) {
     return nullptr;
   }
 
   context->CopyResource(softTexture, texture);
 
   RefPtr<gfx::DataSourceSurface> surface =
-    gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+    gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8A8);
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   gfx::DataSourceSurface::MappedSurface mappedSurface;
   if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
     return nullptr;
   }
--- a/gfx/layers/D3D9SurfaceImage.cpp
+++ b/gfx/layers/D3D9SurfaceImage.cpp
@@ -44,24 +44,24 @@ D3D9SurfaceImage::AllocateAndCopy(D3D9Re
 
   D3DSURFACE_DESC desc;
   surface->GetDesc(&desc);
   // Ensure we can convert the textures format to RGB conversion
   // in StretchRect. Fail if we can't.
   hr = d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT,
                                          D3DDEVTYPE_HAL,
                                          desc.Format,
-                                         D3DFMT_X8R8G8B8);
+                                         D3DFMT_A8R8G8B8);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   // DXVA surfaces aren't created sharable, so we need to copy the surface
   // to a sharable texture to that it's accessible to the layer manager's
   // device.
   RefPtr<TextureClient> textureClient =
-    aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8X8, aRegion.Size());
+    aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, aRegion.Size());
   if (!textureClient) {
     return E_FAIL;
   }
 
   // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
   RefPtr<IDirect3DSurface9> textureSurface = static_cast<DXGID3D9TextureData*>(
     textureClient->GetInternalData())->GetD3D9Surface();
   if (!textureSurface) {
@@ -128,17 +128,17 @@ D3D9SurfaceImage::GetAsSourceSurface()
   RefPtr<IDirect3DDevice9> device = texData->GetD3D9Device();
   if (!device) {
     return nullptr;
   }
 
   RefPtr<IDirect3DSurface9> systemMemorySurface;
   hr = device->CreateOffscreenPlainSurface(mSize.width,
                                            mSize.height,
-                                           D3DFMT_X8R8G8B8,
+                                           D3DFMT_A8R8G8B8,
                                            D3DPOOL_SYSTEMMEM,
                                            getter_AddRefs(systemMemorySurface),
                                            0);
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = device->GetRenderTargetData(textureSurface, systemMemorySurface);
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
--- a/gfx/layers/apz/test/mochitest/helper_long_tap.html
+++ b/gfx/layers/apz/test/mochitest/helper_long_tap.html
@@ -5,22 +5,16 @@
   <meta name="viewport" content="width=device-width; initial-scale=1.0">
   <title>Ensure we get a touch-cancel after a contextmenu comes up</title>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <script type="application/javascript">
 
 function longPressLink() {
-  if (!window.TouchEvent) {
-    ok(true, "Touch events are not supported on this platform, sorry!\n");
-    subtestDone();
-    return;
-  }
-
   synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
     dump("Finished synthesizing touch-start, waiting for events...\n");
   });
 }
 
 var eventsFired = 0;
 function recordEvent(e) {
   switch (eventsFired) {
--- a/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
+++ b/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
@@ -33,22 +33,16 @@ function startTest() {
     }
     // After that's done, we click on the button to make sure the
     // skipped-paint codepath still has working APZ event transformations.
     clickButton();
   });
 }
 
 function clickButton() {
-  if (!window.TouchEvent) {
-    ok(true, "Touch events are not supported on this platform, sorry!\n");
-    subtestDone();
-    return;
-  }
-
   document.addEventListener('click', clicked, false);
 
   synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
     dump("Finished synthesizing tap, waiting for button to be clicked...\n");
   });
 }
 
 function clicked(e) {
--- a/gfx/layers/apz/test/mochitest/helper_tap.html
+++ b/gfx/layers/apz/test/mochitest/helper_tap.html
@@ -5,22 +5,16 @@
   <meta name="viewport" content="width=device-width; initial-scale=1.0">
   <title>Sanity touch-tapping test</title>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <script type="application/javascript">
 
 function clickButton() {
-  if (!window.TouchEvent) {
-    ok(true, "Touch events are not supported on this platform, sorry!\n");
-    subtestDone();
-    return;
-  }
-
   document.addEventListener('click', clicked, false);
 
   synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
     dump("Finished synthesizing tap, waiting for button to be clicked...\n");
   });
 }
 
 function clicked(e) {
--- a/gfx/layers/apz/test/mochitest/helper_tap_passive.html
+++ b/gfx/layers/apz/test/mochitest/helper_tap_passive.html
@@ -7,22 +7,16 @@
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <script type="application/javascript">
 
 var touchdownTime;
 
 function longPressLink() {
-  if (!window.TouchEvent) {
-    ok(true, "Touch events are not supported on this platform, sorry!\n");
-    subtestDone();
-    return;
-  }
-
   synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
     dump("Finished synthesizing touch-start, waiting for events...\n");
   });
 }
 
 var touchstartReceived = false;
 function recordEvent(e) {
   if (!touchstartReceived) {
--- a/gfx/layers/apz/test/mochitest/helper_touch_action.html
+++ b/gfx/layers/apz/test/mochitest/helper_touch_action.html
@@ -23,17 +23,17 @@ function* test(testDriver) {
   yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
       "Synthesized native vertical drag (1), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
   checkScroll(0, 50, "After first vertical drag, with pan-y" );
 
   // switch style to pan-x
   document.body.style.touchAction = 'pan-x';
   ok(true, "Waiting for pan-x to propagate...");
-  yield waitForAllPaints(function() {
+  yield waitForAllPaintsFlushed(function() {
     flushApzRepaints(testDriver);
   });
 
   // drag the page up to scroll down by 50px, but it won't happen because pan-x
   yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
      "Synthesized native vertical drag (2), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
   checkScroll(0, 50, "After second vertical drag, with pan-x");
@@ -49,44 +49,44 @@ function* test(testDriver) {
   yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
      "Synthesized diagonal drag (1), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
   checkScroll(10, 50, "After first diagonal drag, with pan-x");
 
   // switch style back to pan-y
   document.body.style.touchAction = 'pan-y';
   ok(true, "Waiting for pan-y to propagate...");
-  yield waitForAllPaints(function() {
+  yield waitForAllPaintsFlushed(function() {
     flushApzRepaints(testDriver);
   });
 
   // drag the page diagonally right/down to scroll up/left by 40px each axis;
   // only the y-axis will actually scroll because pan-y
   yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
      "Synthesized diagonal drag (2), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
   checkScroll(10, 10, "After second diagonal drag, with pan-y");
 
   // switch style to none
   document.body.style.touchAction = 'none';
   ok(true, "Waiting for none to propagate...");
-  yield waitForAllPaints(function() {
+  yield waitForAllPaintsFlushed(function() {
     flushApzRepaints(testDriver);
   });
 
   // drag the page diagonally up/left to scroll down/right by 40px each axis;
   // neither will scroll because of touch-action
   yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
       "Synthesized diagonal drag (3), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
   checkScroll(10, 10, "After third diagonal drag, with none");
 
   document.body.style.touchAction = 'manipulation';
   ok(true, "Waiting for manipulation to propagate...");
-  yield waitForAllPaints(function() {
+  yield waitForAllPaintsFlushed(function() {
     flushApzRepaints(testDriver);
   });
 
   // drag the page diagonally up/left to scroll down/right by 40px each axis;
   // both will scroll because of touch-action
   yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
       "Synthesized diagonal drag (4), waiting for touch-end event...");
   yield flushApzRepaints(testDriver);
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -43,18 +43,17 @@ skip-if = (os == 'android') || (os == 'b
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_scroll_inactive_bug1190112.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_scroll_subframe_scrollbar.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_frame_reconstruction.html]
 [test_group_touchevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
-# On OS X we don't support touch events at all.
-skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
+skip-if = (toolkit == 'windows')
 [test_group_wheelevents.html]
 skip-if = (toolkit == 'android') # wheel events not supported on mobile
 [test_group_mouseevents.html]
 [test_touch_listeners_impacting_wheel.html]
 skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
 [test_bug1253683.html]
 skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile
 [test_group_zoom.html]
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -65,16 +65,17 @@ var subtests = [
   // More complex touch-action tests, with overlapping regions and such
   {'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
   // Tests that touch-action CSS properties are handled in APZ without waiting
   // on the main-thread, when possible
   {'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
 ];
 
 if (isApzEnabled()) {
+  ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
   if (getPlatform() == "android") {
     // This has a lot of subtests, and Android emulators are slow.
     SimpleTest.requestLongerTimeout(2);
   }
 
   SimpleTest.waitForExplicitFinish();
   window.onload = function() {
     runSubtestsSeriallyInFreshWindows(subtests)
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -549,39 +549,44 @@ GetRootDocumentElementFor(nsIWidget* aWi
     if (nsIPresShell* shell = view->GetPresShell()) {
       MOZ_ASSERT(shell->GetDocument());
       return shell->GetDocument()->GetDocumentElement();
     }
   }
   return nullptr;
 }
 
+static nsIFrame*
+UpdateRootFrameForTouchTargetDocument(nsIFrame* aRootFrame)
+{
+#if defined(MOZ_ANDROID_APZ)
+  // Re-target so that the hit test is performed relative to the frame for the
+  // Root Content Document instead of the Root Document which are different in
+  // Android. See bug 1229752 comment 16 for an explanation of why this is necessary.
+  if (nsIDocument* doc = aRootFrame->PresContext()->PresShell()->GetTouchEventTargetDocument()) {
+    if (nsIPresShell* shell = doc->GetShell()) {
+      if (nsIFrame* frame = shell->GetRootFrame()) {
+        return frame;
+      }
+    }
+  }
+#endif
+  return aRootFrame;
+}
+
 // Determine the scrollable target frame for the given point and add it to
 // the target list. If the frame doesn't have a displayport, set one.
 // Return whether or not a displayport was set.
 static bool
 PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
                                     const ScrollableLayerGuid& aGuid,
                                     nsIFrame* aRootFrame,
                                     const LayoutDeviceIntPoint& aRefPoint,
                                     nsTArray<ScrollableLayerGuid>* aTargets)
 {
-#if defined(MOZ_ANDROID_APZ)
-  // Re-target so that the hit test is performed relative to the frame for the
-  // Root Content Document instead of the Root Document which are different in
-  // Android. See bug 1229752 comment 16 for an explanation of why this is necessary.
-  if (nsIDocument* doc = aRootFrame->PresContext()->PresShell()->GetTouchEventTargetDocument()) {
-    if (nsIPresShell* shell = doc->GetShell()) {
-      if(nsIFrame* frame = shell->GetRootFrame()) {
-        aRootFrame = frame;
-      }
-    }
-  }
-#endif
-
   ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
   nsPoint point =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
   nsIFrame* target =
     nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
   nsIScrollableFrame* scrollAncestor = target
     ? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
     : aRootFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
@@ -734,16 +739,18 @@ APZCCallbackHelper::SendSetTargetAPZCNot
     // race the original confirmation (which needs to go through a layers
     // transaction).
     APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64 "\n", aInputBlockId);
     return;
   }
   sLastTargetAPZCNotificationInputBlock = aInputBlockId;
   if (nsIPresShell* shell = aDocument->GetShell()) {
     if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+      rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
       bool waitForRefresh = false;
       nsTArray<ScrollableLayerGuid> targets;
 
       if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
         for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
           waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
               rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets);
         }
@@ -766,27 +773,34 @@ APZCCallbackHelper::SendSetTargetAPZCNot
       }
     }
   }
 }
 
 void
 APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
         nsIWidget* aWidget,
+        nsIDocument* aDocument,
         const WidgetTouchEvent& aEvent,
         uint64_t aInputBlockId,
         const SetAllowedTouchBehaviorCallback& aCallback)
 {
-  nsTArray<TouchBehaviorFlags> flags;
-  for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
-    flags.AppendElement(
-      widget::TouchActionHelper::GetAllowedTouchBehavior(
-                               aWidget, aEvent.mTouches[i]->mRefPoint));
+  if (nsIPresShell* shell = aDocument->GetShell()) {
+    if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+      rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
+      nsTArray<TouchBehaviorFlags> flags;
+      for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
+        flags.AppendElement(
+          TouchActionHelper::GetAllowedTouchBehavior(aWidget,
+                rootFrame, aEvent.mTouches[i]->mRefPoint));
+      }
+      aCallback(aInputBlockId, Move(flags));
+    }
   }
-  aCallback(aInputBlockId, Move(flags));
 }
 
 void
 APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
   nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
   if (!targetContent) {
     return;
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -136,16 +136,17 @@ public:
                                               nsIDocument* aDocument,
                                               const WidgetGUIEvent& aEvent,
                                               const ScrollableLayerGuid& aGuid,
                                               uint64_t aInputBlockId);
 
     /* Figure out the allowed touch behaviors of each touch point in |aEvent|
      * and send that information to the provided callback. */
     static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget,
+                                                        nsIDocument* aDocument,
                                                         const WidgetTouchEvent& aEvent,
                                                         uint64_t aInputBlockId,
                                                         const SetAllowedTouchBehaviorCallback& aCallback);
 
     /* Notify content of a mouse scroll testing event. */
     static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
 
     /* Notify content that the repaint flush is complete. */
--- a/gfx/layers/apz/util/TouchActionHelper.cpp
+++ b/gfx/layers/apz/util/TouchActionHelper.cpp
@@ -1,27 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TouchActionHelper.h"
 
+#include "mozilla/layers/APZCTreeManager.h"
 #include "nsContainerFrame.h"
-#include "nsIContent.h"
 #include "nsIScrollableFrame.h"
 #include "nsLayoutUtils.h"
-#include "nsStyleConsts.h"
-#include "nsView.h"
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 void
-TouchActionHelper::UpdateAllowedBehavior(uint32_t aTouchActionValue, bool aConsiderPanning, mozilla::layers::TouchBehaviorFlags& aOutBehavior)
+TouchActionHelper::UpdateAllowedBehavior(uint32_t aTouchActionValue,
+                                         bool aConsiderPanning,
+                                         TouchBehaviorFlags& aOutBehavior)
 {
   if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_AUTO) {
     // Double-tap-zooming need property value AUTO
     aOutBehavior &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
     if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_MANIPULATION) {
       // Pinch-zooming need value AUTO or MANIPULATION
       aOutBehavior &= ~AllowedTouchBehavior::PINCH_ZOOM;
     }
@@ -38,33 +38,28 @@ TouchActionHelper::UpdateAllowedBehavior
     if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y)) {
       aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN;
     } else if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X)) {
       aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
     }
   }
 }
 
-mozilla::layers::TouchBehaviorFlags
-TouchActionHelper::GetAllowedTouchBehavior(nsIWidget* aWidget, const LayoutDeviceIntPoint& aPoint)
+TouchBehaviorFlags
+TouchActionHelper::GetAllowedTouchBehavior(nsIWidget* aWidget,
+                                           nsIFrame* aRootFrame,
+                                           const LayoutDeviceIntPoint& aPoint)
 {
-  nsView *view = nsView::GetViewFor(aWidget);
   TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN |
                                 AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
 
-  if (!view) {
-    return behavior;
-  }
+  nsPoint relativePoint =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, aRootFrame);
 
-  nsIFrame *viewFrame = view->GetFrame();
-
-  nsPoint relativePoint =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, viewFrame);
-
-  nsIFrame *target = nsLayoutUtils::GetFrameForPoint(viewFrame, relativePoint, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+  nsIFrame *target = nsLayoutUtils::GetFrameForPoint(aRootFrame, relativePoint, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
   if (!target) {
     return behavior;
   }
   nsIScrollableFrame *nearestScrollableParent = nsLayoutUtils::GetNearestScrollableFrame(target, 0);
   nsIFrame* nearestScrollableFrame = do_QueryFrame(nearestScrollableParent);
 
   // We're walking up the DOM tree until we meet the element with touch behavior and accumulating
   // touch-action restrictions of all elements in this chain.
@@ -92,10 +87,10 @@ TouchActionHelper::GetAllowedTouchBehavi
       // values for the purpose of panning but only for zooming.
       considerPanning = false;
     }
   }
 
   return behavior;
 }
 
-} // namespace widget
+} // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/util/TouchActionHelper.h
+++ b/gfx/layers/apz/util/TouchActionHelper.h
@@ -1,39 +1,42 @@
 /* -*- Mode: C++; 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/. */
 
 #ifndef __mozilla_layers_TouchActionHelper_h__
 #define __mozilla_layers_TouchActionHelper_h__
 
-#include "nsIFrame.h"
-#include "nsIWidget.h"
-#include "mozilla/layers/APZCTreeManager.h"
-#include "mozilla/layers/APZUtils.h"  
+#include "mozilla/layers/APZUtils.h" // for TouchBehaviorFlags
+
+class nsIFrame;
+class nsIWidget;
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 /*
- * Allow different platform widgets to access Content/DOM stuff.
+ * Helper class to figure out the allowed touch behavior for frames, as per
+ * the touch-action spec.
  */
 class TouchActionHelper
 {
-  typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
-
 private:
-  static void UpdateAllowedBehavior(uint32_t aTouchActionValue, bool aConsiderPanning, mozilla::layers::TouchBehaviorFlags& aOutBehavior);
+  static void UpdateAllowedBehavior(uint32_t aTouchActionValue,
+                                    bool aConsiderPanning,
+                                    TouchBehaviorFlags& aOutBehavior);
 
 public:
   /*
    * Performs hit testing on content, finds frame that corresponds to the aPoint and retrieves
    * touch-action css property value from it according the rules specified in the spec:
    * http://www.w3.org/TR/pointerevents/#the-touch-action-css-property.
    */
-  static mozilla::layers::TouchBehaviorFlags GetAllowedTouchBehavior(nsIWidget* aWidget, const LayoutDeviceIntPoint& aPoint);
+  static TouchBehaviorFlags GetAllowedTouchBehavior(nsIWidget* aWidget,
+                                                    nsIFrame* aRootFrame,
+                                                    const LayoutDeviceIntPoint& aPoint);
 };
 
-} // namespace widget
+} // namespace layers
 } // namespace mozilla
 
 #endif /*__mozilla_layers_TouchActionHelper_h__ */
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -503,42 +503,57 @@ AsyncCompositionManager::AlignFixedAndSt
         // transform. This translation will apply on top of the layer's local
         // transform, but |anchor| and |transformedAnchor| are in a coordinate space
         // where the local transform isn't applied yet, so apply it and then subtract
         // to get the desired translation.
         auto localTransformTyped = ViewAs<LayerToParentLayerMatrix4x4>(localTransform);
         ParentLayerPoint translation = TransformBy(localTransformTyped, transformedAnchor)
                                      - TransformBy(localTransformTyped, anchor);
 
+        // A fixed layer will "consume" (be unadjusted by) the entire translation
+        // calculated above. A sticky layer may consume all, part, or none of it,
+        // depending on where we are relative to its sticky scroll range.
+        bool translationConsumed = true;
+
         if (layer->GetIsStickyPosition()) {
           // For sticky positioned layers, the difference between the two rectangles
           // defines a pair of translation intervals in each dimension through which
           // the layer should not move relative to the scroll container. To
           // accomplish this, we limit each dimension of the |translation| to that
           // part of it which overlaps those intervals.
           const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
           const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
 
           // TODO: There's a unit mismatch here, as |translation| is in ParentLayer
           //       space while |stickyOuter| and |stickyInner| are in Layer space.
+          ParentLayerPoint originalTranslation = translation;
           translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
                           IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
           translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
                           IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
+          if (translation != originalTranslation) {
+            translationConsumed = false;
+          }
         }
 
         // Finally, apply the translation to the layer transform. Note that in cases
         // where the async transform on |aTransformedSubtreeRoot| affects this layer's
         // clip rect, we need to apply the same translation to said clip rect, so
         // that the effective transform on the clip rect takes it back to where it was
         // originally, had there been no async scroll.
         TranslateShadowLayer(layer, ThebesPoint(translation.ToUnknownPoint()),
             true, aClipPartsCache);
 
-        return TraversalFlag::Skip;
+        // If we didn't consume the entire translation, continue the traversal
+        // to allow a descendant fixed or sticky layer to consume the rest.
+        // TODO: We curently don't handle the case where we consume part but not
+        //       all of the translation correctly. In such a case,
+        //       |a[Previous|Current]TransformForRoot| would need to be adjusted
+        //       to reflect only the unconsumed part of the translation.
+        return translationConsumed ? TraversalFlag::Skip : TraversalFlag::Continue;
       });
 }
 
 static void
 SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
             StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
 {
   StyleAnimationValue interpolatedValue;
--- a/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
@@ -52,17 +52,17 @@ public:
       mTask->mSink->ProcessReadback(nullptr);
       return NS_OK;
     }
 
     {
       RefPtr<DataSourceSurface> surf =
         Factory::CreateWrappingDataSourceSurface((uint8_t*)mappedTex.pData, mappedTex.RowPitch,
                                                  IntSize(desc.Width, desc.Height),
-                                                 SurfaceFormat::B8G8R8X8);
+                                                 SurfaceFormat::B8G8R8A8);
 
       mTask->mSink->ProcessReadback(surf);
 
       MOZ_ASSERT(surf->hasOneRef());
     }
 
     mTask->mReadbackTexture->Unmap(0);
 
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -756,27 +756,27 @@ DXGID3D9TextureData::~DXGID3D9TextureDat
 
 // static
 DXGID3D9TextureData*
 DXGID3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                             TextureFlags aFlags,
                             IDirect3DDevice9* aDevice)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
-  MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8X8);
-  if (aFormat != gfx::SurfaceFormat::B8G8R8X8) {
+  MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8A8);
+  if (aFormat != gfx::SurfaceFormat::B8G8R8A8) {
     return nullptr;
   }
 
   RefPtr<IDirect3DTexture9> texture;
   HANDLE shareHandle = nullptr;
   HRESULT hr = aDevice->CreateTexture(aSize.width, aSize.height,
                                       1,
                                       D3DUSAGE_RENDERTARGET,
-                                      D3DFMT_X8R8G8B8,
+                                      D3DFMT_A8R8G8B8,
                                       D3DPOOL_DEFAULT,
                                       getter_AddRefs(texture),
                                       &shareHandle);
   if (FAILED(hr) || !shareHandle) {
     return nullptr;
   }
 
   D3DSURFACE_DESC surfaceDesc;
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -440,17 +440,17 @@ CompositorOGL::Initialize(nsCString* con
  * then it will just return aSize.
  */
 static IntSize
 CalculatePOTSize(const IntSize& aSize, GLContext* gl)
 {
   if (CanUploadNonPowerOfTwo(gl))
     return aSize;
 
-  return IntSize(NextPowerOfTwo(aSize.width), NextPowerOfTwo(aSize.height));
+  return IntSize(RoundUpPow2(aSize.width), RoundUpPow2(aSize.height));
 }
 
 // |aRect| is the rectangle we want to draw to. We will draw it with
 // up to 4 draw commands if necessary to avoid wrapping.
 // |aTexCoordRect| is the rectangle from the texture that we want to
 // draw using the given program.
 // |aTexture| is the texture we are drawing. Its actual size can be
 // larger than the rectangle given by |texCoordRect|.
--- a/gfx/layers/opengl/GLBlitTextureImageHelper.cpp
+++ b/gfx/layers/opengl/GLBlitTextureImageHelper.cpp
@@ -121,18 +121,18 @@ GLBlitTextureImageHelper::BlitTextureIma
             float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
             float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
             ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height);
 
             RectTriangles rects;
 
             gfx::IntSize realTexSize = srcSize;
             if (!CanUploadNonPowerOfTwo(gl)) {
-                realTexSize = gfx::IntSize(gfx::NextPowerOfTwo(srcSize.width),
-                                           gfx::NextPowerOfTwo(srcSize.height));
+                realTexSize = gfx::IntSize(RoundUpPow2(srcSize.width),
+                                           RoundUpPow2(srcSize.height));
             }
 
             if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
                 rects.addRect(/* dest rectangle */
                         dx0, dy0, dx1, dy1,
                         /* tex coords */
                         srcSubRect.x / float(realTexSize.width),
                         srcSubRect.y / float(realTexSize.height),
--- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -490,19 +490,21 @@ void SkScalerContext_CairoFT::parsePatte
     }
 }
 
 void SkScalerContext_CairoFT::resolvePattern(FcPattern* pattern)
 {
     if (!pattern) {
         return;
     }
-    SkAutoTUnref<FcPattern> scalePattern(FcPatternDuplicate(pattern));
-    if (scalePattern) {
-        if (FcPatternAddDouble(scalePattern, FC_PIXEL_SIZE, fScaleY) &&
+    FcValue value;
+    if (FcPatternGet(pattern, FC_PIXEL_SIZE, 0, &value) == FcResultNoMatch) {
+        SkAutoTUnref<FcPattern> scalePattern(FcPatternDuplicate(pattern));
+        if (scalePattern &&
+            FcPatternAddDouble(scalePattern, FC_PIXEL_SIZE, fScaleY) &&
             FcConfigSubstitute(nullptr, scalePattern, FcMatchPattern)) {
             FcDefaultSubstitute(scalePattern);
             FcResult result;
             SkAutoTUnref<FcPattern> resolved(FcFontMatch(nullptr, scalePattern, &result));
             if (resolved) {
                 parsePattern(resolved);
                 return;
             }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1197,17 +1197,17 @@ gfxPlatform::ComputeTileSize()
   int32_t h = gfxPrefs::LayersTileHeight();
 
   if (gfxPrefs::LayersTilesAdjust()) {
     gfx::IntSize screenSize = GetScreenSize();
     if (screenSize.width > 0) {
       // Choose a size so that there are between 2 and 4 tiles per screen width.
       // FIXME: we should probably make sure this is within the max texture size,
       // but I think everything should at least support 1024
-      w = h = clamped(NextPowerOfTwo(screenSize.width) / 4, 256, 1024);
+      w = h = clamped(int32_t(RoundUpPow2(screenSize.width)) / 4, 256, 1024);
     }
 
 #ifdef MOZ_WIDGET_GONK
     android::sp<android::GraphicBuffer> alloc =
           new android::GraphicBuffer(w, h, android::PIXEL_FORMAT_RGBA_8888,
                                      android::GraphicBuffer::USAGE_SW_READ_OFTEN |
                                      android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                                      android::GraphicBuffer::USAGE_HW_TEXTURE);
@@ -2495,17 +2495,17 @@ gfxPlatform::InitOpenGLConfig()
 bool
 gfxPlatform::IsGfxInfoStatusOkay(int32_t aFeature, nsCString* aOutMessage, nsCString& aFailureId)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (!gfxInfo) {
     return true;
   }
 
-  int32_t status;    
+  int32_t status;
   if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aFailureId, &status)) &&
       status != nsIGfxInfo::FEATURE_STATUS_OK)
   {
     aOutMessage->AssignLiteral("#BLOCKLIST_");
     aOutMessage->AppendASCII(aFailureId.get());
     return false;
   }
 
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -90,18 +90,22 @@ gfxPlatformGtk::gfxPlatformGtk()
 
     mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
 
 #ifdef MOZ_X11
     sUseXRender = (GDK_IS_X11_DISPLAY(gdk_display_get_default())) ?
                     mozilla::Preferences::GetBool("gfx.xrender.enabled") : false;
 #endif
 
-    uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
-    uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
+    uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+    uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+#ifdef USE_SKIA
+    canvasMask |= BackendTypeBit(BackendType::SKIA);
+    contentMask |= BackendTypeBit(BackendType::SKIA);
+#endif
     InitBackendPrefs(canvasMask, BackendType::CAIRO,
                      contentMask, BackendType::CAIRO);
 
 #ifdef MOZ_X11
     if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
       mCompositorDisplay = XOpenDisplay(nullptr);
       MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
     } else {
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -287,49 +287,16 @@ namespace gfx {
  * color to a device color using the transform returened by gfxPlatform::
  * GetCMSRGBTransform().  If the CMS mode is some other value, the color is
  * returned unchanged (other than a type change to Moz2D Color, if
  * applicable).
  */
 Color ToDeviceColor(Color aColor);
 Color ToDeviceColor(nscolor aColor);
 
-/* These techniques are suggested by "Bit Twiddling Hacks"
- */
-
-/**
- * Returns true if |aNumber| is a power of two
- * 0 is incorreclty considered a power of two
- */
-static inline bool
-IsPowerOfTwo(int aNumber)
-{
-    return (aNumber & (aNumber - 1)) == 0;
-}
-
-/**
- * Returns the first integer greater than or equal to |aNumber| which is a
- * power of two. Undefined for |aNumber| < 0.
- */
-static inline int
-NextPowerOfTwo(int aNumber)
-{
-#if defined(__arm__)
-    return 1 << (32 - __builtin_clz(aNumber - 1));
-#else
-    --aNumber;
-    aNumber |= aNumber >> 1;
-    aNumber |= aNumber >> 2;
-    aNumber |= aNumber >> 4;
-    aNumber |= aNumber >> 8;
-    aNumber |= aNumber >> 16;
-    return ++aNumber;
-#endif
-}
-
 /**
  * Performs a checked multiply of the given width, height, and bytes-per-pixel
  * values.
  */
 static inline CheckedInt<uint32_t>
 SafeBytesForBitmap(uint32_t aWidth, uint32_t aHeight, unsigned aBytesPerPixel)
 {
   MOZ_ASSERT(aBytesPerPixel > 0);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -471,24 +471,22 @@ gfxWindowsPlatform::HandleDeviceReset()
   gfxAlphaBoxBlur::ShutdownBlurCache();
 
   InitializeDevices();
   UpdateANGLEConfig();
   BumpDeviceCounter();
   return true;
 }
 
-static const BackendType SOFTWARE_BACKEND = BackendType::CAIRO;
-
 void
 gfxWindowsPlatform::UpdateBackendPrefs()
 {
-  uint32_t canvasMask = BackendTypeBit(SOFTWARE_BACKEND);
-  uint32_t contentMask = BackendTypeBit(SOFTWARE_BACKEND);
-  BackendType defaultBackend = SOFTWARE_BACKEND;
+  uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+  uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+  BackendType defaultBackend = BackendType::CAIRO;
   if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::GetD2D1Device()) {
     contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     defaultBackend = BackendType::DIRECT2D1_1;
   } else {
     canvasMask |= BackendTypeBit(BackendType::SKIA);
   }
   contentMask |= BackendTypeBit(BackendType::SKIA);
@@ -531,22 +529,28 @@ gfxWindowsPlatform::ForceDeviceReset(For
 
   mDeviceResetReason = DeviceResetReason::FORCED_RESET;
   mHasDeviceReset = true;
 }
 
 mozilla::gfx::BackendType
 gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
 {
+  mozilla::gfx::BackendType defaultBackend = gfxPlatform::GetDefaultContentBackend();
   if (aLayers == LayersBackend::LAYERS_D3D11) {
-    return gfxPlatform::GetDefaultContentBackend();
+    return defaultBackend;
   }
 
-  // If we're not accelerated with D3D11, never use D2D.
-  return SOFTWARE_BACKEND;
+  if (defaultBackend == BackendType::DIRECT2D1_1) {
+    // We can't have D2D without D3D11 layers, so fallback to Cairo.
+    return BackendType::CAIRO;
+  }
+
+  // Otherwise we have some non-accelerated backend and that's ok.
+  return defaultBackend;
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
     gfxPlatformFontList *pfl;
 
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -202,17 +202,17 @@ FileDescriptor::PlatformHandleHelper::Pl
 }
 
 bool
 FileDescriptor::PlatformHandleHelper::operator!=(std::nullptr_t) const
 {
   return mHandle != INVALID_HANDLE;
 }
 
-FileDescriptor::PlatformHandleHelper::operator PlatformHandleType () const
+FileDescriptor::PlatformHandleHelper::operator FileDescriptor::PlatformHandleType () const
 {
   return mHandle;
 }
 
 #ifdef XP_WIN
 FileDescriptor::PlatformHandleHelper::operator std::intptr_t () const
 {
   return reinterpret_cast<std::intptr_t>(mHandle);
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -467,16 +467,20 @@ class BaseCompiler
     bool                        scratchRegisterTaken_;
 #endif
 
     TempObjectPool<PooledLabel> labelPool_;
 
     Vector<Local, 8, SystemAllocPolicy> localInfo_;
     Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
 
+    // Index into localInfo_ of the special local used for saving the TLS
+    // pointer. This follows the function's real arguments and locals.
+    uint32_t                    tlsSlot_;
+
     // On specific platforms we sometimes need to use specific registers.
 
 #ifdef JS_CODEGEN_X64
     RegI64 specific_rax;
     RegI64 specific_rcx;
     RegI64 specific_rdx;
 #endif
 
@@ -569,16 +573,20 @@ class BaseCompiler
     void storeToFrameI64(Register64 r, int32_t offset) {
 #ifdef JS_CODEGEN_X64
         masm.movq(r.reg, Operand(StackPointer, localOffsetToSPOffset(offset)));
 #else
         MOZ_CRASH("BaseCompiler platform hook: storeToFrameI64");
 #endif
     }
 
+    void storeToFramePtr(Register r, int32_t offset) {
+        masm.storePtr(r, Address(StackPointer, localOffsetToSPOffset(offset)));
+    }
+
     void storeToFrameF64(FloatRegister r, int32_t offset) {
         masm.storeDouble(r, Address(StackPointer, localOffsetToSPOffset(offset)));
     }
 
     void storeToFrameF32(FloatRegister r, int32_t offset) {
         masm.storeFloat32(r, Address(StackPointer, localOffsetToSPOffset(offset)));
     }
 
@@ -589,16 +597,20 @@ class BaseCompiler
     void loadFromFrameI64(Register64 r, int32_t offset) {
 #ifdef JS_CODEGEN_X64
         masm.movq(Operand(StackPointer, localOffsetToSPOffset(offset)), r.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: loadFromFrameI64");
 #endif
     }
 
+    void loadFromFramePtr(Register r, int32_t offset) {
+        masm.loadPtr(Address(StackPointer, localOffsetToSPOffset(offset)), r);
+    }
+
     void loadFromFrameF64(FloatRegister r, int32_t offset) {
         masm.loadDouble(Address(StackPointer, localOffsetToSPOffset(offset)), r);
     }
 
     void loadFromFrameF32(FloatRegister r, int32_t offset) {
         masm.loadFloat32(Address(StackPointer, localOffsetToSPOffset(offset)), r);
     }
 
@@ -1778,16 +1790,20 @@ class BaseCompiler
                 if (i->argInRegister())
                     storeToFrameF32(i->fpu(), l.offs());
                 break;
               default:
                 MOZ_CRASH("Function argument type");
             }
         }
 
+        // The TLS pointer is always passed as a hidden argument in WasmTlsReg.
+        // Save it into its assigned local slot.
+        storeToFramePtr(WasmTlsReg, localInfo_[tlsSlot_].offs());
+
         // Initialize the stack locals to zero.
         //
         // TODO / OPTIMIZE: on x64, at least, scratch will be a 64-bit
         // register and we can move 64 bits at a time.
         //
         // TODO / OPTIMIZE: On SSE2 or better SIMD systems we may be
         // able to store 128 bits at a time.  (I suppose on some
         // systems we have 512-bit SIMD for that matter.)
@@ -1812,30 +1828,34 @@ class BaseCompiler
 
         MOZ_ASSERT(maxFramePushed_ >= localSize_);
 
         // ABINonArgReg0 != ScratchReg, which can be used by branchPtr().
 
         masm.movePtr(masm.getStackPointer(), ABINonArgReg0);
         masm.subPtr(Imm32(maxFramePushed_ - localSize_), ABINonArgReg0);
         masm.branchPtr(Assembler::Below,
-                       SymbolicAddress::StackLimit,
+                       Address(WasmTlsReg, offsetof(wasm::TlsData, stackLimit)),
                        ABINonArgReg0,
                        &bodyLabel_);
 
 
         // The stack overflow stub assumes that only sizeof(AsmJSFrame) bytes
         // have been pushed. The overflow check occurs after incrementing by
         // localSize_, so pop that before jumping to the overflow exit.
 
         masm.addToStackPtr(Imm32(localSize_));
         masm.jump(wasm::JumpTarget::StackOverflow);
 
         masm.bind(&returnLabel_);
 
+        // The return value was set up before jumping here, but we also need to
+        // preserve the TLS register.
+        loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+
         wasm::GenerateFunctionEpilogue(masm, localSize_, &compileResults_.offsets());
 
 #if defined(JS_ION_PERF)
         // FIXME - profiling code missing
 
         // Note the end of the inline code and start of the OOL code.
         //gen->perfSpewer().noteEndInlineCode(masm);
 #endif
@@ -4990,16 +5010,21 @@ BaseCompiler::emitCallArgs(const ValType
         ValType argType = args[i];
         Nothing arg_;
         if (!iter_.readCallArg(argType, numArgs, i, &arg_))
             return false;
         Stk& arg = peek(numArgs - 1 - i);
         passArg(baselineCall, argType, arg);
     }
 
+    // Always pass the TLS pointer as a hidden argument in WasmTlsReg.
+    // Load it directly out if its stack slot so we don't interfere with the
+    // stk_.
+    loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+
     if (!iter_.readCallArgsEnd(numArgs))
         return false;
 
     return true;
 }
 
 bool
 BaseCompiler::skipCall(const ValTypeVector& args, ExprType maybeReturnType)
@@ -6547,16 +6572,17 @@ BaseCompiler::BaseCompiler(const ModuleG
       deadCode_(false),
       compileResults_(compileResults),
       masm(compileResults_.masm()),
       availGPR_(GeneralRegisterSet::All()),
       availFPU_(FloatRegisterSet::All()),
 #ifdef DEBUG
       scratchRegisterTaken_(false),
 #endif
+      tlsSlot_(0),
 #ifdef JS_CODEGEN_X64
       specific_rax(RegI64(Register64(rax))),
       specific_rcx(RegI64(Register64(rcx))),
       specific_rdx(RegI64(Register64(rdx))),
 #endif
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
       specific_eax(RegI32(eax)),
       specific_ecx(RegI32(ecx)),
@@ -6610,17 +6636,21 @@ BaseCompiler::init()
         return false;
     if (!SigD_.append(ValType::F64))
         return false;
     if (!SigF_.append(ValType::F32))
         return false;
 
     const ValTypeVector& args = func_.sig().args();
 
-    if (!localInfo_.resize(locals_.length()))
+    // localInfo_ contains an entry for every local in locals_, followed by
+    // entries for special locals. Currently the only special local is the TLS
+    // pointer.
+    tlsSlot_ = locals_.length();
+    if (!localInfo_.resize(locals_.length() + 1))
         return false;
 
     localSize_ = 0;
 
     for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
         Local& l = localInfo_[i.index()];
         switch (i.mirType()) {
           case MIRType::Int32:
@@ -6647,16 +6677,20 @@ BaseCompiler::init()
             else
                 l.init(MIRType::Float32, -(i->offsetFromArgBase() + sizeof(AsmJSFrame)));
             break;
           default:
             MOZ_CRASH("Argument type");
         }
     }
 
+    // Reserve a stack slot for the TLS pointer before the varLow - varHigh
+    // range so it isn't zero-filled like the normal locals.
+    localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
+
     varLow_ = localSize_;
 
     for (size_t i = args.length(); i < locals_.length(); i++) {
         Local& l = localInfo_[i];
         switch (locals_[i]) {
           case ValType::I32:
             l.init(MIRType::Int32, pushLocal(4));
             break;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -74,16 +74,22 @@ class FunctionDecoder
     ValidatingExprIter& iter() { return iter_; }
     const ValTypeVector& locals() const { return locals_; }
 
     bool checkI64Support() {
         if (!IsI64Implemented())
             return iter().notYetImplemented("i64 NYI on this platform");
         return true;
     }
+
+    bool checkHasMemory() {
+        if (!mg().usesMemory())
+            return iter().fail("can't touch memory without memory");
+        return true;
+    }
 };
 
 } // end anonymous namespace
 
 static bool
 CheckValType(Decoder& d, ValType type)
 {
     switch (type) {
@@ -141,16 +147,19 @@ DecodeCall(FunctionDecoder& f)
     const Sig& sig = f.mg().funcSig(calleeIndex);
     return DecodeCallArgs(f, arity, sig) &&
            DecodeCallReturn(f, sig);
 }
 
 static bool
 DecodeCallIndirect(FunctionDecoder& f)
 {
+    if (!f.mg().numTables())
+        return f.iter().fail("can't call_indirect without a table");
+
     uint32_t sigIndex;
     uint32_t arity;
     if (!f.iter().readCallIndirect(&sigIndex, &arity))
         return false;
 
     if (sigIndex >= f.mg().numSigs())
         return f.iter().fail("signature index out of range");
 
@@ -393,63 +402,81 @@ DecodeExpr(FunctionDecoder& f)
       case Expr::F64ConvertUI64:
       case Expr::F64ReinterpretI64:
         return f.checkI64Support() &&
                f.iter().readConversion(ValType::I64, ValType::F64, nullptr);
       case Expr::F64PromoteF32:
         return f.iter().readConversion(ValType::F32, ValType::F64, nullptr);
       case Expr::I32Load8S:
       case Expr::I32Load8U:
-        return f.iter().readLoad(ValType::I32, 1, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 1, nullptr);
       case Expr::I32Load16S:
       case Expr::I32Load16U:
-        return f.iter().readLoad(ValType::I32, 2, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 2, nullptr);
       case Expr::I32Load:
-        return f.iter().readLoad(ValType::I32, 4, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 4, nullptr);
       case Expr::I64Load8S:
       case Expr::I64Load8U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 1, nullptr);
       case Expr::I64Load16S:
       case Expr::I64Load16U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 2, nullptr);
       case Expr::I64Load32S:
       case Expr::I64Load32U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 4, nullptr);
       case Expr::I64Load:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 8, nullptr);
       case Expr::F32Load:
-        return f.iter().readLoad(ValType::F32, 4, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::F32, 4, nullptr);
       case Expr::F64Load:
-        return f.iter().readLoad(ValType::F64, 8, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::F64, 8, nullptr);
       case Expr::I32Store8:
-        return f.iter().readStore(ValType::I32, 1, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 1, nullptr, nullptr);
       case Expr::I32Store16:
-        return f.iter().readStore(ValType::I32, 2, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 2, nullptr, nullptr);
       case Expr::I32Store:
-        return f.iter().readStore(ValType::I32, 4, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 4, nullptr, nullptr);
       case Expr::I64Store8:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 1, nullptr, nullptr);
       case Expr::I64Store16:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 2, nullptr, nullptr);
       case Expr::I64Store32:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 4, nullptr, nullptr);
       case Expr::I64Store:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 8, nullptr, nullptr);
       case Expr::F32Store:
-        return f.iter().readStore(ValType::F32, 4, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::F32, 4, nullptr, nullptr);
       case Expr::F64Store:
-        return f.iter().readStore(ValType::F64, 8, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::F64, 8, nullptr, nullptr);
       case Expr::Br:
         return f.iter().readBr(nullptr, nullptr, nullptr);
       case Expr::BrIf:
         return f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr);
       case Expr::BrTable:
         return DecodeBrTable(f);
       case Expr::Return:
         return f.iter().readReturn(nullptr);
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -148,16 +148,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     SignalUsage usesSignal() const { return metadata_->assumptions.usesSignal; }
     jit::MacroAssembler& masm() { return masm_; }
 
     // Memory:
     bool usesMemory() const { return UsesMemory(shared_->memoryUsage); }
     uint32_t minMemoryLength() const { return shared_->minMemoryLength; }
 
     // Tables:
+    uint32_t numTables() const { return numTables_; }
     const TableDescVector& tables() const { return shared_->tables; }
 
     // Signatures:
     uint32_t numSigs() const { return numSigs_; }
     const SigWithId& sig(uint32_t sigIndex) const;
 
     // Function declarations:
     uint32_t numFuncSigs() const { return shared_->funcSigs.length(); }
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -412,17 +412,18 @@ Instance::callImport_f64(int32_t funcImp
 
     RootedValue rval(cx);
     if (!activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval))
         return false;
 
     return ToNumber(cx, rval, (double*)argv);
 }
 
-Instance::Instance(UniqueCodeSegment codeSegment,
+Instance::Instance(JSContext* cx,
+                   UniqueCodeSegment codeSegment,
                    const Metadata& metadata,
                    const ShareableBytes* maybeBytecode,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports)
   : codeSegment_(Move(codeSegment)),
     metadata_(&metadata),
     maybeBytecode_(maybeBytecode),
@@ -441,16 +442,18 @@ Instance::Instance(UniqueCodeSegment cod
         exit.baselineScript = nullptr;
     }
 
     if (memory)
         *addressOfMemoryBase() = memory->buffer().dataPointerEither().unwrap();
 
     for (size_t i = 0; i < tables_.length(); i++)
         *addressOfTableBase(i) = tables_[i]->array();
+
+   updateStackLimit(cx);
 }
 
 bool
 Instance::init(JSContext* cx)
 {
     if (!metadata_->sigIds.empty()) {
         ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
 
@@ -504,16 +507,23 @@ Instance::memoryBase() const
 }
 
 size_t
 Instance::memoryLength() const
 {
     return memory_->buffer().byteLength();
 }
 
+void
+Instance::updateStackLimit(JSContext* cx)
+{
+    // Capture the stack limit for cx's thread.
+    tlsData_.stackLimit = *(void**)cx->stackLimitAddressForJitCode(StackForUntrustedScript);
+}
+
 bool
 Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
 {
     const FuncExport& func = metadata_->lookupFuncExport(funcIndex);
 
     // Enable/disable profiling in the Module to match the current global
     // profiling state. Don't do this if the Module is already active on the
     // stack since this would leave the Module in a state where profiling is
@@ -619,17 +629,17 @@ Instance::callExport(JSContext* cx, uint
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
         // stack iteration will skip over it.
         WasmActivation activation(cx, *this);
         JitActivation jitActivation(cx, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
         auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeSegment_->code() + func.entryOffset());
-        if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), codeSegment_->globalData()))
+        if (!CALL_GENERATED_3(funcPtr, exportArgs.begin(), codeSegment_->globalData(), tlsData()))
             return false;
     }
 
     if (args.isConstructing()) {
         // By spec, when a function is called as a constructor and this function
         // returns a primary type, which is the case for all wasm exported
         // functions, the returned value is discarded and an empty object is
         // returned instead.
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -45,41 +45,51 @@ class Instance
     const SharedMetadata                 metadata_;
     const SharedBytes                    maybeBytecode_;
     GCPtrWasmMemoryObject                memory_;
     SharedTableVector                    tables_;
 
     bool                                 profilingEnabled_;
     CacheableCharsVector                 funcLabels_;
 
+
     UniquePtr<GeneratedSourceMap>        maybeSourceMap_;
 
+    // Thread-local data for code running in this instance.
+    // When threading is supported, we need a TlsData object per thread per
+    // instance.
+    TlsData                              tlsData_;
+
     // Internal helpers:
     uint8_t** addressOfMemoryBase() const;
     void** addressOfTableBase(size_t tableIndex) const;
     const void** addressOfSigId(const SigIdDesc& sigId) const;
     FuncImportExit& funcImportToExit(const FuncImport& fi);
     MOZ_MUST_USE bool toggleProfiling(JSContext* cx);
 
+    // Get this instance's TLS data pointer for the current thread.
+    TlsData* tlsData() { return &tlsData_; }
+
     // An instance keeps track of its innermost WasmActivation. A WasmActivation
     // is pushed for the duration of each call of an export.
     friend class js::WasmActivation;
     WasmActivation*& activation();
 
     // Import call slow paths which are called directly from wasm code.
     friend void* AddressOf(SymbolicAddress, ExclusiveContext*);
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
     static int32_t callImport_void(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_i32(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_i64(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_f64(int32_t importIndex, int32_t argc, uint64_t* argv);
 
   public:
-    Instance(UniqueCodeSegment codeSegment,
+    Instance(JSContext* cx,
+             UniqueCodeSegment codeSegment,
              const Metadata& metadata,
              const ShareableBytes* maybeBytecode,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports);
     ~Instance();
     bool init(JSContext* cx);
     void trace(JSTracer* trc);
@@ -131,16 +141,19 @@ class Instance
     // Stack frame iterator support:
 
     const CallSite* lookupCallSite(void* returnAddress) const;
     const CodeRange* lookupCodeRange(void* pc) const;
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
     const MemoryAccess* lookupMemoryAccess(void* pc) const;
 #endif
 
+    // Update the instance's copy of the stack limit.
+    void updateStackLimit(JSContext*);
+
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Table::SeenSet* seenTables,
                        size_t* code,
                        size_t* data) const;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -91,16 +91,19 @@ class FunctionCompiler
     uint32_t                   maxStackArgBytes_;
 
     uint32_t                   loopDepth_;
     uint32_t                   blockDepth_;
     ControlFlowPatchsVector    blockPatches_;
 
     FuncCompileResults&        compileResults_;
 
+    // TLS pointer argument to the current function.
+    MAsmJSParameter*           tlsPointer_;
+
   public:
     FunctionCompiler(const ModuleGeneratorData& mg,
                      Decoder& decoder,
                      const FuncBytes& func,
                      const ValTypeVector& locals,
                      MIRGenerator& mirGen,
                      FuncCompileResults& compileResults)
       : mg_(mg),
@@ -112,17 +115,18 @@ class FunctionCompiler
         graph_(mirGen.graph()),
         info_(mirGen.info()),
         mirGen_(mirGen),
         dummyIns_(nullptr),
         curBlock_(nullptr),
         maxStackArgBytes_(0),
         loopDepth_(0),
         blockDepth_(0),
-        compileResults_(compileResults)
+        compileResults_(compileResults),
+        tlsPointer_(nullptr)
     {}
 
     const ModuleGeneratorData& mg() const    { return mg_; }
     IonExprIter&               iter()        { return iter_; }
     TempAllocator&             alloc() const { return alloc_; }
     MacroAssembler&            masm() const  { return compileResults_.masm(); }
     const Sig&                 sig() const   { return func_.sig(); }
 
@@ -140,16 +144,22 @@ class FunctionCompiler
         for (ABIArgValTypeIter i(args); !i.done(); i++) {
             MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i.index()), ins);
             if (!mirGen_.ensureBallast())
                 return false;
         }
 
+        // Set up a parameter that receives the hidden TLS pointer argument.
+        tlsPointer_ = MAsmJSParameter::New(alloc(), ABIArg(WasmTlsReg), MIRType::Pointer);
+        curBlock_->add(tlsPointer_);
+        if (!mirGen_.ensureBallast())
+            return false;
+
         for (size_t i = args.length(); i < locals_.length(); i++) {
             MInstruction* ins = nullptr;
             switch (locals_[i]) {
               case ValType::I32:
                 ins = MConstant::NewAsmJS(alloc(), Int32Value(0), MIRType::Int32);
                 break;
               case ValType::I64:
                 ins = MConstant::NewInt64(alloc(), 0);
@@ -775,25 +785,27 @@ class FunctionCompiler
     {
         uint32_t lineOrBytecode_;
         ABIArgGenerator abi_;
         uint32_t maxChildStackBytes_;
         uint32_t spIncrement_;
         MAsmJSCall::Args regArgs_;
         Vector<MAsmJSPassStackArg*, 0, SystemAllocPolicy> stackArgs_;
         bool childClobbers_;
+        bool preservesTlsReg_;
 
         friend class FunctionCompiler;
 
       public:
         CallArgs(FunctionCompiler& f, uint32_t lineOrBytecode)
           : lineOrBytecode_(lineOrBytecode),
             maxChildStackBytes_(0),
             spIncrement_(0),
-            childClobbers_(false)
+            childClobbers_(false),
+            preservesTlsReg_(false)
         { }
     };
 
     bool startCallArgs(CallArgs* args)
     {
         // Always push calls to maintain the invariant that if we're inDeadCode
         // in finishCallArgs, we have something to pop.
         return callStack_.append(args);
@@ -808,16 +820,26 @@ class FunctionCompiler
         if (arg.kind() != ABIArg::Stack)
             return args->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef));
 
         auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
         curBlock_->add(mir);
         return args->stackArgs_.append(mir);
     }
 
+    // Add the hidden TLS pointer argument to CallArgs, and assume that it will
+    // be preserved by the call.
+    bool passTlsPointer(CallArgs* args)
+    {
+        if (inDeadCode())
+            return true;
+        args->preservesTlsReg_ = true;
+        return args->regArgs_.append(MAsmJSCall::Arg(AnyRegister(WasmTlsReg), tlsPointer_));
+    }
+
     void propagateMaxStackArgBytes(uint32_t stackBytes)
     {
         if (callStack_.empty()) {
             // Outermost call
             maxStackArgBytes_ = Max(maxStackArgBytes_, stackBytes);
             return;
         }
 
@@ -862,18 +884,19 @@ class FunctionCompiler
 
         CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);
         switch (callee.which()) {
           case MAsmJSCall::Callee::Internal: kind = CallSiteDesc::Relative; break;
           case MAsmJSCall::Callee::Dynamic:  kind = CallSiteDesc::Register; break;
           case MAsmJSCall::Callee::Builtin:  kind = CallSiteDesc::Register; break;
         }
 
-        MAsmJSCall* ins = MAsmJSCall::New(alloc(), CallSiteDesc(args.lineOrBytecode_, kind),
-                                          callee, args.regArgs_, ToMIRType(ret), args.spIncrement_);
+        MAsmJSCall* ins =
+          MAsmJSCall::New(alloc(), CallSiteDesc(args.lineOrBytecode_, kind), callee, args.regArgs_,
+                          ToMIRType(ret), args.spIncrement_, args.preservesTlsReg_);
         if (!ins)
             return false;
 
         curBlock_->add(ins);
         *def = ins;
         return true;
     }
 
@@ -934,26 +957,28 @@ class FunctionCompiler
     inline bool inDeadCode() const {
         return curBlock_ == nullptr;
     }
 
     void returnExpr(MDefinition* expr)
     {
         if (inDeadCode())
             return;
-        MAsmJSReturn* ins = MAsmJSReturn::New(alloc(), expr);
+
+        MAsmJSReturn* ins = MAsmJSReturn::New(alloc(), expr, tlsPointer_);
         curBlock_->end(ins);
         curBlock_ = nullptr;
     }
 
     void returnVoid()
     {
         if (inDeadCode())
             return;
-        MAsmJSVoidReturn* ins = MAsmJSVoidReturn::New(alloc());
+
+        MAsmJSVoidReturn* ins = MAsmJSVoidReturn::New(alloc(), tlsPointer_);
         curBlock_->end(ins);
         curBlock_ = nullptr;
     }
 
     void unreachableTrap()
     {
         if (inDeadCode())
             return;
@@ -1651,18 +1676,26 @@ EmitReturn(FunctionCompiler& f)
         f.returnVoid();
         return true;
     }
 
     f.returnExpr(value);
     return true;
 }
 
+// Is a callee within the same module instance?
+enum class IntraModule
+{
+    False,
+    True
+};
+
 static bool
-EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::CallArgs* args)
+EmitCallArgs(FunctionCompiler& f, const Sig& sig, IntraModule intraModule,
+             FunctionCompiler::CallArgs* args)
 {
     if (!f.startCallArgs(args))
         return false;
 
     MDefinition* arg;
     const ValTypeVector& argTypes = sig.args();
     uint32_t numArgs = argTypes.length();
     for (size_t i = 0; i < numArgs; ++i) {
@@ -1671,16 +1704,21 @@ EmitCallArgs(FunctionCompiler& f, const 
             return false;
         if (!f.passArg(arg, argType, args))
             return false;
     }
 
     if (!f.iter().readCallArgsEnd(numArgs))
         return false;
 
+    // Calls within the module pass the module's TLS pointer.
+    // Calls to other modules go through stubs that set up their TLS pointers.
+    if (intraModule == IntraModule::True)
+        f.passTlsPointer(args);
+
     f.finishCallArgs(args);
     return true;
 }
 
 static bool
 EmitCall(FunctionCompiler& f, uint32_t callOffset)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset);
@@ -1688,17 +1726,17 @@ EmitCall(FunctionCompiler& f, uint32_t c
     uint32_t calleeIndex;
     uint32_t arity;
     if (!f.iter().readCall(&calleeIndex, &arity))
         return false;
 
     const Sig& sig = *f.mg().funcSigs[calleeIndex];
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::True, &args))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
 
     MDefinition* def;
     if (!f.internalCall(sig, calleeIndex, args, &def))
         return false;
@@ -1718,17 +1756,17 @@ EmitCallIndirect(FunctionCompiler& f, ui
     uint32_t sigIndex;
     uint32_t arity;
     if (!f.iter().readCallIndirect(&sigIndex, &arity))
         return false;
 
     const Sig& sig = f.mg().sigs[sigIndex];
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::True, &args))
         return false;
 
     MDefinition* callee;
     if (!f.iter().readCallIndirectCallee(&callee))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
@@ -1757,17 +1795,17 @@ EmitCallImport(FunctionCompiler& f, uint
     uint32_t arity;
     if (!f.iter().readCallImport(&funcImportIndex, &arity))
         return false;
 
     const FuncImportGenDesc& funcImport = f.mg().funcImports[funcImportIndex];
     const Sig& sig = *funcImport.sig;
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::False, &args))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
 
     MDefinition* def;
     if (!f.ffiCall(funcImport.globalDataOffset, args, sig.ret(), &def))
         return false;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -569,17 +569,18 @@ Module::instantiate(JSContext* cx,
     // before any GC can occur and invalidate the pointers stored in global
     // memory.
 
     {
         instanceObj.set(WasmInstanceObject::create(cx, instanceProto));
         if (!instanceObj)
             return false;
 
-        auto instance = cx->make_unique<Instance>(Move(codeSegment),
+        auto instance = cx->make_unique<Instance>(cx,
+                                                  Move(codeSegment),
                                                   *metadata_,
                                                   maybeBytecode,
                                                   memory,
                                                   Move(tables),
                                                   funcImports);
         if (!instance)
             return false;
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -86,20 +86,20 @@ static const unsigned FramePushedAfterSa
 #elif defined(JS_CODEGEN_NONE)
 static const unsigned FramePushedAfterSave = 0;
 #else
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t)
                                            + NonVolatileRegs.fpus().getPushSizeInBytes();
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
-// Generate a stub that enters wasm from a C++ caller via the native ABI.
-// The signature of the entry point is Module::CodePtr. The exported wasm
+// Generate a stub that enters wasm from a C++ caller via the native ABI. The
+// signature of the entry point is Module::ExportFuncPtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
-// must map from the ABI of CodePtr to the export's signature's ABI.
+// must map from the ABI of ExportFuncPtr to the export's signature's ABI.
 Offsets
 wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe, bool usesHeap)
 {
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -127,16 +127,29 @@ wasm::GenerateEntry(MacroAssembler& masm
 #endif
 
     // ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses). Loading the heap register depends on the global
     // register already having been loaded.
     if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
+    // Put the per-thread, per-module TLS pointer into WasmTlsReg.
+    // This is the third argument in the ExportFuncPtr prototype.
+#if defined(JS_CODEGEN_X86)
+    masm.loadPtr(
+      Address(masm.getStackPointer(), EntryFrameSize + masm.framePushed() + 2 * sizeof(void*)),
+      WasmTlsReg);
+#else
+    masm.movePtr(IntArgReg2, WasmTlsReg);
+#endif
+    // Make sure the TLS pointer is not clobbered by the following code.
+    MOZ_ASSERT(WasmTlsReg != ABINonArgReg0, "TLS pointer can't be scratch reg");
+    MOZ_ASSERT(WasmTlsReg != ABINonArgReg1, "TLS pointer can't be scratch reg");
+
     // Put the 'argv' argument into a non-argument/return register so that we
     // can use 'argv' while we fill in the arguments for the asm.js callee.
     // Also, save 'argv' on the stack so that we can recover it after the call.
     // Use a second non-argument/return register as temporary scratch.
     Register argv = ABINonArgReturnReg0;
     Register scratch = ABINonArgReturnReg1;
     Register64 scratch64(scratch);
 
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -176,18 +176,16 @@ FuncCast(F* pf, ABIFunctionType type)
 void*
 wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
 {
     switch (imm) {
       case SymbolicAddress::Runtime:
         return cx->runtimeAddressForJit();
       case SymbolicAddress::RuntimeInterruptUint32:
         return cx->runtimeAddressOfInterruptUint32();
-      case SymbolicAddress::StackLimit:
-        return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case SymbolicAddress::ReportOverRecursed:
         return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
         return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::HandleTrap:
         return FuncCast(HandleTrap, Args_General1);
       case SymbolicAddress::CallImport_Void:
         return FuncCast(Instance::callImport_void, Args_General3);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -760,17 +760,16 @@ enum class SymbolicAddress
     NearbyIntD,
     NearbyIntF,
     ExpD,
     LogD,
     PowD,
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
-    StackLimit,
     ReportOverRecursed,
     HandleExecutionInterrupt,
     HandleTrap,
     CallImport_Void,
     CallImport_I32,
     CallImport_I64,
     CallImport_F64,
     CoerceInPlace_ToInt32,
@@ -895,17 +894,37 @@ struct FuncImportExit
 // be called through an ExportFuncPtr.
 
 struct ExportArg
 {
     uint64_t lo;
     uint64_t hi;
 };
 
-typedef int32_t (*ExportFuncPtr)(ExportArg* args, uint8_t* global);
+// TLS data for a single module instance.
+//
+// Every WebAssembly function expects to be passed a hidden TLS pointer argument
+// in WasmTlsReg. The TLS pointer argument points to a TlsData struct.
+// Compiled functions expect that the TLS pointer does not change for the
+// lifetime of the thread.
+//
+// There is a TlsData per module instance per thread, so inter-module calls need
+// to pass the TLS pointer appropriate for the callee module.
+//
+// After the TlsData struct follows the module's declared TLS variables.
+//
+struct TlsData
+{
+    // Stack limit for the current thread. This limit is checked against the
+    // stack pointer in the prologue of functions that allocate stack space. See
+    // `CodeGenerator::generateWasm`.
+    void* stackLimit;
+};
+
+typedef int32_t (*ExportFuncPtr)(ExportArg* args, uint8_t* global, TlsData* tls);
 
 // Constants:
 
 // The WebAssembly spec hard-codes the virtual page size to be 64KiB and
 // requires linear memory to always be a multiple of 64KiB.
 static const unsigned PageSize = 64 * 1024;
 
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1,16 +1,17 @@
 /* -*- 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/. */
 
 #include "builtin/TestingFunctions.h"
 
+#include "mozilla/FloatingPoint.h"
 #include "mozilla/Move.h"
 #include "mozilla/unused.h"
 
 #include <cmath>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
@@ -1100,27 +1101,28 @@ GetSavedFrameCount(JSContext* cx, unsign
 
 static bool
 SaveStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS::StackCapture capture((JS::AllFrames()));
     if (args.length() >= 1) {
-        double d;
-        if (!ToNumber(cx, args[0], &d))
+        double maxDouble;
+        if (!ToNumber(cx, args[0], &maxDouble))
             return false;
-        if (d < 0) {
+        if (mozilla::IsNaN(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
             ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                   JSDVG_SEARCH_STACK, args[0], nullptr,
                                   "not a valid maximum frame count", NULL);
             return false;
         }
-        if (d > 0)
-            capture = JS::StackCapture(JS::MaxFrames(d));
+        uint32_t max = uint32_t(maxDouble);
+        if (max > 0)
+            capture = JS::StackCapture(JS::MaxFrames(max));
     }
 
     JSCompartment* targetCompartment = cx->compartment();
     if (args.length() >= 2) {
         if (!args[1].isObject()) {
             ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                   JSDVG_SEARCH_STACK, args[0], nullptr,
                                   "not an object", NULL);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/bug-1289058.js
@@ -0,0 +1,13 @@
+const g1 = newGlobal({});
+const g2 = newGlobal(newGlobal);
+g1.g2obj = g2.eval("new Object");
+g1.evaluate(`
+  const global = this;
+  function capture(shouldIgnoreSelfHosted = true) {
+    return captureFirstSubsumedFrame(global.g2obj, shouldIgnoreSelfHosted);
+  }
+  (function iife1() {
+    const captureTrueStack = capture(true);
+  }());
+`, {
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/bug-1289073.js
@@ -0,0 +1,1 @@
+saveStack(0.2);
--- a/js/src/jit-test/tests/wasm/basic-memory.js
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -99,16 +99,62 @@ function testStore(type, ext, base, offs
 }
 
 function testStoreOOB(type, ext, base, offset, align, value) {
     if (type === 'i64')
         value = createI64(value);
     assertErrorMessage(() => storeModule(type, ext, offset, align).store(base, value), Error, /invalid or out-of-range index/);
 }
 
+function badLoadModule(type, ext) {
+    return wasmEvalText( `(module (func (param i32) (${type}.load${ext} (get_local 0))) (export "" 0))`);
+}
+
+function badStoreModule(type, ext) {
+    return wasmEvalText( `(module (func (param i32) (${type}.store${ext} (get_local 0) (${type}.const 0))) (export "" 0))`);
+}
+
+// Can't touch memory.
+for (let [type, ext] of [
+    ['i32', ''],
+    ['i32', '8_s'],
+    ['i32', '8_u'],
+    ['i32', '16_s'],
+    ['i32', '16_u'],
+    ['i64', ''],
+    ['i64', '8_s'],
+    ['i64', '8_u'],
+    ['i64', '16_s'],
+    ['i64', '16_u'],
+    ['i64', '32_s'],
+    ['i64', '32_u'],
+    ['f32', ''],
+    ['f64', ''],
+])
+{
+    if (type !== 'i64' || hasI64())
+        assertErrorMessage(() => badLoadModule(type, ext), TypeError, /can't touch memory/);
+}
+
+for (let [type, ext] of [
+    ['i32', ''],
+    ['i32', '8'],
+    ['i32', '16'],
+    ['i64', ''],
+    ['i64', '8'],
+    ['i64', '16'],
+    ['i64', '32'],
+    ['f32', ''],
+    ['f64', ''],
+])
+{
+    if (type !== 'i64' || hasI64())
+        assertErrorMessage(() => badStoreModule(type, ext), TypeError, /can't touch memory/);
+}
+
 // Test bounds checks and edge cases.
 const align = 0;
 
 for (var ind = 0; ind < 2; ind++) {
   if (ind == 1)
     setJitCompilerOption('wasm.explicit-bounds-checks', 1);
 
   testLoad('i32', '', 0, 0, 0, 0x03020100);
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -398,16 +398,18 @@ if (hasI64()) {
     (export "" 0))`, imp)(), { low: 1337, high: 0x12345678 });
 
     setJitCompilerOption('wasm.test-mode', 0);
 } else {
     assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /NYI/);
     assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /NYI/);
 }
 
+assertErrorMessage(() => wasmEvalText(`(module (type $t (func)) (func (call_indirect $t (i32.const 0))))`), TypeError, /can't call_indirect without a table/);
+
 var {v2i, i2i, i2v} = wasmEvalText(`(module
     (type (func (result i32)))
     (type (func (param i32) (result i32)))
     (type (func (param i32)))
     (func (type 0) (i32.const 13))
     (func (type 0) (i32.const 42))
     (func (type 1) (i32.add (get_local 0) (i32.const 1)))
     (func (type 1) (i32.add (get_local 0) (i32.const 2)))
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -564,17 +564,20 @@ BacktrackingAllocator::buildLivenessInfo
                     bool found = false;
                     for (size_t i = 0; i < ins->numDefs(); i++) {
                         if (ins->getDef(i)->isFixed() &&
                             ins->getDef(i)->output()->aliases(LAllocation(*iter))) {
                             found = true;
                             break;
                         }
                     }
-                    if (!found) {
+                    // If this register doesn't have an explicit def above, mark
+                    // it as clobbered by the call unless it is actually
+                    // call-preserved.
+                    if (!found && !ins->isCallPreserved(*iter)) {
                         if (!addInitialFixedRange(*iter, outputOf(*ins), outputOf(*ins).next()))
                             return false;
                     }
                 }
 
                 CallRange* callRange =
                     new(alloc().fallible()) CallRange(outputOf(*ins), outputOf(*ins).next());
                 if (!callRange)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8978,18 +8978,20 @@ CodeGenerator::generateWasm(wasm::SigIdD
 
     wasm::GenerateFunctionPrologue(masm, frameSize(), sigId, offsets);
 
     // Overflow checks are omitted by CodeGenerator in some cases (leaf
     // functions with small framePushed). Perform overflow-checking after
     // pushing framePushed to catch cases with really large frames.
     Label onOverflow;
     if (!omitOverRecursedCheck()) {
+        // Get the per-thread stack limit from the TlsData struct pointed
+        // to by the WasmTlsReg hidden argument register.
         masm.branchPtr(Assembler::AboveOrEqual,
-                       wasm::SymbolicAddress::StackLimit,
+                       Address(WasmTlsReg, offsetof(wasm::TlsData, stackLimit)),
                        masm.getStackPointer(),
                        &onOverflow);
     }
 
     if (!generateBody())
         return false;
 
     masm.bind(&returnLabel_);
--- a/js/src/jit/JitCommon.h
+++ b/js/src/jit/JitCommon.h
@@ -28,20 +28,25 @@
 #define CALL_GENERATED_1(entry, p0)                     \
     (js::jit::Simulator::Current()->call(               \
         JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 1, p0))
 
 #define CALL_GENERATED_2(entry, p0, p1)                                 \
     (js::jit::Simulator::Current()->call(                               \
         JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 2, p0, p1))
 
+#define CALL_GENERATED_3(entry, p0, p1, p2)                             \
+    (js::jit::Simulator::Current()->call(                               \
+        JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 3, p0, p1, p2))
+
 #else
 
 // Call into jitted code by following the ABI of the native architecture.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7)   \
   entry(p0, p1, p2, p3, p4, p5, p6, p7)
 
-#define CALL_GENERATED_1(entry, p0)      entry(p0)
-#define CALL_GENERATED_2(entry, p0, p1)  entry(p0, p1)
+#define CALL_GENERATED_1(entry, p0)         entry(p0)
+#define CALL_GENERATED_2(entry, p0, p1)     entry(p0, p1)
+#define CALL_GENERATED_3(entry, p0, p1, p2) entry(p0, p1, p2)
 
 #endif
 
 #endif // jit_JitCommon_h
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -721,16 +721,23 @@ class LNode
     // transfer instruction, or zero otherwise.
     virtual size_t numSuccessors() const = 0;
     virtual MBasicBlock* getSuccessor(size_t i) const = 0;
     virtual void setSuccessor(size_t i, MBasicBlock* successor) = 0;
 
     virtual bool isCall() const {
         return false;
     }
+
+    // Does this call preserve the given register?
+    // By default, it is assumed that all registers are clobbered by a call.
+    virtual bool isCallPreserved(AnyRegister reg) const {
+        return false;
+    }
+
     uint32_t id() const {
         return id_;
     }
     void setId(uint32_t id) {
         MOZ_ASSERT(!id_);
         MOZ_ASSERT(id);
         id_ = id;
     }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4130,23 +4130,34 @@ LIRGenerator::visitAsmJSReturn(MAsmJSRet
     else if (rval->type() == MIRType::Int32)
         lir->setOperand(0, useFixed(rval, ReturnReg));
 #if JS_BITS_PER_WORD == 64
     else if (rval->type() == MIRType::Int64)
         lir->setOperand(0, useFixed(rval, ReturnReg));
 #endif
     else
         MOZ_CRASH("Unexpected asm.js return type");
+
+    // Preserve the TLS pointer we were passed in `WasmTlsReg`.
+    MDefinition* tlsPtr = ins->getOperand(1);
+    lir->setOperand(1, useFixed(tlsPtr, WasmTlsReg));
+
     add(lir);
 }
 
 void
 LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn* ins)
 {
-    add(new(alloc()) LAsmJSVoidReturn);
+    auto* lir = new(alloc()) LAsmJSVoidReturn;
+
+    // Preserve the TLS pointer we were passed in `WasmTlsReg`.
+    MDefinition* tlsPtr = ins->getOperand(0);
+    lir->setOperand(0, useFixed(tlsPtr, WasmTlsReg));
+
+    add(lir);
 }
 
 void
 LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg* ins)
 {
     if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) {
         MOZ_ASSERT(!ins->arg()->isEmittedAtUses());
         add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5355,19 +5355,20 @@ MAsmJSUnsignedToFloat32::foldsTo(TempAll
             return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType::Float32);
     }
 
     return this;
 }
 
 MAsmJSCall*
 MAsmJSCall::New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, Callee callee,
-                const Args& args, MIRType resultType, size_t spIncrement)
-{
-    MAsmJSCall* call = new(alloc) MAsmJSCall(desc, callee, spIncrement);
+                const Args& args, MIRType resultType, size_t spIncrement,
+                bool preservesTlsReg)
+{
+    MAsmJSCall* call = new(alloc) MAsmJSCall(desc, callee, spIncrement, preservesTlsReg);
     call->setResultType(resultType);
 
     if (!call->argRegs_.init(alloc, args.length()))
         return nullptr;
     for (size_t i = 0; i < call->argRegs_.length(); i++)
         call->argRegs_[i] = args[i].reg;
 
     if (!call->init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0)))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13417,32 +13417,37 @@ class MAsmJSParameter : public MNullaryI
   public:
     INSTRUCTION_HEADER(AsmJSParameter)
     TRIVIAL_NEW_WRAPPERS
 
     ABIArg abi() const { return abi_; }
 };
 
 class MAsmJSReturn
-  : public MAryControlInstruction<1, 0>,
+  : public MAryControlInstruction<2, 0>,
     public NoTypePolicy::Data
 {
-    explicit MAsmJSReturn(MDefinition* ins) {
+    explicit MAsmJSReturn(MDefinition* ins, MDefinition* tlsPtr) {
         initOperand(0, ins);
+        initOperand(1, tlsPtr);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSReturn)
     TRIVIAL_NEW_WRAPPERS
 };
 
 class MAsmJSVoidReturn
-  : public MAryControlInstruction<0, 0>,
+  : public MAryControlInstruction<1, 0>,
     public NoTypePolicy::Data
 {
+    explicit MAsmJSVoidReturn(MDefinition* tlsPtr) {
+        initOperand(0, tlsPtr);
+    }
+
   public:
     INSTRUCTION_HEADER(AsmJSVoidReturn)
     TRIVIAL_NEW_WRAPPERS
 };
 
 class MAsmJSPassStackArg
   : public MUnaryInstruction,
     public NoTypePolicy::Data
@@ -13521,33 +13526,39 @@ class MAsmJSCall final
         }
     };
 
   private:
     wasm::CallSiteDesc desc_;
     Callee callee_;
     FixedList<AnyRegister> argRegs_;
     size_t spIncrement_;
-
-    MAsmJSCall(const wasm::CallSiteDesc& desc, Callee callee, size_t spIncrement)
-     : desc_(desc), callee_(callee), spIncrement_(spIncrement)
+    bool preservesTlsReg_;
+
+    MAsmJSCall(const wasm::CallSiteDesc& desc, Callee callee, size_t spIncrement,
+               bool preservesTlsReg)
+      : desc_(desc)
+      , callee_(callee)
+      , spIncrement_(spIncrement)
+      , preservesTlsReg_(preservesTlsReg)
     { }
 
   public:
     INSTRUCTION_HEADER(AsmJSCall)
 
     struct Arg {
         AnyRegister reg;
         MDefinition* def;
         Arg(AnyRegister reg, MDefinition* def) : reg(reg), def(def) {}
     };
     typedef Vector<Arg, 8, SystemAllocPolicy> Args;
 
     static MAsmJSCall* New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, Callee callee,
-                           const Args& args, MIRType resultType, size_t spIncrement);
+                           const Args& args, MIRType resultType, size_t spIncrement,
+                           bool preservesTlsReg);
 
     size_t numArgs() const {
         return argRegs_.length();
     }
     AnyRegister registerForArg(size_t index) const {
         MOZ_ASSERT(index < numArgs());
         return argRegs_[index];
     }
@@ -13561,16 +13572,21 @@ class MAsmJSCall final
         MOZ_ASSERT(callee_.which() == Callee::Dynamic);
         MOZ_ASSERT(numArgs() == numOperands() - 1);
         return numArgs();
     }
     size_t spIncrement() const {
         return spIncrement_;
     }
 
+    // Does this call preserve the value of the TLS pointer register?
+    bool preservesTlsReg() const {
+        return preservesTlsReg_;
+    }
+
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MAsmSelect
   : public MTernaryInstruction,
     public NoTypePolicy::Data
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -69,16 +69,21 @@ static constexpr Register IntArgReg1 = r
 static constexpr Register IntArgReg2 = r2;
 static constexpr Register IntArgReg3 = r3;
 static constexpr Register GlobalReg = r10;
 static constexpr Register HeapReg = r11;
 static constexpr Register CallTempNonArgRegs[] = { r5, r6, r7, r8 };
 static const uint32_t NumCallTempNonArgRegs =
     mozilla::ArrayLength(CallTempNonArgRegs);
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = r9;
+
 class ABIArgGenerator
 {
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
     uint32_t stackOffset_;
     ABIArg current_;
 
     // ARM can either use HardFp (use float registers for float arguments), or
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -83,16 +83,21 @@ static constexpr Register IntArgReg3 = {
 static constexpr Register IntArgReg4 = { Registers::x4 };
 static constexpr Register IntArgReg5 = { Registers::x5 };
 static constexpr Register IntArgReg6 = { Registers::x6 };
 static constexpr Register IntArgReg7 = { Registers::x7 };
 static constexpr Register GlobalReg =  { Registers::x20 };
 static constexpr Register HeapReg = { Registers::x21 };
 static constexpr Register HeapLenReg = { Registers::x22 };
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = { Registers::x17 };
+
 // Define unsized Registers.
 #define DEFINE_UNSIZED_REGISTERS(N)  \
 static constexpr Register r##N = { Registers::x##N };
 REGISTER_CODE_LIST(DEFINE_UNSIZED_REGISTERS)
 #undef DEFINE_UNSIZED_REGISTERS
 static constexpr Register ip0 = { Registers::x16 };
 static constexpr Register ip1 = { Registers::x16 };
 static constexpr Register fp  = { Registers::x30 };
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -102,16 +102,21 @@ static constexpr Register InvalidReg = {
 static constexpr FloatRegister InvalidFloatReg;
 
 static constexpr Register StackPointer = sp;
 static constexpr Register FramePointer = InvalidReg;
 static constexpr Register ReturnReg = v0;
 static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
 static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = s5;
+
 // A bias applied to the GlobalReg to allow the use of instructions with small
 // negative immediate offsets which doubles the range of global data that can be
 // accessed with a single instruction.
 static const int32_t AsmJSGlobalRegBias = 32768;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static constexpr Register AsmJSIonExitRegCallee = t0;
 static constexpr Register AsmJSIonExitRegE0 = a0;
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8102,23 +8102,23 @@ class LAsmJSLoadFFIFunc : public LInstru
 };
 
 class LAsmJSParameter : public LInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(AsmJSParameter);
 };
 
-class LAsmJSReturn : public LInstructionHelper<0, 1, 0>
+class LAsmJSReturn : public LInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(AsmJSReturn);
 };
 
-class LAsmJSVoidReturn : public LInstructionHelper<0, 0, 0>
+class LAsmJSVoidReturn : public LInstructionHelper<0, 1, 0>
 {
   public:
     LIR_HEADER(AsmJSVoidReturn);
 };
 
 class LAsmJSPassStackArg : public LInstructionHelper<0, 1, 0>
 {
   public:
@@ -8152,16 +8152,23 @@ class LAsmJSCall final : public LInstruc
     MAsmJSCall* mir() const {
         return mir_->toAsmJSCall();
     }
 
     bool isCall() const {
         return true;
     }
 
+    bool isCallPreserved(AnyRegister reg) const {
+        // WebAssembly functions preserve the TLS pointer register.
+        if (reg.isFloat() || reg.gpr() != WasmTlsReg)
+            return false;
+        return mir()->preservesTlsReg();
+    }
+
     // LInstruction interface
     size_t numDefs() const {
         return def_.isBogusTemp() ? 0 : 1;
     }
     LDefinition* getDef(size_t index) {
         MOZ_ASSERT(numDefs() == 1);
         MOZ_ASSERT(index == 0);
         return &def_;
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -144,16 +144,21 @@ static constexpr FloatRegister FloatArgR
 static constexpr FloatRegister FloatArgReg4 = xmm4;
 static constexpr FloatRegister FloatArgReg5 = xmm5;
 static constexpr FloatRegister FloatArgReg6 = xmm6;
 static constexpr FloatRegister FloatArgReg7 = xmm7;
 static constexpr uint32_t NumFloatArgRegs = 8;
 static constexpr FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
 #endif
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = r14;
+
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static constexpr Register AsmJSIonExitRegCallee = r10;
 static constexpr Register AsmJSIonExitRegE0 = rax;
 static constexpr Register AsmJSIonExitRegE1 = rdi;
 static constexpr Register AsmJSIonExitRegE2 = rbx;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static constexpr Register AsmJSIonExitRegReturnData = ecx;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -47,16 +47,21 @@ static constexpr Register ReturnReg = ea
 static constexpr Register64 ReturnReg64(InvalidReg, InvalidReg);
 static constexpr FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single);
 static constexpr FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double);
 static constexpr FloatRegister ReturnSimd128Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Simd128);
 static constexpr FloatRegister ScratchFloat32Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Single);
 static constexpr FloatRegister ScratchDoubleReg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Double);
 static constexpr FloatRegister ScratchSimd128Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Simd128);
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = esi;
+
 // Avoid ebp, which is the FramePointer, which is unavailable in some modes.
 static constexpr Register ArgumentsRectifierReg = esi;
 static constexpr Register CallTempReg0 = edi;
 static constexpr Register CallTempReg1 = eax;
 static constexpr Register CallTempReg2 = ebx;
 static constexpr Register CallTempReg3 = ecx;
 static constexpr Register CallTempReg4 = esi;
 static constexpr Register CallTempReg5 = edx;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5898,19 +5898,19 @@ SetOutOfMemoryCallback(JSContext* cx, Ou
  */
 struct AllFrames { };
 
 /**
  * Capture at most this many frames.
  */
 struct MaxFrames
 {
-    unsigned maxFrames;
-
-    explicit MaxFrames(unsigned max)
+    uint32_t maxFrames;
+
+    explicit MaxFrames(uint32_t max)
       : maxFrames(max)
     {
         MOZ_ASSERT(max > 0);
     }
 };
 
 /**
  * Capture the first frame with the given principals. By default, do not
@@ -5928,17 +5928,18 @@ struct FirstSubsumedFrame
      */
     explicit FirstSubsumedFrame(JSContext* cx, bool ignoreSelfHostedFrames = true);
 
     explicit FirstSubsumedFrame(JSContext* ctx, JSPrincipals* p, bool ignoreSelfHostedFrames = true)
       : cx(ctx)
       , principals(p)
       , ignoreSelfHosted(ignoreSelfHostedFrames)
     {
-        JS_HoldPrincipals(principals);
+        if (principals)
+            JS_HoldPrincipals(principals);
     }
 
     // No copying because we want to avoid holding and dropping principals
     // unnecessarily.
     FirstSubsumedFrame(const FirstSubsumedFrame&) = delete;
     FirstSubsumedFrame& operator=(const FirstSubsumedFrame&) = delete;
 
     FirstSubsumedFrame(FirstSubsumedFrame&& rhs)
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -47,17 +47,17 @@ using mozilla::Move;
 using mozilla::Nothing;
 using mozilla::Some;
 
 namespace js {
 
 /**
  * Maximum number of saved frames returned for an async stack.
  */
-const unsigned ASYNC_STACK_MAX_FRAME_COUNT = 60;
+const uint32_t ASYNC_STACK_MAX_FRAME_COUNT = 60;
 
 /* static */ Maybe<LiveSavedFrameCache::FramePtr>
 LiveSavedFrameCache::getFramePtr(FrameIter& iter)
 {
     if (iter.hasUsableAbstractFramePtr())
         return Some(FramePtr(iter.abstractFramePtr()));
 
     if (iter.isPhysicalIonFrame())
@@ -1086,17 +1086,17 @@ SavedStacks::saveCurrentStack(JSContext*
 
     AutoSPSEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
     FrameIter iter(cx);
     return insertFrames(cx, iter, frame, mozilla::Move(capture));
 }
 
 bool
 SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
-                            MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount)
+                            MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount)
 {
     MOZ_ASSERT(initialized());
     MOZ_RELEASE_ASSERT(cx->compartment());
     assertSameCompartment(cx, this);
 
     RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
     MOZ_RELEASE_ASSERT(asyncStackObj);
     MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
@@ -1302,17 +1302,17 @@ SavedStacks::insertFrames(JSContext* cx,
             capture.as<JS::MaxFrames>().maxFrames--;
     }
 
     // Limit the depth of the async stack, if any, and ensure that the
     // SavedFrame instances we use are stored in the same compartment as the
     // rest of the synchronous stack chain.
     RootedSavedFrame parentFrame(cx, cachedFrame);
     if (asyncStack && !capture.is<JS::FirstSubsumedFrame>()) {
-        unsigned maxAsyncFrames = capture.is<JS::MaxFrames>()
+        uint32_t maxAsyncFrames = capture.is<JS::MaxFrames>()
             ? capture.as<JS::MaxFrames>().maxFrames
             : ASYNC_STACK_MAX_FRAME_COUNT;
         if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxAsyncFrames))
             return false;
     }
 
     // Iterate through |stackChain| in reverse order and get or create the
     // actual SavedFrame instances.
@@ -1333,33 +1333,33 @@ SavedStacks::insertFrames(JSContext* cx,
     frame.set(parentFrame);
     return true;
 }
 
 bool
 SavedStacks::adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
                              HandleString asyncCause,
                              MutableHandleSavedFrame adoptedStack,
-                             unsigned maxFrameCount)
+                             uint32_t maxFrameCount)
 {
     RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
     if (!asyncCauseAtom)
         return false;
 
     // If maxFrameCount is zero, the caller asked for an unlimited number of
     // stack frames, but async stacks are not limited by the available stack
     // memory, so we need to set an arbitrary limit when collecting them. We
     // still don't enforce an upper limit if the caller requested more frames.
-    unsigned maxFrames = maxFrameCount > 0 ? maxFrameCount : ASYNC_STACK_MAX_FRAME_COUNT;
+    uint32_t maxFrames = maxFrameCount > 0 ? maxFrameCount : ASYNC_STACK_MAX_FRAME_COUNT;
 
     // Accumulate the vector of Lookup objects in |stackChain|.
     SavedFrame::AutoLookupVector stackChain(cx);
     SavedFrame* currentSavedFrame = asyncStack;
     SavedFrame* firstSavedFrameParent = nullptr;
-    for (unsigned i = 0; i < maxFrames && currentSavedFrame; i++) {
+    for (uint32_t i = 0; i < maxFrames && currentSavedFrame; i++) {
         if (!stackChain->emplaceBack(*currentSavedFrame)) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         currentSavedFrame = currentSavedFrame->getParent();
 
         // Attach the asyncCause to the youngest frame.
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -164,17 +164,17 @@ class SavedStacks {
 
     MOZ_MUST_USE bool init();
     bool initialized() const { return frames.initialized(); }
     MOZ_MUST_USE bool saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
                                        JS::StackCapture&& capture = JS::StackCapture(JS::AllFrames()));
     MOZ_MUST_USE bool copyAsyncStack(JSContext* cx, HandleObject asyncStack,
                                      HandleString asyncCause,
                                      MutableHandleSavedFrame adoptedStack,
-                                     unsigned maxFrameCount = 0);
+                                     uint32_t maxFrameCount = 0);
     void sweep();
     void trace(JSTracer* trc);
     uint32_t count();
     void clear();
     void chooseSamplingProbability(JSCompartment*);
 
     // Set the sampling random number generator's state to |state0| and
     // |state1|. One or the other must be non-zero. See the comments for
@@ -220,17 +220,17 @@ class SavedStacks {
     };
 
     MOZ_MUST_USE bool insertFrames(JSContext* cx, FrameIter& iter,
                                    MutableHandleSavedFrame frame,
                                    JS::StackCapture&& capture);
     MOZ_MUST_USE bool adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
                                       HandleString asyncCause,
                                       MutableHandleSavedFrame adoptedStack,
-                                      unsigned maxFrameCount);
+                                      uint32_t maxFrameCount);
     SavedFrame* getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup);
     SavedFrame* createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup);
 
     // Cache for memoizing PCToLineNumber lookups.
 
     struct PCKey {
         PCKey(JSScript* script, jsbytecode* pc) : script(script), pc(pc) { }
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -1736,17 +1736,17 @@ public:
 public:
     // all the interface method declarations...
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCCONSTRUCTOR
     NS_DECL_NSIXPCSCRIPTABLE
     NS_DECL_NSICLASSINFO
 
 public:
-    nsXPCConstructor(); // not implemented
+    nsXPCConstructor() = delete;
     nsXPCConstructor(nsIJSCID* aClassID,
                      nsIJSIID* aInterfaceID,
                      const char* aInitializer);
 
 private:
     virtual ~nsXPCConstructor();
     nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
                              JSContext* cx, HandleObject obj,
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -837,17 +837,17 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
                         shared->Unmark();
                     } else if (doSweep) {
                         delete shared;
                         i.Remove();
                     }
                 }
             }
 
-            if (!isCompartmentGC) {
+            if (doSweep) {
                 for (auto i = self->mClassInfo2NativeSetMap->Iter(); !i.Done(); i.Next()) {
                     auto entry = static_cast<ClassInfo2NativeSetMap::Entry*>(i.Get());
                     if (!entry->value->IsMarked())
                         i.Remove();
                 }
             }
 
             for (auto i = self->mNativeSetMap->Iter(); !i.Done(); i.Next()) {
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -12,22 +12,20 @@
 using namespace mozilla;
 
 #ifdef DEBUG
 int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0;
 #endif
 
 XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                                              nsIClassInfo* ClassInfo,
-                                             uint32_t ClassInfoFlags,
                                              XPCNativeSet* Set)
     : mScope(Scope),
       mJSProtoObject(nullptr),
       mClassInfo(ClassInfo),
-      mClassInfoFlags(ClassInfoFlags),
       mSet(Set),
       mScriptableInfo(nullptr)
 {
     // This native object lives as long as its associated JSObject - killed
     // by finalization of the JSObject (or explicitly if Init fails).
 
     MOZ_COUNT_CTOR(XPCWrappedNativeProto);
     MOZ_ASSERT(mScope);
@@ -159,31 +157,27 @@ XPCWrappedNativeProto::GetNewOrUsed(XPCW
 {
     AutoJSContext cx;
     MOZ_ASSERT(scope, "bad param");
     MOZ_ASSERT(classInfo, "bad param");
 
     AutoMarkingWrappedNativeProtoPtr proto(cx);
     ClassInfo2WrappedNativeProtoMap* map = nullptr;
 
-    uint32_t ciFlags;
-    if (NS_FAILED(classInfo->GetFlags(&ciFlags)))
-        ciFlags = 0;
-
     map = scope->GetWrappedNativeProtoMap();
     proto = map->Find(classInfo);
     if (proto)
         return proto;
 
     AutoMarkingNativeSetPtr set(cx);
     set = XPCNativeSet::GetNewOrUsed(classInfo);
     if (!set)
         return nullptr;
 
-    proto = new XPCWrappedNativeProto(scope, classInfo, ciFlags, set);
+    proto = new XPCWrappedNativeProto(scope, classInfo, set);
 
     if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) {
         delete proto.get();
         return nullptr;
     }
 
     map->Add(classInfo, proto);
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -167,29 +167,27 @@
 #ifdef GetClassName
 #undef GetClassName
 #endif
 #endif /* XP_WIN */
 
 /***************************************************************************/
 // default initial sizes for maps (hashtables)
 
-#define XPC_CONTEXT_MAP_LENGTH                   8
 #define XPC_JS_MAP_LENGTH                       32
 #define XPC_JS_CLASS_MAP_LENGTH                 32
 
 #define XPC_NATIVE_MAP_LENGTH                    8
 #define XPC_NATIVE_PROTO_MAP_LENGTH              8
 #define XPC_DYING_NATIVE_PROTO_MAP_LENGTH        8
 #define XPC_DETACHED_NATIVE_PROTO_MAP_LENGTH    16
 #define XPC_NATIVE_INTERFACE_MAP_LENGTH         32
 #define XPC_NATIVE_SET_MAP_LENGTH               32
 #define XPC_NATIVE_JSCLASS_MAP_LENGTH           16
 #define XPC_THIS_TRANSLATOR_MAP_LENGTH           4
-#define XPC_NATIVE_WRAPPER_MAP_LENGTH            8
 #define XPC_WRAPPER_MAP_LENGTH                   8
 
 /***************************************************************************/
 // data declarations...
 extern const char XPC_CONTEXT_STACK_CONTRACTID[];
 extern const char XPC_EXCEPTION_CONTRACTID[];
 extern const char XPC_CONSOLE_CONTRACTID[];
 extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
@@ -208,18 +206,16 @@ extern const char XPC_XPCONNECT_CONTRACT
     else                                                                      \
         result = nullptr;                                                      \
     *dest = result;                                                           \
     return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
 
 
 #define WRAPPER_FLAGS JSCLASS_HAS_PRIVATE
 
-#define INVALID_OBJECT ((JSObject*)1)
-
 // If IS_WN_CLASS for the JSClass of an object is true, the object is a
 // wrappednative wrapper, holding the XPCWrappedNative in its private slot.
 static inline bool IS_WN_CLASS(const js::Class* clazz)
 {
     return clazz->isWrappedNative();
 }
 
 static inline bool IS_WN_REFLECTOR(JSObject* obj)
@@ -408,17 +404,17 @@ public:
         }
 #endif
     }
 
 private:
     mozilla::Maybe<StringType> mStrings[2];
 };
 
-class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime
+class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime
 {
 public:
     static XPCJSRuntime* newXPCJSRuntime();
     static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); }
 
     void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
     void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
 
@@ -666,17 +662,17 @@ private:
 // init'd and leave other members undefined. In debug builds the accessors
 // use a CHECK_STATE macro to track whether or not the object is in a valid
 // state to answer the question a caller might be asking. As long as this
 // class is maintained correctly it can do its job without a bunch of added
 // overhead from useless initializations and non-DEBUG error checking.
 //
 // Note that most accessors are inlined.
 
-class MOZ_STACK_CLASS XPCCallContext : public nsAXPCNativeCallContext
+class MOZ_STACK_CLASS XPCCallContext final : public nsAXPCNativeCallContext
 {
 public:
     NS_IMETHOD GetCallee(nsISupports** aResult);
     NS_IMETHOD GetCalleeMethodIndex(uint16_t* aResult);
     NS_IMETHOD GetJSContext(JSContext** aResult);
     NS_IMETHOD GetArgc(uint32_t* aResult);
     NS_IMETHOD GetArgvPtr(JS::Value** aResult);
     NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo** aResult);
@@ -743,18 +739,18 @@ public:
 
     void SystemIsBeingShutDown();
 
     operator JSContext*() const {return GetJSContext();}
 
 private:
 
     // no copy ctor or assignment allowed
-    XPCCallContext(const XPCCallContext& r); // not implemented
-    XPCCallContext& operator= (const XPCCallContext& r); // not implemented
+    XPCCallContext(const XPCCallContext& r) = delete;
+    XPCCallContext& operator= (const XPCCallContext& r) = delete;
 
 private:
     // posible values for mState
     enum State {
         INIT_FAILED,
         SYSTEM_SHUTDOWN,
         HAVE_RUNTIME,
         HAVE_OBJECT,
@@ -847,17 +843,17 @@ struct InterpositionWhitelistPair {
 
 typedef nsTArray<InterpositionWhitelistPair> InterpositionWhitelistArray;
 
 /***************************************************************************/
 // XPCWrappedNativeScope is one-to-one with a JS global object.
 
 class nsIAddonInterposition;
 class nsXPCComponentsBase;
-class XPCWrappedNativeScope : public PRCList
+class XPCWrappedNativeScope final : public PRCList
 {
 public:
 
     XPCJSRuntime*
     GetRuntime() const {return XPCJSRuntime::Get();}
 
     Native2WrappedNativeMap*
     GetWrappedNativeMap() const {return mWrappedNativeMap;}
@@ -1033,17 +1029,17 @@ public:
     void SetAddonCallInterposition() { mHasCallInterpositions = true; }
     bool HasCallInterposition() { return mHasCallInterpositions; };
 
     static bool AllowCPOWsInAddon(JSContext* cx, JSAddonId* addonId, bool allow);
 
 protected:
     virtual ~XPCWrappedNativeScope();
 
-    XPCWrappedNativeScope(); // not implemented
+    XPCWrappedNativeScope() = delete;
 
 private:
     class ClearInterpositionsObserver final : public nsIObserver {
         ~ClearInterpositionsObserver() {}
 
       public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIOBSERVER
@@ -1113,17 +1109,17 @@ private:
 #define XPC_FUNCTION_PARENT_OBJECT_SLOT 1
 
 /***************************************************************************/
 // XPCNativeMember represents a single idl declared method, attribute or
 // constant.
 
 // Tight. No virtual methods. Can be bitwise copied (until any resolution done).
 
-class XPCNativeMember
+class XPCNativeMember final
 {
 public:
     static bool GetCallInfo(JSObject* funobj,
                             XPCNativeInterface** pInterface,
                             XPCNativeMember**    pMember);
 
     jsid   GetName() const {return mName;}
 
@@ -1210,17 +1206,17 @@ private:
 } JS_HAZ_NON_GC_POINTER; // Only stores a pinned string
 
 /***************************************************************************/
 // XPCNativeInterface represents a single idl declared interface. This is
 // primarily the set of XPCNativeMembers.
 
 // Tight. No virtual methods.
 
-class XPCNativeInterface
+class XPCNativeInterface final
 {
   public:
     static XPCNativeInterface* GetNewOrUsed(const nsIID* iid);
     static XPCNativeInterface* GetNewOrUsed(nsIInterfaceInfo* info);
     static XPCNativeInterface* GetNewOrUsed(const char* name);
     static XPCNativeInterface* GetISupports();
 
     inline nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo.get();}
@@ -1238,18 +1234,16 @@ class XPCNativeInterface
     }
     XPCNativeMember* GetMemberAt(uint16_t i) {
         MOZ_ASSERT(i < mMemberCount, "bad index");
         return &mMembers[i];
     }
 
     void DebugDump(int16_t depth);
 
-#define XPC_NATIVE_IFACE_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set
-
     void Mark() {
         mMarked = 1;
     }
 
     void Unmark() {
         mMarked = 0;
     }
 
@@ -1263,43 +1257,43 @@ class XPCNativeInterface
 
     static void DestroyInstance(XPCNativeInterface* inst);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
   protected:
     static XPCNativeInterface* NewInstance(nsIInterfaceInfo* aInfo);
 
-    XPCNativeInterface();   // not implemented
+    XPCNativeInterface() = delete;
     XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName)
       : mInfo(aInfo), mName(aName), mMemberCount(0), mMarked(0)
     {
         MOZ_COUNT_CTOR(XPCNativeInterface);
     }
     ~XPCNativeInterface() {
         MOZ_COUNT_DTOR(XPCNativeInterface);
     }
 
     void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
 
-    XPCNativeInterface(const XPCNativeInterface& r); // not implemented
-    XPCNativeInterface& operator= (const XPCNativeInterface& r); // not implemented
+    XPCNativeInterface(const XPCNativeInterface& r) = delete;
+    XPCNativeInterface& operator= (const XPCNativeInterface& r) = delete;
 
 private:
     nsCOMPtr<nsIInterfaceInfo> mInfo;
     jsid                       mName;
     uint16_t                   mMemberCount : 15;
     uint16_t                   mMarked : 1;
     XPCNativeMember            mMembers[1]; // always last - object sized for array
 };
 
 /***************************************************************************/
 // XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
 
-class XPCNativeSetKey
+class XPCNativeSetKey final
 {
 public:
     explicit XPCNativeSetKey(XPCNativeSet*       BaseSet  = nullptr,
                              XPCNativeInterface* Addition = nullptr,
                              uint16_t            Position = 0)
         : mIsAKey(IS_A_KEY), mPosition(Position), mBaseSet(BaseSet),
           mAddition(Addition) {}
     ~XPCNativeSetKey() {}
@@ -1339,17 +1333,17 @@ private:
     uint16_t                mPosition;
     XPCNativeSet*           mBaseSet;
     XPCNativeInterface*     mAddition;
 };
 
 /***************************************************************************/
 // XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
 
-class XPCNativeSet
+class XPCNativeSet final
 {
   public:
     static XPCNativeSet* GetNewOrUsed(const nsIID* iid);
     static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo);
     static XPCNativeSet* GetNewOrUsed(XPCNativeSet* otherSet,
                                       XPCNativeInterface* newInterface,
                                       uint16_t position);
 
@@ -1397,18 +1391,16 @@ class XPCNativeSet
     }
 
     XPCNativeInterface* GetInterfaceAt(uint16_t i)
         {MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];}
 
     inline bool MatchesSetUpToInterface(const XPCNativeSet* other,
                                           XPCNativeInterface* iface) const;
 
-#define XPC_NATIVE_SET_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set
-
     inline void Mark();
 
     // NOP. This is just here to make the AutoMarkingPtr code compile.
     inline void TraceJS(JSTracer* trc) {}
     inline void AutoTrace(JSTracer* trc) {}
 
   private:
     void MarkSelfOnly() {
@@ -1460,17 +1452,17 @@ class XPCNativeSet
 // XPCNativeScriptableFlags is a wrapper class that holds the flags returned
 // from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience
 // methods to check for particular bitflags. Since we also use this class as
 // a member of the gc'd class XPCNativeScriptableShared, this class holds the
 // bit and exposes the inlined methods to support marking.
 
 #define XPC_WN_SJSFLAGS_MARK_FLAG JS_BIT(31) // only high bit of 32 is set
 
-class XPCNativeScriptableFlags
+class XPCNativeScriptableFlags final
 {
 private:
     uint32_t mFlags;
 
 public:
 
     explicit XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {}
 
@@ -1524,17 +1516,17 @@ public:
 // XPCNativeScriptableShared is used to hold the JSClass and the
 // associated scriptable flags for XPCWrappedNatives. These are shared across
 // the runtime and are garbage collected by xpconnect. We *used* to just store
 // this inside the XPCNativeScriptableInfo (usually owned by instances of
 // XPCWrappedNativeProto. This had two problems... It was wasteful, and it
 // was a big problem when wrappers are reparented to different scopes (and
 // thus different protos (the DOM does this).
 
-class XPCNativeScriptableShared
+class XPCNativeScriptableShared final
 {
 public:
     const XPCNativeScriptableFlags& GetFlags() const { return mFlags; }
 
     const JSClass* GetJSClass() { return Jsvalify(&mJSClass); }
 
     XPCNativeScriptableShared(uint32_t aFlags, char* aName, bool aPopulate);
 
@@ -1562,74 +1554,65 @@ private:
     // allocated. So we must free them in the destructor.
     js::Class mJSClass;
 };
 
 /***************************************************************************/
 // XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a
 // given class or instance.
 
-class XPCNativeScriptableInfo
+class XPCNativeScriptableInfo final
 {
 public:
     static XPCNativeScriptableInfo*
     Construct(const XPCNativeScriptableCreateInfo* sci);
 
     nsIXPCScriptable*
     GetCallback() const {return mCallback;}
 
     const XPCNativeScriptableFlags&
     GetFlags() const      {return mShared->GetFlags();}
 
     const JSClass*
     GetJSClass()          {return mShared->GetJSClass();}
 
-    XPCNativeScriptableShared*
-    GetScriptableShared() {return mShared;}
-
-    void
-    SetCallback(nsIXPCScriptable* s) {mCallback = s;}
-    void
-    SetCallback(already_AddRefed<nsIXPCScriptable>&& s) {mCallback = s;}
-
     void
     SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;}
 
     void Mark() {
         if (mShared)
             mShared->Mark();
     }
 
     void TraceJS(JSTracer* trc) {}
     void AutoTrace(JSTracer* trc) {}
 
 protected:
-    explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable = nullptr,
-                                     XPCNativeScriptableShared* shared = nullptr)
-        : mCallback(scriptable), mShared(shared)
+    explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable)
+        : mCallback(scriptable), mShared(nullptr)
                                {MOZ_COUNT_CTOR(XPCNativeScriptableInfo);}
 public:
     ~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);}
 private:
 
     // disable copy ctor and assignment
-    XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r); // not implemented
-    XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r); // not implemented
+    XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
+    XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;
 
 private:
     nsCOMPtr<nsIXPCScriptable>  mCallback;
     XPCNativeScriptableShared*  mShared;
 };
 
 /***************************************************************************/
 // XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
 // it abstracts out the scriptable interface pointer and the flags. After
 // creation these are factored differently using XPCNativeScriptableInfo.
 
-class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo
+class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
 {
 public:
 
     explicit XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si)
         : mCallback(si.GetCallback()), mFlags(si.GetFlags()) {}
 
     XPCNativeScriptableCreateInfo(already_AddRefed<nsIXPCScriptable>&& callback,
                                   XPCNativeScriptableFlags flags)
@@ -1656,17 +1639,17 @@ private:
     nsCOMPtr<nsIXPCScriptable>  mCallback;
     XPCNativeScriptableFlags    mFlags;
 };
 
 /***********************************************/
 // XPCWrappedNativeProto hold the additional shared wrapper data
 // for XPCWrappedNative whose native objects expose nsIClassInfo.
 
-class XPCWrappedNativeProto
+class XPCWrappedNativeProto final
 {
 public:
     static XPCWrappedNativeProto*
     GetNewOrUsed(XPCWrappedNativeScope* scope,
                  nsIClassInfo* classInfo,
                  const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
                  bool callPostCreatePrototype = true);
 
@@ -1686,33 +1669,16 @@ public:
     GetClassInfo()     const {return mClassInfo;}
 
     XPCNativeSet*
     GetSet()           const {return mSet;}
 
     XPCNativeScriptableInfo*
     GetScriptableInfo()   {return mScriptableInfo;}
 
-    uint32_t
-    GetClassInfoFlags() const {return mClassInfoFlags;}
-
-#ifdef GET_IT
-#undef GET_IT
-#endif
-#define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );}
-
-    bool ClassIsSingleton()           GET_IT(SINGLETON)
-    bool ClassIsDOMObject()           GET_IT(DOM_OBJECT)
-    bool ClassIsPluginObject()        GET_IT(PLUGIN_OBJECT)
-
-#undef GET_IT
-
-    void SetScriptableInfo(XPCNativeScriptableInfo* si)
-        {MOZ_ASSERT(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
-
     bool CallPostCreatePrototype();
     void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
     void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
 
     void SystemIsBeingShutDown();
 
     void DebugDump(int16_t depth);
 
@@ -1753,47 +1719,45 @@ public:
 #ifdef DEBUG
     void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();}
 #endif
 
     ~XPCWrappedNativeProto();
 
 protected:
     // disable copy ctor and assignment
-    XPCWrappedNativeProto(const XPCWrappedNativeProto& r); // not implemented
-    XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r); // not implemented
+    XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
+    XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r) = delete;
 
     // hide ctor
     XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                           nsIClassInfo* ClassInfo,
-                          uint32_t ClassInfoFlags,
                           XPCNativeSet* Set);
 
     bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
               bool callPostCreatePrototype);
 
 private:
 #ifdef DEBUG
     static int32_t gDEBUG_LiveProtoCount;
 #endif
 
 private:
     XPCWrappedNativeScope*   mScope;
     JS::ObjectPtr            mJSProtoObject;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
-    uint32_t                 mClassInfoFlags;
     XPCNativeSet*            mSet;
     XPCNativeScriptableInfo* mScriptableInfo;
 };
 
 /***********************************************/
 // XPCWrappedNativeTearOff represents the info needed to make calls to one
 // interface on the underlying native object of a XPCWrappedNative.
 
-class XPCWrappedNativeTearOff
+class XPCWrappedNativeTearOff final
 {
 public:
     bool IsAvailable() const {return mInterface == nullptr;}
     bool IsReserved()  const {return mInterface == (XPCNativeInterface*)1;}
     bool IsValid()     const {return !IsAvailable() && !IsReserved();}
     void   SetReserved()       {mInterface = (XPCNativeInterface*)1;}
 
     XPCNativeInterface* GetInterface() const {return mInterface;}
@@ -2034,18 +1998,17 @@ public:
     void TraceJS(JSTracer* trc) {
         TraceInside(trc);
     }
 
     void TraceSelf(JSTracer* trc) {
         // If this got called, we're being kept alive by someone who really
         // needs us alive and whole.  Do not let our mFlatJSObject go away.
         // This is the only time we should be tracing our mFlatJSObject,
-        // normally somebody else is doing that. Be careful not to trace the
-        // bogus INVALID_OBJECT value we can have during init, though.
+        // normally somebody else is doing that.
         JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject");
     }
 
     static void Trace(JSTracer* trc, JSObject* obj);
 
     void AutoTrace(JSTracer* trc) {
         TraceSelf(trc);
     }
@@ -2066,17 +2029,17 @@ public:
 
     bool HasExternalReference() const {return mRefCnt > 1;}
 
     void Suspect(nsCycleCollectionNoteRootCallback& cb);
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     // Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
 protected:
-    XPCWrappedNative(); // not implemented
+    XPCWrappedNative() = delete;
 
     // This ctor is used if this object will have a proto.
     XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                      XPCWrappedNativeProto* aProto);
 
     // This ctor is used if this object will NOT have a proto.
     XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                      XPCWrappedNativeScope* aScope,
@@ -2196,17 +2159,17 @@ private:
     // XPCCallContext.
     static nsresult CheckForException(XPCCallContext & ccx,
                                       mozilla::dom::AutoEntryScript& aes,
                                       const char * aPropertyName,
                                       const char * anInterfaceName,
                                       nsIException* aSyntheticException = nullptr);
     virtual ~nsXPCWrappedJSClass();
 
-    nsXPCWrappedJSClass();   // not implemented
+    nsXPCWrappedJSClass() = delete;
     nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
                         nsIInterfaceInfo* aInfo);
 
     bool IsReflectable(uint16_t i) const
         {return (bool)(mDescriptors[i/32] & (1 << (i%32)));}
     void SetReflectable(uint16_t i, bool b)
         {if (b) mDescriptors[i/32] |= (1 << (i%32));
          else mDescriptors[i/32] &= ~(1 << (i%32));}
@@ -2359,35 +2322,35 @@ private:
     RefPtr<nsXPCWrappedJSClass> mClass;
     nsXPCWrappedJS* mRoot;    // If mRoot != this, it is an owning pointer.
     nsXPCWrappedJS* mNext;
     nsCOMPtr<nsISupports> mOuter;    // only set in root
 };
 
 /***************************************************************************/
 
-class XPCJSObjectHolder : public nsIXPConnectJSObjectHolder,
-                          public XPCRootSetElem
+class XPCJSObjectHolder final : public nsIXPConnectJSObjectHolder,
+                                public XPCRootSetElem
 {
 public:
     // all the interface method declarations...
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
 
     // non-interface implementation
 
 public:
     void TraceJS(JSTracer* trc);
 
     explicit XPCJSObjectHolder(JSObject* obj);
 
 private:
     virtual ~XPCJSObjectHolder();
 
-    XPCJSObjectHolder(); // not implemented
+    XPCJSObjectHolder() = delete;
 
     JS::Heap<JSObject*> mJSObj;
 };
 
 /***************************************************************************
 ****************************************************************************
 *
 * All manner of utility classes follow...
@@ -2522,17 +2485,17 @@ public:
                                        const char* ifaceName,
                                        const char* methodName,
                                        nsISupports* data,
                                        nsIException** exception,
                                        JSContext* cx,
                                        JS::Value* jsExceptionPtr);
 
 private:
-    XPCConvert(); // not implemented
+    XPCConvert() = delete;
 
 };
 
 /***************************************************************************/
 // code for throwing exceptions into JS
 
 class nsXPCException;
 
@@ -2629,17 +2592,17 @@ public:
 
     // we implement the rest...
     NS_DECL_NSIJSIID
     NS_DECL_NSIXPCSCRIPTABLE
 
     static already_AddRefed<nsJSIID> NewID(nsIInterfaceInfo* aInfo);
 
     explicit nsJSIID(nsIInterfaceInfo* aInfo);
-    nsJSIID(); // not implemented
+    nsJSIID() = delete;
 
 private:
     virtual ~nsJSIID();
 
     nsCOMPtr<nsIInterfaceInfo> mInfo;
 };
 
 // nsJSCID
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -284,17 +284,17 @@ public:
 
 private:
     static const JSStringFinalizer sLiteralFinalizer, sDOMStringFinalizer;
 
     static void FinalizeLiteral(const JSStringFinalizer* fin, char16_t* chars);
 
     static void FinalizeDOMString(const JSStringFinalizer* fin, char16_t* chars);
 
-    XPCStringConvert();         // not implemented
+    XPCStringConvert() = delete;
 };
 
 class nsIAddonInterposition;
 
 namespace xpc {
 
 // If these functions return false, then an exception will be set on cx.
 bool Base64Encode(JSContext* cx, JS::HandleValue val, JS::MutableHandleValue out);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-fixed-inside-sticky-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<style>
+body {
+  height: 4000px;
+  margin: 0;
+  overflow: hidden;
+}
+#fixed {
+  position: fixed;
+  top: 50px;
+  left: 50px;
+  width: 100px;
+  height: 100px;
+  box-sizing: border-box;
+  border: 1px solid blue;
+}
+</style>
+<div id="fixed"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-fixed-inside-sticky-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-displayport-x="0" reftest-displayport-y="0"
+      reftest-displayport-w="800" reftest-displayport-h="2000"
+      reftest-async-scroll-x="0" reftest-async-scroll-y="50">
+<style>
+body {
+  height: 4000px;
+  margin: 0;
+  overflow: hidden;
+}
+#sticky {
+  position: sticky;
+  top: -1000px; /* scrolls regularly until the top edge hits -1000px, then stays fixed */
+  height: 1200px;
+  border-bottom: 1px solid black;
+}
+#fixed {
+  position: fixed;
+  top: 50px;
+  left: 50px;
+  width: 100px;
+  height: 100px;
+  box-sizing: border-box;
+  border: 1px solid blue;
+}
+</style>
+<div id="sticky">
+  <div id="fixed"></div>
+</div>
+</html>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -24,16 +24,17 @@ skip-if(!asyncPan) == split-layers-1.htm
 skip-if(!asyncPan) == split-layers-multi-scrolling-1.html split-layers-multi-scrolling-1-ref.html
 fuzzy-if(skiaContent,2,240000) fuzzy-if(browserIsRemote&&!skiaContent&&(cocoaWidget||winWidget),1,240000) skip-if(!asyncPan) == split-opacity-layers-1.html split-opacity-layers-1-ref.html
 skip-if(!asyncPan) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == culling-1.html culling-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-1.html position-fixed-iframe-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-2.html position-fixed-iframe-2-ref.html
 fuzzy-if(skiaContent||(browserIsRemote&&cocoaWidget),1,10000) skip-if(!asyncPan) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container-ref.html
+skip-if(!asyncPan) == position-fixed-inside-sticky-1.html position-fixed-inside-sticky-1-ref.html
 fuzzy(1,60000) skip-if(!asyncPan) == group-opacity-surface-size-1.html group-opacity-surface-size-1-ref.html
 skip-if(!asyncPan) == position-sticky-transformed.html position-sticky-transformed-ref.html
 skip-if(!asyncPan) == offscreen-prerendered-active-opacity.html offscreen-prerendered-active-opacity-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-1.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-2.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip == offscreen-clipped-blendmode-3.html offscreen-clipped-blendmode-ref.html # bug 1251588 - wrong AGR on mix-blend-mode item
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-4.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-1.html perspective-scrolling-1-ref.html
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -440,22 +440,27 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayou
     if (!shouldPosition && !aSizedToPopup) {
       RemoveStateBits(NS_FRAME_FIRST_REFLOW);
       return;
     }
   }
 
   // if the popup has just been opened, make sure the scrolled window is at 0,0
   if (mIsOpenChanged) {
-    nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
-    if (scrollframe) {
-      nsWeakFrame weakFrame(this);
-      scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
-      if (!weakFrame.IsAlive()) {
-        return;
+    // Don't scroll menulists as they will scroll to their selected item on their own.
+    nsCOMPtr<nsIDOMXULMenuListElement> menulist =
+      do_QueryInterface(aParentMenu ? aParentMenu->GetContent() : nullptr);
+    if (!menulist) {
+      nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
+      if (scrollframe) {
+        nsWeakFrame weakFrame(this);
+        scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
+        if (!weakFrame.IsAlive()) {
+          return;
+        }
       }
     }
   }
 
   // get the preferred, minimum and maximum size. If the menu is sized to the
   // popup, then the popup's width is the menu's width.
   nsSize prefSize = GetXULPrefSize(aState);
   nsSize minSize = GetXULMinSize(aState); 
--- a/mfbt/LinuxSignal.h
+++ b/mfbt/LinuxSignal.h
@@ -20,23 +20,20 @@ namespace mozilla {
 template <void (*H)(int, siginfo_t*, void*)>
 __attribute__((naked)) void
 SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext)
 {
   asm volatile (
     "nop; nop; nop; nop"
     : : : "memory");
 
-  // Because the assembler may generate additional insturctions below, we
-  // need to ensure NOPs are inserted first by separating them out above.
-
   asm volatile (
-    "bx %0"
+    "b %0"
     :
-    : "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext)
+    : "X"(H)
     : "memory");
 }
 
 # define MOZ_SIGNAL_TRAMPOLINE(h) (mozilla::SignalTrampoline<h>)
 
 #else // __arm__
 
 # define MOZ_SIGNAL_TRAMPOLINE(h) (h)
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -359,9 +359,16 @@ public class AppConstants {
 //#ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
         if (BuildConfig.FLAVOR.equals("automation")) {
             MultiDex.install(context);
         }
 //#else
         // Do nothing.
 //#endif
     }
+
+    public static final boolean MOZ_ANDROID_ACTIVITY_STREAM =
+//#ifdef MOZ_ANDROID_ACTIVITY_STREAM
+        true;
+//#else
+        false;
+//#endif
 }
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -2686,39 +2686,47 @@ public class BrowserApp extends GeckoApp
 
         // Show the toolbar before hiding about:home so the
         // onMetricsChanged callback still works.
         if (mDynamicToolbar.isEnabled()) {
             mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE);
         }
 
         if (mHomeScreen == null) {
-            final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
-            mHomeScreen = (HomePager) homePagerStub.inflate();
-
-            mHomeScreen.setOnPanelChangeListener(new HomeScreen.OnPanelChangeListener() {
-                @Override
-                public void onPanelSelected(String panelId) {
-                    final Tab currentTab = Tabs.getInstance().getSelectedTab();
-                    if (currentTab != null) {
-                        currentTab.setMostRecentHomePanel(panelId);
+            if (AppConstants.MOZ_ANDROID_ACTIVITY_STREAM) {
+                final ViewStub asStub = (ViewStub) findViewById(R.id.activity_stream_stub);
+                mHomeScreen = (HomeScreen) asStub.inflate();
+            } else {
+                final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
+                mHomeScreen = (HomeScreen) homePagerStub.inflate();
+
+                // For now these listeners are HomePager specific. In future we might want
+                // to have a more abstracted data storage, with one Bundle containing all
+                // relevant restore data.
+                mHomeScreen.setOnPanelChangeListener(new HomeScreen.OnPanelChangeListener() {
+                    @Override
+                    public void onPanelSelected(String panelId) {
+                        final Tab currentTab = Tabs.getInstance().getSelectedTab();
+                        if (currentTab != null) {
+                            currentTab.setMostRecentHomePanel(panelId);
+                        }
                     }
-                }
-            });
-
-            // Set this listener to persist restore data (via the Tab) every time panel state changes.
-            mHomeScreen.setPanelStateChangeListener(new HomeFragment.PanelStateChangeListener() {
-                @Override
-                public void onStateChanged(Bundle bundle) {
-                    final Tab currentTab = Tabs.getInstance().getSelectedTab();
-                    if (currentTab != null) {
-                        currentTab.setMostRecentHomePanelData(bundle);
+                });
+
+                // Set this listener to persist restore data (via the Tab) every time panel state changes.
+                mHomeScreen.setPanelStateChangeListener(new HomeFragment.PanelStateChangeListener() {
+                    @Override
+                    public void onStateChanged(Bundle bundle) {
+                        final Tab currentTab = Tabs.getInstance().getSelectedTab();
+                        if (currentTab != null) {
+                            currentTab.setMostRecentHomePanelData(bundle);
+                        }
                     }
-                }
-            });
+                });
+            }
 
             // Don't show the banner in guest mode.
             if (!Restrictions.isUserRestricted()) {
                 final ViewStub homeBannerStub = (ViewStub) findViewById(R.id.home_banner_stub);
                 final HomeBanner homeBanner = (HomeBanner) homeBannerStub.inflate();
                 mHomeScreen.setBanner(homeBanner);
 
                 // Remove the banner from the view hierarchy if it is dismissed.
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/ActivityStream.java
@@ -0,0 +1,69 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+ package org.mozilla.gecko.home.activitystream;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.LoaderManager;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import org.mozilla.gecko.animation.PropertyAnimator;
+import org.mozilla.gecko.home.HomeBanner;
+import org.mozilla.gecko.home.HomeFragment;
+import org.mozilla.gecko.home.HomeScreen;
+
+public class ActivityStream extends FrameLayout implements HomeScreen {
+
+    public ActivityStream(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean isVisible() {
+        // This is dependent on the loading state - currently we're a dumb panel so we're always
+        // "visible"
+        return true;
+    }
+
+    @Override
+    public void onToolbarFocusChange(boolean hasFocus) {
+        // We don't care: this is HomePager specific
+    }
+
+    @Override
+    public void showPanel(String panelId, Bundle restoreData) {
+        // We could use this to restore Panel data. In practice this isn't likely to be relevant for
+        // AS and can be ignore for now.
+    }
+
+    @Override
+    public void setOnPanelChangeListener(OnPanelChangeListener listener) {
+        // As with showPanel: not relevant yet, could be used for persistence (scroll position?)
+    }
+
+    @Override
+    public void setPanelStateChangeListener(HomeFragment.PanelStateChangeListener listener) {
+        // See setOnPanelChangeListener
+    }
+
+    @Override
+    public void setBanner(HomeBanner banner) {
+        // TODO: we should probably implement this to show snippets.
+    }
+
+    @Override
+    public void load(LoaderManager lm, FragmentManager fm, String panelId, Bundle restoreData,
+                     PropertyAnimator animator) {
+        // Signal to load data from storage as needed, compare with HomePager
+    }
+
+    @Override
+    public void unload() {
+        // Signal to clear data that has been loaded, compare with HomePager
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
@@ -73,19 +73,30 @@ public class TelemetryJSONFilePingStore 
 
     private final File storeDir;
     private final FilenameFilter uuidFilenameFilter;
     private final FileLastModifiedComparator fileLastModifiedComparator = new FileLastModifiedComparator();
 
     @WorkerThread // Writes to disk
     public TelemetryJSONFilePingStore(final File storeDir, final String profileName) {
         super(profileName);
+        if (storeDir.exists() && !storeDir.isDirectory()) {
+            // An alternative is to create a new directory, but we wouldn't
+            // be able to access it later so it's better to throw.
+            throw new IllegalStateException("Store dir unexpectedly exists & is not a directory - cannot continue");
+        }
+
         this.storeDir = storeDir;
         this.storeDir.mkdirs();
         uuidFilenameFilter = new FilenameRegexFilter(UUIDUtil.UUID_PATTERN);
+
+        if (!this.storeDir.canRead() || !this.storeDir.canWrite() || !this.storeDir.canExecute()) {
+            throw new IllegalStateException("Cannot read, write, or execute store dir: " +
+                    this.storeDir.canRead() + " " + this.storeDir.canWrite() + " " + this.storeDir.canExecute());
+        }
     }
 
     @VisibleForTesting File getPingFile(final String docID) {
         return new File(storeDir, docID);
     }
 
     @Override
     public void storePing(final TelemetryPing ping) throws IOException {
@@ -124,17 +135,25 @@ public class TelemetryJSONFilePingStore 
             // Sorted set so we're iterating over ascending files.
             final File file = it.next(); // file count > files to remove so this should not throw.
             file.delete();
         }
     }
 
     @Override
     public ArrayList<TelemetryPing> getAllPings() {
-        final List<File> files = Arrays.asList(storeDir.listFiles(uuidFilenameFilter));
+        final File[] fileArray = storeDir.listFiles(uuidFilenameFilter);
+        if (fileArray == null) {
+            // Intentionally don't log all info for the store directory to prevent leaking the path.
+            Log.w(LOGTAG, "listFiles unexpectedly returned null - unable to retrieve pings. Debug: exists? " +
+                    storeDir.exists() + "; directory? " + storeDir.isDirectory());
+            return new ArrayList<>(1);
+        }
+
+        final List<File> files = Arrays.asList(fileArray);
         Collections.sort(files, fileLastModifiedComparator); // oldest to newest
         final ArrayList<TelemetryPing> out = new ArrayList<>(files.size());
         for (final File file : files) {
             final JSONObject obj = lockAndReadJSONFromFile(file);
             if (obj == null) {
                 // We log in the method to get the JSONObject if we return null.
                 continue;
             }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -396,16 +396,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'gfx/TouchEventHandler.java',
     'gfx/ViewTransform.java',
     'gfx/VirtualLayer.java',
     'GlobalHistory.java',
     'GuestSession.java',
     'health/HealthRecorder.java',
     'health/SessionInformation.java',
     'health/StubbedHealthRecorder.java',
+    'home/activitystream/ActivityStream.java',
     'home/BookmarkFolderView.java',
     'home/BookmarkScreenshotRow.java',
     'home/BookmarksListAdapter.java',
     'home/BookmarksListView.java',
     'home/BookmarksPanel.java',
     'home/BrowserSearch.java',
     'home/ClientsAdapter.java',
     'home/CombinedHistoryAdapter.java',
@@ -960,17 +961,18 @@ if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRE
 # We do not expose MOZ_INSTALL_TRACKING_ADJUST_SDK_APP_TOKEN here because that
 # would leak the value to build logs.  Instead we expose the token quietly where
 # appropriate in Makefile.in.
 for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_LINKER_EXTRACT', 'MOZ_DEBUG',
             'MOZ_ANDROID_SEARCH_ACTIVITY', 'MOZ_NATIVE_DEVICES', 'MOZ_ANDROID_MLS_STUMBLER',
             'MOZ_ANDROID_DOWNLOADS_INTEGRATION', 'MOZ_INSTALL_TRACKING',
             'MOZ_ANDROID_GCM', 'MOZ_ANDROID_EXCLUDE_FONTS', 'MOZ_LOCALE_SWITCHER',
             'MOZ_ANDROID_BEAM', 'MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE',
-            'MOZ_SWITCHBOARD', 'MOZ_ANDROID_CUSTOM_TABS'):
+            'MOZ_SWITCHBOARD', 'MOZ_ANDROID_CUSTOM_TABS',
+            'MOZ_ANDROID_ACTIVITY_STREAM'):
     if CONFIG[var]:
         DEFINES[var] = 1
 
 for var in ('MOZ_UPDATER', 'MOZ_PKG_SPECIAL', 'MOZ_ANDROID_GCM_SENDERID'):
     if CONFIG[var]:
         DEFINES[var] = CONFIG[var]
 
 for var in ('ANDROID_PACKAGE_NAME', 'ANDROID_CPU_ARCH',
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/activity_stream.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.mozilla.gecko.home.activitystream.ActivityStream xmlns:android="http://schemas.android.com/apk/res/android"
+                                                      xmlns:tools="http://schemas.android.com/tools"
+                                                      android:orientation="vertical"
+                                                      android:layout_width="match_parent"
+                                                      android:layout_height="match_parent"
+                                                      android:background="@android:color/white">
+
+    <TextView
+        tools:ignore="HardcodedText"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Activity Stream \\o/"
+        android:id="@+id/textView"
+        android:layout_gravity="center_horizontal"/>
+</org.mozilla.gecko.home.activitystream.ActivityStream>
\ No newline at end of file
--- a/mobile/android/base/resources/layout/gecko_app.xml
+++ b/mobile/android/base/resources/layout/gecko_app.xml
@@ -61,16 +61,21 @@
                          android:layout_height="match_parent"
                          android:visibility="gone">
 
                 <ViewStub android:id="@+id/home_pager_stub"
                           android:layout="@layout/home_pager"
                           android:layout_width="match_parent"
                           android:layout_height="match_parent"/>
 
+                <ViewStub android:id="@+id/activity_stream_stub"
+                          android:layout="@layout/activity_stream"
+                          android:layout_width="match_parent"
+                          android:layout_height="match_parent"/>
+
                 <ViewStub android:id="@+id/home_banner_stub"
                           android:layout="@layout/home_banner"
                           android:layout_width="match_parent"
                           android:layout_height="@dimen/home_banner_height"
                           android:layout_gravity="bottom"/>
 
                 <ViewStub android:id="@+id/firstrun_pager_stub"
                           android:layout="@layout/firstrun_animation_container"
--- a/mobile/android/moz.configure
+++ b/mobile/android/moz.configure
@@ -43,16 +43,23 @@ project_flag('MOZ_ANDROID_CUSTOM_TABS',
 
 # Enable the Switchboard A/B framework code.
 # Note: The framework is always included in the app. This flag controls
 # usage of the framework.
 project_flag('MOZ_SWITCHBOARD',
              help='Include Switchboard A/B framework on Android',
              default=True)
 
+option(env='MOZ_ANDROID_ACTIVITY_STREAM',
+       help='Enable Activity Stream on Android (replacing the default HomePager)',
+       default=False)
+
+set_config('MOZ_ANDROID_ACTIVITY_STREAM',
+           depends_if('MOZ_ANDROID_ACTIVITY_STREAM')(lambda _: True))
+
 option('--disable-android-apz', env='MOZ_ANDROID_APZ',
        help='Disable the C++ async pan/zoom code and use the Java version instead')
 
 android_apz = depends_if('--disable-android-apz')(lambda _: True)
 
 set_config('MOZ_ANDROID_APZ', android_apz)
 set_define('MOZ_ANDROID_APZ', android_apz)
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
@@ -14,16 +14,17 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.telemetry.TelemetryPing;
 import org.mozilla.gecko.util.FileUtils;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FilenameFilter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
 import static org.junit.Assert.*;
@@ -65,16 +66,43 @@ public class TestTelemetryJSONFilePingSt
 
     @Test
     public void testConstructorOnlyWritesToGivenDir() throws Exception {
         // Constructor is called in @Before method
         assertTrue("Store dir exists", testDir.exists());
         assertEquals("Temp dir contains one dir (the store dir)", 1, tempDir.getRoot().list().length);
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorStoreAlreadyExistsAsNonDirectory() throws Exception {
+        final File file = tempDir.newFile();
+        new TelemetryJSONFilePingStore(file, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotReadable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setReadable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotWritable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setWritable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotExecutable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setExecutable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
     @Test
     public void testStorePingStoresCorrectData() throws Exception {
         assertStoreFileCount(0);
 
         final String expectedID = getDocID();
         final TelemetryPing expectedPing = new TelemetryPing("a/server/url", generateTelemetryPayload(), expectedID);
         testStore.storePing(expectedPing);
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1174,16 +1174,17 @@ pref("dom.event.contextmenu.enabled",   
 pref("dom.event.clipboardevents.enabled",   true);
 #if defined(XP_WIN) && !defined(RELEASE_BUILD) || defined(MOZ_WIDGET_GTK) && !defined(RELEASE_BUILD)
 pref("dom.event.highrestimestamp.enabled",  true);
 #else
 pref("dom.event.highrestimestamp.enabled",  false);
 #endif
 
 pref("dom.webcomponents.enabled",           false);
+pref("dom.webcomponents.customelements.enabled", false);
 
 pref("javascript.enabled",                  true);
 pref("javascript.options.strict",           false);
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     false);
 #endif
 pref("javascript.options.baselinejit",      true);
 pref("javascript.options.ion",              true);
rename from netwerk/test/TestStandardURL.cpp
rename to netwerk/test/gtest/TestStandardURL.cpp
--- a/netwerk/test/TestStandardURL.cpp
+++ b/netwerk/test/gtest/TestStandardURL.cpp
@@ -1,127 +1,69 @@
-#include <stdio.h>
-#include <stdlib.h>
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH
 
-#include "TestCommon.h"
 #include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
 #include "nsNetCID.h"
 #include "nsIURL.h"
-#include "prinrval.h"
-#include "nsStringAPI.h"
+#include "nsString.h"
 #include "nsComponentManagerUtils.h"
 
-static nsIURL     *test_url = 0;
-static nsCString   test_param;
+TEST(TestStandardURL, Simple) {
+    nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
+    ASSERT_TRUE(url);
+    ASSERT_EQ(url->SetSpec(NS_LITERAL_CSTRING("http://example.com")), NS_OK);
+
+    nsAutoCString out;
 
-static void run_test(const char *testname, int count, void (* testfunc)())
-{
-    PRIntervalTime start, end;
-    start = PR_IntervalNow();
-    for (; count; --count)
-        testfunc();
-    end = PR_IntervalNow();
-    printf("completed %s test in %u milliseconds\n", testname,
-            PR_IntervalToMilliseconds(end - start));
-}
+    ASSERT_EQ(url->GetSpec(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/"));
 
-static void set_spec_test()
-{
-    test_url->SetSpec(test_param);
-}
+    ASSERT_EQ(url->Resolve(NS_LITERAL_CSTRING("foo.html?q=45"), out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/foo.html?q=45"));
+
+    ASSERT_EQ(url->SetScheme(NS_LITERAL_CSTRING("foo")), NS_OK);
+
+    ASSERT_EQ(url->GetScheme(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("foo"));
 
-static void get_spec_test()
-{
-    nsAutoCString spec;
-    test_url->GetSpec(spec);
-}
-
-static void resolve_test()
-{
-    nsAutoCString spec;
-    test_url->Resolve(NS_LITERAL_CSTRING("foo.html?q=45"), spec);
-}
+    ASSERT_EQ(url->GetHost(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("example.com"));
+    ASSERT_EQ(url->SetHost(NS_LITERAL_CSTRING("www.yahoo.com")), NS_OK);
+    ASSERT_EQ(url->GetHost(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("www.yahoo.com"));
 
-static void set_scheme_test()
-{
-    test_url->SetScheme(NS_LITERAL_CSTRING("foo"));
-}
+    ASSERT_EQ(url->SetPath(NS_LITERAL_CSTRING("/some-path/one-the-net/about.html?with-a-query#for-you")), NS_OK);
+    ASSERT_EQ(url->GetPath(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("/some-path/one-the-net/about.html?with-a-query#for-you"));
 
-static void get_scheme_test()
-{
-    nsAutoCString scheme;
-    test_url->GetScheme(scheme);
-}
+    ASSERT_EQ(url->SetQuery(NS_LITERAL_CSTRING("a=b&d=c&what-ever-you-want-to-be-called=45")), NS_OK);
+    ASSERT_EQ(url->GetQuery(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("a=b&d=c&what-ever-you-want-to-be-called=45"));
 
-static void host_test()
-{
-    nsAutoCString host;
-    test_url->GetHost(host);
-    test_url->SetHost(NS_LITERAL_CSTRING("www.yahoo.com"));
-    test_url->SetHost(host);
+    ASSERT_EQ(url->SetRef(NS_LITERAL_CSTRING("#some-book-mark")), NS_OK);
+    ASSERT_EQ(url->GetRef(out), NS_OK);
+    ASSERT_TRUE(out == NS_LITERAL_CSTRING("some-book-mark"));
 }
 
-static void set_path_test()
-{
-    test_url->SetPath(NS_LITERAL_CSTRING("/some-path/one-the-net/about.html?with-a-query#for-you"));
-}
-
-static void get_path_test()
-{
-    nsAutoCString path;
-    test_url->GetPath(path);
-}
+#define COUNT 10000
 
-static void query_test()
-{
-    nsAutoCString query;
-    test_url->GetQuery(query);
-    test_url->SetQuery(NS_LITERAL_CSTRING("a=b&d=c&what-ever-you-want-to-be-called=45"));
-    test_url->SetQuery(query);
-}
-
-static void ref_test()
-{
-    nsAutoCString ref;
-    test_url->GetRef(ref);
-    test_url->SetRef(NS_LITERAL_CSTRING("#some-book-mark"));
-    test_url->SetRef(ref);
-}
-
-int main(int argc, char **argv)
-{
-    if (test_common_init(&argc, &argv) != 0)
-        return -1;
+MOZ_GTEST_BENCH(TestStandardURL, Perf, [] {
+    nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
+    ASSERT_TRUE(url);
+    nsAutoCString out;
 
-    if (argc < 2) {
-        printf("usage: TestURL url [count]\n");
-        return -1;
-    }
-
-    int count = 1000;
-    if (argc == 3)
-        count = atoi(argv[2]);
-    else
-        printf("using a default count of %d\n", count);
-
-    nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
-    if (!url) {
-        printf("failed to instantiate component: %s\n", NS_STANDARDURL_CONTRACTID);
-        return -1;
+    for (int i = COUNT; i; --i) {
+        url->SetSpec(NS_LITERAL_CSTRING("http://example.com"));
+        url->GetSpec(out);
+        url->Resolve(NS_LITERAL_CSTRING("foo.html?q=45"), out);
+        url->SetScheme(NS_LITERAL_CSTRING("foo"));
+        url->GetScheme(out);
+        url->SetHost(NS_LITERAL_CSTRING("www.yahoo.com"));
+        url->GetHost(out);
+        url->SetPath(NS_LITERAL_CSTRING("/some-path/one-the-net/about.html?with-a-query#for-you"));
+        url->GetPath(out);
+        url->SetQuery(NS_LITERAL_CSTRING("a=b&d=c&what-ever-you-want-to-be-called=45"));
+        url->GetQuery(out);
+        url->SetRef(NS_LITERAL_CSTRING("#some-book-mark"));
+        url->GetRef(out);
     }
-
-    test_url = url;
-    test_param = argv[1];
-
-    run_test("SetSpec", count, set_spec_test);
-    run_test("GetSpec", count, get_spec_test);
-    run_test("Resolve", count, resolve_test);
-    run_test("SetScheme", count, set_scheme_test);
-    run_test("GetScheme", count, get_scheme_test);
-    run_test("[GS]etHost", count, host_test);
-    run_test("SetPath", count, set_path_test);
-    run_test("GetPath", count, get_path_test);
-    run_test("[GS]etQuery", count, query_test);
-    run_test("[GS]etRef", count, ref_test);
-
-    return 0;
-}
+});
new file mode 100644
--- /dev/null
+++ b/netwerk/test/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; 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/.
+
+UNIFIED_SOURCES += [
+    'TestStandardURL.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -1,15 +1,15 @@
 # -*- 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/.
 
-TEST_DIRS += ['httpserver']
+TEST_DIRS += ['httpserver', 'gtest']
 
 BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']
 MOCHITEST_MANIFESTS += ['mochitests/mochitest.ini']
 
 XPCSHELL_TESTS_MANIFESTS += [
     'unit/xpcshell.ini',
     'unit/xpcshell_b2g.ini',
     'unit_ipc/xpcshell.ini',
@@ -19,17 +19,16 @@ GeckoSimplePrograms([
     'PropertiesTest',
     'ReadNTLM',
     'TestBlockingSocket',
     'TestDNS',
     'TestIncrementalDownload',
     'TestOpen',
     'TestProtocols',
     'TestServ',
-    'TestStandardURL',
     'TestStreamLoader',
     'TestUpload',
     'TestURLParser',
     'urltest',
 ])
 
 # XXX Make this work in libxul builds.
 #SIMPLE_PROGRAMS += [
--- a/security/manager/ssl/tests/unit/test_content_signing.js
+++ b/security/manager/ssl/tests/unit/test_content_signing.js
@@ -7,17 +7,17 @@
 // These tests ensure content signatures are working correctly.
 
 // First, we need to set up some data
 const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
 
 const TEST_DATA_DIR = "test_content_signing/";
 
 const ONECRL_NAME = "oneCRL-signer.mozilla.org";
-const ABOUT_NEWTAB_NAME = "remote-newtab-signer.mozilla.org";
+const ABOUT_NEWTAB_NAME = "remotenewtab.content-signature.mozilla.org";
 
 function getSignatureVerifier() {
   return Cc["@mozilla.org/security/contentsignatureverifier;1"]
            .createInstance(Ci.nsIContentSignatureVerifier);
 }
 
 function setRoot(filename) {
   let cert = constructCertFromFile(filename);
--- a/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
@@ -1,15 +1,15 @@
 -----BEGIN CERTIFICATE-----
-MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
+MIICUzCCAT2gAwIBAgIUNy0IWlDRDL53zwvj1lq0GCpIe2EwCwYJKoZIhvcNAQEL
 MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
 MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
 IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
 nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
-dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
-ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
-feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
-OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
-+q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
-RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
-5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
-27HBxIpd4tYniYrtfg==
+dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90
+ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B
+AQsDggEBAIeB4WKghknsrow+lj3qzDiHrPBc9AMlb4aZvS6yzazmXr80rXxnsKkb
+ZV1PW/cU6xXH5srWHpfJwypvvYS74btNtuacjKVH2AJdua4482WQIi9gCkXIufRx
+2nSS6pYgYZ4vD+yG8v+3SCChOCXnLjRaN9WxMi8tldbOW9pH44O3vrSSL70pQ2Ph
+8ncUbUbCNNtYhtOe2Z4XT9Cswmfkf4OIQ3gy9eYK2ySEUWP+lHs9KnnNXrLcA/ae
+cSUdI00i3C3OS9yldeyNHzVb8mSsZ5d1WkJrkf/hnXWGrMHRTtlJlG7t7cN8S0Oi
+tQoinJyxrZ+zabFIyl/euDc+Y/dijOU=
 -----END CERTIFICATE-----
--- a/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
@@ -1,5 +1,5 @@
 issuer:int-CA
 subject:ee-int-CA
 subjectKey:secp384r1
 extension:extKeyUsage:codeSigning
-extension:subjectAlternativeName:remote-newtab-signer.mozilla.org
+extension:subjectAlternativeName:remotenewtab.content-signature.mozilla.org
--- a/taskcluster/ci/android-test/tests.yml
+++ b/taskcluster/ci/android-test/tests.yml
@@ -90,16 +90,20 @@ mochitest:
         extra-options:
             - --test-suite=mochitest
 
 mochitest-chrome:
     description: "Mochitest chrome run"
     suite: mochitest/chrome
     treeherder-symbol: tc-M(c)
     instance-size: xlarge
+    chunks:
+        by-test-platform:
+            android-4.3-arm7-api-15/debug: 2
+            android-4.3-arm7-api-15/opt: 1
     loopback-video: true
     e10s: false
     max-run-time: 5400
     mozharness:
         script: mozharness/scripts/android_emulator_unittest.py
         no-read-buildbot-config: true
         config:
             - mozharness/configs/android/androidarm_4_3.py
--- a/taskcluster/ci/desktop-test/tests.yml
+++ b/taskcluster/ci/desktop-test/tests.yml
@@ -269,16 +269,18 @@ mochitest-jetpack:
             - --mochitest-suite=jetpack-package
 
 mochitest-media:
     description: "Mochitest media run"
     suite: mochitest/mochitest-media
     treeherder-symbol: tc-M(mda)
     max-run-time: 5400
     loopback-video: true
+    instance-size: large
+    docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: mozharness/scripts/desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             - mozharness/configs/unittests/linux_unittest.py
             - mozharness/configs/remove_executables.py
         extra-options:
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -7,14 +7,15 @@ images_path: '../../../testing/docker'
 
 # make a task for each docker-image we might want.  For the moment, since we
 # write artifacts for each, these are whitelisted, but ideally that will change
 # (to use subdirectory clones of the proper directory), at which point we can
 # generate tasks for every docker image in the directory, secure in the
 # knowledge that unnecessary images will be omitted from the target task graph
 images:
   - desktop-test
+  - desktop1604-test
   - desktop-build
   - builder
   - tester
   - lint
   - android-gradle-build
   - phone-builder
--- a/taskcluster/ci/legacy/tasks/windows_build.yml
+++ b/taskcluster/ci/legacy/tasks/windows_build.yml
@@ -1,21 +1,22 @@
 $inherits:
   from: 'tasks/build.yml'
 
 task:
-  workerType: win2012
+  workerType: gecko-1-b-win2012
   payload:
     artifacts:
       -
         type: 'directory'
         path: 'public\build'
         expires:
           relative-datestamp: '1 year'
   extra:
     treeherderEnv:
       - staging
+      - production
     treeherder:
       tier: 2
       jobKind: build
       machine:
         # https://github.com/mozilla/treeherder/blob/master/ui/js/values.js
         platform: {{platform}}
rename from taskcluster/scripts/tester/test-linux.sh
rename to taskcluster/scripts/tester/test-ubuntu1204.sh
--- a/taskcluster/scripts/tester/test-linux.sh
+++ b/taskcluster/scripts/tester/test-ubuntu1204.sh
@@ -59,17 +59,17 @@ rm -rf mozharness
 unzip -q mozharness.zip
 rm mozharness.zip
 
 if ! [ -d mozharness ]; then
     fail "mozharness zip did not contain mozharness/"
 fi
 
 # start up the pulseaudio daemon.  Note that it's important this occur
-# before the Xvfb startup.
+# before the Xvfb startup for ubuntu 12.04, not for 16.04
 if $NEED_PULSEAUDIO; then
     pulseaudio --fail --daemonize --start
     pactl load-module module-null-sink
 fi
 
 # run XVfb in the background, if necessary
 if $NEED_XVFB; then
     Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
new file mode 100644
--- /dev/null
+++ b/taskcluster/scripts/tester/test-ubuntu1604.sh
@@ -0,0 +1,158 @@
+#! /bin/bash -xe
+
+set -x -e
+
+echo "running as" $(id)
+
+####
+# Taskcluster friendly wrapper for performing fx desktop tests via mozharness.
+####
+
+# Inputs, with defaults
+
+: MOZHARNESS_URL                ${MOZHARNESS_URL}
+: MOZHARNESS_SCRIPT             ${MOZHARNESS_SCRIPT}
+: MOZHARNESS_CONFIG             ${MOZHARNESS_CONFIG}
+: NEED_XVFB                     ${NEED_XVFB:=true}
+: NEED_WINDOW_MANAGER           ${NEED_WINDOW_MANAGER:=false}
+: NEED_PULSEAUDIO               ${NEED_PULSEAUDIO:=false}
+: START_VNC                     ${START_VNC:=false}
+: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
+: mozharness args               "${@}"
+
+set -v
+cd $WORKSPACE
+
+fail() {
+    echo # make sure error message is on a new line
+    echo "[test-linux.sh:error]" "${@}"
+    exit 1
+}
+
+# test required parameters are supplied
+if [[ -z ${MOZHARNESS_URL} ]]; then fail "MOZHARNESS_URL is not set"; fi
+if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
+if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
+
+mkdir -p ~/artifacts/public
+
+cleanup() {
+    local rv=$?
+    if [[ -s /home/worker/.xsession-errors ]]; then
+      # To share X issues
+      cp /home/worker/.xsession-errors ~/artifacts/public/xsession-errors.log
+    fi
+    # When you call this script with START_VNC we make sure we
+    # don't kill xvfb so you don't lose your VNC connection
+    if [ -n "$xvfb_pid" ] && [ $START_VNC == false ] ; then
+        kill $xvfb_pid || true
+    fi
+    exit $rv
+}
+trap cleanup EXIT INT
+
+# Unzip the mozharness ZIP file created by the build task
+if ! curl --fail -o mozharness.zip --retry 10 -L $MOZHARNESS_URL; then
+    fail "failed to download mozharness zip"
+fi
+rm -rf mozharness
+unzip -q mozharness.zip
+rm mozharness.zip
+
+if ! [ -d mozharness ]; then
+    fail "mozharness zip did not contain mozharness/"
+fi
+
+# run XVfb in the background, if necessary
+if $NEED_XVFB; then
+    Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
+       > ~/artifacts/public/xvfb.log 2>&1 &
+    export DISPLAY=:0
+    xvfb_pid=$!
+    # Only error code 255 matters, because it signifies that no
+    # display could be opened. As long as we can open the display
+    # tests should work. We'll retry a few times with a sleep before
+    # failing.
+    retry_count=0
+    max_retries=2
+    xvfb_test=0
+    until [ $retry_count -gt $max_retries ]; do
+        xvinfo || xvfb_test=$?
+        if [ $xvfb_test != 255 ]; then
+            retry_count=$(($max_retries + 1))
+        else
+            retry_count=$(($retry_count + 1))
+            echo "Failed to start Xvfb, retry: $retry_count"
+            sleep 2
+        fi
+    done
+    if [ $xvfb_test == 255 ]; then fail "xvfb did not start properly"; fi
+fi
+
+if $START_VNC; then
+    x11vnc > ~/artifacts/public/x11vnc.log 2>&1 &
+fi
+
+if $NEED_WINDOW_MANAGER; then
+    # This is read by xsession to select the window manager
+    echo DESKTOP_SESSION=ubuntu > /home/worker/.xsessionrc
+
+    # note that doing anything with this display before running Xsession will cause sadness (like,
+    # crashes in compiz). Make sure that X has enough time to start
+    sleep 15
+    # DISPLAY has already been set above
+    # XXX: it would be ideal to add a semaphore logic to make sure that the
+    # window manager is ready
+    /etc/X11/Xsession 2>&1 &
+
+    # Turn off the screen saver and screen locking
+    gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
+    gsettings set org.gnome.desktop.screensaver lock-enabled false
+    gsettings set org.gnome.desktop.screensaver lock-delay 3600
+    # Disable the screen saver
+    xset s off s reset
+
+    # start compiz for our window manager
+    compiz 2>&1 &
+
+    #TODO: how to determine if compiz starts correctly?
+fi
+
+# start up the pulseaudio daemon.  Note that it's important this occur
+# before the Xvfb startup for ubuntu 12.04, not for 16.04
+if $NEED_PULSEAUDIO; then
+    pulseaudio --fail --daemonize --start
+    pactl load-module module-null-sink
+fi
+
+# For telemetry purposes, the build process wants information about the
+# source it is running; tc-vcs obscures this a little, but we can provide
+# it directly.
+export MOZ_SOURCE_REPO="${GECKO_HEAD_REPOSITORY}"
+export MOZ_SOURCE_CHANGESET="${GECKO_HEAD_REV}"
+
+# support multiple, space delimited, config files
+config_cmds=""
+for cfg in $MOZHARNESS_CONFIG; do
+  config_cmds="${config_cmds} --config-file $WORKSPACE/${cfg}"
+done
+
+mozharness_bin="/home/worker/bin/run-mozharness"
+
+# Save the computed mozharness command to a binary which is useful
+# for interactive mode.
+echo -e "#!/usr/bin/env bash
+# Some mozharness scripts assume base_work_dir is in
+# the current working directory, see bug 1279237
+cd $WORKSPACE
+cmd=\"python2.7 $WORKSPACE/${MOZHARNESS_SCRIPT} ${config_cmds} ${@} \${@}\"
+echo \"Running: \${cmd}\"
+exec \${cmd}" > ${mozharness_bin}
+chmod +x ${mozharness_bin}
+
+# In interactive mode, the user will be prompted with options for what to do.
+if [ "$TASKCLUSTER_INTERACTIVE" != "true" ]; then
+  # run the given mozharness script and configs, but pass the rest of the
+  # arguments in from our own invocation
+  ${mozharness_bin};
+fi
--- a/taskcluster/taskgraph/transforms/tests/android_test.py
+++ b/taskcluster/taskgraph/transforms/tests/android_test.py
@@ -46,38 +46,8 @@ def set_treeherder_machine_platform(conf
     translation = {
         'android-api-15/debug': 'android-4-3-armv7-api15/debug',
         'android-api-15/opt': 'android-4-3-armv7-api15/opt',
     }
     for test in tests:
         build_platform = test['build-platform']
         test['treeherder-machine-platform'] = translation.get(build_platform, build_platform)
         yield test
-
-
-@transforms.add
-def set_chunk_args(config, tests):
-    # Android tests do not take the --this-chunk/--total-chunk args like linux
-    # tests, preferring to define a --test-suite argument for each chunk.
-    # Where debug and opt have different chunk counts, there are *different*
-    # test-suite definitions for the debug and opt runs.
-    #
-    # Within the mozharness scripts, there is a translation *back* to
-    # --this-chunk/--total-chunk.
-    #
-    # TODO: remove the need for this with some changes to the mozharness script
-    # to take --total-chunk/this-chunk
-
-    for test in tests:
-        test['mozharness']['chunking-args'] = 'test-suite-suffix'
-
-        # if the chunks are an integer, then they do not differ between
-        # platforms, so the suffix is always "-<CHUNK>"
-        if isinstance(test['chunks'], int):
-            test['mozharness']['chunk-suffix'] = "-<CHUNK>"
-        else:
-            # otherwise, by convention, the debug version has "-debug" in the
-            # suite name and the opt version does not
-            if test['test-platform'].endswith('debug'):
-                test['mozharness']['chunk-suffix'] = '-debug-<CHUNK>'
-            else:
-                test['mozharness']['chunk-suffix'] = '-<CHUNK>'
-        yield test
--- a/taskcluster/taskgraph/transforms/tests/make_task_description.py
+++ b/taskcluster/taskgraph/transforms/tests/make_task_description.py
@@ -121,16 +121,17 @@ def docker_worker_setup(config, test, ta
     installer_url = ARTIFACT_URL.format('<build>', mozharness['build-artifact-name'])
     test_packages_url = ARTIFACT_URL.format('<build>',
                                             'public/build/target.test_packages.json')
     mozharness_url = ARTIFACT_URL.format('<build>',
                                          'public/build/mozharness.zip')
 
     taskdesc['worker-type'] = {
         'default': 'aws-provisioner-v1/desktop-test',
+        'large': 'aws-provisioner-v1/desktop-test-large',
         'xlarge': 'aws-provisioner-v1/desktop-test-xlarge',
     }[test['instance-size']]
 
     worker = taskdesc['worker'] = {}
     worker['implementation'] = test['worker-implementation']
 
     docker_image = test.get('docker-image')
     assert docker_image, "no docker image defined for a docker-worker/docker-engine task"
--- a/taskcluster/taskgraph/transforms/tests/test_description.py
+++ b/taskcluster/taskgraph/transforms/tests/test_description.py
@@ -72,18 +72,18 @@ test_description_schema = Schema({
     # and treeherder group.
     Required('e10s', default='both'): Any(
         bool, 'both',
         {'by-test-platform': {basestring: Any(bool, 'both')}},
     ),
 
     # The EC2 instance size to run these tests on.
     Required('instance-size', default='default'): Any(
-        Any('default', 'xlarge'),
-        {'by-test-platform': {basestring: Any('default', 'xlarge')}},
+        Any('default', 'large', 'xlarge'),
+        {'by-test-platform': {basestring: Any('default', 'large', 'xlarge')}},
     ),
 
     # Whether the task requires loopback audio or video (whatever that may mean
     # on the platform)
     Required('loopback-audio', default=False): bool,
     Required('loopback-video', default=False): bool,
 
     # The worker implementation for this test, as dictated by policy and by the
--- a/testing/docker/desktop-test/bin/test.sh
+++ b/testing/docker/desktop-test/bin/test.sh
@@ -24,15 +24,15 @@ fail() {
 ####
 # Now get the test-linux.sh script from the given Gecko tree and run it with
 # the same arguments.
 ####
 
 [ -d $WORKSPACE ] || mkdir -p $WORKSPACE
 cd $WORKSPACE
 
-script=taskcluster/scripts/tester/test-linux.sh
+script=taskcluster/scripts/tester/test-ubuntu1204.sh
 url=${GECKO_HEAD_REPOSITORY}/raw-file/${GECKO_HEAD_REV}/${script}
 if ! curl --fail -o ./test-linux.sh --retry 10 $url; then
     fail "failed downloading test-linux.sh from ${GECKO_HEAD_REPOSITORY}"
 fi
 chmod +x ./test-linux.sh
 exec ./test-linux.sh "${@}"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/Dockerfile
@@ -0,0 +1,72 @@
+FROM          taskcluster/ubuntu1604-test:0.1.3
+MAINTAINER    Joel Maher <joel.maher@gmail.com>
+
+# Add utilities and configuration
+COPY           dot-files/config              /home/worker/.config
+COPY           dot-files/pulse               /home/worker/.pulse
+COPY           dot-files/hgrc                /home/worker/.hgrc
+COPY           bin                           /home/worker/bin
+RUN            chmod +x bin/*
+# TODO: remove this when buildbot is gone
+COPY           buildprops.json               /home/worker/buildprops.json
+COPY           tc-vcs-config.yml /etc/taskcluster-vcs.yml
+
+# TODO: remove
+ADD            https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
+RUN chmod u+x /home/worker/bin/buildbot_step
+
+# TODO: remove
+ADD            https://s3-us-west-2.amazonaws.com/test-caching/packages/linux64-stackwalk /usr/local/bin/linux64-minidump_stackwalk
+RUN chmod +x /usr/local/bin/linux64-minidump_stackwalk
+
+# allow the worker user to access video devices
+RUN usermod -a -G video worker
+
+RUN mkdir Documents; mkdir Pictures; mkdir Music; mkdir Videos; mkdir artifacts
+
+# install a new enough npm, plus tc-vcs and tc-npm-cache
+RUN npm install -g npm@^2.0.0 \
+ && npm install -g taskcluster-vcs@2.3.12 \
+ && npm install -g taskcluster-npm-cache@1.1.14 \
+ && rm -rf ~/.npm
+ENV PATH $PATH:/home/worker/bin
+
+# Remove once running under 'worker' user.  This is necessary for pulseaudio to start
+# XXX: change this back to worker:worker once permissions issues are resolved
+RUN            chown -R root:root /home/worker
+
+
+# TODO Re-enable worker when bug 1093833 lands
+#USER          worker
+
+# clean up
+RUN rm -Rf .cache && mkdir -p .cache
+
+# Disable Ubuntu update prompt
+# http://askubuntu.com/questions/515161/ubuntu-12-04-disable-release-notification-of-14-04-in-update-manager
+ADD release-upgrades /etc/update-manager/release-upgrades
+
+# Disable tools with on-login popups that interfere with tests; see bug 1240084 and bug 984944.
+ADD jockey-gtk.desktop deja-dup-monitor.desktop /etc/xdg/autostart/
+
+# In test.sh we accept START_VNC to start a vnc daemon.
+# Exposing this port allows it to work.
+EXPOSE 5900
+
+# This helps not forgetting setting DISPLAY=:0 when running
+# tests outside of test.sh
+ENV DISPLAY :0
+
+# Disable apport (Ubuntu app crash reporter) to avoid stealing focus from test runs
+ADD apport /etc/default/apport
+
+# Disable font antialiasing for now to match releng's setup
+ADD fonts.conf /home/worker/.fonts.conf
+
+# Set up first-run experience for interactive mode
+ADD motd /etc/taskcluster-motd
+ADD taskcluster-interactive-shell /bin/taskcluster-interactive-shell
+RUN chmod +x /bin/taskcluster-interactive-shell
+
+# Set a default command useful for debugging
+CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/apport
@@ -0,0 +1,1 @@
+enabled=0
new file mode 100755
--- /dev/null
+++ b/testing/docker/desktop1604-test/bin/run-wizard
@@ -0,0 +1,108 @@
+#!/usr/bin/env 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/.
+
+from __future__ import print_function, unicode_literals
+
+import os
+import subprocess
+import sys
+from textwrap import wrap
+
+
+def call(cmd, **kwargs):
+    print(" ".join(cmd))
+    return subprocess.call(cmd, **kwargs)
+
+
+def resume():
+    call(['run-mozharness'])
+
+
+def setup():
+    call(['run-mozharness', '--no-run-tests'])
+    print("Mozharness has finished downloading the build and "
+          "tests to {}.".format(os.path.join(os.getcwd(), 'build')))
+
+
+def clone():
+    repo = os.environ['GECKO_HEAD_REPOSITORY']
+    rev = os.environ['GECKO_HEAD_REV']
+    clone_path = os.path.expanduser(os.path.join('~', 'gecko'))
+
+    # try is too large to clone, instead clone central and pull
+    # in changes from try
+    if "hg.mozilla.org/try" in repo:
+        central = 'http://hg.mozilla.org/mozilla-central'
+        call(['hg', 'clone', '-U', central, clone_path])
+        call(['hg', 'pull', '-u', '-r', rev, repo], cwd=clone_path)
+    else:
+        call(['hg', 'clone', '-u', rev, repo, clone_path])
+    print("Finished cloning to {} at revision {}.".format(
+                clone_path, rev))
+
+
+def exit():
+    pass
+
+
+OPTIONS = [
+    ('Resume task', resume,
+     "Resume the original task without modification. This can be useful for "
+     "passively monitoring it from another shell."),
+    ('Setup task', setup,
+     "Setup the task (download the application and tests) but don't run the "
+     "tests just yet. The tests can be run with a custom configuration later "
+     "(experimental)."),
+    ('Clone gecko', clone,
+     "Perform a clone of gecko using the task's repo and update it to the "
+     "task's revision."),
+    ('Exit', exit, "Exit this wizard and return to the shell.")
+]
+
+
+def _fmt_options():
+    max_line_len = 60
+    max_name_len = max(len(o[0]) for o in OPTIONS)
+
+    # TODO Pad will be off if there are more than 9 options.
+    pad = ' ' * (max_name_len+6)
+
+    msg = []
+    for i, (name, _, desc) in enumerate(OPTIONS):
+        desc = wrap(desc, width=max_line_len)
+        desc = [desc[0]] + [pad + l for l in desc[1:]]
+
+        optstr = '{}) {} - {}\n'.format(
+            i+1, name.ljust(max_name_len), '\n'.join(desc))
+        msg.append(optstr)
+    msg.append("Select one of the above options: ")
+    return '\n'.join(msg)
+
+
+def wizard():
+    print("This wizard can help you get started with some common debugging "
+          "workflows.\nWhat would you like to do?\n")
+    print(_fmt_options(), end="")
+    choice = None
+    while True:
+        choice = raw_input().decode('utf8')
+        try:
+            choice = int(choice)-1
+            if 0 <= choice < len(OPTIONS):
+                break
+        except ValueError:
+            pass
+
+        print("Must provide an integer from 1-{}:".format(len(OPTIONS)))
+
+    func = OPTIONS[choice][1]
+    ret = func()
+
+    print("Use the 'run-wizard' command to start this wizard again.")
+    return ret
+
+
+if __name__ == '__main__':
+    sys.exit(wizard())
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/bin/test.sh
@@ -0,0 +1,38 @@
+#! /bin/bash -vex
+
+set -x -e
+
+: GECKO_HEAD_REPOSITORY         ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
+: GECKO_HEAD_REV                ${GECKO_HEAD_REV:=default}
+: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
+
+
+# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
+# to a simple fail-if-root check
+if [ $(id -u) = 0 ]; then
+    chown -R worker:worker /home/worker
+    # drop privileges by re-running this script
+    exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
+fi
+
+fail() {
+    echo # make sure error message is on a new line
+    echo "[test.sh:error]" "${@}"
+    exit 1
+}
+
+####
+# Now get the test-linux.sh/test-ubuntu.sh script from the given Gecko tree
+# and run it with the same arguments.
+####
+
+[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
+cd $WORKSPACE
+
+script=taskcluster/scripts/tester/test-ubuntu1604.sh
+url=${GECKO_HEAD_REPOSITORY}/raw-file/${GECKO_HEAD_REV}/${script}
+if ! curl --fail -o ./test-linux.sh --retry 10 $url; then
+    fail "failed downloading test-ubuntu1604.sh from ${GECKO_HEAD_REPOSITORY}"
+fi
+chmod +x ./test-linux.sh
+exec ./test-linux.sh "${@}"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/buildprops.json
@@ -0,0 +1,8 @@
+{
+  "properties": {
+    "buildername": ""
+  },
+  "sourcestamp": {
+    "changes": []
+  }
+}
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/deja-dup-monitor.desktop
@@ -0,0 +1,19 @@
+[Desktop Entry]
+Version=1.0
+X-Ubuntu-Gettext-Domain=deja-dup
+
+Name=Backup Monitor
+Comment=Schedules backups at regular intervals
+
+Icon=deja-dup
+TryExec=/usr/lib/deja-dup/deja-dup/deja-dup-monitor
+Exec=/usr/lib/deja-dup/deja-dup/deja-dup-monitor
+
+# Bug 984944/1240084 - It prevents taking screenshots
+X-GNOME-Autostart-Delay=false
+
+StartupNotify=false
+NoDisplay=true
+
+Type=Application
+Categories=System;Utility;Archiving;
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/pip/pip.conf
@@ -0,0 +1,4 @@
+[global]
+disable-pip-version-check = true
+trusted-host = pypi.pub.build.mozilla.org
+
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+
+XDG_DESKTOP_DIR="$HOME/Desktop"
+XDG_DOWNLOAD_DIR="$HOME/Downloads"
+XDG_TEMPLATES_DIR="$HOME/Templates"
+XDG_PUBLICSHARE_DIR="$HOME/Public"
+XDG_DOCUMENTS_DIR="$HOME/Documents"
+XDG_MUSIC_DIR="$HOME/Music"
+XDG_PICTURES_DIR="$HOME/Pictures"
+XDG_VIDEOS_DIR="$HOME/Videos"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/user-dirs.locale
@@ -0,0 +1,1 @@
+en_US
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/hgrc
@@ -0,0 +1,12 @@
+[diff]
+showfunc = 1
+unified = 8
+
+[extensions]
+color =
+pager =
+progress =
+rebase =
+
+[web]
+cacerts = /etc/ssl/certs/ca-certificates.crt
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/pulse/default.pa
@@ -0,0 +1,164 @@
+#!/usr/bin/pulseaudio -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+# This startup script is used only if PulseAudio is started per-user
+# (i.e. not in system mode)
+
+.nofail
+
+### Load something into the sample cache
+#load-sample-lazy x11-bell /usr/share/sounds/gtk-events/activate.wav
+#load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-coldplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-access /usr/share/sounds/generic.wav
+
+.fail
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+load-module module-card-restore
+
+### Automatically augment property information from .desktop files
+### stored in /usr/share/application
+load-module module-augment-properties
+
+### Load audio drivers statically
+### (it's probably better to not load these drivers manually, but instead
+### use module-udev-detect -- see below -- for doing this automatically)
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#load-module module-null-sink
+#load-module module-pipe-sink
+
+### Automatically load driver modules depending on the hardware available
+.ifexists module-udev-detect.so
+load-module module-udev-detect
+.else
+### Use the static hardware detection module (for systems that lack udev/hal support)
+load-module module-detect
+.endif
+
+### Automatically connect sink and source if JACK server is present
+.ifexists module-jackdbus-detect.so
+.nofail
+load-module module-jackdbus-detect
+.fail
+.endif
+
+### Automatically load driver modules for Bluetooth hardware
+# This module causes a pulseaudio startup failure on "gecko-tester"
+#.ifexists module-bluetooth-discover.so
+#load-module module-bluetooth-discover
+#.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix.so
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+#load-module module-zeroconf-publish
+
+### Load the RTP receiver module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
+#load-module module-rtp-send source=rtp.monitor
+
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf.so
+.nofail
+load-module module-gconf
+.fail
+.endif
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Automatically move streams to the default sink if the sink they are
+### connected to dies, similar for sources
+load-module module-rescue-streams
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Honour intended role device property
+load-module module-intended-roles
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+# This module causes a pulseaudio startup failure on "gecko-tester"
+#.ifexists module-console-kit.so
+#load-module module-console-kit
+#.endif
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Cork music streams when a phone stream is active
+#load-module module-cork-music-on-phone
+
+### Modules to allow autoloading of filters (such as echo cancellation)
+### on demand. module-filter-heuristics tries to determine what filters
+### make sense, and module-filter-apply does the heavy-lifting of
+### loading modules and rerouting streams.
+load-module module-filter-heuristics
+load-module module-filter-apply
+
+### Load DBus protocol
+#.ifexists module-dbus-protocol.so
+#load-module module-dbus-protocol
+#.endif
+
+# X11 modules should not be started from default.pa so that one daemon
+# can be shared by multiple sessions.
+
+### Load X11 bell module
+#load-module module-x11-bell sample=bell-windowing-system
+
+### Register ourselves in the X11 session manager
+#load-module module-x11-xsmp
+
+### Publish connection data in the X11 root window
+#.ifexists module-x11-publish.so
+#.nofail
+#load-module module-x11-publish
+#.fail
+#.endif
+
+load-module module-switch-on-port-available
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/fonts.conf
@@ -0,0 +1,5 @@
+<match target="font">
+  <edit name="antialias" mode="assign">
+   <bool>false</bool>
+  </edit>
+</match>
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/jockey-gtk.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Name=Check for new hardware drivers
+Comment=Notify about new hardware drivers available for the system
+Icon=jockey
+Exec=sh -c "test -e /var/cache/jockey/check || exec jockey-gtk --check"
+Terminal=false
+Type=Application
+Categories=System;Settings;GTK;HardwareSettings;
+NotShowIn=KDE;
+X-Ubuntu-Gettext-Domain=jockey
+
+# Bug 984944/1240084 - It prevents taking screenshots
+X-GNOME-Autostart-Delay=false
+
+NoDisplay=true
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/motd
@@ -0,0 +1,6 @@
+Welcome to your taskcluster interactive shell! The regularly scheduled task
+has been paused to give you a chance to set up your debugging environment.
+
+For your convenience, the exact mozharness command needed for this task can
+be invoked using the 'run-mozharness' command.
+
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/release-upgrades
@@ -0,0 +1,17 @@
+# Default behavior for the release upgrader.
+
+[DEFAULT]
+# Default prompting behavior, valid options:
+#
+#  never  - Never check for a new release.
+#  normal - Check to see if a new release is available.  If more than one new
+#           release is found, the release upgrader will attempt to upgrade to
+#           the release that immediately succeeds the currently-running
+#           release.
+#  lts    - Check to see if a new LTS release is available.  The upgrader
+#           will attempt to upgrade to the first LTS release available after
+#           the currently-running one.  Note that this option should not be
+#           used if the currently-running release is not itself an LTS
+#           release, since in that case the upgrader won't be able to
+#           determine if a newer release is available.
+Prompt=never
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/taskcluster-interactive-shell
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+/home/worker/bin/run-wizard;
+
+SPAWN="$SHELL";
+
+if [ "$SHELL" = "bash" ]; then
+  SPAWN="bash -li";
+fi;
+
+exec $SPAWN;
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/tc-vcs-config.yml
@@ -0,0 +1,40 @@
+# Default configuration used by the tc-vs tools these can be overridden by
+# passing the config you wish to use over the command line...
+git: git
+hg: hg
+
+repoCache:
+  # Repo url to clone when running repo init..
+  repoUrl: https://git.mozilla.org/external/google/gerrit/git-repo.git
+  # Version of repo to utilize...
+  repoRevision: master
+  # The root where all downloaded cache files are stored on the local machine...
+  cacheDir: '{{env.HOME}}/.tc-vcs-repo/'
+  # Name/prefixed used as part of the base url.
+  cacheName: sources/{{name}}.tar.gz
+  # Command used to upload the tarball
+  uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
+  # Large http get requests are often slower using nodes built in http layer so
+  # we utilize a subprocess which is responsible for fetching...
+  get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
+  # Used to create clone tarball
+  compress: tar -czf {{dest}} {{source}}
+  # All cache urls use tar + gz this is the command used to extract those files
+  # downloaded by the "get" command.
+  extract: tar -x -z -C {{dest}} -f {{source}}
+
+cloneCache:
+  # The root where all downloaded cache files are stored on the local machine...
+  cacheDir: '{{env.HOME}}/.tc-vcs/'
+  # Command used to upload the tarball
+  uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
+  # Large http get requests are often slower using nodes built in http layer so
+  # we utilize a subprocess which is responsible for fetching...
+  get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
+  # Used to create clone tarball
+  compress: tar -czf {{dest}} {{source}}
+  # All cache urls use tar + gz this is the command used to extract those files
+  # downloaded by the "get" command.
+  extract: tar -x -z --strip-components 1 -C {{dest}} -f {{source}}
+  # Name/prefixed used as part of the base url.
+  cacheName: clones/{{name}}.tar.gz
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/tester.env
@@ -0,0 +1,4 @@
+GAIA_REV=tip
+GAIA_REF=tip
+GAIA_BASE_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
+GAIA_HEAD_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/Dockerfile
@@ -0,0 +1,22 @@
+FROM          ubuntu
+MAINTAINER    Joel Maher <joel.maher@gmail.com>
+
+RUN useradd -d /home/worker -s /bin/bash -m worker
+WORKDIR /home/worker
+
+# install non-build specific dependencies in a single layer
+ADD           system-setup.sh   /tmp/system-setup.sh
+RUN           bash /tmp/system-setup.sh
+
+# Set variable normally configured at login, by the shells parent process, these
+# are taken from GNU su manual
+ENV           HOME          /home/worker
+ENV           SHELL         /bin/bash
+ENV           USER          worker
+ENV           LOGNAME       worker
+ENV           HOSTNAME      taskcluster-worker
+ENV           LANG          en_US.UTF-8
+ENV           LC_ALL        en_US.UTF-8
+
+# Set a default command useful for debugging
+CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/REGISTRY
@@ -0,0 +1,1 @@
+taskcluster
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/VERSION
@@ -0,0 +1,1 @@
+0.1.3
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/system-setup.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env bash
+
+set -ve
+
+test `whoami` == 'root'
+
+mkdir -p /setup
+cd /setup
+
+apt_packages=()
+
+apt_packages+=('alsa-base')
+apt_packages+=('alsa-utils')
+apt_packages+=('autoconf2.13')
+apt_packages+=('bluez-cups')
+apt_packages+=('build-essential')
+apt_packages+=('ca-certificates')
+apt_packages+=('ccache')
+apt_packages+=('curl')
+apt_packages+=('fonts-kacst')
+apt_packages+=('fonts-kacst-one')
+apt_packages+=('fonts-liberation')
+apt_packages+=('fonts-stix')
+apt_packages+=('fonts-unfonts-core')
+apt_packages+=('fonts-unfonts-extra')
+apt_packages+=('fonts-vlgothic')
+apt_packages+=('g++-multilib')
+apt_packages+=('gcc-multilib')
+apt_packages+=('gir1.2-gnomebluetooth-1.0')
+apt_packages+=('git')
+apt_packages+=('gstreamer0.10-alsa')
+apt_packages+=('gstreamer0.10-plugins-base')
+apt_packages+=('gstreamer0.10-plugins-good')
+apt_packages+=('gstreamer0.10-tools')
+apt_packages+=('language-pack-en-base')
+apt_packages+=('libasound2-dev')
+apt_packages+=('libcanberra-pulse')
+apt_packages+=('libcurl4-openssl-dev')
+apt_packages+=('libdbus-1-dev')
+apt_packages+=('libdbus-glib-1-dev')
+apt_packages+=('libgconf2-dev')
+apt_packages+=('libgstreamer-plugins-base0.10-dev')
+apt_packages+=('libgstreamer0.10-dev')
+apt_packages+=('libgtk2.0-dev')
+apt_packages+=('libiw-dev')
+apt_packages+=('libnotify-dev')
+apt_packages+=('libpulse-dev')
+apt_packages+=('libsox-fmt-alsa')
+apt_packages+=('libxt-dev')
+apt_packages+=('libxxf86vm1')
+apt_packages+=('llvm')
+apt_packages+=('llvm-dev')
+apt_packages+=('llvm-runtime')
+apt_packages+=('nano')
+apt_packages+=('pulseaudio')
+apt_packages+=('pulseaudio-module-bluetooth')
+apt_packages+=('pulseaudio-module-gconf')
+apt_packages+=('rlwrap')
+apt_packages+=('screen')
+apt_packages+=('software-properties-common')
+apt_packages+=('sudo')
+apt_packages+=('tar')
+apt_packages+=('ttf-dejavu')
+apt_packages+=('ubuntu-desktop')
+apt_packages+=('unzip')
+apt_packages+=('uuid')
+apt_packages+=('vim')
+apt_packages+=('wget')
+apt_packages+=('xvfb')
+apt_packages+=('yasm')
+apt_packages+=('zip')
+
+# get xvinfo for test-linux.sh to monitor Xvfb startup
+apt_packages+=('x11-utils')
+
+# Bug 1232407 - this allows the user to start vnc
+apt_packages+=('x11vnc')
+
+# Bug 1176031: need `xset` to disable screensavers
+apt_packages+=('x11-xserver-utils')
+
+# use Ubuntu's Python-2.7 (2.7.3 on Precise)
+apt_packages+=('python-dev')
+apt_packages+=('python-pip')
+
+apt-get update
+# This allows ubuntu-desktop to be installed without human interaction
+export DEBIAN_FRONTEND=noninteractive
+apt-get install -y -f ${apt_packages[@]}
+
+dpkg-reconfigure locales
+
+# set up tooltool (temporarily)
+curl https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py > /setup/tooltool.py
+tooltool_fetch() {
+    cat >manifest.tt
+    python /setup/tooltool.py fetch
+    rm manifest.tt
+}
+
+pip install --upgrade pip
+
+pip install virtualenv
+pip install mercurial
+
+# Install node
+tooltool_fetch <<'EOF'
+[
+{
+    "size": 5676610,
+    "digest": "ce27b788dfd141a5ba7674332825fc136fe2c4f49a319dd19b3a87c8fffa7a97d86cbb8535661c9a68c9122719aa969fc6a8c886458a0df9fc822eec99ed130b",
+    "algorithm": "sha512",
+    "filename": "node-v0.10.36-linux-x64.tar.gz"
+}
+]
+
+EOF
+tar -C /usr/local -xz --strip-components 1 < node-*.tar.gz
+node -v  # verify
+
+# Install custom-built Debian packages.  These come from a set of repositories
+# packaged in tarballs on tooltool to make them replicable.  Because they have
+# inter-dependenices, we install all repositories first, then perform the
+# installation.
+cp /etc/apt/sources.list sources.list.orig
+
+# Install Valgrind (trunk, late Jan 2016) and do some crude sanity
+# checks.  It has to go in /usr/local, otherwise it won't work.  Copy
+# the launcher binary to /usr/bin, though, so that direct invokations
+# of /usr/bin/valgrind also work.  Also install libc6-dbg since
+# Valgrind won't work at all without the debug symbols for libc.so and
+# ld.so being available.
+tooltool_fetch <<'EOF'
+[
+{
+    "size": 41331092,
+    "visibility": "public",
+    "digest": "a89393c39171b8304fc262094a650df9a756543ffe9fbec935911e7b86842c4828b9b831698f97612abb0eca95cf7f7b3ff33ea7a9b0313b30c9be413a5efffc",
+    "algorithm": "sha512",
+    "filename": "valgrind-15775-3206-ubuntu1204.tgz"
+}
+]
+EOF
+cp valgrind-15775-3206-ubuntu1204.tgz /tmp
+(cd / && tar xzf /tmp/valgrind-15775-3206-ubuntu1204.tgz)
+rm /tmp/valgrind-15775-3206-ubuntu1204.tgz
+cp /usr/local/bin/valgrind /usr/bin/valgrind
+apt-get install -y libc6-dbg
+valgrind --version
+valgrind date
+
+# adding multiverse to get 'ubuntu-restricted-extras' below
+apt-add-repository multiverse
+apt-get update
+
+# for mp4 codec (used in MSE tests)
+apt-get -q -y -f install ubuntu-restricted-extras
+
+apt-get -q -y -f install \
+    libxcb1 \
+    libxcb-render0 \
+    libxcb-shm0 \
+    libxcb-glx0 \
+    libxcb-shape0
+libxcb1_version=$(dpkg-query -s libxcb1 | grep ^Version | awk '{ print $2 }')
+[ "$libxcb1_version" = "1.11.1-1ubuntu1" ] || exit 1
+
+apt-get -q -y -f install \
+    libgl1-mesa-dri \