merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 18 Jul 2016 17:07:59 +0200
changeset 305394 cde56ead650fd302be1d440507485b9abf7c163a
parent 305311 9d95b275e2cf97f87d313ec24cf0e52409d6fc46 (current diff)
parent 305393 317caf83210fb756304a5fcead0e6c824557466b (diff)
child 305395 69a9474a32061e0afc22ad93f0683934cff0c531
child 305405 7324716faa31f0e982a4c83144ea19ea84085197
child 305486 ec19b4cb42d5355d336967e00059ddee3053f924
child 305598 003901a846c74e63c614cc7fa6653a3683f71cd5
push id30460
push usercbook@mozilla.com
push dateMon, 18 Jul 2016 15:08:19 +0000
treeherdermozilla-central@cde56ead650f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
cde56ead650f / 50.0a1 / 20160718081125 / files
nightly linux64
cde56ead650f / 50.0a1 / 20160718081125 / files
nightly mac
cde56ead650f / 50.0a1 / 20160718081125 / files
nightly win32
cde56ead650f / 50.0a1 / 20160718081125 / files
nightly win64
cde56ead650f / 50.0a1 / 20160718081125 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/components/nsBrowserGlue.js
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
testing/web-platform/meta/XMLHttpRequest/event-progress.htm.ini
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -97,16 +97,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/Feeds.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SelfSupportBackend",
                                   "resource:///modules/SelfSupportBackend.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
                                   "resource:///modules/sessionstore/SessionStore.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUsageTelemetry",
+                                  "resource:///modules/BrowserUsageTelemetry.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
                                   "resource:///modules/BrowserUITelemetry.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
                                   "resource://gre/modules/LoginManagerParent.jsm");
@@ -776,16 +779,17 @@ BrowserGlue.prototype = {
     DirectoryLinksProvider.init();
     NewTabUtils.init();
     NewTabUtils.links.addProvider(DirectoryLinksProvider);
     AboutNewTab.init();
 
     NewTabMessages.init();
 
     SessionStore.init();
+    BrowserUsageTelemetry.init();
     BrowserUITelemetry.init();
     ContentSearch.init();
     FormValidationHandler.init();
 
     ContentClick.init();
     RemotePrompt.init();
     Feeds.init();
     ContentPrefServiceParent.init();
@@ -1169,16 +1173,17 @@ BrowserGlue.prototype = {
     try {
       let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
                          .getService(Ci.nsIAppStartup);
       appStartup.trackStartupCrashEnd();
     } catch (e) {
       Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
     }
 
+    BrowserUsageTelemetry.uninit();
     SelfSupportBackend.uninit();
     NewTabMessages.uninit();
 
     CaptivePortalWatcher.uninit();
 
     AboutNewTab.uninit();
     webrtcUI.uninit();
     FormValidationHandler.uninit();
new file mode 100644
--- /dev/null
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -0,0 +1,175 @@
+/* -*- js-indent-level: 2; 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["BrowserUsageTelemetry"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+// Observed topic names.
+const WINDOWS_RESTORED_TOPIC = "sessionstore-windows-restored";
+const TELEMETRY_SUBSESSIONSPLIT_TOPIC = "internal-telemetry-after-subsession-split";
+const DOMWINDOW_OPENED_TOPIC = "domwindowopened";
+const DOMWINDOW_CLOSED_TOPIC = "domwindowclosed";
+
+// Probe names.
+const MAX_TAB_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_tab_count";
+const MAX_WINDOW_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_window_count";
+const TAB_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.tab_open_event_count";
+const WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.window_open_event_count";
+
+function getOpenTabsAndWinsCounts() {
+  let tabCount = 0;
+  let winCount = 0;
+
+  let browserEnum = Services.wm.getEnumerator("navigator:browser");
+  while (browserEnum.hasMoreElements()) {
+    let win = browserEnum.getNext();
+    winCount++;
+    tabCount += win.gBrowser.tabs.length;
+  }
+
+  return { tabCount, winCount };
+}
+
+let BrowserUsageTelemetry = {
+  init() {
+    Services.obs.addObserver(this, WINDOWS_RESTORED_TOPIC, false);
+  },
+
+  /**
+   * Handle subsession splits in the parent process.
+   */
+  afterSubsessionSplit() {
+    // Scalars just got cleared due to a subsession split. We need to set the maximum
+    // concurrent tab and window counts so that they reflect the correct value for the
+    // new subsession.
+    const counts = getOpenTabsAndWinsCounts();
+    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
+    Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
+  },
+
+  uninit() {
+    Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC, false);
+    Services.obs.removeObserver(this, DOMWINDOW_CLOSED_TOPIC, false);
+    Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
+    Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC, false);
+  },
+
+  observe(subject, topic, data) {
+    switch(topic) {
+      case WINDOWS_RESTORED_TOPIC:
+        this._setupAfterRestore();
+        break;
+      case DOMWINDOW_OPENED_TOPIC:
+        this._onWindowOpen(subject);
+        break;
+      case DOMWINDOW_CLOSED_TOPIC:
+        this._unregisterWindow(subject);
+        break;
+      case TELEMETRY_SUBSESSIONSPLIT_TOPIC:
+        this.afterSubsessionSplit();
+        break;
+    }
+  },
+
+  handleEvent(event) {
+    switch(event.type) {
+      case "TabOpen":
+        this._onTabOpen();
+        break;
+    }
+  },
+
+  /**
+   * This gets called shortly after the SessionStore has finished restoring
+   * windows and tabs. It counts the open tabs and adds listeners to all the
+   * windows.
+   */
+  _setupAfterRestore() {
+    // Make sure to catch new chrome windows and subsession splits.
+    Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, false);
+    Services.obs.addObserver(this, DOMWINDOW_CLOSED_TOPIC, false);
+    Services.obs.addObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
+
+    // Attach the tabopen handlers to the existing Windows.
+    let browserEnum = Services.wm.getEnumerator("navigator:browser");
+    while (browserEnum.hasMoreElements()) {
+      this._registerWindow(browserEnum.getNext());
+    }
+
+    // Get the initial tab and windows max counts.
+    const counts = getOpenTabsAndWinsCounts();
+    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
+    Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
+  },
+
+  /**
+   * Adds listeners to a single chrome window.
+   */
+  _registerWindow(win) {
+    win.addEventListener("TabOpen", this, true);
+  },
+
+  /**
+   * Removes listeners from a single chrome window.
+   */
+  _unregisterWindow(win) {
+    // Ignore non-browser windows.
+    if (!(win instanceof Ci.nsIDOMWindow) ||
+        win.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
+      return;
+    }
+
+    win.removeEventListener("TabOpen", this, true);
+  },
+
+  /**
+   * Updates the tab counts.
+   * @param {Number} [newTabCount=0] The count of the opened tabs across all windows. This
+   *        is computed manually if not provided.
+   */
+  _onTabOpen(tabCount = 0) {
+    // Use the provided tab count if available. Otherwise, go on and compute it.
+    tabCount = tabCount || getOpenTabsAndWinsCounts().tabCount;
+    // Update the "tab opened" count and its maximum.
+    Services.telemetry.scalarAdd(TAB_OPEN_EVENT_COUNT_SCALAR_NAME, 1);
+    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, tabCount);
+  },
+
+  /**
+   * Tracks the window count and registers the listeners for the tab count.
+   * @param{Object} win The window object.
+   */
+  _onWindowOpen(win) {
+    // Make sure to have a |nsIDOMWindow|.
+    if (!(win instanceof Ci.nsIDOMWindow)) {
+      return;
+    }
+
+    let onLoad = () => {
+      win.removeEventListener("load", onLoad, false);
+
+      // Ignore non browser windows.
+      if (win.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
+        return;
+      }
+
+      this._registerWindow(win);
+      // Track the window open event and check the maximum.
+      const counts = getOpenTabsAndWinsCounts();
+      Services.telemetry.scalarAdd(WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME, 1);
+      Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
+
+      // We won't receive the "TabOpen" event for the first tab within a new window.
+      // Account for that.
+      this._onTabOpen(counts.tabCount);
+    };
+    win.addEventListener("load", onLoad, false);
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -9,16 +9,17 @@ XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/social/xpcshell.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
     'AboutHome.jsm',
     'AboutNewTab.jsm',
     'BrowserUITelemetry.jsm',
+    'BrowserUsageTelemetry.jsm',
     'CaptivePortalWatcher.jsm',
     'CastingApps.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentObservers.jsm',
     'ContentSearch.jsm',
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -18,8 +18,9 @@ support-files =
   contentSearchSuggestions.xml
 [browser_NetworkPrioritizer.js]
 [browser_SelfSupportBackend.js]
 support-files =
   ../../components/uitour/test/uitour.html
   ../../components/uitour/UITour-lib.js
 [browser_taskbar_preview.js]
 skip-if = os != "win"
+[browser_UsageTelemetry.js]
--- a/browser/modules/test/browser_SelfSupportBackend.js
+++ b/browser/modules/test/browser_SelfSupportBackend.js
@@ -15,16 +15,22 @@ const PREF_SELFSUPPORT_ENABLED = "browse
 const PREF_SELFSUPPORT_URL = "browser.selfsupport.url";
 const PREF_UITOUR_ENABLED = "browser.uitour.enabled";
 
 const TEST_WAIT_RETRIES = 60;
 
 const TEST_PAGE_URL = getRootDirectory(gTestPath) + "uitour.html";
 const TEST_PAGE_URL_HTTPS = TEST_PAGE_URL.replace("chrome://mochitests/content/", "https://example.com/");
 
+function sendSessionRestoredNotification() {
+  let selfSupportBackendImpl =
+    Cu.import("resource:///modules/SelfSupportBackend.jsm", {}).SelfSupportBackendInternal;
+  selfSupportBackendImpl.observe(null, "sessionstore-windows-restored", null);
+}
+
 /**
  * Find a browser, with an IFRAME as parent, who has aURL as the source attribute.
  *
  * @param aURL The URL to look for to identify the browser.
  *
  * @returns {Object} The browser element or null on failure.
  */
 function findSelfSupportBrowser(aURL) {
@@ -123,17 +129,17 @@ add_task(function* setupEnvironment() {
  * Test that the self support page can use the UITour API and close itself.
  */
 add_task(function* test_selfSupport() {
   // Initialise the SelfSupport backend and trigger the load.
   SelfSupportBackend.init();
 
   // SelfSupportBackend waits for "sessionstore-windows-restored" to start loading. Send it.
   info("Sending sessionstore-windows-restored");
-  Services.obs.notifyObservers(null, "sessionstore-windows-restored", null);
+  sendSessionRestoredNotification();
 
   // Wait for the SelfSupport page to load.
   info("Waiting for the SelfSupport local page to load.");
   let selfSupportBrowser = yield promiseSelfSupportLoad(TEST_PAGE_URL_HTTPS);
   Assert.ok(!!selfSupportBrowser, "SelfSupport browser must exist.");
 
   // Get a reference to the UITour API.
   info("Testing access to the UITour API.");
@@ -191,17 +197,17 @@ add_task(function* test_selfSupport() {
  */
 add_task(function* test_selfSupport_noHTTPS() {
   Preferences.set(PREF_SELFSUPPORT_URL, TEST_PAGE_URL);
 
   SelfSupportBackend.init();
 
   // SelfSupportBackend waits for "sessionstore-windows-restored" to start loading. Send it.
   info("Sending sessionstore-windows-restored");
-  Services.obs.notifyObservers(null, "sessionstore-windows-restored", null);
+  sendSessionRestoredNotification();
 
   // Find the SelfSupport browser. We don't expect to find it since we are not using https.
   let selfSupportBrowser = findSelfSupportBrowser(TEST_PAGE_URL);
   Assert.ok(!selfSupportBrowser, "SelfSupport browser must not exist.");
 
   // We shouldn't need this, but let's keep it to make sure closing SelfSupport twice
   // doesn't create any problem.
   SelfSupportBackend.uninit();
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser_UsageTelemetry.js
@@ -0,0 +1,143 @@
+"use strict";
+
+const MAX_CONCURRENT_TABS = "browser.engagement.max_concurrent_tab_count";
+const TAB_EVENT_COUNT = "browser.engagement.tab_open_event_count";
+const MAX_CONCURRENT_WINDOWS = "browser.engagement.max_concurrent_window_count";
+const WINDOW_OPEN_COUNT = "browser.engagement.window_open_event_count";
+
+const TELEMETRY_SUBSESSION_TOPIC = "internal-telemetry-after-subsession-split";
+
+/**
+ * An helper that checks the value of a scalar if it's expected to be > 0,
+ * otherwise makes sure that the scalar it's not reported.
+ */
+let checkScalar = (scalars, scalarName, value, msg) => {
+  if (value > 0) {
+    is(scalars[scalarName], value, msg);
+    return;
+  }
+  ok(!(scalarName in scalars), scalarName + " must not be reported.");
+};
+
+/**
+ * Get a snapshot of the scalars and check them against the provided values.
+ */
+let checkScalars = (maxTabs, tabOpenCount, maxWindows, windowsOpenCount) => {
+  const scalars =
+    Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+
+  // Check the expected values. Scalars that are never set must not be reported.
+  checkScalar(scalars, MAX_CONCURRENT_TABS, maxTabs,
+              "The maximum tab count must match the expected value.");
+  checkScalar(scalars, TAB_EVENT_COUNT, tabOpenCount,
+              "The number of open tab event count must match the expected value.");
+  checkScalar(scalars, MAX_CONCURRENT_WINDOWS, maxWindows,
+              "The maximum window count must match the expected value.");
+  checkScalar(scalars, WINDOW_OPEN_COUNT, windowsOpenCount,
+              "The number of window open event count must match the expected value.");
+};
+
+add_task(function* test_tabsAndWindows() {
+  // Let's reset the counts.
+  Services.telemetry.clearScalars();
+
+  let openedTabs = [];
+  let expectedTabOpenCount = 0;
+  let expectedWinOpenCount = 0;
+  let expectedMaxTabs = 0;
+  let expectedMaxWins = 0;
+
+  // Add a new tab and check that the count is right.
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
+  expectedTabOpenCount = 1;
+  expectedMaxTabs = 2;
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+
+  // Add two new tabs in the same window.
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
+  expectedTabOpenCount += 2;
+  expectedMaxTabs += 2;
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+
+  // Add a new window and then some tabs in it. An empty new windows counts as a tab.
+  let win = yield BrowserTestUtils.openNewBrowserWindow();
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
+  // The new window started with a new tab, so account for it.
+  expectedTabOpenCount += 4;
+  expectedWinOpenCount += 1;
+  expectedMaxWins = 2;
+  expectedMaxTabs += 4;
+
+  // Remove a tab from the first window, the max shouldn't change.
+  yield BrowserTestUtils.removeTab(openedTabs.pop());
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+
+  // Remove all the extra windows and tabs.
+  for (let tab of openedTabs) {
+    yield BrowserTestUtils.removeTab(tab);
+  }
+  yield BrowserTestUtils.closeWindow(win);
+
+  // Make sure all the scalars still have the expected values.
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+});
+
+add_task(function* test_subsessionSplit() {
+  // Let's reset the counts.
+  Services.telemetry.clearScalars();
+
+  // Add a new window (that will have 4 tabs).
+  let win = yield BrowserTestUtils.openNewBrowserWindow();
+  let openedTabs = [];
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+
+  // Check that the scalars have the right values.
+  checkScalars(5 /*maxTabs*/, 4 /*tabOpen*/, 2 /*maxWins*/, 1 /*winOpen*/);
+
+  // Remove a tab.
+  yield BrowserTestUtils.removeTab(openedTabs.pop());
+
+  // Simulate a subsession split by clearing the scalars (via |snapshotScalars|) and
+  // notifying the subsession split topic.
+  Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
+                                     true /* clearScalars*/);
+  Services.obs.notifyObservers(null, TELEMETRY_SUBSESSION_TOPIC, "");
+
+  // After a subsession split, only the MAX_CONCURRENT_* scalars must be available
+  // and have the correct value. No tabs or windows were opened so other scalars
+  // must not be reported.
+  checkScalars(4 /*maxTabs*/, 0 /*tabOpen*/, 2 /*maxWins*/, 0 /*winOpen*/);
+
+  // Remove all the extra windows and tabs.
+  for (let tab of openedTabs) {
+    yield BrowserTestUtils.removeTab(tab);
+  }
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+add_task(function* test_privateMode() {
+  // Let's reset the counts.
+  Services.telemetry.clearScalars();
+
+  // Open a private window and load a website in it.
+  let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true});
+  yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/");
+  yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
+
+  // Check that tab and window count is recorded.
+  const scalars =
+    Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+
+  is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value.");
+  is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value.");
+  is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value.");
+  is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value.");
+
+  // Clean up.
+  yield BrowserTestUtils.closeWindow(privateWin);
+});
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -67,16 +67,17 @@ support-files =
 [browser_styleeditor_fetch-from-cache.js]
 [browser_styleeditor_filesave.js]
 [browser_styleeditor_highlight-selector.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
 [browser_styleeditor_inline_friendly_names.js]
 [browser_styleeditor_loading.js]
+[browser_styleeditor_loading_with_containers.js]
 [browser_styleeditor_media_sidebar.js]
 [browser_styleeditor_media_sidebar_links.js]
 skip-if = e10s && debug # Bug 1252201 - Docshell leak on debug e10s
 [browser_styleeditor_media_sidebar_sourcemaps.js]
 [browser_styleeditor_missing_stylesheet.js]
 [browser_styleeditor_navigate.js]
 [browser_styleeditor_new.js]
 [browser_styleeditor_nostyle.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/browser_styleeditor_loading_with_containers.js
@@ -0,0 +1,63 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the stylesheets can be loaded correctly with containers
+// (bug 1282660).
+
+const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
+const EXPECTED_SHEETS = [
+  {
+    sheetIndex: 0,
+    name: /^simple.css$/,
+    rules: 1,
+    active: true
+  }, {
+    sheetIndex: 1,
+    name: /^<.*>$/,
+    rules: 3,
+    active: false
+  }
+];
+
+add_task(function* () {
+  // Using the personal container.
+  let userContextId = 1;
+  let { tab } = yield* openTabInUserContext(TESTCASE_URI, userContextId);
+  let { ui } = yield openStyleEditor(tab);
+
+  is(ui.editors.length, 2, "The UI contains two style sheets.");
+  checkSheet(ui.editors[0], EXPECTED_SHEETS[0]);
+  checkSheet(ui.editors[1], EXPECTED_SHEETS[1]);
+});
+
+function* openTabInUserContext(uri, userContextId) {
+  // Open the tab in the correct userContextId.
+  let tab = gBrowser.addTab(uri, {userContextId});
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+function checkSheet(editor, expected) {
+  is(editor.styleSheet.styleSheetIndex, expected.sheetIndex,
+    "Style sheet has correct index.");
+
+  let summary = editor.summary;
+  let name = summary.querySelector(".stylesheet-name > label")
+                    .getAttribute("value");
+  ok(expected.name.test(name), "The name '" + name + "' is correct.");
+
+  let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+  is(parseInt(ruleCount, 10), expected.rules, "the rule count is correct");
+
+  is(summary.classList.contains("splitview-active"), expected.active,
+    "The active status for this sheet is correct.");
+}
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -447,20 +447,29 @@ var StyleSheetActor = protocol.ActorClas
       let content = this.ownerNode.textContent;
       this.text = content;
       return promise.resolve(content);
     }
 
     let options = {
       loadFromCache: true,
       policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
-      window: this.window,
       charset: this._getCSSCharset()
     };
 
+    // Bug 1282660 - We use the system principal to load the default internal
+    // stylesheets instead of the content principal since such stylesheets
+    // require system principal to load. At meanwhile, we strip the loadGroup
+    // for preventing the assertion of the userContextId mismatching.
+    // The default internal stylesheets load from the 'resource:' URL.
+    if (!/^resource:\/\//.test(this.href)) {
+      options.window = this.window;
+      options.principal = this.document.nodePrincipal;
+    }
+
     return fetch(this.href, options).then(({ content }) => {
       this.text = content;
       return content;
     });
   },
 
   /**
    * Protocol method to get the original source (actors) for this
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -14231,17 +14231,20 @@ nsDocShell::SetOriginAttributes(const Do
 NS_IMETHODIMP
 nsDocShell::SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)
 {
   if (!aOriginAttributes.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   AutoJSAPI jsapi;
-  jsapi.Init(&aOriginAttributes.toObject());
+  if (!jsapi.Init(&aOriginAttributes.toObject())) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   JSContext* cx = jsapi.cx();
   if (NS_WARN_IF(!cx)) {
     return NS_ERROR_FAILURE;
   }
 
   DocShellOriginAttributes attrs;
   if (!aOriginAttributes.isObject() || !attrs.Init(cx, aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -220,39 +220,39 @@ public:
   // This uses the SafeJSContext (or worker equivalent), and enters the
   // compartment of aGlobalObject.
   // If aGlobalObject or its associated JS global are null then it returns
   // false and use of cx() will cause an assertion.
   //
   // If aGlobalObject represents a web-visible global, errors reported by this
   // AutoJSAPI as it comes off the stack will fire the relevant error events and
   // show up in the corresponding web console.
-  bool Init(nsIGlobalObject* aGlobalObject);
+  MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject);
 
   // This is a helper that grabs the native global associated with aObject and
   // invokes the above Init() with that.
-  bool Init(JSObject* aObject);
+  MOZ_MUST_USE bool Init(JSObject* aObject);
 
   // Unsurprisingly, this uses aCx and enters the compartment of aGlobalObject.
   // If aGlobalObject or its associated JS global are null then it returns
   // false and use of cx() will cause an assertion.
   // If aCx is null it will cause an assertion.
   //
   // If aGlobalObject represents a web-visible global, errors reported by this
   // AutoJSAPI as it comes off the stack will fire the relevant error events and
   // show up in the corresponding web console.
-  bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx);
+  MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx);
 
   // Convenience functions to take an nsPIDOMWindow* or nsGlobalWindow*,
   // when it is more easily available than an nsIGlobalObject.
-  bool Init(nsPIDOMWindowInner* aWindow);
-  bool Init(nsPIDOMWindowInner* aWindow, JSContext* aCx);
+  MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow);
+  MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow, JSContext* aCx);
 
-  bool Init(nsGlobalWindow* aWindow);
-  bool Init(nsGlobalWindow* aWindow, JSContext* aCx);
+  MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow);
+  MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow, JSContext* aCx);
 
   JSContext* cx() const {
     MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI");
     MOZ_ASSERT(IsStackTop());
     return mCx;
   }
 
 #ifdef DEBUG
@@ -268,25 +268,25 @@ public:
   };
 
   // Transfers ownership of the current exception from the JS engine to the
   // caller. Callers must ensure that HasException() is true, and that cx()
   // is in a non-null compartment.
   //
   // Note that this fails if and only if we OOM while wrapping the exception
   // into the current compartment.
-  bool StealException(JS::MutableHandle<JS::Value> aVal);
+  MOZ_MUST_USE bool StealException(JS::MutableHandle<JS::Value> aVal);
 
   // Peek the current exception from the JS engine, without stealing it.
   // Callers must ensure that HasException() is true, and that cx() is in a
   // non-null compartment.
   //
   // Note that this fails if and only if we OOM while wrapping the exception
   // into the current compartment.
-  bool PeekException(JS::MutableHandle<JS::Value> aVal);
+  MOZ_MUST_USE bool PeekException(JS::MutableHandle<JS::Value> aVal);
 
   void ClearException() {
     MOZ_ASSERT(IsStackTop());
     JS_ClearPendingException(cx());
   }
 
 protected:
   // Protected constructor for subclasses.  This constructor initialises the
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -322,17 +322,19 @@ nsDOMClassInfo::GetNative(nsIXPConnectWr
 {
   return wrapper ? wrapper->Native() : static_cast<nsISupports*>(js::GetObjectPrivate(obj));
 }
 
 nsresult
 nsDOMClassInfo::DefineStaticJSVals()
 {
   AutoJSAPI jsapi;
-  jsapi.Init(xpc::UnprivilegedJunkScope());
+  if (!jsapi.Init(xpc::UnprivilegedJunkScope())) {
+    return NS_ERROR_UNEXPECTED;
+  }
   JSContext* cx = jsapi.cx();
 
 #define SET_JSID_TO_STRING(_id, _cx, _str)                              \
   if (JSString *str = ::JS_AtomizeAndPinString(_cx, _str))                             \
       _id = INTERNED_STRING_TO_JSID(_cx, str);                                \
   else                                                                        \
       return NS_ERROR_OUT_OF_MEMORY;
 
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -2,18 +2,21 @@
 /* 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 "nsHostObjectProtocolHandler.h"
 
 #include "DOMMediaStream.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsClassHashtable.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
@@ -33,19 +36,89 @@ struct DataInfo
   // mObject is expected to be an BlobImpl, DOMMediaStream, or MediaSource
   nsCOMPtr<nsISupports> mObject;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mStack;
 };
 
 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
 
+static DataInfo*
+GetDataInfo(const nsACString& aUri)
+{
+  if (!gDataTable) {
+    return nullptr;
+  }
+
+  DataInfo* res;
+
+  // Let's remove any fragment and query from this URI.
+  int32_t hasFragmentPos = aUri.FindChar('#');
+  int32_t hasQueryPos = aUri.FindChar('?');
+
+  int32_t pos = -1;
+  if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
+    pos = std::min(hasFragmentPos, hasQueryPos);
+  } else if (hasFragmentPos >= 0) {
+    pos = hasFragmentPos;
+  } else {
+    pos = hasQueryPos;
+  }
+
+  if (pos < 0) {
+    gDataTable->Get(aUri, &res);
+  } else {
+    gDataTable->Get(StringHead(aUri, pos), &res);
+  }
+
+  return res;
+}
+
 // Memory reporting for the hash table.
 namespace mozilla {
 
+void
+BroadcastBlobURLRegistration(const nsACString& aURI,
+                             BlobImpl* aBlobImpl,
+                             nsIPrincipal* aPrincipal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aBlobImpl);
+
+  if (XRE_IsParentProcess()) {
+    ContentParent::BroadcastBlobURLRegistration(aURI, aBlobImpl,
+                                                aPrincipal);
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  BlobChild* actor = cc->GetOrCreateActorForBlobImpl(aBlobImpl);
+  if (NS_WARN_IF(!actor)) {
+    return;
+  }
+
+  NS_WARN_IF(!cc->SendStoreAndBroadcastBlobURLRegistration(nsCString(aURI), actor,
+                                                           IPC::Principal(aPrincipal)));
+}
+
+void
+BroadcastBlobURLUnregistration(const nsACString& aURI, DataInfo* aInfo)
+{
+  MOZ_ASSERT(aInfo);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_IsParentProcess()) {
+    ContentParent::BroadcastBlobURLUnregistration(aURI);
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  NS_WARN_IF(!cc->SendUnstoreAndBroadcastBlobURLUnregistration(nsCString(aURI)));
+}
+
 class HostObjectURLsReporter final : public nsIMemoryReporter
 {
   ~HostObjectURLsReporter() {}
 
  public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
@@ -316,57 +389,134 @@ nsHostObjectProtocolHandler::nsHostObjec
 
 nsresult
 nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aScheme,
                                           nsISupports* aObject,
                                           nsIPrincipal* aPrincipal,
                                           nsACString& aUri)
 {
 #ifdef DEBUG
-  nsCOMPtr<BlobImpl> blobImpl(do_QueryInterface(aObject));
-  nsCOMPtr<MediaSource> mediaSource(do_QueryInterface(aObject));
-  nsCOMPtr<DOMMediaStream> mediaStream(do_QueryInterface(aObject));
+  {
+    nsCOMPtr<BlobImpl> blobImpl(do_QueryInterface(aObject));
+    nsCOMPtr<MediaSource> mediaSource(do_QueryInterface(aObject));
+    nsCOMPtr<DOMMediaStream> mediaStream(do_QueryInterface(aObject));
 
-  // We support only these types.
-  MOZ_ASSERT(blobImpl || mediaSource || mediaStream);
+    // We support only these types.
+    MOZ_ASSERT(blobImpl || mediaSource || mediaStream);
+  }
 #endif
 
   Init();
 
   nsresult rv = GenerateURIString(aScheme, aPrincipal, aUri);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = AddDataEntry(aUri, aObject, aPrincipal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(aObject);
+  if (blobImpl) {
+    BroadcastBlobURLRegistration(aUri, blobImpl, aPrincipal);
+  }
+
+  return NS_OK;
+}
+
+/* static */ nsresult
+nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aURI,
+                                          nsISupports* aObject,
+                                          nsIPrincipal* aPrincipal)
+{
   if (!gDataTable) {
     gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
   }
 
   DataInfo* info = new DataInfo;
 
   info->mObject = aObject;
   info->mPrincipal = aPrincipal;
   mozilla::BlobURLsReporter::GetJSStackForBlob(info);
 
-  gDataTable->Put(aUri, info);
+  gDataTable->Put(aURI, info);
   return NS_OK;
 }
 
+/* static */ bool
+nsHostObjectProtocolHandler::GetAllBlobURLEntries(nsTArray<BlobURLRegistrationData>& aRegistrations,
+                                                  ContentParent* aCP)
+{
+  MOZ_ASSERT(aCP);
+
+  if (!gDataTable) {
+    return true;
+  }
+
+  for (auto iter = gDataTable->ConstIter(); !iter.Done(); iter.Next()) {
+    DataInfo* info = iter.UserData();
+    MOZ_ASSERT(info);
+
+    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
+    if (!blobImpl) {
+      continue;
+    }
+
+    PBlobParent* blobParent = aCP->GetOrCreateActorForBlobImpl(blobImpl);
+    if (!blobParent) {
+      return false;
+    }
+
+    aRegistrations.AppendElement(
+      BlobURLRegistrationData(nsCString(iter.Key()), blobParent, nullptr,
+                              IPC::Principal(info->mPrincipal)));
+  }
+
+  return true;
+}
+
 void
-nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri)
+nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri,
+                                             bool aBroadcastToOtherProcesses)
 {
   if (!gDataTable) {
     return;
   }
 
+  DataInfo* info = GetDataInfo(aUri);
+  if (!info) {
+    return;
+  }
+
+  if (aBroadcastToOtherProcesses) {
+    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
+    if (blobImpl) {
+      BroadcastBlobURLUnregistration(aUri, info);
+    }
+  }
+
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
 
+void
+nsHostObjectProtocolHandler::RemoveDataEntries()
+{
+  MOZ_ASSERT(XRE_IsContentProcess());
+
+  if (!gDataTable) {
+    return;
+  }
+
+  gDataTable->Clear();
+  delete gDataTable;
+  gDataTable = nullptr;
+}
+
 nsresult
 nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
                                                nsIPrincipal* aPrincipal,
                                                nsACString& aUri)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
@@ -393,47 +543,16 @@ nsHostObjectProtocolHandler::GenerateURI
     aUri.Append('/');
   }
 
   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
 
   return NS_OK;
 }
 
-static DataInfo*
-GetDataInfo(const nsACString& aUri)
-{
-  if (!gDataTable) {
-    return nullptr;
-  }
-
-  DataInfo* res;
-
-  // Let's remove any fragment and query from this URI.
-  int32_t hasFragmentPos = aUri.FindChar('#');
-  int32_t hasQueryPos = aUri.FindChar('?');
-
-  int32_t pos = -1;
-  if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
-    pos = std::min(hasFragmentPos, hasQueryPos);
-  } else if (hasFragmentPos >= 0) {
-    pos = hasFragmentPos;
-  } else {
-    pos = hasQueryPos;
-  }
-
-  if (pos < 0) {
-    gDataTable->Get(aUri, &res);
-  } else {
-    gDataTable->Get(StringHead(aUri, pos), &res);
-  }
-
-  return res;
-}
-
 nsIPrincipal*
 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
 {
   if (!gDataTable) {
     return nullptr;
   }
 
   DataInfo* res = GetDataInfo(aUri);
@@ -504,18 +623,23 @@ nsHostObjectProtocolHandler::NewURI(cons
                                     nsIURI *aBaseURI,
                                     nsIURI **aResult)
 {
   *aResult = nullptr;
   nsresult rv;
 
   DataInfo* info = GetDataInfo(aSpec);
 
-  RefPtr<nsHostObjectURI> uri =
-    new nsHostObjectURI(info ? info->mPrincipal.get() : nullptr);
+  RefPtr<nsHostObjectURI> uri;
+  if (info) {
+    nsCOMPtr<BlobImpl> blob = do_QueryInterface(info->mObject);
+    uri = new nsHostObjectURI(info->mPrincipal, blob);
+  } else {
+    uri = new nsHostObjectURI(nullptr, nullptr);
+  }
 
   rv = uri->SetSpec(aSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_TryToSetImmutable(uri);
   uri.forget(aResult);
 
   return NS_OK;
@@ -523,67 +647,71 @@ nsHostObjectProtocolHandler::NewURI(cons
 
 NS_IMETHODIMP
 nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
                                          nsILoadInfo* aLoadInfo,
                                          nsIChannel** result)
 {
   *result = nullptr;
 
+  nsCOMPtr<nsIURIWithBlobImpl> uriBlobImpl = do_QueryInterface(uri);
+  if (!uriBlobImpl) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  nsCOMPtr<nsISupports> tmp;
+  MOZ_ALWAYS_SUCCEEDS(uriBlobImpl->GetBlobImpl(getter_AddRefs(tmp)));
+  nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(tmp);
+  if (!blobImpl) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+#ifdef DEBUG
   nsCString spec;
   uri->GetSpec(spec);
 
   DataInfo* info = GetDataInfo(spec);
 
-  if (!info) {
-    return NS_ERROR_DOM_BAD_URI;
-  }
-
-  nsCOMPtr<BlobImpl> blob = do_QueryInterface(info->mObject);
-  if (!blob) {
-    return NS_ERROR_DOM_BAD_URI;
-  }
-
-#ifdef DEBUG
-  {
+  // Info can be null, in case this blob URL has been revoked already.
+  if (info) {
     nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
     nsCOMPtr<nsIPrincipal> principal;
     uriPrinc->GetPrincipal(getter_AddRefs(principal));
     NS_ASSERTION(info->mPrincipal == principal, "Wrong principal!");
   }
 #endif
 
   ErrorResult rv;
   nsCOMPtr<nsIInputStream> stream;
-  blob->GetInternalStream(getter_AddRefs(stream), rv);
+  blobImpl->GetInternalStream(getter_AddRefs(stream), rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   nsAutoString contentType;
-  blob->GetType(contentType);
+  blobImpl->GetType(contentType);
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
                                         uri,
                                         stream,
                                         NS_ConvertUTF16toUTF8(contentType),
                                         EmptyCString(), // aContentCharset
                                         aLoadInfo);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
-  if (blob->IsFile()) {
+  if (blobImpl->IsFile()) {
     nsString filename;
-    blob->GetName(filename);
+    blobImpl->GetName(filename);
     channel->SetContentDispositionFilename(filename);
   }
 
-  uint64_t size = blob->GetSize(rv);
+  uint64_t size = blobImpl->GetSize(rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   channel->SetOriginalURI(uri);
   channel->SetContentType(NS_ConvertUTF16toUTF8(contentType));
   channel->SetContentLength(size);
 
@@ -790,8 +918,9 @@ static const mozilla::Module::ContractID
 
 static const mozilla::Module kHostObjectProtocolHandlerModule = {
   mozilla::Module::kVersion,
   kHostObjectProtocolHandlerCIDs,
   kHostObjectProtocolHandlerContracts
 };
 
 NSMODULE_DEFN(HostObjectProtocolHandler) = &kHostObjectProtocolHandlerModule;
+
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -7,29 +7,32 @@
 #ifndef nsHostObjectProtocolHandler_h
 #define nsHostObjectProtocolHandler_h
 
 #include "mozilla/Attributes.h"
 #include "nsIProtocolHandler.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
+#include "nsTArray.h"
 
 #define BLOBURI_SCHEME "blob"
 #define MEDIASTREAMURI_SCHEME "mediastream"
 #define MEDIASOURCEURI_SCHEME "mediasource"
 #define FONTTABLEURI_SCHEME "moz-fonttable"
 #define RTSPURI_SCHEME "rtsp"
 
 class nsIPrincipal;
 
 namespace mozilla {
 class DOMMediaStream;
 namespace dom {
 class BlobImpl;
+class BlobURLRegistrationData;
+class ContentParent;
 class MediaSource;
 } // namespace dom
 } // namespace mozilla
 
 class nsHostObjectProtocolHandler : public nsIProtocolHandler
 {
 public:
   nsHostObjectProtocolHandler();
@@ -50,20 +53,33 @@ public:
                                     nsACString &aUri);
 
   // Methods for managing uri->object mapping
   // AddDataEntry creates the URI with the given scheme and returns it in aUri
   static nsresult AddDataEntry(const nsACString& aScheme,
                                nsISupports* aObject,
                                nsIPrincipal* aPrincipal,
                                nsACString& aUri);
-  static void RemoveDataEntry(const nsACString& aUri);
+  static void RemoveDataEntry(const nsACString& aUri,
+                              bool aBroadcastToOTherProcesses = true);
+
+  // This is for IPC only.
+  static void RemoveDataEntries();
+
   static nsIPrincipal* GetDataEntryPrincipal(const nsACString& aUri);
   static void Traverse(const nsACString& aUri, nsCycleCollectionTraversalCallback& aCallback);
 
+  // IPC or internal use only
+  static nsresult AddDataEntry(const nsACString& aURI,
+                               nsISupports* aObject,
+                               nsIPrincipal* aPrincipal);
+  static bool
+  GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
+                       mozilla::dom::ContentParent* aCP);
+
 protected:
   virtual ~nsHostObjectProtocolHandler() {}
 
 private:
   static void Init(void);
 };
 
 class nsBlobProtocolHandler : public nsHostObjectProtocolHandler
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -16,29 +16,40 @@ static NS_DEFINE_CID(kHOSTOBJECTURICID, 
 
 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
 
 NS_IMPL_ADDREF_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 NS_IMPL_RELEASE_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 
 NS_INTERFACE_MAP_BEGIN(nsHostObjectURI)
+  NS_INTERFACE_MAP_ENTRY(nsIURIWithBlobImpl)
   NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
   if (aIID.Equals(kHOSTOBJECTURICID))
     foundInterface = static_cast<nsIURI*>(this);
   else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
     // Need to return explicitly here, because if we just set foundInterface
     // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
     // nsSimplURI::QueryInterface and finding something for this CID.
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
   }
   else
 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI)
 
+// nsIURIWithBlobImpl methods:
+
+NS_IMETHODIMP
+nsHostObjectURI::GetBlobImpl(nsISupports** aBlobImpl)
+{
+  RefPtr<BlobImpl> blobImpl(mBlobImpl);
+  blobImpl.forget(aBlobImpl);
+  return NS_OK;
+}
+
 // nsIURIWithPrincipal methods:
 
 NS_IMETHODIMP
 nsHostObjectURI::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   NS_IF_ADDREF(*aPrincipal = mPrincipal);
 
   return NS_OK;
@@ -121,16 +132,20 @@ nsHostObjectURI::Deserialize(const mozil
       return false;
   }
 
   const HostObjectURIParams& hostParams = aParams.get_HostObjectURIParams();
 
   if (!mozilla::net::nsSimpleURI::Deserialize(hostParams.simpleParams())) {
     return false;
   }
+
+  // XXXbaku: when we will have shared blobURL maps, we can populate mBlobImpl
+  // here asll well.
+
   if (hostParams.principal().type() == OptionalPrincipalInfo::Tvoid_t) {
     return true;
   }
 
   mPrincipal = PrincipalInfoToPrincipal(hostParams.principal().get_PrincipalInfo());
   return mPrincipal != nullptr;
 }
 
@@ -157,16 +172,17 @@ nsHostObjectURI::CloneInternal(mozilla::
   RefPtr<nsHostObjectURI> uriCheck;
   rv = simpleClone->QueryInterface(kHOSTOBJECTURICID, getter_AddRefs(uriCheck));
   MOZ_ASSERT(NS_SUCCEEDED(rv) && uriCheck);
 #endif
 
   nsHostObjectURI* u = static_cast<nsHostObjectURI*>(simpleClone.get());
 
   u->mPrincipal = mPrincipal;
+  u->mBlobImpl = mBlobImpl;
 
   simpleClone.forget(aClone);
   return NS_OK;
 }
 
 /* virtual */ nsresult
 nsHostObjectURI::EqualsInternal(nsIURI* aOther,
                                 mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
@@ -185,17 +201,20 @@ nsHostObjectURI::EqualsInternal(nsIURI* 
   }
 
   // Compare the member data that our base class knows about.
   if (!mozilla::net::nsSimpleURI::EqualsInternal(otherUri, aRefHandlingMode)) {
     *aResult = false;
     return NS_OK;
   }
 
-  // Compare the piece of additional member data that we add to base class.
+  // Compare the piece of additional member data that we add to base class,
+  // but we cannot compare BlobImpl. This should not be a problem, because we
+  // don't support changing the underlying mBlobImpl.
+
   if (mPrincipal && otherUri->mPrincipal) {
     // Both of us have mPrincipals. Compare them.
     return mPrincipal->Equals(otherUri->mPrincipal, aResult);
   }
   // else, at least one of us lacks a principal; only equal if *both* lack it.
   *aResult = (!mPrincipal && !otherUri->mPrincipal);
   return NS_OK;
 }
--- a/dom/base/nsHostObjectURI.h
+++ b/dom/base/nsHostObjectURI.h
@@ -3,41 +3,48 @@
 /* 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 nsHostObjectURI_h
 #define nsHostObjectURI_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/File.h"
 #include "nsCOMPtr.h"
 #include "nsIClassInfo.h"
 #include "nsIPrincipal.h"
 #include "nsISerializable.h"
+#include "nsIURIWithBlobImpl.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsSimpleURI.h"
 #include "nsIIPCSerializableURI.h"
 
 /**
  * These URIs refer to host objects: Blobs, with scheme "blob",
  * MediaStreams, with scheme "mediastream", and MediaSources, with scheme
  * "mediasource".
  */
-class nsHostObjectURI : public mozilla::net::nsSimpleURI,
-                        public nsIURIWithPrincipal
+class nsHostObjectURI : public mozilla::net::nsSimpleURI
+                      , public nsIURIWithPrincipal
+                      , public nsIURIWithBlobImpl
 {
 public:
-  explicit nsHostObjectURI(nsIPrincipal* aPrincipal) :
-      mozilla::net::nsSimpleURI(), mPrincipal(aPrincipal)
+  nsHostObjectURI(nsIPrincipal* aPrincipal,
+                  mozilla::dom::BlobImpl* aBlobImpl)
+    : mozilla::net::nsSimpleURI()
+    , mPrincipal(aPrincipal)
+    , mBlobImpl(aBlobImpl)
   {}
 
   // For use only from deserialization
   nsHostObjectURI() : mozilla::net::nsSimpleURI() {}
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIURIWITHBLOBIMPL
   NS_DECL_NSIURIWITHPRINCIPAL
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
   NS_DECL_NSIIPCSERIALIZABLEURI
 
   NS_IMETHOD SetScheme(const nsACString &aProtocol) override;
 
   // Override CloneInternal() and EqualsInternal()
@@ -47,16 +54,17 @@ public:
                                   RefHandlingEnum aRefHandlingMode,
                                   bool* aResult) override;
 
   // Override StartClone to hand back a nsHostObjectURI
   virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum /* unused */) override
   { return new nsHostObjectURI(); }
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  RefPtr<mozilla::dom::BlobImpl> mBlobImpl;
 
 protected:
   virtual ~nsHostObjectURI() {}
 };
 
 #define NS_HOSTOBJECTURI_CID \
 { 0xf5475c51, 0x59a7, 0x4757, \
   { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -186,64 +186,103 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
     //////
 
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
     const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
 
     const auto pi = dstDUI->ToPacking();
-    const auto dstFormat = FormatForPackingInfo(pi);
 
     const auto dstBPP = webgl::BytesPerPixel(pi);
     const auto dstWidthBytes = CheckedUint32(dstBPP) * mWidth;
     const auto dstRowLengthBytes = CheckedUint32(dstBPP) * mRowLength;
 
     const auto dstAlignment = mAlignment;
     const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
 
     //////
 
     const auto dstTotalRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight;
+    const auto dstUsedSizeExceptLastRow = (dstTotalRows - 1) * dstStride;
 
-    const auto dstSize = skipBytes + (dstTotalRows - 1) * dstStride + dstWidthBytes;
+    const auto dstSize = skipBytes + dstUsedSizeExceptLastRow + dstWidthBytes;
     if (!dstSize.isValid()) {
         webgl->ErrorOutOfMemory("%s: Invalid dstSize calculation during conversion.",
                                 funcName);
         return false;
     }
 
     //////
 
-    bool needsConvert = (srcOrigin != dstOrigin ||
-                         srcFormat != dstFormat ||
-                         srcStride != dstStride.value());
+    const auto dstFormat = FormatForPackingInfo(pi);
 
-    if (UnpackFormatHasAlpha(dstDUI->unpackFormat)) {
-        needsConvert |= (mIsSrcPremult != isDstPremult);
+    bool premultMatches = (mIsSrcPremult == isDstPremult);
+    if (!UnpackFormatHasAlpha(dstDUI->unpackFormat)) {
+        premultMatches = true;
     }
 
-    if (!needsConvert)
-        return true;
+    const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches);
+    const bool originsMatch = (srcOrigin == dstOrigin);
+
+    MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP);
 
-    ////////////
-    // Ugh, ok, fine!
-
-    webgl->GenerateWarning("%s: Incurred CPU data conversion, which is slow.",
-                           funcName);
+    if (!needsPixelConversion &&
+        originsMatch &&
+        srcStride == dstStride.value())
+    {
+        // No conversion needed!
+        return true;
+    }
 
     //////
+    // We need some sort of conversion, so create the dest buffer.
 
     *out_anchoredBuffer = calloc(1, dstSize.value());
-    if (!out_anchoredBuffer->get()) {
+    *out_bytes = out_anchoredBuffer->get();
+    if (!*out_bytes) {
         webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.",
                                 funcName);
         return false;
     }
-    const auto dstBegin = (uint8_t*)out_anchoredBuffer->get() + skipBytes;
+    const auto dstBegin = (uint8_t*)(*out_bytes) + skipBytes;
+
+    //////
+    // Row conversion
+
+    if (!needsPixelConversion) {
+        webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.",
+                               funcName);
+
+        const uint8_t* srcRow = srcBegin;
+        uint8_t* dstRow = dstBegin;
+        const auto widthBytes = dstWidthBytes.value();
+        ptrdiff_t dstCopyStride = dstStride.value();
+
+        if (!originsMatch) {
+            dstRow += dstUsedSizeExceptLastRow.value();
+            dstCopyStride = -dstCopyStride;
+        }
+
+        for (uint32_t i = 0; i < dstTotalRows.value(); i++) {
+            memcpy(dstRow, srcRow, widthBytes);
+            srcRow += srcStride;
+            dstRow += dstCopyStride;
+        }
+        return true;
+    }
+
+    ////////////
+    // Pixel conversion.
+
+    MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
+    MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
+
+    webgl->GenerateWarning("%s: Incurred CPU pixel conversion, which is very slow.",
+                           funcName);
 
     //////
 
     // And go!:
     bool wasTrivial;
     if (!ConvertImage(mWidth, dstTotalRows.value(),
                       srcBegin, srcStride, srcOrigin, srcFormat, mIsSrcPremult,
                       dstBegin, dstStride.value(), dstOrigin, dstFormat, isDstPremult,
@@ -254,17 +293,16 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
     }
 
     if (!wasTrivial) {
         webgl->GenerateWarning("%s: Chosen format/type incurred an expensive reformat:"
                                " 0x%04x/0x%04x",
                                funcName, dstDUI->unpackFormat, dstDUI->unpackType);
     }
 
-    *out_bytes = out_anchoredBuffer->get();
     return true;
 }
 
 static GLenum
 DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
                 const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
                 GLsizei width, GLsizei height, GLsizei depth, const void* data)
 {
@@ -292,25 +330,26 @@ bool
 TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, GLenum* const out_error) const
 {
     WebGLContext* webgl = tex->mContext;
 
     const auto pi = dui->ToPacking();
-    const auto format = FormatForPackingInfo(pi);
 
     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
     const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
     if (!rowStride.isValid()) {
         MOZ_CRASH("Should be checked earlier.");
     }
 
+    const auto format = FormatForPackingInfo(pi);
+
     const void* uploadBytes;
     UniqueBuffer tempBuffer;
     if (!ConvertIfNeeded(webgl, funcName, mBytes, rowStride.value(), bytesPerPixel,
                          format, dui, &uploadBytes, &tempBuffer))
     {
         return false;
     }
 
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -50,17 +50,26 @@ private:
     template<typename BufferT>
     void GetBufferSubDataT(GLenum target, GLintptr offset, const BufferT& data);
 
 public:
     void GetBufferSubData(GLenum target, GLintptr offset,
                           const dom::Nullable<dom::ArrayBuffer>& maybeData);
     void GetBufferSubData(GLenum target, GLintptr offset,
                           const dom::SharedArrayBuffer& data);
+    void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                    GLenum type, WebGLsizeiptr offset, ErrorResult& out_error);
 
+    void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+                    GLenum format, GLenum type,
+                    const dom::Nullable<dom::ArrayBufferView>& pixels,
+                    ErrorResult& out_error)
+    {
+        WebGLContext::ReadPixels(x, y, width, height, format, type, pixels, out_error);
+    }
 
     // -------------------------------------------------------------------------
     // Framebuffer objects - WebGL2ContextFramebuffers.cpp
 
     void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                          GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                          GLbitfield mask, GLenum filter);
     void FramebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer);
@@ -137,121 +146,78 @@ public:
     GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
     void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
     // GL 3.0 & ES 3.0
-    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);
+    void Uniform1ui(WebGLUniformLocation* loc, GLuint v0);
+    void Uniform2ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1);
+    void Uniform3ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2);
+    void Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2,
+                    GLuint v3);
+
+    ////////////////
 
-private:
-    void Uniform1uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
-    void Uniform2uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
-    void Uniform3uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
-    void Uniform4uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
+protected:
+    typedef Arr<GLuint, dom::Uint32Array> UintArr;
+
+    void UniformNuiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                     const UintArr& arr);
+
+    //////
 
 public:
-    void Uniform1uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform1uiv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform2uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform2uiv_base(loc, arr.Length(), arr.Elements());
+    template<typename T>
+    void Uniform1uiv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNuiv("uniform1uiv", 1, loc, UintArr(arr));
     }
-    void Uniform3uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform3uiv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform4uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform4uiv_base(loc, arr.Length(), arr.Elements());
+    template<typename T>
+    void Uniform2uiv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNuiv("uniform2uiv", 2, loc, UintArr(arr));
     }
-    void Uniform1uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform1uiv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform2uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform2uiv_base(loc, arr.Length(), arr.Data());
+    template<typename T>
+    void Uniform3uiv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNuiv("uniform3uiv", 3, loc, UintArr(arr));
     }
-    void Uniform3uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform3uiv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform4uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform4uiv_base(loc, arr.Length(), arr.Data());
+    template<typename T>
+    void Uniform4uiv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNuiv("uniform4uiv", 4, loc, UintArr(arr));
     }
 
-private:
-    void UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
-    void UniformMatrix3x2fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
-    void UniformMatrix2x4fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
-    void UniformMatrix4x2fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
-    void UniformMatrix3x4fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
-    void UniformMatrix4x3fv_base(WebGLUniformLocation* loc, bool transpose,
-                                 size_t arrayLength, const GLfloat* data);
+    //////
 
-public:
-    // GL 2.1 & ES 3.0
-    void UniformMatrix2x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix2x3fv_base(loc, transpose, value.Length(), value.Elements());
+    template<typename T>
+    void UniformMatrix2x3fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix2x3fv", 2, 3, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix2x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix2x4fv_base(loc, transpose, value.Length(), value.Elements());
+    template<typename T>
+    void UniformMatrix2x4fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix2x4fv", 2, 4, loc, transpose, FloatArr(arr));
+    }
+    template<typename T>
+    void UniformMatrix3x2fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix3x2fv", 3, 2, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix3x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix3x2fv_base(loc, transpose, value.Length(), value.Elements());
-    }
-    void UniformMatrix3x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix3x4fv_base(loc, transpose, value.Length(), value.Elements());
+    template<typename T>
+    void UniformMatrix3x4fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix3x4fv", 3, 4, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix4x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix4x2fv_base(loc, transpose, value.Length(), value.Elements());
+    template<typename T>
+    void UniformMatrix4x2fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix4x2fv", 4, 2, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix4x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
-        UniformMatrix4x3fv_base(loc, transpose, value.Length(), value.Elements());
+    template<typename T>
+    void UniformMatrix4x3fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix4x3fv", 4, 3, loc, transpose, FloatArr(arr));
     }
 
-    void UniformMatrix2x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix2x3fv_base(loc, transpose, value.Length(), value.Data());
-    }
-
-    void UniformMatrix2x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix2x4fv_base(loc, transpose, value.Length(), value.Data());
-    }
-
-    void UniformMatrix3x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix3x2fv_base(loc, transpose, value.Length(), value.Data());
-    }
-
-    void UniformMatrix3x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix3x4fv_base(loc, transpose, value.Length(), value.Data());
-    }
-
-    void UniformMatrix4x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix4x2fv_base(loc, transpose, value.Length(), value.Data());
-    }
-
-    void UniformMatrix4x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
-        value.ComputeLengthAndData();
-        UniformMatrix4x3fv_base(loc, transpose, value.Length(), value.Data());
-    }
+    ////////////////
 
 private:
     void VertexAttribI4iv(GLuint index, size_t length, const GLint* v);
     void VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v);
 
 public:
     // GL 3.0 & ES 3.0
     void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -7,36 +7,36 @@
 
 #include "GLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLTransformFeedback.h"
 
 namespace mozilla {
 
 bool
-WebGL2Context::ValidateBufferTarget(GLenum target, const char* info)
+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_TRANSFORM_FEEDBACK_BUFFER:
     case LOCAL_GL_UNIFORM_BUFFER:
         return true;
 
-    case LOCAL_GL_PIXEL_PACK_BUFFER:
     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
         ErrorInvalidOperation("%s: PBOs are still under development, and are currently"
                               " disabled.",
-                              info);
+                              funcName);
         return false;
 
     default:
-        ErrorInvalidEnumInfo(info, target);
+        ErrorInvalidEnumInfo(funcName, target);
         return false;
     }
 }
 
 bool
 WebGL2Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
 {
     switch (target) {
@@ -75,66 +75,68 @@ WebGL2Context::ValidateBufferUsageEnum(G
 // -------------------------------------------------------------------------
 // Buffer objects
 
 void
 WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
                                  GLintptr readOffset, GLintptr writeOffset,
                                  GLsizeiptr size)
 {
+    const char funcName[] = "copyBufferSubData";
     if (IsContextLost())
         return;
 
-    if (!ValidateBufferTarget(readTarget, "copyBufferSubData") ||
-        !ValidateBufferTarget(writeTarget, "copyBufferSubData"))
+    if (!ValidateBufferTarget(readTarget, funcName) ||
+        !ValidateBufferTarget(writeTarget, funcName))
     {
         return;
     }
 
     const WebGLRefPtr<WebGLBuffer>& readBufferSlot = GetBufferSlotByTarget(readTarget);
     const WebGLRefPtr<WebGLBuffer>& writeBufferSlot = GetBufferSlotByTarget(writeTarget);
     if (!readBufferSlot || !writeBufferSlot)
         return;
 
     const WebGLBuffer* readBuffer = readBufferSlot.get();
-    if (!readBuffer)
-        return ErrorInvalidOperation("copyBufferSubData: No buffer bound to readTarget");
-
-    WebGLBuffer* writeBuffer = writeBufferSlot.get();
-    if (!writeBuffer)
-        return ErrorInvalidOperation("copyBufferSubData: No buffer bound to writeTarget");
-
-    if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(),
-        "copyBufferSubData"))
-    {
+    if (!readBuffer) {
+        ErrorInvalidOperation("%s: No buffer bound to readTarget.", funcName);
         return;
     }
 
-    if (!ValidateDataOffsetSize(writeOffset, size, writeBuffer->ByteLength(),
-        "copyBufferSubData"))
-    {
+    WebGLBuffer* writeBuffer = writeBufferSlot.get();
+    if (!writeBuffer) {
+        ErrorInvalidOperation("%s: No buffer bound to writeTarget.", funcName);
         return;
     }
 
+    if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(), funcName))
+        return;
+
+    if (!ValidateDataOffsetSize(writeOffset, size, writeBuffer->ByteLength(), funcName))
+        return;
+
     if (readTarget == writeTarget &&
-        !ValidateDataRanges(readOffset, writeOffset, size, "copyBufferSubData"))
+        !ValidateDataRanges(readOffset, writeOffset, size, funcName))
     {
         return;
     }
 
     WebGLBuffer::Kind readType = readBuffer->Content();
     WebGLBuffer::Kind writeType = writeBuffer->Content();
 
     if (readType != WebGLBuffer::Kind::Undefined &&
         writeType != WebGLBuffer::Kind::Undefined &&
         writeType != readType)
     {
-        ErrorInvalidOperation("copyBufferSubData: Can't copy %s data to %s data",
-                              (readType == WebGLBuffer::Kind::OtherData) ? "other" : "element",
-                              (writeType == WebGLBuffer::Kind::OtherData) ? "other" : "element");
+        ErrorInvalidOperation("%s: Can't copy %s data to %s data.",
+                              funcName,
+                              (readType == WebGLBuffer::Kind::OtherData) ? "other"
+                                                                         : "element",
+                              (writeType == WebGLBuffer::Kind::OtherData) ? "other"
+                                                                          : "element");
         return;
     }
 
     WebGLContextUnchecked::CopyBufferSubData(readTarget, writeTarget, readOffset,
                                              writeOffset, size);
 
     if (writeType == WebGLBuffer::Kind::Undefined) {
         writeBuffer->BindTo(
@@ -145,64 +147,71 @@ WebGL2Context::CopyBufferSubData(GLenum 
 
 // BufferT may be one of
 // const dom::ArrayBuffer&
 // const dom::SharedArrayBuffer&
 template<typename BufferT>
 void
 WebGL2Context::GetBufferSubDataT(GLenum target, GLintptr offset, const BufferT& data)
 {
+    const char funcName[] = "getBufferSubData";
     if (IsContextLost())
         return;
 
     // For the WebGLBuffer bound to the passed target, read
     // returnedData.byteLength bytes from the buffer starting at byte
     // offset offset and write them to returnedData.
 
     // If zero is bound to target, an INVALID_OPERATION error is
     // generated.
-    if (!ValidateBufferTarget(target, "getBufferSubData"))
+    if (!ValidateBufferTarget(target, funcName))
         return;
 
     // If offset is less than zero, an INVALID_VALUE error is
     // generated.
-    if (offset < 0)
-        return ErrorInvalidValue("getBufferSubData: negative offset");
+    if (offset < 0) {
+        ErrorInvalidValue("%s: Offset must be non-negative.", funcName);
+        return;
+    }
 
     WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
     WebGLBuffer* boundBuffer = bufferSlot.get();
-    if (!boundBuffer)
-        return ErrorInvalidOperation("getBufferSubData: no buffer bound");
+    if (!boundBuffer) {
+        ErrorInvalidOperation("%s: No buffer bound.", funcName);
+        return;
+    }
 
     // If offset + returnedData.byteLength would extend beyond the end
     // of the buffer an INVALID_VALUE error is generated.
     data.ComputeLengthAndData();
 
     CheckedInt<WebGLsizeiptr> neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + data.LengthAllowShared();
     if (!neededByteLength.isValid()) {
-        ErrorInvalidValue("getBufferSubData: Integer overflow computing the needed"
-                          " byte length.");
+        ErrorInvalidValue("%s: Integer overflow computing the needed byte length.",
+                          funcName);
         return;
     }
 
     if (neededByteLength.value() > boundBuffer->ByteLength()) {
-        ErrorInvalidValue("getBufferSubData: Not enough data. Operation requires"
-                          " %d bytes, but buffer only has %d bytes.",
-                          neededByteLength.value(), boundBuffer->ByteLength());
+        ErrorInvalidValue("%s: Not enough data. Operation requires %d bytes, but buffer"
+                          " only has %d bytes.",
+                          funcName, neededByteLength.value(), boundBuffer->ByteLength());
         return;
     }
 
     // If target is TRANSFORM_FEEDBACK_BUFFER, and any transform
     // feedback object is currently active, an INVALID_OPERATION error
     // is generated.
     WebGLTransformFeedback* currentTF = mBoundTransformFeedback;
     if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
-        if (currentTF->mIsActive)
-            return ErrorInvalidOperation("getBufferSubData: Currently bound transform"
-                                         " feedback is active");
+        if (currentTF->mIsActive) {
+            ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
+                                  funcName);
+            return;
+        }
 
         // https://github.com/NVIDIA/WebGL/commit/63aff5e58c1d79825a596f0f4aa46174b9a5f72c
         // Performing reads and writes on a buffer that is currently
         // bound for transform feedback causes undefined results in
         // GLES3.0 and OpenGL 4.5. In practice results of reads and
         // writes might be consistent as long as transform feedback
         // objects are not active, but neither GLES3.0 nor OpenGL 4.5
         // spec guarantees this - just being bound for transform
@@ -213,37 +222,44 @@ WebGL2Context::GetBufferSubDataT(GLenum 
 
     /* If the buffer is written and read sequentially by other
      * operations and getBufferSubData, it is the responsibility of
      * the WebGL API to ensure that data are access
      * consistently. This applies even if the buffer is currently
      * bound to a transform feedback binding point.
      */
 
-    void* ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(), LOCAL_GL_MAP_READ_BIT);
+    void* ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(),
+                                    LOCAL_GL_MAP_READ_BIT);
     // Warning: Possibly shared memory.  See bug 1225033.
     memcpy(data.DataAllowShared(), ptr, data.LengthAllowShared());
     gl->fUnmapBuffer(target);
 
+    ////
+
     if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
         BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, currentTF);
     }
 }
 
-void WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
-                                     const dom::Nullable<dom::ArrayBuffer>& maybeData)
+void
+WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
+                                const dom::Nullable<dom::ArrayBuffer>& maybeData)
 {
     // If returnedData is null then an INVALID_VALUE error is
     // generated.
-    if (maybeData.IsNull())
-        return ErrorInvalidValue("getBufferSubData: returnedData is null");
+    if (maybeData.IsNull()) {
+        ErrorInvalidValue("getBufferSubData: returnedData is null");
+        return;
+    }
 
     const dom::ArrayBuffer& data = maybeData.Value();
     GetBufferSubDataT(target, offset, data);
 }
 
-void WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
-                                     const dom::SharedArrayBuffer& data)
+void
+WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
+                                const dom::SharedArrayBuffer& data)
 {
     GetBufferSubDataT(target, offset, data);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -6,16 +6,17 @@
 #include "WebGL2Context.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/RefPtr.h"
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
+#include "WebGLUniformLocation.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 bool
 WebGL2Context::ValidateUniformMatrixTranspose(bool /*transpose*/, const char* /*info*/)
 {
@@ -23,230 +24,52 @@ WebGL2Context::ValidateUniformMatrixTran
 }
 
 // -------------------------------------------------------------------------
 // Uniforms
 
 void
 WebGL2Context::Uniform1ui(WebGLUniformLocation* loc, GLuint v0)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT, "uniform1ui", &rawLoc))
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT, "uniform1ui"))
         return;
 
     MakeContextCurrent();
-    gl->fUniform1ui(rawLoc, v0);
+    gl->fUniform1ui(loc->mLoc, v0);
 }
 
 void
 WebGL2Context::Uniform2ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_UNSIGNED_INT, "uniform2ui", &rawLoc))
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_UNSIGNED_INT, "uniform2ui"))
         return;
 
     MakeContextCurrent();
-    gl->fUniform2ui(rawLoc, v0, v1);
+    gl->fUniform2ui(loc->mLoc, v0, v1);
 }
 
 void
 WebGL2Context::Uniform3ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_UNSIGNED_INT, "uniform3ui", &rawLoc))
-        return;
-
-    MakeContextCurrent();
-    gl->fUniform3ui(rawLoc, v0, v1, v2);
-}
-
-void
-WebGL2Context::Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
-{
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_UNSIGNED_INT, "uniform4ui", &rawLoc))
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_UNSIGNED_INT, "uniform3ui"))
         return;
 
     MakeContextCurrent();
-    gl->fUniform4ui(rawLoc, v0, v1, v2, v3);
-}
-
-void
-WebGL2Context::Uniform1uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                                const GLuint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_UNSIGNED_INT, arrayLength,
-                                    "uniform1uiv", &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform1uiv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGL2Context::Uniform2uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                                const GLuint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_UNSIGNED_INT, arrayLength,
-                                    "uniform2uiv", &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform2uiv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGL2Context::Uniform3uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                                const GLuint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_UNSIGNED_INT, arrayLength,
-                                    "uniform3uiv", &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform1uiv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGL2Context::Uniform4uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                                const GLuint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_UNSIGNED_INT, arrayLength,
-                                    "uniform4uiv", &rawLoc, &numElementsToUpload)) {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform4uiv(rawLoc, numElementsToUpload, data);
+    gl->fUniform3ui(loc->mLoc, v0, v1, v2);
 }
 
 void
-WebGL2Context::UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 2, 3, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix2x3fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix2x3fv(rawLoc, numElementsToUpload, transpose, data);
-}
-
-void
-WebGL2Context::UniformMatrix2x4fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
+WebGL2Context::Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2,
+                          GLuint v3)
 {
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 2, 4, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix2x4fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_UNSIGNED_INT, "uniform4ui"))
         return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix2x4fv(rawLoc, numElementsToUpload, transpose, data);
-}
-
-void
-WebGL2Context::UniformMatrix3x2fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 3, 2, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix3x2fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
 
     MakeContextCurrent();
-    gl->fUniformMatrix3x2fv(rawLoc, numElementsToUpload, transpose, data);
-}
-
-void
-WebGL2Context::UniformMatrix3x4fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 3, 4, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix3x4fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix3x4fv(rawLoc, numElementsToUpload, transpose, data);
-}
-
-void
-WebGL2Context::UniformMatrix4x2fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 4, 2, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix4x2fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix4x2fv(rawLoc, numElementsToUpload, transpose, data);
-}
-
-void
-WebGL2Context::UniformMatrix4x3fv_base(WebGLUniformLocation* loc, bool transpose,
-                                       size_t arrayLength, const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-
-    if (!ValidateUniformMatrixArraySetter(loc, 4, 3, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix4x3fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix4x3fv(rawLoc, numElementsToUpload, transpose, data);
+    gl->fUniform4ui(loc->mLoc, v0, v1, v2, v3);
 }
 
 
 // -------------------------------------------------------------------------
 // Uniform Buffer Objects and Transform Feedback Buffers
 // TODO(djg): Implemented in WebGLContext
 /*
     void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLActiveInfo.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 namespace mozilla {
 
-uint8_t
+static uint8_t
 ElemSizeFromType(GLenum elemType)
 {
     switch (elemType) {
     case LOCAL_GL_BOOL:
     case LOCAL_GL_FLOAT:
     case LOCAL_GL_INT:
     case LOCAL_GL_UNSIGNED_INT:
     case LOCAL_GL_SAMPLER_2D:
@@ -71,28 +71,56 @@ ElemSizeFromType(GLenum elemType)
     case LOCAL_GL_FLOAT_MAT4:
         return 16;
 
     default:
         MOZ_CRASH("GFX: Bad `elemType`.");
     }
 }
 
+////////////////////
+
 WebGLActiveInfo::WebGLActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType,
                                  bool isArray, const nsACString& baseUserName,
                                  const nsACString& baseMappedName)
     : mWebGL(webgl)
     , mElemCount(elemCount)
     , mElemType(elemType)
     , mBaseUserName(baseUserName)
     , mIsArray(isArray)
     , mElemSize(ElemSizeFromType(elemType))
     , mBaseMappedName(baseMappedName)
 { }
 
+bool
+WebGLActiveInfo::IsSampler() const
+{
+    switch (mElemType) {
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_3D:
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
+    case LOCAL_GL_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+        return true;
+
+    default:
+        return false;
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
 WebGLActiveInfo::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLActiveInfoBinding::Wrap(js, this, givenProto);
 }
 
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -25,29 +25,30 @@ public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo)
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     WebGLContext* GetParentObject() const {
         return mWebGL;
     }
 
-
     WebGLContext* const mWebGL;
 
     // ActiveInfo state:
-    const GLint mElemCount; // `size`
+    const uint32_t mElemCount; // `size`
     const GLenum mElemType; // `type`
     const nsCString mBaseUserName; // `name`, but ASCII, and without any final "[0]".
 
     // Not actually part of ActiveInfo:
     const bool mIsArray;
     const uint8_t mElemSize;
     const nsCString mBaseMappedName; // Without any final "[0]".
 
+    bool IsSampler() const;
+
     WebGLActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
                     const nsACString& baseUserName, const nsACString& baseMappedName);
 
     /* GLES 2.0.25, p33:
      *   This command will return as much information about active
      *   attributes as possible. If no information is available, length will
      *   be set to zero and name will be an empty string. This situation
      *   could arise if GetActiveAttrib is issued after a failed link.
@@ -85,13 +86,13 @@ private:
     { }
 
     // Private destructor, to discourage deletion outside of Release():
     ~WebGLActiveInfo() { }
 };
 
 //////////
 
-uint8_t ElemSizeFromType(GLenum elemType);
+bool IsElemTypeSampler(GLenum elemType);
 
 } // namespace mozilla
 
 #endif // WEBGL_ACTIVE_INFO_H_
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -117,16 +117,17 @@ template<typename> struct Nullable;
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
 class ShaderValidator;
 class TexUnpackBlob;
+struct UniformInfo;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
@@ -534,19 +535,23 @@ public:
     bool IsRenderbuffer(WebGLRenderbuffer* rb);
     bool IsShader(WebGLShader* shader);
     bool IsVertexArray(WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram* prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 protected:
-    bool DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height,
-                                GLenum destFormat, GLenum destType, void* destBytes,
-                                GLenum auxReadFormat, GLenum auxReadType);
+    bool ReadPixels_SharedPrecheck(ErrorResult* const out_error);
+    void ReadPixelsImpl(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                        GLenum type, void* data, uint32_t dataLen);
+    bool DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
+                                GLsizei width, GLsizei height, GLenum format,
+                                GLenum destType, void* dest, uint32_t dataLen,
+                                uint32_t rowStride);
 public:
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type,
                     const dom::Nullable<dom::ArrayBufferView>& pixels,
                     ErrorResult& rv);
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height);
 protected:
@@ -560,193 +565,138 @@ public:
     void StencilFunc(GLenum func, GLint ref, GLuint mask);
     void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void StencilMask(GLuint mask);
     void StencilMaskSeparate(GLenum face, GLuint mask);
     void StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
     void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
                            GLenum dppass);
 
+    //////
+
     void Uniform1i(WebGLUniformLocation* loc, GLint x);
     void Uniform2i(WebGLUniformLocation* loc, GLint x, GLint y);
     void Uniform3i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z);
-    void Uniform4i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z,
-                   GLint w);
+    void Uniform4i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z, GLint w);
 
     void Uniform1f(WebGLUniformLocation* loc, GLfloat x);
     void Uniform2f(WebGLUniformLocation* loc, GLfloat x, GLfloat y);
     void Uniform3f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z);
-    void Uniform4f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z,
-                   GLfloat w);
+    void Uniform4f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+
+    //////////////////////////
+
+protected:
+    template<typename elemT, typename arrT>
+    struct Arr {
+        size_t dataCount;
+        const elemT* data;
 
-    // Int array
-    void Uniform1iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform1iv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform1iv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLint>& arr)
-    {
-        Uniform1iv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLint* data);
+        explicit Arr(const arrT& arr) {
+            arr.ComputeLengthAndData();
+            dataCount = arr.Length();
+            data = arr.Data();
+        }
 
-    void Uniform2iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform2iv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform2iv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLint>& arr)
-    {
-        Uniform2iv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLint* data);
+        explicit Arr(const dom::Sequence<elemT>& arr) {
+            dataCount = arr.Length();
+            data = arr.Elements();
+        }
+    };
 
-    void Uniform3iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform3iv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform3iv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLint>& arr)
-    {
-        Uniform3iv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLint* data);
+    typedef Arr<GLint, dom::Int32Array> IntArr;
+    typedef Arr<GLfloat, dom::Float32Array> FloatArr;
+
+    ////////////////
+
+    void UniformNiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                    const IntArr& arr);
+
+    void UniformNfv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                    const FloatArr& arr);
 
-    void Uniform4iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform4iv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform4iv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLint>& arr)
-    {
-        Uniform4iv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLint* data);
+    void UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
+                            WebGLUniformLocation* loc, bool transpose,
+                            const FloatArr& arr);
 
-    // Float array
-    void Uniform1fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform1fv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform1fv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLfloat>& arr)
-    {
-        Uniform1fv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLfloat* data);
+    ////////////////
 
-    void Uniform2fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform2fv_base(loc, arr.Length(), arr.Data());
+public:
+    template<typename T>
+    void Uniform1iv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNiv("uniform1iv", 1, loc, IntArr(arr));
     }
-    void Uniform2fv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLfloat>& arr)
-    {
-        Uniform2fv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLfloat* data);
-
-    void Uniform3fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform3fv_base(loc, arr.Length(), arr.Data());
+    template<typename T>
+    void Uniform2iv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNiv("uniform2iv", 2, loc, IntArr(arr));
     }
-    void Uniform3fv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLfloat>& arr)
-    {
-        Uniform3fv_base(loc, arr.Length(), arr.Elements());
+    template<typename T>
+    void Uniform3iv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNiv("uniform3iv", 3, loc, IntArr(arr));
     }
-    void Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLfloat* data);
+    template<typename T>
+    void Uniform4iv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNiv("uniform4iv", 4, loc, IntArr(arr));
+    }
 
-    void Uniform4fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform4fv_base(loc, arr.Length(), arr.Data());
-    }
-    void Uniform4fv(WebGLUniformLocation* loc,
-                    const dom::Sequence<GLfloat>& arr)
-    {
-        Uniform4fv_base(loc, arr.Length(), arr.Elements());
-    }
-    void Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                         const GLfloat* data);
+    //////
 
-    // Matrix
-    void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose,
-                          const dom::Float32Array& value)
-    {
-        value.ComputeLengthAndData();
-        UniformMatrix2fv_base(loc, transpose, value.Length(), value.Data());
+    template<typename T>
+    void Uniform1fv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNfv("uniform1fv", 1, loc, FloatArr(arr));
+    }
+    template<typename T>
+    void Uniform2fv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNfv("uniform2fv", 2, loc, FloatArr(arr));
     }
-    void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose,
-                          const dom::Sequence<float>& value)
-    {
-        UniformMatrix2fv_base(loc, transpose, value.Length(),
-                              value.Elements());
+    template<typename T>
+    void Uniform3fv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNfv("uniform3fv", 3, loc, FloatArr(arr));
     }
-    void UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose,
-                               size_t arrayLength, const float* data);
-
-    void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
-                          const dom::Float32Array& value)
-    {
-        value.ComputeLengthAndData();
-        UniformMatrix3fv_base(loc, transpose, value.Length(), value.Data());
+    template<typename T>
+    void Uniform4fv(WebGLUniformLocation* loc, const T& arr) {
+        UniformNfv("uniform4fv", 4, loc, FloatArr(arr));
     }
-    void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
-                          const dom::Sequence<float>& value)
-    {
-        UniformMatrix3fv_base(loc, transpose, value.Length(), value.Elements());
-    }
-    void UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose,
-                               size_t arrayLength, const float* data);
+
+    //////
 
-    void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose,
-                          const dom::Float32Array& value)
-    {
-        value.ComputeLengthAndData();
-        UniformMatrix4fv_base(loc, transpose, value.Length(), value.Data());
+    template<typename T>
+    void UniformMatrix2fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix2fv", 2, 2, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix4fv(WebGLUniformLocation* loc, bool transpose,
-                          const dom::Sequence<float>& value)
-    {
-        UniformMatrix4fv_base(loc, transpose, value.Length(),
-                              value.Elements());
+    template<typename T>
+    void UniformMatrix3fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix3fv", 3, 3, loc, transpose, FloatArr(arr));
     }
-    void UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose,
-                               size_t arrayLength, const float* data);
+    template<typename T>
+    void UniformMatrix4fv(WebGLUniformLocation* loc, bool transpose, const T& arr) {
+        UniformMatrixAxBfv("uniformMatrix4fv", 4, 4, loc, transpose, FloatArr(arr));
+    }
+
+    ////////////////////////////////////
 
     void UseProgram(WebGLProgram* prog);
 
     bool ValidateAttribArraySetter(const char* name, uint32_t count,
                                    uint32_t arrayLength);
     bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName);
     bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
-                               GLenum setterType, const char* info,
-                               GLuint* out_rawLoc);
+                               GLenum setterType, const char* funcName);
     bool ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
-                                    size_t setterArraySize, const char* info,
-                                    GLuint* out_rawLoc,
-                                    GLsizei* out_numElementsToUpload);
+                                    uint32_t setterArraySize, const char* funcName,
+                                    uint32_t* out_numElementsToUpload);
     bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                           uint8_t setterCols,
                                           uint8_t setterRows,
                                           GLenum setterType,
-                                          size_t setterArraySize,
+                                          uint32_t setterArraySize,
                                           bool setterTranspose,
-                                          const char* info,
-                                          GLuint* out_rawLoc,
-                                          GLsizei* out_numElementsToUpload);
+                                          const char* funcName,
+                                          uint32_t* out_numElementsToUpload);
     void ValidateProgram(WebGLProgram* prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation* loc);
     bool ValidateSamplerUniformSetter(const char* info,
                                       WebGLUniformLocation* loc, GLint value);
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
 public:
@@ -1598,24 +1548,28 @@ public:
     void GenerateWarning(const char* fmt, va_list ap);
 
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
 
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
 
+
+    const decltype(mBound2DTextures)* TexListForElemType(GLenum elemType) const;
+
     // Friend list
     friend class ScopedCopyTexImageSource;
     friend class ScopedResolveTexturesForDraw;
     friend class ScopedUnpackReset;
     friend class webgl::TexUnpackBlob;
     friend class webgl::TexUnpackBytes;
     friend class webgl::TexUnpackImage;
     friend class webgl::TexUnpackSurface;
+    friend struct webgl::UniformInfo;
     friend class WebGLTexture;
     friend class WebGLFBAttachPoint;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -37,58 +37,115 @@ class ScopedResolveTexturesForDraw
     std::vector<TexRebindRequest> mRebindRequests;
 
 public:
     ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
                                  bool* const out_error);
     ~ScopedResolveTexturesForDraw();
 };
 
+bool
+WebGLTexture::IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+                         const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const
+{
+    auto itr = fbAttachments.cbegin();
+    for (; itr != fbAttachments.cend(); ++itr) {
+        const auto& attach = *itr;
+        if (attach->Texture() == this)
+            break;
+    }
+
+    if (itr == fbAttachments.cend())
+        return false;
+
+    ////
+
+    const auto minLevel = mBaseMipmapLevel;
+    uint32_t maxLevel;
+    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel)) {
+        // No valid mips. Will need fake-black.
+        return false;
+    }
+
+    ////
+
+    for (; itr != fbAttachments.cend(); ++itr) {
+        const auto& attach = *itr;
+        if (attach->Texture() != this)
+            continue;
+
+        const auto dstLevel = attach->MipLevel();
+
+        if (minLevel <= dstLevel && dstLevel <= maxLevel) {
+            webgl->ErrorInvalidOperation("%s: Feedback loop detected between tex target"
+                                         " 0x%04x, tex unit %u, levels %u-%u; and"
+                                         " framebuffer attachment 0x%04x, level %u.",
+                                         funcName, mTarget.get(), texUnit, minLevel,
+                                         maxLevel, attach->mAttachmentPoint, dstLevel);
+            return true;
+        }
+    }
+
+    return false;
+}
+
 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
                                                            const char* funcName,
                                                            bool* const out_error)
     : mWebGL(webgl)
 {
-    MOZ_ASSERT(webgl->gl->IsCurrent());
+    MOZ_ASSERT(mWebGL->gl->IsCurrent());
 
-    typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
+    if (!mWebGL->mActiveProgramLinkInfo) {
+        mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
+        *out_error = true;
+        return;
+    }
 
-    const auto fnResolveAll = [this, funcName](const TexturesT& textures)
-    {
-        const auto len = textures.Length();
-        for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
-            WebGLTexture* tex = textures[texUnit];
+    std::vector<const WebGLFBAttachPoint*> fbAttachments;
+    if (mWebGL->mBoundDrawFramebuffer) {
+        const auto& fb = mWebGL->mBoundDrawFramebuffer;
+        fb->GatherAttachments(&fbAttachments);
+    }
+
+    MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
+    const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
+    for (const auto& uniform : uniformSamplers) {
+        const auto& texList = *(uniform->mSamplerTexList);
+
+        for (const auto& texUnit : uniform->mSamplerValues) {
+            if (texUnit >= texList.Length())
+                continue;
+
+            const auto& tex = texList[texUnit];
             if (!tex)
                 continue;
 
+            if (tex->IsFeedback(mWebGL, funcName, texUnit, fbAttachments)) {
+                *out_error = true;
+                return;
+            }
+
             FakeBlackType fakeBlack;
-            if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack))
-                return false;
+            if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
+                mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
+                                         funcName);
+                *out_error = true;
+                return;
+            }
 
             if (fakeBlack == FakeBlackType::None)
                 continue;
 
             mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
             mRebindRequests.push_back({texUnit, tex});
         }
-
-        return true;
-    };
-
-    bool ok = true;
-    ok &= fnResolveAll(mWebGL->mBound2DTextures);
-    ok &= fnResolveAll(mWebGL->mBoundCubeMapTextures);
-    ok &= fnResolveAll(mWebGL->mBound3DTextures);
-    ok &= fnResolveAll(mWebGL->mBound2DArrayTextures);
-
-    if (!ok) {
-        mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
     }
 
-    *out_error = !ok;
+    *out_error = false;
 }
 
 ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
 {
     if (!mRebindRequests.size())
         return;
 
     gl::GLContext* gl = mWebGL->gl;
@@ -190,22 +247,16 @@ WebGLContext::DrawArrays_check(GLint fir
         return false;
     }
 
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0) {
         return false;
     }
 
-    // Any checks below this depend on a program being available.
-    if (!mCurrentProgram) {
-        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
-        return false;
-    }
-
     if (!ValidateBufferFetching(info)) {
         return false;
     }
 
     CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
 
     if (!checked_firstPlusCount.isValid()) {
         ErrorInvalidOperation("%s: overflow in first+count", info);
@@ -355,24 +406,16 @@ WebGLContext::DrawElements_check(GLsizei
     const GLsizei first = byteOffset / bytesPerElem;
     const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
 
     if (!checked_byteCount.isValid()) {
         ErrorInvalidValue("%s: overflow in byteCount", info);
         return false;
     }
 
-    // Any checks below this depend on mActiveProgramLinkInfo being available.
-    if (!mActiveProgramLinkInfo) {
-        // Technically, this will only be null iff CURRENT_PROGRAM is null.
-        // But it's better to branch on what we actually care about.
-        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
-        return false;
-    }
-
     if (!mBoundVertexArray->mElementArrayBuffer) {
         ErrorInvalidOperation("%s: must have element array buffer binding", info);
         return false;
     }
 
     WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
 
     if (!elemArrayBuffer.ByteLength()) {
@@ -598,18 +641,18 @@ WebGLContext::ValidateBufferFetching(con
             return false;
         }
 
         ++i;
     }
 
     mBufferFetch_IsAttrib0Active = false;
 
-    for (const auto& pair : mActiveProgramLinkInfo->activeAttribLocs) {
-        const uint32_t attribLoc = pair.second;
+    for (const auto& attrib : mActiveProgramLinkInfo->attribs) {
+        const auto& attribLoc = attrib.mLoc;
 
         if (attribLoc >= attribCount)
             continue;
 
         if (attribLoc == 0) {
             mBufferFetch_IsAttrib0Active = true;
         }
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "WebGL2Context.h"
 
 #include "WebGLActiveInfo.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
@@ -965,20 +966,20 @@ WebGLContext::GetUniform(JSContext* js, 
         return JS::NullValue();
 
     if (!ValidateObject("getUniform: `program`", prog))
         return JS::NullValue();
 
     if (!ValidateObject("getUniform: `location`", loc))
         return JS::NullValue();
 
-    if (!loc->ValidateForProgram(prog, this, "getUniform"))
+    if (!loc->ValidateForProgram(prog, "getUniform"))
         return JS::NullValue();
 
-    return loc->GetUniform(js, this);
+    return loc->GetUniform(js);
 }
 
 already_AddRefed<WebGLUniformLocation>
 WebGLContext::GetUniformLocation(WebGLProgram* prog, const nsAString& name)
 {
     if (IsContextLost())
         return nullptr;
 
@@ -1209,38 +1210,49 @@ WebGLContext::PixelStorei(GLenum pname, 
 
     default:
         break;
     }
 
     ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
 }
 
+static bool
+IsNeedsANGLEWorkAround(const webgl::FormatInfo* format)
+{
+    switch (format->effectiveFormat) {
+    case webgl::EffectiveFormat::RGB16F:
+    case webgl::EffectiveFormat::RGBA16F:
+        return true;
+
+    default:
+        return false;
+    }
+}
+
 bool
-WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height,
-                                     GLenum destFormat, GLenum destType, void* destBytes,
-                                     GLenum auxReadFormat, GLenum auxReadType)
+WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
+                                     GLsizei width, GLsizei height, GLenum format,
+                                     GLenum destType, void* dest, uint32_t destSize,
+                                     uint32_t rowStride)
 {
-    GLenum readFormat = destFormat;
-    GLenum readType = destType;
-
     if (gl->WorkAroundDriverBugs() &&
         gl->IsANGLE() &&
         gl->Version() < 300 && // ANGLE ES2 doesn't support HALF_FLOAT reads properly.
-        readType == LOCAL_GL_FLOAT &&
-        auxReadFormat == destFormat &&
-        auxReadType == LOCAL_GL_HALF_FLOAT)
+        IsNeedsANGLEWorkAround(srcFormat))
     {
         MOZ_RELEASE_ASSERT(!IsWebGL2()); // No SKIP_PIXELS, etc.
-
-        readType = auxReadType;
+        MOZ_ASSERT(!mBoundPixelPackBuffer); // Let's be real clear.
+
+        // You'd think ANGLE would want HALF_FLOAT_OES, but it rejects that.
+        const GLenum readType = LOCAL_GL_HALF_FLOAT;
 
         const char funcName[] = "readPixels";
-        const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType});
-        const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType});
+        const auto readBytesPerPixel = webgl::BytesPerPixel({format, readType});
+        const auto destBytesPerPixel = webgl::BytesPerPixel({format, destType});
 
         uint32_t readStride;
         uint32_t readByteCount;
         uint32_t destStride;
         uint32_t destByteCount;
         if (!ValidatePackSize(funcName, width, height, readBytesPerPixel, &readStride,
                               &readByteCount) ||
             !ValidatePackSize(funcName, width, height, destBytesPerPixel, &destStride,
@@ -1253,34 +1265,34 @@ WebGLContext::DoReadPixelsAndConvert(GLi
         UniqueBuffer readBuffer = malloc(readByteCount);
         if (!readBuffer) {
             ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion.");
             return false;
         }
 
         gl::GLContext::LocalErrorScope errorScope(*gl);
 
-        gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get());
+        gl->fReadPixels(x, y, width, height, format, readType, readBuffer.get());
 
         const GLenum error = errorScope.GetError();
         if (error == LOCAL_GL_OUT_OF_MEMORY) {
             ErrorOutOfMemory("readPixels: Driver ran out of memory.");
             return false;
         }
 
         if (error) {
             MOZ_RELEASE_ASSERT(false, "GFX: Unexpected driver error.");
             return false;
         }
 
         size_t channelsPerRow = std::min(readStride / sizeof(uint16_t),
                                          destStride / sizeof(float));
 
         const uint8_t* srcRow = (uint8_t*)readBuffer.get();
-        uint8_t* dstRow = (uint8_t*)destBytes;
+        uint8_t* dstRow = (uint8_t*)dest;
 
         for (size_t j = 0; j < (size_t)height; j++) {
             auto src = (const uint16_t*)srcRow;
             auto dst = (float*)dstRow;
 
             const auto srcEnd = src + channelsPerRow;
             while (src != srcEnd) {
                 *dst = unpackFromFloat16(*src);
@@ -1290,115 +1302,112 @@ WebGLContext::DoReadPixelsAndConvert(GLi
 
             srcRow += readStride;
             dstRow += destStride;
         }
 
         return true;
     }
 
-    gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes);
+    // On at least Win+NV, we'll get PBO errors if we don't have at least
+    // `rowStride * height` bytes available to read into.
+    const auto naiveBytesNeeded = CheckedUint32(rowStride) * height;
+    const bool isDangerCloseToEdge = (!naiveBytesNeeded.isValid() ||
+                                      naiveBytesNeeded.value() > destSize);
+    const bool useParanoidHandling = (gl->WorkAroundDriverBugs() &&
+                                      isDangerCloseToEdge &&
+                                      mBoundPixelPackBuffer);
+    if (!useParanoidHandling) {
+        gl->fReadPixels(x, y, width, height, format, destType, dest);
+        return true;
+    }
+
+    // Read everything but the last row.
+    const auto bodyHeight = height - 1;
+    if (bodyHeight) {
+        gl->fReadPixels(x, y, width, bodyHeight, format, destType, dest);
+    }
+
+    // Now read the last row.
+    gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
+    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
+    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
+
+    const auto tailRowOffset = (char*)dest + rowStride * bodyHeight;
+    gl->fReadPixels(x, y+bodyHeight, width, 1, format, destType, tailRowOffset);
+
+    gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment);
+    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
+    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     return true;
 }
 
 static bool
-IsFormatAndTypeUnpackable(GLenum format, GLenum type, bool isWebGL2)
+GetJSScalarFromGLType(GLenum type, js::Scalar::Type* const out_scalarType)
 {
     switch (type) {
+    case LOCAL_GL_BYTE:
+        *out_scalarType = js::Scalar::Int8;
+        return true;
+
     case LOCAL_GL_UNSIGNED_BYTE:
-        switch (format) {
-        case LOCAL_GL_LUMINANCE:
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            return isWebGL2;
-        case LOCAL_GL_ALPHA:
-        case LOCAL_GL_RED:
-        case LOCAL_GL_RED_INTEGER:
-        case LOCAL_GL_RG:
-        case LOCAL_GL_RG_INTEGER:
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_RGB_INTEGER:
-        case LOCAL_GL_RGBA:
-        case LOCAL_GL_RGBA_INTEGER:
-            return true;
-        default:
-            return false;
-        }
-
-    case LOCAL_GL_BYTE:
-        switch (format) {
-        case LOCAL_GL_RED:
-        case LOCAL_GL_RED_INTEGER:
-        case LOCAL_GL_RG:
-        case LOCAL_GL_RG_INTEGER:
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_RGB_INTEGER:
-        case LOCAL_GL_RGBA:
-        case LOCAL_GL_RGBA_INTEGER:
-            return true;
-        default:
-            return false;
-        }
+        *out_scalarType = js::Scalar::Uint8;
+        return true;
+
+    case LOCAL_GL_SHORT:
+        *out_scalarType = js::Scalar::Int16;
+        return true;
+
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+    case LOCAL_GL_UNSIGNED_SHORT:
+    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+        *out_scalarType = js::Scalar::Uint16;
+        return true;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
+    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+        *out_scalarType = js::Scalar::Uint32;
+        return true;
+    case LOCAL_GL_INT:
+        *out_scalarType = js::Scalar::Int32;
+        return true;
 
     case LOCAL_GL_FLOAT:
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-        switch (format) {
-        case LOCAL_GL_RED:
-        case LOCAL_GL_RG:
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_RGBA:
-            return true;
-        default:
-            return false;
-        }
-
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-        return format == LOCAL_GL_RGBA;
-
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        return format == LOCAL_GL_RGB;
+        *out_scalarType = js::Scalar::Float32;
+        return true;
 
     default:
         return false;
     }
 }
 
-static bool
-IsIntegerFormatAndTypeUnpackable(GLenum format, GLenum type)
+bool
+WebGLContext::ReadPixels_SharedPrecheck(ErrorResult* const out_error)
 {
-    switch (type) {
-    case LOCAL_GL_UNSIGNED_SHORT:
-    case LOCAL_GL_SHORT:
-    case LOCAL_GL_UNSIGNED_INT:
-    case LOCAL_GL_INT:
-        switch (format) {
-        case LOCAL_GL_RED_INTEGER:
-        case LOCAL_GL_RG_INTEGER:
-        case LOCAL_GL_RGB_INTEGER:
-        case LOCAL_GL_RGBA_INTEGER:
-            return true;
-        default:
-            return false;
-        }
-
-    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
-        return format == LOCAL_GL_RGBA ||
-               format == LOCAL_GL_RGBA_INTEGER;
-
-    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
-    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
-        return format == LOCAL_GL_RGB;
-
-    default:
+    if (IsContextLost())
+        return false;
+
+    if (mCanvasElement &&
+        mCanvasElement->IsWriteOnly() &&
+        !nsContentUtils::IsCallerChrome())
+    {
+        GenerateWarning("readPixels: Not allowed");
+        out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
         return false;
     }
+
+    return true;
 }
 
-
 bool
 WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
                                uint8_t bytesPerPixel, uint32_t* const out_rowStride,
                                uint32_t* const out_endOffset)
 {
     if (!width || !height) {
         *out_rowStride = 0;
         *out_endOffset = 0;
@@ -1438,237 +1447,271 @@ WebGLContext::ValidatePackSize(const cha
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                          GLenum type,
                          const dom::Nullable<dom::ArrayBufferView>& pixels,
                          ErrorResult& out_error)
 {
-    const char funcName[] = "readPixels";
-    if (IsContextLost())
-        return;
-
-    if (mCanvasElement &&
-        mCanvasElement->IsWriteOnly() &&
-        !nsContentUtils::IsCallerChrome())
-    {
-        GenerateWarning("readPixels: Not allowed");
-        out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    if (!ReadPixels_SharedPrecheck(&out_error))
         return;
-    }
-
-    if (width < 0 || height < 0)
-        return ErrorInvalidValue("readPixels: negative size passed");
-
-    if (pixels.IsNull())
-        return ErrorInvalidValue("readPixels: null destination buffer");
-
-    if (!(IsWebGL2() && IsIntegerFormatAndTypeUnpackable(format, type)) &&
-        !IsFormatAndTypeUnpackable(format, type, IsWebGL2())) {
-        return ErrorInvalidEnum("readPixels: Bad format or type.");
-    }
-
-    int channels = 0;
-
-    // Check the format param
-    switch (format) {
-    case LOCAL_GL_ALPHA:
-    case LOCAL_GL_LUMINANCE:
-    case LOCAL_GL_RED:
-    case LOCAL_GL_RED_INTEGER:
-        channels = 1;
-        break;
-    case LOCAL_GL_LUMINANCE_ALPHA:
-    case LOCAL_GL_RG:
-    case LOCAL_GL_RG_INTEGER:
-        channels = 2;
-        break;
-    case LOCAL_GL_RGB:
-    case LOCAL_GL_RGB_INTEGER:
-        channels = 3;
-        break;
-    case LOCAL_GL_RGBA:
-    case LOCAL_GL_RGBA_INTEGER:
-        channels = 4;
-        break;
-    default:
-        MOZ_CRASH("GFX: bad `format`");
-    }
-
-
-    // Check the type param
-    int bytesPerPixel;
-    int requiredDataType;
-    switch (type) {
-    case LOCAL_GL_BYTE:
-        bytesPerPixel = 1*channels;
-        requiredDataType = js::Scalar::Int8;
-        break;
-
-    case LOCAL_GL_UNSIGNED_BYTE:
-        bytesPerPixel = 1*channels;
-        requiredDataType = js::Scalar::Uint8;
-        break;
-
-    case LOCAL_GL_SHORT:
-        bytesPerPixel = 2*channels;
-        requiredDataType = js::Scalar::Int16;
-        break;
-
-    case LOCAL_GL_UNSIGNED_SHORT:
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        bytesPerPixel = 2;
-        requiredDataType = js::Scalar::Uint16;
-        break;
-
-    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
-    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
-    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
-    case LOCAL_GL_UNSIGNED_INT_24_8:
-        bytesPerPixel = 4;
-        requiredDataType = js::Scalar::Uint32;
-        break;
-
-    case LOCAL_GL_UNSIGNED_INT:
-        bytesPerPixel = 4*channels;
-        requiredDataType = js::Scalar::Uint32;
-        break;
-
-    case LOCAL_GL_INT:
-        bytesPerPixel = 4*channels;
-        requiredDataType = js::Scalar::Int32;
-        break;
-
-    case LOCAL_GL_FLOAT:
-        bytesPerPixel = 4*channels;
-        requiredDataType = js::Scalar::Float32;
-        break;
-
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-        bytesPerPixel = 2*channels;
-        requiredDataType = js::Scalar::Uint16;
-        break;
-
-    default:
-        MOZ_CRASH("GFX: bad `type`");
+
+    if (mBoundPixelPackBuffer) {
+        ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must be null.");
+        return;
     }
 
     //////
 
+    if (pixels.IsNull()) {
+        ErrorInvalidValue("readPixels: null destination buffer");
+        return;
+    }
+
     const auto& view = pixels.Value();
 
+    //////
+
+    js::Scalar::Type reqScalarType;
+    if (!GetJSScalarFromGLType(type, &reqScalarType)) {
+        ErrorInvalidEnum("readPixels: Bad `type`.");
+        return;
+    }
+
+    const js::Scalar::Type dataScalarType = JS_GetArrayBufferViewType(view.Obj());
+    if (dataScalarType != reqScalarType) {
+        ErrorInvalidOperation("readPixels: `pixels` type does not match `type`.");
+        return;
+    }
+
+    //////
+
     // Compute length and data.  Don't reenter after this point, lest the
     // precomputed go out of sync with the instant length/data.
     view.ComputeLengthAndData();
     void* data = view.DataAllowShared();
-    size_t bytesAvailable = view.LengthAllowShared();
-    js::Scalar::Type dataType = JS_GetArrayBufferViewType(view.Obj());
-
-    // Check the pixels param type
-    if (dataType != requiredDataType)
-        return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
+    const auto dataLen = view.LengthAllowShared();
 
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
         out_error.Throw(NS_ERROR_OUT_OF_MEMORY);
         return;
     }
 
+    ReadPixelsImpl(x, y, width, height, format, type, data, dataLen);
+}
+
+void
+WebGL2Context::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                          GLenum type, WebGLsizeiptr offset, ErrorResult& out_error)
+{
+    if (!ReadPixels_SharedPrecheck(&out_error))
+        return;
+
+    if (!mBoundPixelPackBuffer) {
+        ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must not be null.");
+        return;
+    }
+
     //////
 
-    uint32_t rowStride;
-    uint32_t bytesNeeded;
-    if (!ValidatePackSize(funcName, width, height, bytesPerPixel, &rowStride,
-                          &bytesNeeded))
-    {
+    if (offset < 0) {
+        ErrorInvalidValue("readPixels: offset must not be negative.");
         return;
     }
 
-    if (bytesNeeded > bytesAvailable) {
-        ErrorInvalidOperation("readPixels: buffer too small");
+    {
+        const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
+
+        if (offset % bytesPerType != 0) {
+            ErrorInvalidOperation("readPixels: `offset` must be divisible by the size"
+                                  " a `type` in bytes.");
+            return;
+        }
+    }
+
+    //////
+
+    const auto bytesAvailable = mBoundPixelPackBuffer->ByteLength();
+    const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
+
+    uint32_t bytesAfterOffset = 0;
+    if (checkedBytesAfterOffset.isValid()) {
+        bytesAfterOffset = checkedBytesAfterOffset.value();
+    }
+
+    ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
+}
+
+static bool
+ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
+                                const webgl::PackingInfo& pi, gl::GLContext* gl,
+                                WebGLContext* webgl)
+{
+    // Check the format and type params to assure they are an acceptable pair (as per spec)
+    GLenum mainFormat;
+    GLenum mainType;
+
+    switch (srcFormat->componentType) {
+    case webgl::ComponentType::Float:
+        mainFormat = LOCAL_GL_RGBA;
+        mainType = LOCAL_GL_FLOAT;
+        break;
+
+    case webgl::ComponentType::UInt:
+        mainFormat = LOCAL_GL_RGBA_INTEGER;
+        mainType = LOCAL_GL_UNSIGNED_INT;
+        break;
+
+    case webgl::ComponentType::Int:
+        mainFormat = LOCAL_GL_RGBA_INTEGER;
+        mainType = LOCAL_GL_INT;
+        break;
+
+    case webgl::ComponentType::NormInt:
+    case webgl::ComponentType::NormUInt:
+        mainFormat = LOCAL_GL_RGBA;
+        mainType = LOCAL_GL_UNSIGNED_BYTE;
+        break;
+
+    default:
+        gfxCriticalNote << "Unhandled srcFormat->componentType: "
+                        << (uint32_t)srcFormat->componentType;
+        webgl->ErrorInvalidOperation("readPixels: Unhandled srcFormat->componentType."
+                                     " Please file a bug!");
+        return false;
+    }
+
+    if (pi.format == mainFormat && pi.type == mainType)
+        return true;
+
+    //////
+    // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
+    // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
+    // is accepted.
+
+    if (webgl->IsWebGL2() &&
+        srcFormat->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
+        pi.format == LOCAL_GL_RGBA &&
+        pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
+    {
+        return true;
+    }
+
+    //////
+    // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
+    // combination for glReadPixels()...
+
+    // So yeah, we are actually checking that these are valid as /unpack/ formats, instead
+    // of /pack/ formats here, but it should cover the INVALID_ENUM cases.
+    if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type)) {
+        webgl->ErrorInvalidEnum("readPixels: Bad format and/or type.");
+        return false;
+    }
+
+    // Only valid when pulled from:
+    // * GLES 2.0.25 p105:
+    //   "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
+    // * GLES 3.0.4 p193:
+    //   "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
+    switch (pi.format) {
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+    case LOCAL_GL_DEPTH_COMPONENT:
+    case LOCAL_GL_DEPTH_STENCIL:
+        webgl->ErrorInvalidEnum("readPixels: Invalid format: 0x%04x", pi.format);
+        return false;
+    }
+
+    if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) {
+        webgl->ErrorInvalidEnum("readPixels: Invalid type: 0x%04x", pi.type);
+        return false;
+    }
+
+    MOZ_ASSERT(gl->IsCurrent());
+    if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
+        const auto auxFormat = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT);
+        const auto auxType = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE);
+
+        if (auxFormat && auxType &&
+            pi.format == auxFormat && pi.type == auxType)
+        {
+            return true;
+        }
+    }
+
+    //////
+
+    webgl->ErrorInvalidOperation("readPixels: Invalid format or type.");
+    return false;
+}
+
+void
+WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
+                             GLenum packFormat, GLenum packType, void* dest,
+                             uint32_t dataLen)
+{
+    if (rawWidth < 0 || rawHeight < 0) {
+        ErrorInvalidValue("readPixels: negative size passed");
         return;
     }
 
+    const uint32_t width(rawWidth);
+    const uint32_t height(rawHeight);
+
     //////
 
     MakeContextCurrent();
 
     const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
     if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
         return;
 
-    // Check the format and type params to assure they are an acceptable pair (as per spec)
-    auto srcType = srcFormat->format->componentType;
-    GLenum mainReadFormat;
-    GLenum mainReadType;
-    switch (srcType) {
-        case webgl::ComponentType::Float:
-            mainReadFormat = LOCAL_GL_RGBA;
-            mainReadType = LOCAL_GL_FLOAT;
-            break;
-        case webgl::ComponentType::UInt:
-            mainReadFormat = LOCAL_GL_RGBA_INTEGER;
-            mainReadType = LOCAL_GL_UNSIGNED_INT;
-            break;
-        case webgl::ComponentType::Int:
-            mainReadFormat = LOCAL_GL_RGBA_INTEGER;
-            mainReadType = LOCAL_GL_INT;
-            break;
-        default:
-            mainReadFormat = LOCAL_GL_RGBA;
-            mainReadType = LOCAL_GL_UNSIGNED_BYTE;
-            break;
+    //////
+
+    const webgl::PackingInfo pi = {packFormat, packType};
+    if (!ValidateReadPixelsFormatAndType(srcFormat->format, pi, gl, this))
+        return;
+
+    uint8_t bytesPerPixel;
+    if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) {
+        ErrorInvalidOperation("readPixels: Unsupported format and type.");
+        return;
     }
 
-    GLenum auxReadFormat = mainReadFormat;
-    GLenum auxReadType = mainReadType;
-
-    // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
-    // combination for glReadPixels().
-    if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
-        gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT,
-                         reinterpret_cast<GLint*>(&auxReadFormat));
-        gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE,
-                         reinterpret_cast<GLint*>(&auxReadType));
+    //////
+
+    uint32_t rowStride;
+    uint32_t bytesNeeded;
+    if (!ValidatePackSize("readPixels", width, height, bytesPerPixel, &rowStride,
+                          &bytesNeeded))
+    {
+        return;
     }
 
-    const bool mainMatches = (format == mainReadFormat && type == mainReadType);
-    const bool auxMatches = (format == auxReadFormat && type == auxReadType);
-    bool isValid = mainMatches || auxMatches;
-
-    // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
-    // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
-    // is accepted.
-    if (srcFormat->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
-        format == LOCAL_GL_RGBA &&
-        type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
-    {
-        isValid = true;
+    if (bytesNeeded > dataLen) {
+        ErrorInvalidOperation("readPixels: buffer too small");
+        return;
     }
 
-    if (!isValid)
-        return ErrorInvalidOperation("readPixels: Invalid format/type pair");
-
+    ////////////////
     // Now that the errors are out of the way, on to actually reading!
 
     uint32_t readX, readY;
     uint32_t writeX, writeY;
     uint32_t rwWidth, rwHeight;
     Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
     Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
 
     if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
-        DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat,
-                               auxReadType);
+        DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
+                               packType, dest, dataLen, rowStride);
         return;
     }
 
     // Read request contains out-of-bounds pixels. Unfortunately:
     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
     // "If any of these pixels lies outside of the window allocated to the current GL
     //  context, or outside of the image attached to the currently bound framebuffer
     //  object, then the values obtained for those pixels are undefined."
@@ -1683,35 +1726,36 @@ WebGLContext::ReadPixels(GLint x, GLint 
     if (!rwWidth || !rwHeight) {
         // There aren't any, so we're 'done'.
         DummyReadFramebufferOperation("readPixels");
         return;
     }
 
     if (IsWebGL2()) {
         if (!mPixelStore_PackRowLength) {
-            gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackSkipPixels + width);
+            gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
+                             mPixelStore_PackSkipPixels + width);
         }
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
 
-        DoReadPixelsAndConvert(readX, readY, rwWidth, rwHeight, format, type, data,
-                               auxReadFormat, auxReadType);
+        DoReadPixelsAndConvert(srcFormat->format, readX, readY, rwWidth, rwHeight,
+                               packFormat, packType, dest, dataLen, rowStride);
 
         gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     } else {
         // I *did* say "hilariously slow".
 
-        uint8_t* row = (uint8_t*)data + writeX * bytesPerPixel;
+        uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel;
         row += writeY * rowStride;
         for (uint32_t j = 0; j < rwHeight; j++) {
-            DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row,
-                                   auxReadFormat, auxReadType);
+            DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
+                                   packFormat, packType, row, dataLen, rowStride);
             row += rowStride;
         }
     }
 }
 
 void
 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
                                        GLsizei samples, GLenum internalFormat,
@@ -1842,326 +1886,278 @@ WebGLContext::StencilOpSeparate(GLenum f
 
     MakeContextCurrent();
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Uniform setters.
 
+class ValidateIfSampler
+{
+    const WebGLUniformLocation* const mLoc;
+    const size_t mDataCount;
+    const GLint* const mData;
+    bool mIsValidatedSampler;
+
+public:
+    ValidateIfSampler(WebGLContext* webgl, const char* funcName,
+                      WebGLUniformLocation* loc, size_t dataCount, const GLint* data,
+                      bool* const out_error)
+        : mLoc(loc)
+        , mDataCount(dataCount)
+        , mData(data)
+        , mIsValidatedSampler(false)
+    {
+        if (!mLoc->mInfo->mSamplerTexList) {
+            *out_error = false;
+            return;
+        }
+
+        for (size_t i = 0; i < mDataCount; i++) {
+            const auto& val = mData[i];
+            if (val < 0 || uint32_t(val) >= webgl->GLMaxTextureUnits()) {
+                webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d"
+                                         " is not a valid texture unit.",
+                                         funcName, val);
+                *out_error = true;
+                return;
+            }
+        }
+
+        mIsValidatedSampler = true;
+        *out_error = false;
+    }
+
+    ~ValidateIfSampler() {
+        if (!mIsValidatedSampler)
+            return;
+
+        auto& samplerValues = mLoc->mInfo->mSamplerValues;
+
+        for (size_t i = 0; i < mDataCount; i++) {
+            const size_t curIndex = mLoc->mArrayIndex + i;
+            if (curIndex >= samplerValues.size())
+                break;
+
+            samplerValues[curIndex] = mData[i];
+        }
+    }
+};
+
+////////////////////
+
 void
 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc))
+    const char funcName[] = "uniform1i";
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, funcName))
         return;
 
-    // Only uniform1i can take sampler settings.
-    if (!loc->ValidateSamplerSetter(a1, this, "uniform1i"))
+    bool error;
+    const ValidateIfSampler validate(this, funcName, loc, 1, &a1, &error);
+    if (error)
         return;
 
     MakeContextCurrent();
-    gl->fUniform1i(rawLoc, a1);
+    gl->fUniform1i(loc->mLoc, a1);
 }
 
 void
 WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, "uniform2i", &rawLoc))
+    const char funcName[] = "uniform2i";
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform2i(rawLoc, a1, a2);
+    gl->fUniform2i(loc->mLoc, a1, a2);
 }
 
 void
 WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, "uniform3i", &rawLoc))
+    const char funcName[] = "uniform3i";
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform3i(rawLoc, a1, a2, a3);
+    gl->fUniform3i(loc->mLoc, a1, a2, a3);
 }
 
 void
 WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3,
                         GLint a4)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, "uniform4i", &rawLoc))
+    const char funcName[] = "uniform4i";
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform4i(rawLoc, a1, a2, a3, a4);
+    gl->fUniform4i(loc->mLoc, a1, a2, a3, a4);
 }
 
+//////////
+
 void
 WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, "uniform1f", &rawLoc))
+    const char funcName[] = "uniform1f";
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform1f(rawLoc, a1);
+    gl->fUniform1f(loc->mLoc, a1);
 }
 
 void
 WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, "uniform2f", &rawLoc))
+    const char funcName[] = "uniform2f";
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform2f(rawLoc, a1, a2);
+    gl->fUniform2f(loc->mLoc, a1, a2);
 }
 
 void
 WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
                         GLfloat a3)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, "uniform3f", &rawLoc))
+    const char funcName[] = "uniform3f";
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform3f(rawLoc, a1, a2, a3);
+    gl->fUniform3f(loc->mLoc, a1, a2, a3);
 }
 
 void
 WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
                         GLfloat a3, GLfloat a4)
 {
-    GLuint rawLoc;
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, "uniform4f", &rawLoc))
+    const char funcName[] = "uniform4f";
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, funcName))
         return;
 
     MakeContextCurrent();
-    gl->fUniform4f(rawLoc, a1, a2, a3, a4);
+    gl->fUniform4f(loc->mLoc, a1, a2, a3, a4);
 }
 
 ////////////////////////////////////////
 // Array
 
 void
-WebGLContext::Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_INT, arrayLength,
-                                    "uniform1iv", &rawLoc,
-                                    &numElementsToUpload))
-    {
-        return;
-    }
-
-    if (!loc->ValidateSamplerSetter(data[0], this, "uniform1iv"))
-        return;
-
-    MakeContextCurrent();
-    gl->fUniform1iv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_INT, arrayLength,
-                                    "uniform2iv", &rawLoc,
-                                    &numElementsToUpload))
-    {
-        return;
-    }
-
-    if (!loc->ValidateSamplerSetter(data[0], this, "uniform2iv") ||
-        !loc->ValidateSamplerSetter(data[1], this, "uniform2iv"))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform2iv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLint* data)
+WebGLContext::UniformNiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                         const IntArr& arr)
 {
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_INT, arrayLength,
-                                    "uniform3iv", &rawLoc,
-                                    &numElementsToUpload))
-    {
-        return;
-    }
-
-    if (!loc->ValidateSamplerSetter(data[0], this, "uniform3iv") ||
-        !loc->ValidateSamplerSetter(data[1], this, "uniform3iv") ||
-        !loc->ValidateSamplerSetter(data[2], this, "uniform3iv"))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform3iv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLint* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_INT, arrayLength,
-                                    "uniform4iv", &rawLoc,
-                                    &numElementsToUpload))
-    {
-        return;
-    }
-
-    if (!loc->ValidateSamplerSetter(data[0], this, "uniform4iv") ||
-        !loc->ValidateSamplerSetter(data[1], this, "uniform4iv") ||
-        !loc->ValidateSamplerSetter(data[2], this, "uniform4iv") ||
-        !loc->ValidateSamplerSetter(data[3], this, "uniform4iv"))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniform4iv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_FLOAT, arrayLength,
-                                    "uniform1fv", &rawLoc,
+    uint32_t numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_INT, arr.dataCount, funcName,
                                     &numElementsToUpload))
     {
         return;
     }
 
-    MakeContextCurrent();
-    gl->fUniform1fv(rawLoc, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLfloat* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength,
-                                    "uniform2fv", &rawLoc,
-                                    &numElementsToUpload))
-    {
+    bool error;
+    const ValidateIfSampler samplerValidator(this, funcName, loc, numElementsToUpload,
+                                             arr.data, &error);
+    if (error)
         return;
-    }
+
+    static const decltype(&gl::GLContext::fUniform1iv) kFuncList[] = {
+        &gl::GLContext::fUniform1iv,
+        &gl::GLContext::fUniform2iv,
+        &gl::GLContext::fUniform3iv,
+        &gl::GLContext::fUniform4iv
+    };
+    const auto func = kFuncList[N-1];
 
     MakeContextCurrent();
-    gl->fUniform2fv(rawLoc, numElementsToUpload, data);
+    (gl->*func)(loc->mLoc, numElementsToUpload, arr.data);
 }
 
 void
-WebGLContext::Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLfloat* data)
+WebGL2Context::UniformNuiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                           const UintArr& arr)
 {
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength,
-                                    "uniform3fv", &rawLoc,
-                                    &numElementsToUpload))
+    uint32_t numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_UNSIGNED_INT, arr.dataCount,
+                                    funcName, &numElementsToUpload))
     {
         return;
     }
+    MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
+
+    static const decltype(&gl::GLContext::fUniform1uiv) kFuncList[] = {
+        &gl::GLContext::fUniform1uiv,
+        &gl::GLContext::fUniform2uiv,
+        &gl::GLContext::fUniform3uiv,
+        &gl::GLContext::fUniform4uiv
+    };
+    const auto func = kFuncList[N-1];
 
     MakeContextCurrent();
-    gl->fUniform3fv(rawLoc, numElementsToUpload, data);
+    (gl->*func)(loc->mLoc, numElementsToUpload, arr.data);
 }
 
 void
-WebGLContext::Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength,
-                              const GLfloat* data)
+WebGLContext::UniformNfv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
+                         const FloatArr& arr)
 {
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength,
-                                    "uniform4fv", &rawLoc,
+    uint32_t numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_FLOAT, arr.dataCount, funcName,
                                     &numElementsToUpload))
     {
         return;
     }
+    MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
+
+    static const decltype(&gl::GLContext::fUniform1fv) kFuncList[] = {
+        &gl::GLContext::fUniform1fv,
+        &gl::GLContext::fUniform2fv,
+        &gl::GLContext::fUniform3fv,
+        &gl::GLContext::fUniform4fv
+    };
+    const auto func = kFuncList[N-1];
 
     MakeContextCurrent();
-    gl->fUniform4fv(rawLoc, numElementsToUpload, data);
-}
-
-////////////////////////////////////////
-// Matrix
-
-void
-WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose,
-                                    size_t arrayLength, const float* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 2, 2, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix2fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix2fv(rawLoc, numElementsToUpload, false, data);
+    (gl->*func)(loc->mLoc, numElementsToUpload, arr.data);
 }
 
 void
-WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose,
-                                    size_t arrayLength, const float* data)
+WebGLContext::UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
+                                 WebGLUniformLocation* loc, bool transpose,
+                                 const FloatArr& arr)
 {
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 3, 3, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix3fv",
-                                          &rawLoc, &numElementsToUpload))
+    uint32_t numElementsToUpload;
+    if (!ValidateUniformMatrixArraySetter(loc, A, B, LOCAL_GL_FLOAT, arr.dataCount,
+                                          transpose, funcName, &numElementsToUpload))
     {
         return;
     }
+    MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
+
+    static const decltype(&gl::GLContext::fUniformMatrix2fv) kFuncList[] = {
+        &gl::GLContext::fUniformMatrix2fv,
+        &gl::GLContext::fUniformMatrix2x3fv,
+        &gl::GLContext::fUniformMatrix2x4fv,
+
+        &gl::GLContext::fUniformMatrix3x2fv,
+        &gl::GLContext::fUniformMatrix3fv,
+        &gl::GLContext::fUniformMatrix3x4fv,
+
+        &gl::GLContext::fUniformMatrix4x2fv,
+        &gl::GLContext::fUniformMatrix4x3fv,
+        &gl::GLContext::fUniformMatrix4fv
+    };
+    const auto func = kFuncList[3*(A-2) + (B-2)];
 
     MakeContextCurrent();
-    gl->fUniformMatrix3fv(rawLoc, numElementsToUpload, false, data);
-}
-
-void
-WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose,
-                                    size_t arrayLength, const float* data)
-{
-    GLuint rawLoc;
-    GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 4, 4, LOCAL_GL_FLOAT, arrayLength,
-                                          transpose, "uniformMatrix4fv",
-                                          &rawLoc, &numElementsToUpload))
-    {
-        return;
-    }
-
-    MakeContextCurrent();
-    gl->fUniformMatrix4fv(rawLoc, numElementsToUpload, false, data);
+    (gl->*func)(loc->mLoc, numElementsToUpload, false, arr.data);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 WebGLContext::UseProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -432,17 +432,17 @@ WebGLContext::ValidateUniformLocation(We
     if (!ValidateObject(funcName, loc))
         return false;
 
     if (!mCurrentProgram) {
         ErrorInvalidOperation("%s: No program is currently bound.", funcName);
         return false;
     }
 
-    return loc->ValidateForProgram(mCurrentProgram, this, funcName);
+    return loc->ValidateForProgram(mCurrentProgram, funcName);
 }
 
 bool
 WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize,
                                         uint32_t arrayLength)
 {
     if (IsContextLost())
         return false;
@@ -454,92 +454,92 @@ WebGLContext::ValidateAttribArraySetter(
     }
 
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
-                                    const char* funcName, GLuint* out_rawLoc)
+                                    const char* funcName)
 {
     if (IsContextLost())
         return false;
 
     if (!ValidateUniformLocation(loc, funcName))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
         return false;
 
-    *out_rawLoc = loc->mLoc;
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                          uint8_t setterElemSize,
                                          GLenum setterType,
-                                         size_t setterArraySize,
+                                         uint32_t setterArraySize,
                                          const char* funcName,
-                                         GLuint* const out_rawLoc,
-                                         GLsizei* const out_numElementsToUpload)
+                                         uint32_t* const out_numElementsToUpload)
 {
     if (IsContextLost())
         return false;
 
     if (!ValidateUniformLocation(loc, funcName))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
+        return false;
+
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
         return false;
 
-    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
-        return false;
+    const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
+    MOZ_ASSERT(elemCount > loc->mArrayIndex);
+    const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
 
-    MOZ_ASSERT((size_t)loc->mActiveInfo->mElemCount > loc->mArrayIndex);
-    size_t uniformElemCount = loc->mActiveInfo->mElemCount - loc->mArrayIndex;
-    *out_rawLoc = loc->mLoc;
-    *out_numElementsToUpload = std::min(uniformElemCount, setterArraySize / setterElemSize);
+    *out_numElementsToUpload = std::min(uniformElemCount,
+                                        setterArraySize / setterElemSize);
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                                uint8_t setterCols,
                                                uint8_t setterRows,
                                                GLenum setterType,
-                                               size_t setterArraySize,
+                                               uint32_t setterArraySize,
                                                bool setterTranspose,
                                                const char* funcName,
-                                               GLuint* const out_rawLoc,
-                                               GLsizei* const out_numElementsToUpload)
+                                               uint32_t* const out_numElementsToUpload)
 {
-    uint8_t setterElemSize = setterCols * setterRows;
+    const uint8_t setterElemSize = setterCols * setterRows;
 
     if (IsContextLost())
         return false;
 
     if (!ValidateUniformLocation(loc, funcName))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
         return false;
 
-    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
         return false;
 
     if (!ValidateUniformMatrixTranspose(setterTranspose, funcName))
         return false;
 
-    MOZ_ASSERT((size_t)loc->mActiveInfo->mElemCount > loc->mArrayIndex);
-    size_t uniformElemCount = loc->mActiveInfo->mElemCount - loc->mArrayIndex;
-    *out_rawLoc = loc->mLoc;
-    *out_numElementsToUpload = std::min(uniformElemCount, setterArraySize / setterElemSize);
+    const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
+    MOZ_ASSERT(elemCount > loc->mArrayIndex);
+    const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
 
+    *out_numElementsToUpload = std::min(uniformElemCount,
+                                        setterArraySize / setterElemSize);
     return true;
 }
 
 bool
 WebGLContext::ValidateAttribIndex(GLuint index, const char* info)
 {
     bool valid = (index < MaxVertexAttribs());
 
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -435,34 +435,38 @@ GetFormat(EffectiveFormat format)
 //////////////////////////////////////////////////////////////////////////////////////////
 
 const FormatInfo*
 FormatInfo::GetCopyDecayFormat(UnsizedFormat uf) const
 {
     return FindOrNull(this->copyDecayFormats, uf);
 }
 
-uint8_t
-BytesPerPixel(const PackingInfo& packing)
+bool
+GetBytesPerPixel(const PackingInfo& packing, uint8_t* const out_bytes)
 {
     uint8_t bytesPerChannel;
+
     switch (packing.type) {
     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        return 2;
+        *out_bytes = 2;
+        return true;
 
     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
     case LOCAL_GL_UNSIGNED_INT_24_8:
     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
-        return 4;
+        *out_bytes = 4;
+        return true;
 
     case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
-        return 8;
+        *out_bytes = 8;
+        return true;
 
     // Alright, that's all the fixed-size unpackTypes.
 
     case LOCAL_GL_BYTE:
     case LOCAL_GL_UNSIGNED_BYTE:
         bytesPerChannel = 1;
         break;
 
@@ -475,46 +479,68 @@ BytesPerPixel(const PackingInfo& packing
 
     case LOCAL_GL_INT:
     case LOCAL_GL_UNSIGNED_INT:
     case LOCAL_GL_FLOAT:
         bytesPerChannel = 4;
         break;
 
     default:
-        MOZ_CRASH("GFX: invalid PackingInfo");
+        return false;
     }
 
     uint8_t channels;
+
     switch (packing.format) {
+    case LOCAL_GL_RED:
+    case LOCAL_GL_RED_INTEGER:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_ALPHA:
+    case LOCAL_GL_DEPTH_COMPONENT:
+        channels = 1;
+        break;
+
     case LOCAL_GL_RG:
     case LOCAL_GL_RG_INTEGER:
     case LOCAL_GL_LUMINANCE_ALPHA:
         channels = 2;
         break;
 
     case LOCAL_GL_RGB:
     case LOCAL_GL_RGB_INTEGER:
+    case LOCAL_GL_SRGB:
         channels = 3;
         break;
 
+    case LOCAL_GL_BGRA:
     case LOCAL_GL_RGBA:
     case LOCAL_GL_RGBA_INTEGER:
+    case LOCAL_GL_SRGB_ALPHA:
         channels = 4;
         break;
 
     default:
-        channels = 1;
-        break;
+        return false;
     }
 
-    return bytesPerChannel * channels;
+    *out_bytes = bytesPerChannel * channels;
+    return true;
 }
 
+uint8_t
+BytesPerPixel(const PackingInfo& packing)
+{
+    uint8_t ret;
+    if (MOZ_LIKELY(GetBytesPerPixel(packing, &ret)))
+        return ret;
 
+    gfxCriticalError() << "Bad `packing`: " << gfx::hexa(packing.format) << ", "
+                       << gfx::hexa(packing.type);
+    MOZ_CRASH("Bad `packing`.");
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -249,16 +249,17 @@ struct DriverUnpackInfo
         return {unpackFormat, unpackType};
     }
 };
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 const FormatInfo* GetFormat(EffectiveFormat format);
 uint8_t BytesPerPixel(const PackingInfo& packing);
+bool GetBytesPerPixel(const PackingInfo& packing, uint8_t* const out_bytes);
 /*
 GLint ComponentSize(const FormatInfo* format, GLenum component);
 GLenum ComponentType(const FormatInfo* format);
 */
 ////////////////////////////////////////
 
 struct FormatUsageInfo
 {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1276,16 +1276,44 @@ WebGLFramebuffer::GetAttachmentParameter
     }
 
     FinalizeAttachments();
 
     return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname,
                                      out_error);
 }
 
+
+void
+WebGLFramebuffer::GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const
+{
+    auto itr = mDrawBuffers.cbegin();
+    if (itr != mDrawBuffers.cend() &&
+        *itr != LOCAL_GL_NONE)
+    {
+        out->push_back(&mColorAttachment0);
+        ++itr;
+    }
+
+    size_t i = 0;
+    for (; itr != mDrawBuffers.cend(); ++itr) {
+        if (i >= mMoreColorAttachments.Size())
+            break;
+
+        if (*itr != LOCAL_GL_NONE) {
+            out->push_back(&mMoreColorAttachments[i]);
+        }
+        ++i;
+    }
+
+    out->push_back(&mDepthAttachment);
+    out->push_back(&mStencilAttachment);
+    out->push_back(&mDepthStencilAttachment);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLFramebufferBinding::Wrap(cx, this, givenProto);
 }
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -33,17 +33,17 @@ class WebGLFBAttachPoint
 public:
     WebGLFramebuffer* const mFB;
     const GLenum mAttachmentPoint;
 private:
     WebGLRefPtr<WebGLTexture> mTexturePtr;
     WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
     TexImageTarget mTexImageTarget;
     GLint mTexImageLayer;
-    GLint mTexImageLevel;
+    uint32_t mTexImageLevel;
 
     // PlacementArray needs a default constructor.
     template<typename T>
     friend class PlacementArray;
 
     WebGLFBAttachPoint()
         : mFB(nullptr)
         , mAttachmentPoint(0)
@@ -84,17 +84,17 @@ public:
         return mRenderbufferPtr;
     }
     TexImageTarget ImageTarget() const {
         return mTexImageTarget;
     }
     GLint Layer() const {
         return mTexImageLayer;
     }
-    GLint MipLevel() const {
+    uint32_t MipLevel() const {
         return mTexImageLevel;
     }
     void AttachmentName(nsCString* out) const;
 
     bool HasUninitializedImageData() const;
     void SetImageDataStatus(WebGLImageDataStatus x);
 
     void Size(uint32_t* const out_width, uint32_t* const out_height) const;
@@ -254,16 +254,18 @@ public:
     }
 
     void SetReadBufferMode(GLenum readBufferMode) {
         mReadBufferMode = readBufferMode;
     }
 
     GLenum ReadBufferMode() const { return mReadBufferMode; }
 
+    void GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const;
+
 protected:
     WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
 
 public:
     void DetachTexture(const WebGLTexture* tex);
 
     void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -64,46 +64,70 @@ ParseName(const nsCString& name, nsCStri
         return false;
 
     *out_baseName = StringHead(name, indexOpenBracket);
     *out_isArray = true;
     *out_arrayIndex = indexNum;
     return true;
 }
 
-static WebGLActiveInfo*
-AddActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
-              const nsACString& baseUserName, const nsACString& baseMappedName,
-              std::vector<RefPtr<WebGLActiveInfo>>* activeInfoList,
-              std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
+//////////
+
+/*static*/ const webgl::UniformInfo::TexListT*
+webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
 {
-    RefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
-                                                       isArray, baseUserName,
-                                                       baseMappedName);
-    activeInfoList->push_back(info);
+    const auto& webgl = activeInfo->mWebGL;
+
+    switch (activeInfo->mElemType) {
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+        return &webgl->mBound2DTextures;
+
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+        return &webgl->mBoundCubeMapTextures;
 
-    infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
-    return info.get();
+    case LOCAL_GL_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+        return &webgl->mBound3DTextures;
+
+    case LOCAL_GL_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+        return &webgl->mBound2DArrayTextures;
+
+    default:
+        return nullptr;
+    }
 }
 
-static void
-AddActiveBlockInfo(const nsACString& baseUserName,
-                   const nsACString& baseMappedName,
-                   std::vector<RefPtr<webgl::UniformBlockInfo>>* activeInfoList)
+webgl::UniformInfo::UniformInfo(WebGLActiveInfo* activeInfo)
+    : mActiveInfo(activeInfo)
+    , mSamplerTexList(GetTexList(activeInfo))
 {
-    RefPtr<webgl::UniformBlockInfo> info = new webgl::UniformBlockInfo(baseUserName, baseMappedName);
+    if (mSamplerTexList) {
+        mSamplerValues.assign(mActiveInfo->mElemCount, 0);
+    }
+}
 
-    activeInfoList->push_back(info);
-}
+//////////
 
 //#define DUMP_SHADERVAR_MAPPINGS
 
 static already_AddRefed<const webgl::LinkedProgramInfo>
 QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
 {
+    WebGLContext* const webgl = prog->mContext;
+
     RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
 
     GLuint maxAttribLenWithNull = 0;
     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
                       (GLint*)&maxAttribLenWithNull);
     if (maxAttribLenWithNull < 1)
         maxAttribLenWithNull = 1;
 
@@ -149,43 +173,47 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         GLsizei lengthWithoutNull = 0;
         GLint elemCount = 0; // `size`
         GLenum elemType = 0; // `type`
         gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
                              &elemCount, &elemType, mappedName.BeginWriting());
 
         mappedName.SetLength(lengthWithoutNull);
 
-        // Collect ActiveInfos:
-
         // Attribs can't be arrays, so we can skip some of the mess we have in the Uniform
         // path.
         nsDependentCString userName;
         if (!prog->FindAttribUserNameByMappedName(mappedName, &userName))
             userName.Rebind(mappedName, 0);
 
+        ///////
+
+        const GLint loc = gl->fGetAttribLocation(prog->mGLName,
+                                                 mappedName.BeginReading());
+        if (loc == -1) {
+            MOZ_ASSERT(mappedName == "gl_InstanceID",
+                       "Active attrib should have a location.");
+            continue;
+        }
+
 #ifdef DUMP_SHADERVAR_MAPPINGS
-        printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(),
+        printf_stderr("[attrib %i: %i] %s/%s\n", i, loc, mappedName.BeginReading(),
                       userName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
 #endif
 
-        const bool isArray = false;
-        const auto attrib = AddActiveInfo(prog->mContext, elemCount, elemType, isArray,
-                                          userName, mappedName, &info->activeAttribs,
-                                          &info->attribMap);
+        ///////
 
-        // Collect active locations:
-        GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
-        if (loc == -1) {
-            if (mappedName != "gl_InstanceID")
-                MOZ_CRASH("GFX: Active attrib has no location.");
-        } else {
-            info->activeAttribLocs.insert({attrib, (GLuint)loc});
-        }
+        const bool isArray = false;
+        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
+                                                                       elemType, isArray,
+                                                                       userName,
+                                                                       mappedName);
+        const webgl::AttribInfo attrib = {activeInfo, uint32_t(loc)};
+        info->attribs.push_back(attrib);
     }
 
     // Uniforms
 
     const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
 
     GLuint numActiveUniforms = 0;
     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
@@ -198,55 +226,74 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         GLsizei lengthWithoutNull = 0;
         GLint elemCount = 0; // `size`
         GLenum elemType = 0; // `type`
         gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
                               &elemCount, &elemType, mappedName.BeginWriting());
 
         mappedName.SetLength(lengthWithoutNull);
 
+        ///////
+
         nsAutoCString baseMappedName;
         bool isArray;
         size_t arrayIndex;
         if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
             MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
 
         // Note that for good drivers, `isArray` should already be correct.
         // However, if FindUniform succeeds, it will be validator-guaranteed correct.
 
+        ///////
+
         nsAutoCString baseUserName;
         if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
+            // Validator likely missing.
             baseUserName = baseMappedName;
 
             if (needsCheckForArrays && !isArray) {
                 // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
                 // not an array. Our current linux Try slaves return the location of `foo`
                 // anyways, though.
                 std::string mappedNameStr = baseMappedName.BeginReading();
                 mappedNameStr += "[0]";
 
                 GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
                 if (loc != -1)
                     isArray = true;
             }
         }
 
+        ///////
+
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                       (int)isArray, baseMappedName.BeginReading(),
                       baseUserName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
         printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
-        AddActiveInfo(prog->mContext, elemCount, elemType, isArray, baseUserName,
-                      baseMappedName, &info->activeUniforms, &info->uniformMap);
+        ///////
+
+        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
+                                                                       elemType, isArray,
+                                                                       baseUserName,
+                                                                       baseMappedName);
+
+        auto* uniform = new webgl::UniformInfo(activeInfo);
+        info->uniforms.push_back(uniform);
+
+        if (uniform->mSamplerTexList) {
+            info->uniformSamplers.push_back(uniform);
+        }
     }
 
     // Uniform Blocks
+    // (no sampler types allowed!)
 
     if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
         GLuint numActiveUniformBlocks = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
                           (GLint*)&numActiveUniformBlocks);
 
         for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
             nsAutoCString mappedName;
@@ -276,82 +323,103 @@ QueryProgramInfo(WebGLProgram* prog, gl:
                     GLuint loc = gl->fGetUniformBlockIndex(prog->mGLName,
                                                            mappedNameStr.c_str());
                     if (loc != LOCAL_GL_INVALID_INDEX)
                         isArray = true;
                 }
             }
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
-            printf_stderr("[uniform block %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
-                          (int)isArray, baseMappedName.BeginReading(),
-                          baseUserName.BeginReading());
+            printf_stderr("[uniform block %i] %s/%i/%s/%s\n", i,
+                          mappedName.BeginReading(), (int)isArray,
+                          baseMappedName.BeginReading(), baseUserName.BeginReading());
             printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
             printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
-            AddActiveBlockInfo(baseUserName, baseMappedName, &info->uniformBlocks);
+            const auto* block = new webgl::UniformBlockInfo(baseUserName, baseMappedName);
+            info->uniformBlocks.push_back(block);
         }
     }
 
     // Transform feedback varyings
 
     if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
         GLuint numTransformFeedbackVaryings = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
                           (GLint*)&numTransformFeedbackVaryings);
 
         for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
             nsAutoCString mappedName;
             mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
 
             GLint lengthWithoutNull;
-            GLsizei size;
-            GLenum type;
-            gl->fGetTransformFeedbackVarying(prog->mGLName, i, maxTransformFeedbackVaryingLenWithNull,
-                                             &lengthWithoutNull, &size, &type,
+            GLsizei elemCount;
+            GLenum elemType;
+            gl->fGetTransformFeedbackVarying(prog->mGLName, i,
+                                             maxTransformFeedbackVaryingLenWithNull,
+                                             &lengthWithoutNull, &elemCount, &elemType,
                                              mappedName.BeginWriting());
             mappedName.SetLength(lengthWithoutNull);
 
+            ////
+
             nsAutoCString baseMappedName;
             bool isArray;
             size_t arrayIndex;
             if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
                 MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
 
+
             nsAutoCString baseUserName;
             if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
                 baseUserName = baseMappedName;
 
                 if (needsCheckForArrays && !isArray) {
                     std::string mappedNameStr = baseMappedName.BeginReading();
                     mappedNameStr += "[0]";
 
                     GLuint loc = gl->fGetUniformBlockIndex(prog->mGLName,
                                                            mappedNameStr.c_str());
                     if (loc != LOCAL_GL_INVALID_INDEX)
                         isArray = true;
                 }
             }
 
-            AddActiveInfo(prog->mContext, size, type, isArray, baseUserName, mappedName,
-                          &info->transformFeedbackVaryings,
-                          &info->transformFeedbackVaryingsMap);
+            ////
+
+            const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl,
+                                                                           elemCount,
+                                                                           elemType,
+                                                                           isArray,
+                                                                           baseUserName,
+                                                                           mappedName);
+            info->transformFeedbackVaryings.push_back(activeInfo);
         }
     }
 
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
     : prog(prog)
 { }
 
+webgl::LinkedProgramInfo::~LinkedProgramInfo()
+{
+    for (auto& cur : uniforms) {
+        delete cur;
+    }
+    for (auto& cur : uniformBlocks) {
+        delete cur;
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLProgram
 
 static GLuint
 CreateProgram(gl::GLContext* gl)
 {
     gl->MakeCurrent();
     return gl->fCreateProgram();
@@ -482,46 +550,46 @@ WebGLProgram::DetachShader(WebGLShader* 
 already_AddRefed<WebGLActiveInfo>
 WebGLProgram::GetActiveAttrib(GLuint index) const
 {
     if (!mMostRecentLinkInfo) {
         RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
         return ret.forget();
     }
 
-    const auto& activeList = mMostRecentLinkInfo->activeAttribs;
+    const auto& attribs = mMostRecentLinkInfo->attribs;
 
-    if (index >= activeList.size()) {
+    if (index >= attribs.size()) {
         mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
-                                    index, "ACTIVE_ATTRIBS", activeList.size());
+                                    index, "ACTIVE_ATTRIBS", attribs.size());
         return nullptr;
     }
 
-    RefPtr<WebGLActiveInfo> ret = activeList[index];
+    RefPtr<WebGLActiveInfo> ret = attribs[index].mActiveInfo;
     return ret.forget();
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLProgram::GetActiveUniform(GLuint index) const
 {
     if (!mMostRecentLinkInfo) {
         // According to the spec, this can return null.
         RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
         return ret.forget();
     }
 
-    const auto& activeList = mMostRecentLinkInfo->activeUniforms;
+    const auto& uniforms = mMostRecentLinkInfo->uniforms;
 
-    if (index >= activeList.size()) {
+    if (index >= uniforms.size()) {
         mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
-                                    index, "ACTIVE_UNIFORMS", activeList.size());
+                                    index, "ACTIVE_UNIFORMS", uniforms.size());
         return nullptr;
     }
 
-    RefPtr<WebGLActiveInfo> ret = activeList[index];
+    RefPtr<WebGLActiveInfo> ret = uniforms[index]->mActiveInfo;
     return ret.forget();
 }
 
 void
 WebGLProgram::GetAttachedShaders(nsTArray<RefPtr<WebGLShader>>* const out) const
 {
     out->TruncateLength(0);
 
@@ -540,26 +608,21 @@ WebGLProgram::GetAttribLocation(const ns
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
         return -1;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
 
-    const WebGLActiveInfo* info;
+    const webgl::AttribInfo* info;
     if (!LinkInfo()->FindAttrib(userName, &info))
         return -1;
 
-    const nsCString& mappedName = info->mBaseMappedName;
-
-    gl::GLContext* gl = mContext->GL();
-    gl->MakeCurrent();
-
-    return gl->fGetAttribLocation(mGLName, mappedName.BeginReading());
+    return GLint(info->mLoc);
 }
 
 GLint
 WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const
 {
     if (!ValidateGLSLVariableName(userName_wide, mContext, "getFragDataLocation"))
         return -1;
 
@@ -653,23 +716,22 @@ WebGLProgram::GetUniformBlockIndex(const
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
 
     nsDependentCString baseUserName;
     bool isArray;
     size_t arrayIndex;
     if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
         return LOCAL_GL_INVALID_INDEX;
 
-    RefPtr<const webgl::UniformBlockInfo> info;
+    const webgl::UniformBlockInfo* info;
     if (!LinkInfo()->FindUniformBlock(baseUserName, &info)) {
         return LOCAL_GL_INVALID_INDEX;
     }
 
-    const nsCString& baseMappedName = info->mBaseMappedName;
-    nsAutoCString mappedName(baseMappedName);
+    nsAutoCString mappedName(info->mBaseMappedName);
     if (isArray) {
         mappedName.AppendLiteral("[");
         mappedName.AppendInt(uint32_t(arrayIndex));
         mappedName.AppendLiteral("]");
     }
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
@@ -794,39 +856,36 @@ WebGLProgram::GetUniformLocation(const n
     // element of that array can be retrieved by either using the name of the
     // uniform array, or the name of the uniform array appended with "[0]".
     // The ParseName() can't recognize this rule. So always initialize
     // arrayIndex with 0.
     size_t arrayIndex = 0;
     if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
         return nullptr;
 
-    const WebGLActiveInfo* activeInfo;
-    if (!LinkInfo()->FindUniform(baseUserName, &activeInfo))
+    webgl::UniformInfo* info;
+    if (!LinkInfo()->FindUniform(baseUserName, &info))
         return nullptr;
 
-    const nsCString& baseMappedName = activeInfo->mBaseMappedName;
-
-    nsAutoCString mappedName(baseMappedName);
+    nsAutoCString mappedName(info->mActiveInfo->mBaseMappedName);
     if (isArray) {
         mappedName.AppendLiteral("[");
         mappedName.AppendInt(uint32_t(arrayIndex));
         mappedName.AppendLiteral("]");
     }
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
     if (loc == -1)
         return nullptr;
 
     RefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
-                                                                   loc, arrayIndex,
-                                                                   activeInfo);
+                                                                   info, loc, arrayIndex);
     return locObj.forget();
 }
 
 void
 WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
                                 dom::Nullable< nsTArray<GLuint> >& retval) const
 {
     size_t count = uniformNames.Length();
@@ -841,25 +900,23 @@ WebGLProgram::GetUniformIndices(const do
         nsDependentCString baseUserName;
         bool isArray;
         size_t arrayIndex;
         if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) {
             arr.AppendElement(LOCAL_GL_INVALID_INDEX);
             continue;
         }
 
-        const WebGLActiveInfo* activeInfo;
-        if (!LinkInfo()->FindUniform(baseUserName, &activeInfo)) {
+        webgl::UniformInfo* info;
+        if (!LinkInfo()->FindUniform(baseUserName, &info)) {
             arr.AppendElement(LOCAL_GL_INVALID_INDEX);
             continue;
         }
 
-        const nsCString& baseMappedName = activeInfo->mBaseMappedName;
-
-        nsAutoCString mappedName(baseMappedName);
+        nsAutoCString mappedName(info->mActiveInfo->mBaseMappedName);
         if (isArray) {
             mappedName.AppendLiteral("[");
             mappedName.AppendInt(uint32_t(arrayIndex));
             mappedName.AppendLiteral("]");
         }
 
         const GLchar* mappedNameBytes = mappedName.BeginReading();
 
@@ -874,18 +931,17 @@ void
 WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const
 {
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
         return;
     }
 
     const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
-    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
-    if (uniformBlockIndex >= uniformBlockCount) {
+    if (uniformBlockIndex >= linkInfo->uniformBlocks.size()) {
         mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
         return;
     }
 
     if (uniformBlockBinding > mContext->mGLMaxUniformBufferBindings) {
         mContext->ErrorInvalidEnum("getActiveUniformBlockName: binding %u invalid.", uniformBlockBinding);
         return;
     }
@@ -985,59 +1041,80 @@ WebGLProgram::LinkProgram()
         if (!mLinkLog.IsEmpty()) {
             mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
                                       " log:\n%s\n",
                                       mLinkLog.BeginReading());
         }
     }
 }
 
+static uint8_t
+NumUsedLocationsByElemType(GLenum elemType)
+{
+    // GLES 3.0.4 p55
+
+    switch (elemType) {
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_MAT2x3:
+    case LOCAL_GL_FLOAT_MAT2x4:
+        return 2;
+
+    case LOCAL_GL_FLOAT_MAT3x2:
+    case LOCAL_GL_FLOAT_MAT3:
+    case LOCAL_GL_FLOAT_MAT3x4:
+        return 3;
+
+    case LOCAL_GL_FLOAT_MAT4x2:
+    case LOCAL_GL_FLOAT_MAT4x3:
+    case LOCAL_GL_FLOAT_MAT4:
+        return 4;
+
+    default:
+        return 1;
+    }
+}
+
 bool
 WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
 {
     const auto& linkInfo = mMostRecentLinkInfo;
 
     // Check if the attrib name conflicting to uniform name
-    for (const auto& uniform : linkInfo->uniformMap) {
-        if (linkInfo->attribMap.find(uniform.first) != linkInfo->attribMap.end()) {
-            *out_linkLog = nsPrintfCString("The uniform name (%s) conflicts with"
-                                           " attribute name.",
-                                           uniform.first.get());
-            return false;
+    for (const auto& attrib : linkInfo->attribs) {
+        const auto& attribName = attrib.mActiveInfo->mBaseUserName;
+
+        for (const auto& uniform : linkInfo->uniforms) {
+            const auto& uniformName = uniform->mActiveInfo->mBaseUserName;
+            if (attribName == uniformName) {
+                *out_linkLog = nsPrintfCString("Attrib name conflicts with uniform name:"
+                                               " %s",
+                                               attribName.BeginReading());
+                return false;
+            }
         }
     }
 
-    std::map<GLuint, const WebGLActiveInfo*> attribsByLoc;
-    for (const auto& pair : linkInfo->activeAttribLocs) {
-        const auto dupe = attribsByLoc.find(pair.second);
-        if (dupe != attribsByLoc.end()) {
-            *out_linkLog = nsPrintfCString("Aliased location between active attribs"
-                                           " \"%s\" and \"%s\".",
-                                           dupe->second->mBaseUserName.BeginReading(),
-                                           pair.first->mBaseUserName.BeginReading());
-            return false;
-        }
-    }
+    std::map<uint32_t, const webgl::AttribInfo*> attribsByLoc;
+    for (const auto& attrib : linkInfo->attribs) {
+        const auto& elemType = attrib.mActiveInfo->mElemType;
+        const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
+        for (uint32_t i = 0; i < numUsedLocs; i++) {
+            const uint32_t usedLoc = attrib.mLoc + i;
 
-    for (const auto& pair : attribsByLoc) {
-        const GLuint attribLoc = pair.first;
-        const auto attrib = pair.second;
-
-        const auto elemSize = ElemSizeFromType(attrib->mElemType);
-        const GLuint locationsUsed = (elemSize + 3) / 4;
-        for (GLuint i = 1; i < locationsUsed; i++) {
-            const GLuint usedLoc = attribLoc + i;
-
-            const auto dupe = attribsByLoc.find(usedLoc);
-            if (dupe != attribsByLoc.end()) {
-                *out_linkLog = nsPrintfCString("Attrib \"%s\" of type \"0x%04x\" aliases"
-                                               " \"%s\" by overhanging its location.",
-                                               attrib->mBaseUserName.BeginReading(),
-                                               attrib->mElemType,
-                                               dupe->second->mBaseUserName.BeginReading());
+            const auto res = attribsByLoc.insert({usedLoc, &attrib});
+            const bool& didInsert = res.second;
+            if (!didInsert) {
+                const auto& aliasingName = attrib.mActiveInfo->mBaseUserName;
+                const auto& itrExisting = res.first;
+                const auto& existingInfo = itrExisting->second;
+                const auto& existingName = existingInfo->mActiveInfo->mBaseUserName;
+                *out_linkLog = nsPrintfCString("Attrib \"%s\" aliases locations used by"
+                                               " attrib \"%s\".",
+                                               aliasingName.BeginReading(),
+                                               existingName.BeginReading());
                 return false;
             }
         }
     }
 
     return true;
 }
 
@@ -1229,16 +1306,60 @@ WebGLProgram::FindUniformBlockByMappedNa
     if (mFragShader->FindUniformBlockByMappedName(mappedName, out_userName, out_isArray))
         return true;
 
     return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+bool
+webgl::LinkedProgramInfo::FindAttrib(const nsCString& baseUserName,
+                                     const webgl::AttribInfo** const out) const
+{
+    for (const auto& attrib : attribs) {
+        if (attrib.mActiveInfo->mBaseUserName == baseUserName) {
+            *out = &attrib;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool
+webgl::LinkedProgramInfo::FindUniform(const nsCString& baseUserName,
+                                      webgl::UniformInfo** const out) const
+{
+    for (const auto& uniform : uniforms) {
+        if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
+            *out = uniform;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool
+webgl::LinkedProgramInfo::FindUniformBlock(const nsCString& baseUserName,
+                                           const webgl::UniformBlockInfo** const out) const
+{
+    for (const auto& block : uniformBlocks) {
+        if (block->mBaseUserName == baseUserName) {
+            *out = block;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 JSObject*
 WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
 
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -30,21 +30,40 @@ class WebGLUniformLocation;
 namespace dom {
 template<typename> struct Nullable;
 class OwningUnsignedLongOrUint32ArrayOrBoolean;
 template<typename> class Sequence;
 } // namespace dom
 
 namespace webgl {
 
-struct UniformBlockInfo final
-    : public RefCounted<UniformBlockInfo>
+struct AttribInfo final
+{
+    const RefPtr<WebGLActiveInfo> mActiveInfo;
+    uint32_t mLoc;
+};
+
+struct UniformInfo final
 {
-    MOZ_DECLARE_REFCOUNTED_TYPENAME(UniformBlockInfo);
+    typedef decltype(WebGLContext::mBound2DTextures) TexListT;
+
+    const RefPtr<WebGLActiveInfo> mActiveInfo;
+    const TexListT* const mSamplerTexList;
+    std::vector<uint32_t> mSamplerValues;
 
+protected:
+    static const TexListT*
+    GetTexList(WebGLActiveInfo* activeInfo);
+
+public:
+    explicit UniformInfo(WebGLActiveInfo* activeInfo);
+};
+
+struct UniformBlockInfo final
+{
     const nsCString mBaseUserName;
     const nsCString mBaseMappedName;
 
     UniformBlockInfo(const nsACString& baseUserName,
                      const nsACString& baseMappedName)
         : mBaseUserName(baseUserName)
         , mBaseMappedName(baseMappedName)
     {}
@@ -52,71 +71,37 @@ struct UniformBlockInfo final
 
 struct LinkedProgramInfo final
     : public RefCounted<LinkedProgramInfo>
     , public SupportsWeakPtr<LinkedProgramInfo>
 {
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
 
+    //////
+
     WebGLProgram* const prog;
-    std::vector<RefPtr<WebGLActiveInfo>> activeAttribs;
-    std::vector<RefPtr<WebGLActiveInfo>> activeUniforms;
+
+    std::vector<AttribInfo> attribs;
+    std::vector<UniformInfo*> uniforms; // Owns its contents.
+    std::vector<const UniformBlockInfo*> uniformBlocks; // Owns its contents.
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
-    // Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped
-    // user-facing `GLActiveInfo::name`s, without any final "[0]".
-    std::map<nsCString, const WebGLActiveInfo*> attribMap;
-    std::map<nsCString, const WebGLActiveInfo*> uniformMap;
-    std::map<nsCString, const WebGLActiveInfo*> transformFeedbackVaryingsMap;
-
-    std::vector<RefPtr<UniformBlockInfo>> uniformBlocks;
-
     // Needed for draw call validation.
-    std::map<const WebGLActiveInfo*, GLuint> activeAttribLocs;
+    std::vector<UniformInfo*> uniformSamplers;
 
     //////
 
     explicit LinkedProgramInfo(WebGLProgram* prog);
-
-    bool FindAttrib(const nsCString& baseUserName,
-                    const WebGLActiveInfo** const out_activeInfo) const
-    {
-        auto itr = attribMap.find(baseUserName);
-        if (itr == attribMap.end())
-            return false;
-
-        *out_activeInfo = itr->second;
-        return true;
-    }
+    ~LinkedProgramInfo();
 
-    bool FindUniform(const nsCString& baseUserName,
-                     const WebGLActiveInfo** const out_activeInfo) const
-    {
-        auto itr = uniformMap.find(baseUserName);
-        if (itr == uniformMap.end())
-            return false;
-
-        *out_activeInfo = itr->second;
-        return true;
-    }
-
+    bool FindAttrib(const nsCString& baseUserName, const AttribInfo** const out) const;
+    bool FindUniform(const nsCString& baseUserName, UniformInfo** const out) const;
     bool FindUniformBlock(const nsCString& baseUserName,
-                          RefPtr<const UniformBlockInfo>* const out_info) const
-    {
-        const size_t count = uniformBlocks.size();
-        for (size_t i = 0; i < count; i++) {
-            if (baseUserName == uniformBlocks[i]->mBaseUserName) {
-                *out_info = uniformBlocks[i].get();
-                return true;
-            }
-        }
-
-        return false;
-    }
+                          const UniformBlockInfo** const out) const;
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -190,26 +190,26 @@ WebGLTexture::SetImageInfosAtLevel(uint3
 }
 
 bool
 WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
 {
     MOZ_ASSERT(DoesMinFilterRequireMipmap());
     // GLES 3.0.4, p161
 
-    const uint32_t maxLevel = MaxEffectiveMipmapLevel(texUnit);
+    uint32_t maxLevel;
+    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
+        return false;
 
     // "* `level_base <= level_max`"
     if (mBaseMipmapLevel > maxLevel)
         return false;
 
     // Make a copy so we can modify it.
     const ImageInfo& baseImageInfo = BaseImageInfo();
-    if (!baseImageInfo.IsDefined())
-        return false;
 
     // Reference dimensions based on the current level.
     uint32_t refWidth = baseImageInfo.mWidth;
     uint32_t refHeight = baseImageInfo.mHeight;
     uint32_t refDepth = baseImageInfo.mDepth;
     MOZ_ASSERT(refWidth && refHeight && refDepth);
 
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
@@ -420,34 +420,36 @@ WebGLTexture::IsComplete(uint32_t texUni
         //    image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
         //    mipmap and the texture is not mipmap cube complete."
         // (already covered)
     }
 
     return true;
 }
 
-
-uint32_t
-WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit) const
+bool
+WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
 {
     WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
     TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
     if (minFilter == LOCAL_GL_NEAREST ||
         minFilter == LOCAL_GL_LINEAR)
     {
-        // No mips used.
-        return mBaseMipmapLevel;
+        // No extra mips used.
+        *out = mBaseMipmapLevel;
+        return true;
     }
 
     const auto& imageInfo = BaseImageInfo();
-    MOZ_ASSERT(imageInfo.IsDefined());
+    if (!imageInfo.IsDefined())
+        return false;
 
-    uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1;
-    return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+    uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
+    *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+    return true;
 }
 
 bool
 WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
                                FakeBlackType* const out_fakeBlack)
 {
     const char* incompleteReason;
     if (!IsComplete(texUnit, &incompleteReason)) {
@@ -461,17 +463,19 @@ WebGLTexture::GetFakeBlackType(const cha
         *out_fakeBlack = FakeBlackType::RGBA0001;
         return true;
     }
 
     // We may still want FakeBlack as an optimization for uninitialized image data.
     bool hasUninitializedData = false;
     bool hasInitializedData = false;
 
-    const auto maxLevel = MaxEffectiveMipmapLevel(texUnit);
+    uint32_t maxLevel;
+    MOZ_ALWAYS_TRUE( MaxEffectiveMipmapLevel(texUnit, &maxLevel) );
+
     MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
         for (uint8_t face = 0; face < mFaceCount; face++) {
             const auto& cur = ImageInfoAtFace(face, level);
             if (cur.IsDataInitialized())
                 hasInitializedData = true;
             else
                 hasUninitializedData = true;
@@ -785,18 +789,18 @@ WebGLTexture::GenerateMipmap(TexTarget t
                            mMinFilter.get());
     } else {
         gl->fGenerateMipmap(texTarget.get());
     }
 
     // Record the results.
     // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
     // mBaseMipmapLevel if the min filter doesn't require mipmaps.
-    const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1;
-    PopulateMipChain(mBaseMipmapLevel, lastLevel);
+    const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
+    PopulateMipChain(mBaseMipmapLevel, maxLevel);
 }
 
 JS::Value
 WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
 {
     mContext->MakeContextCurrent();
 
     GLint i = 0;
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -149,19 +149,20 @@ public:
             if (!IsDefined())
                 Clear();
         }
 
     protected:
         ImageInfo& operator =(const ImageInfo& a);
 
     public:
-        uint32_t MaxMipmapLevels() const {
+        uint32_t PossibleMipmapLevels() const {
             // GLES 3.0.4, 3.8 - Mipmapping: `floor(log2(largest_of_dims)) + 1`
-            uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+            const uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+            MOZ_ASSERT(largest != 0);
             return FloorLog2Size(largest) + 1;
         }
 
         bool IsPowerOfTwo() const;
 
         void AddAttachPoint(WebGLFBAttachPoint* attachPoint);
         void RemoveAttachPoint(WebGLFBAttachPoint* attachPoint);
         void OnRespecify() const;
@@ -283,17 +284,17 @@ public:
 
     ////////////////////////////////////
 
 protected:
     void ClampLevelBaseAndMax();
 
     void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
 
-    uint32_t MaxEffectiveMipmapLevel(uint32_t texUnit) const;
+    bool MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const;
 
     static uint8_t FaceForTarget(TexImageTarget texImageTarget) {
         GLenum rawTexImageTarget = texImageTarget.get();
         switch (rawTexImageTarget) {
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
@@ -383,16 +384,19 @@ public:
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
     // Resolve cache optimizations
 protected:
     bool GetFakeBlackType(const char* funcName, uint32_t texUnit,
                           FakeBlackType* const out_fakeBlack);
 public:
+    bool IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+                    const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const;
+
     bool ResolveForDraw(const char* funcName, uint32_t texUnit,
                         FakeBlackType* const out_fakeBlack);
 
     void InvalidateResolveCache() { mIsResolved = false; }
 };
 
 inline TexImageTarget
 TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -11,44 +11,42 @@
 #include "WebGLActiveInfo.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
 
 namespace mozilla {
 
 WebGLUniformLocation::WebGLUniformLocation(WebGLContext* webgl,
                                            const webgl::LinkedProgramInfo* linkInfo,
-                                           GLuint loc,
-                                           size_t arrayIndex,
-                                           const WebGLActiveInfo* activeInfo)
+                                           webgl::UniformInfo* info, GLuint loc,
+                                           size_t arrayIndex)
     : WebGLContextBoundObject(webgl)
     , mLinkInfo(linkInfo)
+    , mInfo(info)
     , mLoc(loc)
     , mArrayIndex(arrayIndex)
-    , mActiveInfo(activeInfo)
 { }
 
 WebGLUniformLocation::~WebGLUniformLocation()
 { }
 
 bool
-WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
-                                         const char* funcName) const
+WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, const char* funcName) const
 {
     // Check the weak-pointer.
     if (!mLinkInfo) {
-        webgl->ErrorInvalidOperation("%s: This uniform location is obsolete because its"
-                                     " program has been successfully relinked.",
-                                     funcName);
+        mContext->ErrorInvalidOperation("%s: This uniform location is obsolete because"
+                                        " its program has been successfully relinked.",
+                                        funcName);
         return false;
     }
 
     if (mLinkInfo->prog != prog) {
-        webgl->ErrorInvalidOperation("%s: This uniform location corresponds to a"
-                                     " different program.", funcName);
+        mContext->ErrorInvalidOperation("%s: This uniform location corresponds to a"
+                                        " different program.", funcName);
         return false;
     }
 
     return true;
 }
 
 static bool
 IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
@@ -116,106 +114,88 @@ IsUniformSetterTypeValid(GLenum setterTy
 
     default:
         MOZ_CRASH("GFX: Bad `uniformType`.");
     }
 }
 
 bool
 WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
-                                          WebGLContext* webgl, const char* funcName) const
+                                          const char* funcName) const
 {
     MOZ_ASSERT(mLinkInfo);
 
-    if (setterElemSize != mActiveInfo->mElemSize) {
-        webgl->ErrorInvalidOperation("%s: Bad uniform size: %i", funcName,
-                                     mActiveInfo->mElemSize);
+    const auto& uniformElemSize = mInfo->mActiveInfo->mElemSize;
+    if (setterElemSize != uniformElemSize) {
+        mContext->ErrorInvalidOperation("%s: Function used differs from uniform size: %i",
+                                        funcName, uniformElemSize);
         return false;
     }
 
-    if (!IsUniformSetterTypeValid(setterType, mActiveInfo->mElemType)) {
-        webgl->ErrorInvalidOperation("%s: Bad uniform type: %i", funcName,
-                                     mActiveInfo->mElemSize);
+    const auto& uniformElemType = mInfo->mActiveInfo->mElemType;
+    if (!IsUniformSetterTypeValid(setterType, uniformElemType)) {
+        mContext->ErrorInvalidOperation("%s: Function used is incompatible with uniform"
+                                        " type: %i",
+                                        funcName, uniformElemType);
         return false;
     }
 
     return true;
 }
 
 bool
 WebGLUniformLocation::ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
-                                          WebGLContext* webgl, const char* funcName) const
+                                          const char* funcName) const
 {
     MOZ_ASSERT(mLinkInfo);
 
     if (setterArraySize == 0 ||
         setterArraySize % setterElemSize)
     {
-        webgl->ErrorInvalidValue("%s: expected an array of length a multiple of"
-                                 " %d, got an array of length %d.",
-                                 funcName, setterElemSize, setterArraySize);
+        mContext->ErrorInvalidValue("%s: Expected an array of length a multiple of %d,"
+                                    " got an array of length %d.",
+                                    funcName, setterElemSize, setterArraySize);
         return false;
     }
 
     /* GLES 2.0.25, Section 2.10, p38
      *   When loading `N` elements starting at an arbitrary position `k` in a uniform
      *   declared as an array, elements `k` through `k + N - 1` in the array will be
      *   replaced with the new values. Values for any array element that exceeds the
      *   highest array element index used, as reported by `GetActiveUniform`, will be
      *   ignored by GL.
      */
-    if (!mActiveInfo->mIsArray &&
+    if (!mInfo->mActiveInfo->mIsArray &&
         setterArraySize != setterElemSize)
     {
-        webgl->ErrorInvalidOperation("%s: expected an array of length exactly %d"
-                                     " (since this uniform is not an array"
-                                     " uniform), got an array of length %d.",
-                                     funcName, setterElemSize, setterArraySize);
+        mContext->ErrorInvalidOperation("%s: Expected an array of length exactly %d"
+                                        " (since this uniform is not an array uniform),"
+                                        " got an array of length %d.",
+                                        funcName, setterElemSize, setterArraySize);
         return false;
     }
 
     return true;
 }
 
-bool
-WebGLUniformLocation::ValidateSamplerSetter(GLint value, WebGLContext* webgl,
-                                            const char* funcName) const
+JS::Value
+WebGLUniformLocation::GetUniform(JSContext* js) const
 {
     MOZ_ASSERT(mLinkInfo);
 
-    if (mActiveInfo->mElemType != LOCAL_GL_SAMPLER_2D &&
-        mActiveInfo->mElemType != LOCAL_GL_SAMPLER_CUBE)
-    {
-        return true;
-    }
-
-    if (value >= 0 && value < (GLint)webgl->GLMaxTextureUnits())
-        return true;
-
-    webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a"
-                             " valid texture unit.",
-                             funcName, value);
-    return false;
-}
-
-JS::Value
-WebGLUniformLocation::GetUniform(JSContext* js, WebGLContext* webgl) const
-{
-    MOZ_ASSERT(mLinkInfo);
-
-    uint8_t elemSize = mActiveInfo->mElemSize;
+    const uint8_t elemSize = mInfo->mActiveInfo->mElemSize;
     static const uint8_t kMaxElemSize = 16;
     MOZ_ASSERT(elemSize <= kMaxElemSize);
 
     GLuint prog = mLinkInfo->prog->mGLName;
 
-    gl::GLContext* gl = webgl->GL();
+    gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
-    switch (mActiveInfo->mElemType) {
+    switch (mInfo->mActiveInfo->mElemType) {
     case LOCAL_GL_INT:
     case LOCAL_GL_INT_VEC2:
     case LOCAL_GL_INT_VEC3:
     case LOCAL_GL_INT_VEC4:
     case LOCAL_GL_SAMPLER_2D:
     case LOCAL_GL_SAMPLER_3D:
     case LOCAL_GL_SAMPLER_CUBE:
     case LOCAL_GL_SAMPLER_2D_SHADOW:
@@ -232,19 +212,19 @@ WebGLUniformLocation::GetUniform(JSConte
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
         {
             GLint buffer[kMaxElemSize] = {0};
             gl->fGetUniformiv(prog, mLoc, buffer);
 
             if (elemSize == 1)
                 return JS::Int32Value(buffer[0]);
 
-            JSObject* obj = dom::Int32Array::Create(js, webgl, elemSize, buffer);
+            JSObject* obj = dom::Int32Array::Create(js, mContext, elemSize, buffer);
             if (!obj) {
-                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                 return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
 
     case LOCAL_GL_BOOL:
     case LOCAL_GL_BOOL_VEC2:
     case LOCAL_GL_BOOL_VEC3:
@@ -258,17 +238,17 @@ WebGLUniformLocation::GetUniform(JSConte
 
             bool boolBuffer[kMaxElemSize];
             for (uint8_t i = 0; i < kMaxElemSize; i++)
                 boolBuffer[i] = buffer[i];
 
             JS::RootedValue val(js);
             // Be careful: we don't want to convert all of |uv|!
             if (!dom::ToJSValue(js, boolBuffer, elemSize, &val)) {
-                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                 return JS::NullValue();
             }
             return val;
         }
 
     case LOCAL_GL_FLOAT:
     case LOCAL_GL_FLOAT_VEC2:
     case LOCAL_GL_FLOAT_VEC3:
@@ -284,38 +264,38 @@ WebGLUniformLocation::GetUniform(JSConte
     case LOCAL_GL_FLOAT_MAT4x3:
         {
             GLfloat buffer[16] = {0.0f};
             gl->fGetUniformfv(prog, mLoc, buffer);
 
             if (elemSize == 1)
                 return JS::DoubleValue(buffer[0]);
 
-            JSObject* obj = dom::Float32Array::Create(js, webgl, elemSize, buffer);
+            JSObject* obj = dom::Float32Array::Create(js, mContext, elemSize, buffer);
             if (!obj) {
-                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                 return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
 
     case LOCAL_GL_UNSIGNED_INT:
     case LOCAL_GL_UNSIGNED_INT_VEC2:
     case LOCAL_GL_UNSIGNED_INT_VEC3:
     case LOCAL_GL_UNSIGNED_INT_VEC4:
         {
             GLuint buffer[kMaxElemSize] = {0};
             gl->fGetUniformuiv(prog, mLoc, buffer);
 
             if (elemSize == 1)
                 return JS::DoubleValue(buffer[0]); // This is Double because only Int32 is special cased.
 
-            JSObject* obj = dom::Uint32Array::Create(js, webgl, elemSize, buffer);
+            JSObject* obj = dom::Uint32Array::Create(js, mContext, elemSize, buffer);
             if (!obj) {
-                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                 return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
 
     default:
         MOZ_CRASH("GFX: Invalid elemType.");
     }
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -34,34 +34,35 @@ public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
+    //////
+
     const WeakPtr<const webgl::LinkedProgramInfo> mLinkInfo;
+    webgl::UniformInfo* const mInfo;
     const GLuint mLoc;
     const size_t mArrayIndex;
-    const WebGLActiveInfo* const mActiveInfo;
+
+    //////
 
     WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo,
-                         GLuint loc, size_t arrayIndex, const WebGLActiveInfo* activeInfo);
+                         webgl::UniformInfo* info, GLuint loc, size_t arrayIndex);
 
-    bool ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
-                            const char* funcName) const;
-    bool ValidateSamplerSetter(GLint value, WebGLContext* webgl,
-                               const char* funcName) const;
+    bool ValidateForProgram(WebGLProgram* prog, const char* funcName) const;
     bool ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
-                             WebGLContext* webgl, const char* funcName) const;
+                             const char* funcName) const;
     bool ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
-                             WebGLContext* webgl, const char* funcName) const;
+                             const char* funcName) const;
 
-    JS::Value GetUniform(JSContext* js, WebGLContext* webgl) const;
+    JS::Value GetUniform(JSContext* js) const;
 
     // Needed for certain helper functions like ValidateObject.
     // `WebGLUniformLocation`s can't be 'Deleted' in the WebGL sense.
     bool IsDeleted() const { return false; }
 
 protected:
     ~WebGLUniformLocation();
 };
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -1,21 +1,21 @@
 # -*- 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/.
 
 TEST_DIRS += [
-    'compiledtest', 
+    'compiledtest',
     'gtest'
 ]
 
 # Change the following line(s) to avoid bug 1081323 (clobber after changing a manifest):
-# * Add a regression test for triangle-then-point rendering.
+# * Implement ReadPixel with PBOs.
 
 MOCHITEST_MANIFESTS += [
     'test/crash/mochitest.ini',
     'test/crossorigin/mochitest.ini',
     'test/mochitest.ini',
     'test/webgl-conf/generated-mochitest.ini',
     'test/webgl-mochitest/mochitest.ini',
 ]
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -6665,17 +6665,16 @@ fail-if = (os == 'b2g')
 [generated/test_conformance__programs__program-test.html]
 fail-if = (os == 'android' && android_version == '10')
 [generated/test_conformance__programs__use-program-crash-with-discard-in-fragment-shader.html]
 [generated/test_conformance__reading__read-pixels-pack-alignment.html]
 [generated/test_conformance__reading__read-pixels-test.html]
 skip-if = (os == 'android') || (os == 'linux')
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__renderbuffers__feedback-loop.html]
-fail-if = 1
 [generated/test_conformance__renderbuffers__framebuffer-object-attachment.html]
 skip-if = (os == 'android')
 [generated/test_conformance__renderbuffers__framebuffer-state-restoration.html]
 [generated/test_conformance__renderbuffers__framebuffer-test.html]
 [generated/test_conformance__renderbuffers__renderbuffer-initialization.html]
 [generated/test_conformance__rendering__culling.html]
 [generated/test_conformance__rendering__default-texture-draw-bug.html]
 [generated/test_conformance__rendering__draw-arrays-out-of-bounds.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -31,19 +31,16 @@ subsuite = webgl
 skip-if = os == 'b2g' || ((os == 'linux') && (buildapp == 'mulet'))
 
 [generated/test_..__always-fail.html]
 fail-if = 1
 [generated/test_conformance__context__context-lost.html]
 fail-if = 1
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 fail-if = 1
-[generated/test_conformance__glsl__misc__shaders-with-name-conflicts.html]
-[generated/test_conformance__renderbuffers__feedback-loop.html]
-fail-if = 1
 
 ####################
 # Tests requesting non-local network connections.
 
 [generated/test_conformance__more__functions__readPixelsBadArgs.html]
 # (TODO) FATAL ERROR: Non-local network connections are disabled and a connection attempt to www.opengl.org (45.55.206.190) was made.
 skip-if = 1
 [generated/test_2_conformance__more__functions__readPixelsBadArgs.html]
--- a/dom/canvas/test/webgl-mochitest/mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest/mochitest.ini
@@ -66,16 +66,17 @@ support-files = ../captureStream_common.
 skip-if = (os == 'b2g') || buildapp == 'mulet' # Mulet - bug 1093639 (crashes in libLLVM-3.0.so)
 [test_hidden_depth_stencil.html]
 fail-if = (os == 'win' && os_version == '5.1')
 [test_implicit_color_buffer_float.html]
 [test_highp_fs.html]
 [test_no_arr_points.html]
 skip-if = android_version == '18' #Android 4.3 aws only; bug 1030942
 [test_noprog_draw.html]
+[test_pixel_pack_buffer.html]
 [test_privileged_exts.html]
 [test_renderer_strings.html]
 [test_sab_with_webgl.html]
 [test_texsubimage_float.html]
 [test_uninit_data.html]
 [test_webgl_available.html]
 #[test_webgl_color_buffer_float.html]
 # We haven't cleaned up the Try results yet, but let's get this on the books first.
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_pixel_pack_buffer.html
@@ -0,0 +1,288 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='UTF-8'>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+  </head>
+  <body>
+    <script>
+
+var RED   = [1, 0, 0, 1];
+var GREEN = [0, 1, 0, 1];
+var BLUE  = [0, 0, 1, 1];
+var WHITE = [1, 1, 1, 1];
+var ZERO  = [0, 0, 0, 0];
+
+function DrawColors(gl) {
+  var fnClearToColor = function(color) {
+    gl.clearColor(color[0], color[1], color[2], color[3]);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+  };
+
+  gl.enable(gl.SCISSOR_TEST);
+
+  // +---+
+  // |G W|
+  // |R B|
+  // +---+
+
+  gl.scissor(0, 0, 1, 1);
+  fnClearToColor(RED);
+
+  gl.scissor(1, 0, 1, 1);
+  fnClearToColor(BLUE);
+
+  gl.scissor(0, 1, 1, 1);
+  fnClearToColor(GREEN);
+
+  gl.scissor(1, 1, 1, 1);
+  fnClearToColor(WHITE);
+}
+
+function ClearBufferPair(gl, byteCount) {
+  // Using `null` here clears to zero according to WebGL.
+  gl.bufferData(gl.PIXEL_PACK_BUFFER, byteCount, gl.STREAM_READ);
+
+  var arr = new Uint8Array(byteCount);
+  return arr;
+}
+
+function ColorToString(color, offset=0) {
+  var arr = [ color[offset+0],
+              color[offset+1],
+              color[offset+2],
+              color[offset+3] ];
+  return '[' + arr.join(', ') + ']';
+}
+
+function TestIsUNormColor(refColor, testData, offset) {
+  if (testData.length < offset + 4) {
+    ok(false, 'testData not long enough.');
+  }
+
+  var refUNormColor = [
+    (refColor[0] * 255) | 0,
+    (refColor[1] * 255) | 0,
+    (refColor[2] * 255) | 0,
+    (refColor[3] * 255) | 0,
+  ];
+
+  var refStr = ColorToString(refUNormColor);
+  var testStr = ColorToString(testData, offset);
+  ok(testStr == refStr, 'Expected ' + refStr + ', was ' + testStr + '.');
+}
+
+function section(text) {
+  ok(true, '');
+  ok(true, 'Section: ' + text);
+}
+
+function EnsureNoError(gl) {
+  var glErr = gl.getError();
+  while (gl.getError()) {}
+
+  if (!glErr)
+    return;
+
+  var extraInfo = '';
+
+  var err = new Error();
+  var stackStr = err.stack;
+  if (stackStr !== undefined) {
+    var stackArr = stackStr.split('\n');
+    stackStr = stackArr[1]; // First one after present scope.
+    extraInfo = ': ' + stackStr;
+  }
+
+  ok(false, 'Unexpected GL error: 0x' + glErr.toString(16) + extraInfo);
+}
+
+function TestError(gl, refErrVal, str='') {
+  if (str == '') {
+    str = 'gl.getError()';
+  } else {
+    str = str + ': gl.getError()';
+  }
+
+  var err = gl.getError();
+  while (gl.getError()) {}
+
+  ShouldBe(err.toString(16), refErrVal.toString(16), str);
+}
+
+function ShouldBe(val, ref, str='') {
+  if (str != '') {
+    str += ': ';
+  }
+
+  ok(val == ref, str + 'Should be `' + ref + '`, was `' + val + '`.');
+}
+
+var gl;
+
+function Test() {
+  var canvas = document.createElement('canvas');
+  canvas.width = 2;
+  canvas.height = 2;
+  canvas.style = 'width: 256px; height: 256px; border: 1px solid black;';
+  document.body.appendChild(canvas);
+
+  var attribs = {
+    antialias: false,
+    alpha: false,
+  };
+  gl = canvas.getContext('webgl2', attribs);
+  if (!gl) {
+    todo(false, 'WebGL 2 not present, skipping.');
+    return;
+  }
+
+  ////////
+
+  TestIsUNormColor(RED, new Uint8Array([255, 0, 0, 255]), 0);
+
+  ////////
+
+  gl.clearColor(RED[0], RED[1], RED[2], RED[3]);
+  gl.clear(gl.COLOR_BUFFER_BIT);
+
+  var data = new Uint8Array(16);
+  gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
+  console.log(JSON.stringify(data));
+  TestIsUNormColor(RED, data, 0);
+
+  ////////
+
+  DrawColors(gl);
+
+  ////////
+
+  EnsureNoError(gl);
+  gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+  TestError(gl, gl.INVALID_OPERATION);
+
+  var data = new Uint8Array(16);
+  gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
+  EnsureNoError(gl);
+  TestIsUNormColor(RED, data, 0);
+  TestIsUNormColor(BLUE, data, 4);
+  TestIsUNormColor(GREEN, data, 8);
+  TestIsUNormColor(WHITE, data, 12);
+
+  ////////
+
+  var a = gl.createBuffer();
+  gl.bindBuffer(gl.PIXEL_PACK_BUFFER, a);
+  EnsureNoError(gl);
+
+  gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
+  TestError(gl, gl.INVALID_OPERATION);
+
+  ////////
+
+  // Basic
+  section('Basic readback');
+  data = ClearBufferPair(gl, 16);
+  EnsureNoError(gl);
+  gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+  EnsureNoError(gl);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data.buffer);
+  EnsureNoError(gl);
+  TestIsUNormColor(RED, data, 0);
+  TestIsUNormColor(BLUE, data, 4);
+  TestIsUNormColor(GREEN, data, 8);
+  TestIsUNormColor(WHITE, data, 12);
+
+  section('Subrect readback');
+  data = ClearBufferPair(gl, 8);
+  gl.readPixels(1, 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data.buffer);
+  EnsureNoError(gl);
+  TestIsUNormColor(WHITE, data, 0);
+  TestIsUNormColor(ZERO, data, 4);
+
+  section('ReadPixels offset:4');
+  data = ClearBufferPair(gl, 16);
+  gl.readPixels(1, 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 4);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data.buffer);
+  EnsureNoError(gl);
+  TestIsUNormColor(ZERO, data, 0);
+  TestIsUNormColor(WHITE, data, 4);
+  TestIsUNormColor(ZERO, data, 8);
+  TestIsUNormColor(ZERO, data, 12);
+
+  section('ReadPixels offset:5');
+  gl.readPixels(1, 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 5);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data.buffer);
+  EnsureNoError(gl);
+  TestIsUNormColor(ZERO, data, 0);
+  TestIsUNormColor(WHITE, data, 4); // Should remain from previous read.
+  TestIsUNormColor(WHITE, data, 5);
+  TestIsUNormColor(ZERO, data, 9);
+  TestIsUNormColor(ZERO, data, 12);
+
+  section('GetBufferSubData src too small');
+  data = ClearBufferPair(gl, 16);
+  EnsureNoError(gl);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 1, data.buffer);
+  TestError(gl, gl.INVALID_VALUE);
+  TestIsUNormColor(ZERO, data, 0);
+  TestIsUNormColor(ZERO, data, 4);
+  TestIsUNormColor(ZERO, data, 8);
+  TestIsUNormColor(ZERO, data, 12);
+
+  section('GetBufferSubData offset:1');
+  data = new Uint8Array(15);
+  gl.readPixels(1, 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 8);
+  gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 1, data.buffer);
+  EnsureNoError(gl);
+  TestIsUNormColor(ZERO, data, 0);
+  TestIsUNormColor(ZERO, data, 3);
+  TestIsUNormColor(WHITE, data, 7);
+  TestIsUNormColor(ZERO, data, 11);
+
+  //////////////////////////////////////
+
+  section('Test packing state');
+  EnsureNoError(gl);
+
+  function TestPackState(enumStr, initialVal, changedVal) {
+    var enumVal = gl[enumStr];
+
+    ShouldBe(gl.getParameter(enumVal), initialVal, 'Initial ' + enumStr);
+    gl.pixelStorei(enumVal, changedVal);
+    ShouldBe(gl.getParameter(enumVal), changedVal, 'Changed ' + enumStr);
+    gl.pixelStorei(enumVal, initialVal);
+    ShouldBe(gl.getParameter(enumVal), initialVal, 'Reverted ' + enumStr);
+    EnsureNoError(gl);
+  }
+
+  TestPackState('PACK_ALIGNMENT', 4, 1);
+  TestPackState('PACK_ROW_LENGTH', 0, 16);
+  TestPackState('PACK_SKIP_PIXELS', 0, 3);
+  TestPackState('PACK_SKIP_ROWS', 0, 3);
+}
+
+function RunTest() {
+  Test();
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+try {
+  var prefArrArr = [
+    ['webgl.force-enabled', true],
+    ['webgl.disable-angle', true],
+  ];
+  var prefEnv = {'set': prefArrArr};
+  SpecialPowers.pushPrefEnv(prefEnv, RunTest);
+} catch (e) {
+  todo(false, 'No SpecialPowers, but trying anyway...');
+  RunTest();
+}
+
+    </script>
+  </body>
+</html>
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -164,19 +164,22 @@ FetchRequest(nsIGlobalObject* aGlobal, c
   // policy type has been overridden.  Note, we must do this before
   // entering the global below.  Otherwise the IsCallerChrome() will
   // always fail.
   MOZ_ASSERT_IF(aInput.IsRequest() &&
                 aInput.GetAsRequest().IsContentPolicyTypeOverridden(),
                 nsContentUtils::IsCallerChrome());
 
   AutoJSAPI jsapi;
-  jsapi.Init(aGlobal);
+  if (!jsapi.Init(aGlobal)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return nullptr;
+  }
+
   JSContext* cx = jsapi.cx();
-
   JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
   GlobalObject global(cx, jsGlobal);
 
   RefPtr<Request> request = Request::Constructor(global, aInput, aInit, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
@@ -1090,19 +1093,22 @@ FetchBody<Derived>::ContinueConsumeBody(
   if (NS_FAILED(aStatus)) {
     return;
   }
 
   // Finish successfully consuming body according to type.
   MOZ_ASSERT(aResult);
 
   AutoJSAPI jsapi;
-  jsapi.Init(DerivedClass()->GetParentObject());
+  if (!jsapi.Init(DerivedClass()->GetParentObject())) {
+    localPromise->MaybeReject(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
   JSContext* cx = jsapi.cx();
-
   ErrorResult error;
 
   switch (mConsumeType) {
     case CONSUME_ARRAYBUFFER: {
       JS::Rooted<JSObject*> arrayBuffer(cx);
       BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult,
                                     error);
 
--- a/dom/flyweb/HttpServer.cpp
+++ b/dom/flyweb/HttpServer.cpp
@@ -46,19 +46,16 @@ HttpServer::~HttpServer()
 
 void
 HttpServer::Init(int32_t aPort, bool aHttps, HttpServerListener* aListener)
 {
   mPort = aPort;
   mHttps = aHttps;
   mListener = aListener;
 
-  nsCOMPtr<nsIPrefBranch> prefService;
-  prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
-
   if (mHttps) {
     nsCOMPtr<nsILocalCertService> lcs =
       do_CreateInstance("@mozilla.org/security/local-cert-service;1");
     nsresult rv = lcs->GetOrCreateCert(NS_LITERAL_CSTRING("flyweb"), this,
                                        nsILocalCertService::KEY_TYPE_EC);
     if (NS_FAILED(rv)) {
       NotifyStarted(rv);
     }
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -256,17 +256,20 @@ nsBrowserElement::Download(const nsAStri
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
   NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(mBrowserElementAPI);
   MOZ_ASSERT(wrappedObj, "Failed to get wrapped JS from XPCOM component.");
   AutoJSAPI jsapi;
-  jsapi.Init(wrappedObj->GetJSObject());
+  if (!jsapi.Init(wrappedObj->GetJSObject())) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
   JSContext* cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   aRv.MightThrowJSException();
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.StealExceptionFromJSContext(cx);
     return nullptr;
   }
   nsresult rv = mBrowserElementAPI->Download(aUrl, options, getter_AddRefs(req));
@@ -709,17 +712,20 @@ nsBrowserElement::ExecuteScript(const ns
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
   NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(mBrowserElementAPI);
   MOZ_ASSERT(wrappedObj, "Failed to get wrapped JS from XPCOM component.");
   AutoJSAPI jsapi;
-  jsapi.Init(wrappedObj->GetJSObject());
+  if (!jsapi.Init(wrappedObj->GetJSObject())) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
   JSContext* cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   aRv.MightThrowJSException();
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.StealExceptionFromJSContext(cx);
     return nullptr;
   }
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -124,16 +124,17 @@
 #include "nsChromeRegistryContent.h"
 #include "nsFrameMessageManager.h"
 
 #include "nsIGeolocationProvider.h"
 #include "mozilla/dom/PMemoryReportRequestChild.h"
 #include "mozilla/dom/PCycleCollectWithLogsChild.h"
 
 #include "nsIScriptSecurityManager.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #ifdef MOZ_WEBRTC
 #include "signaling/src/peerconnection/WebrtcGlobalChild.h"
 #endif
 
 #ifdef MOZ_PERMISSIONS
 #include "nsPermission.h"
 #include "nsPermissionManager.h"
@@ -1236,21 +1237,20 @@ ContentChild::AllocPAPZChild(const TabId
 
 bool
 ContentChild::DeallocPAPZChild(PAPZChild* aActor)
 {
   delete aActor;
   return true;
 }
 
-PCompositorBridgeChild*
-ContentChild::AllocPCompositorBridgeChild(mozilla::ipc::Transport* aTransport,
-                                          base::ProcessId aOtherProcess)
+bool
+ContentChild::RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint)
 {
-  return CompositorBridgeChild::Create(aTransport, aOtherProcess);
+  return CompositorBridgeChild::InitForContent(Move(aEndpoint));
 }
 
 PSharedBufferManagerChild*
 ContentChild::AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                               base::ProcessId aOtherProcess)
 {
   return SharedBufferManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
@@ -2238,16 +2238,18 @@ ContentChild::ActorDestroy(ActorDestroyR
   // going through the full XPCOM shutdown path, because it doesn't
   // keep persistent state.
   ProcessChild::QuickExit();
 #else
   if (sFirstIdleTask) {
     sFirstIdleTask->Cancel();
   }
 
+  nsHostObjectProtocolHandler::RemoveDataEntries();
+
   mAlertObservers.Clear();
 
   mIdleObservers.Clear();
 
   nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   if (svc) {
     svc->UnregisterListener(mConsoleListener);
     mConsoleListener->mChild = nullptr;
@@ -2631,16 +2633,32 @@ ContentChild::RecvInitServiceWorkers(con
 {
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
   return true;
 }
 
 bool
+ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations)
+{
+  for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
+    BlobURLRegistrationData& registration = aRegistrations[i];
+    RefPtr<BlobImpl> blobImpl =
+      static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl();
+    MOZ_ASSERT(blobImpl);
+
+    nsHostObjectProtocolHandler::AddDataEntry(registration.url(), blobImpl,
+                                              registration.principal());
+  }
+
+  return true;
+}
+
+bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
   return true;
 }
 
 bool
@@ -3368,16 +3386,35 @@ ContentChild::RecvNotifyPushSubscription
 {
 #ifndef MOZ_SIMPLEPUSH
   PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
+bool
+ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+                                      const IPC::Principal& aPrincipal)
+{
+  RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl();
+  MOZ_ASSERT(blobImpl);
+
+  nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl, aPrincipal);
+  return true;
+}
+
+bool
+ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
+{
+  nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
+  return true;
+}
+
+
 void
 ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
                                     bool aRecursiveFlag,
                                     nsID& aUUID,
                                     GetFilesHelperChild* aChild)
 {
   MOZ_ASSERT(aChild);
   MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -32,20 +32,16 @@ namespace mozilla {
 class RemoteSpellcheckEngineChild;
 
 namespace ipc {
 class OptionalURIParams;
 class PFileDescriptorSetChild;
 class URIParams;
 }// namespace ipc
 
-namespace layers {
-class PCompositorBridgeChild;
-} // namespace layers
-
 namespace dom {
 
 class AlertObserver;
 class ConsoleListener;
 class PStorageChild;
 class ClonedMessageData;
 class TabChild;
 class GetFilesHelperChild;
@@ -147,19 +143,18 @@ public:
   AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
                         base::ProcessId otherProcess) override;
 
   PAPZChild*
   AllocPAPZChild(const TabId& aTabId) override;
   bool
   DeallocPAPZChild(PAPZChild* aActor) override;
 
-  PCompositorBridgeChild*
-  AllocPCompositorBridgeChild(mozilla::ipc::Transport* aTransport,
-                              base::ProcessId aOtherProcess) override;
+  bool
+  RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint) override;
 
   PSharedBufferManagerChild*
   AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                   base::ProcessId aOtherProcess) override;
 
   PImageBridgeChild*
   AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                          base::ProcessId aOtherProcess) override;
@@ -457,16 +452,19 @@ public:
                            const nsCString& name, const nsCString& UAName,
                            const nsCString& ID, const nsCString& vendor) override;
 
   virtual bool RecvAppInit() override;
 
   virtual bool
   RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
 
+  virtual bool
+  RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
+
   virtual bool RecvLastPrivateDocShellDestroyed() override;
 
   virtual bool RecvVolumes(InfallibleTArray<VolumeInfo>&& aVolumes) override;
 
   virtual bool RecvFilePathUpdate(const nsString& aStorageType,
                                   const nsString& aStorageName,
                                   const nsString& aPath,
                                   const nsCString& aReason) override;
@@ -638,16 +636,23 @@ public:
 
   void
   DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild);
 
   virtual bool
   RecvGetFilesResponse(const nsID& aUUID,
                        const GetFilesResponseResult& aResult) override;
 
+  virtual bool
+  RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+                          const IPC::Principal& aPrincipal) override;
+
+  virtual bool
+  RecvBlobURLUnregistration(const nsCString& aURI) override;
+
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -186,16 +186,17 @@
 #include "ContentProcessManager.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
 #include "nsIBlocklistService.h"
 #include "mozilla/StyleSheetHandle.h"
 #include "mozilla/StyleSheetHandleInlines.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #include "nsIBidiKeyboard.h"
 
 #ifdef MOZ_WEBRTC
 #include "signaling/src/peerconnection/WebrtcGlobalParent.h"
 #endif
 
 #if defined(ANDROID) || defined(LINUX)
@@ -2054,16 +2055,23 @@ ContentParent::ActorDestroy(ActorDestroy
                                                       &ContentParent::ShutDownProcess,
                                                       SEND_SHUTDOWN_MESSAGE));
   }
   cpm->RemoveContentProcess(this->ChildID());
 
   if (mDriverCrashGuard) {
     mDriverCrashGuard->NotifyCrashed();
   }
+
+  // Unregister all the BlobURLs registered by the ContentChild.
+  for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
+    nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
+  }
+
+  mBlobURLs.Clear();
 }
 
 void
 ContentParent::NotifyTabDestroying(const TabId& aTabId,
                                    const ContentParentId& aCpId)
 {
   if (XRE_IsParentProcess()) {
     // There can be more than one PBrowser for a given app process
@@ -2417,18 +2425,23 @@ ContentParent::InitInternal(ProcessPrior
     // message goes through the regular IPC queue for this
     // channel, so delivery will happen-before any other messages
     // we send.  The CompositorBridgeChild must be created before any
     // PBrowsers are created, because they rely on the Compositor
     // already being around.  (Creation is async, so can't happen
     // on demand.)
     bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
     if (useOffMainThreadCompositing) {
-      DebugOnly<bool> opened = PCompositorBridge::Open(this);
+      GPUProcessManager* gpm = GPUProcessManager::Get();
+
+      Endpoint<PCompositorBridgeChild> endpoint;
+      DebugOnly<bool> opened =
+        gpm->CreateContentCompositorBridge(OtherPid(), &endpoint);
       MOZ_ASSERT(opened);
+      Unused << SendInitCompositor(Move(endpoint));
 
       opened = PImageBridge::Open(this);
       MOZ_ASSERT(opened);
 
       opened = gfx::PVRManager::Open(this);
       MOZ_ASSERT(opened);
     }
 #ifdef MOZ_WIDGET_GONK
@@ -2508,23 +2521,32 @@ ContentParent::InitInternal(ProcessPrior
   nsString sessionName;
   nsString iconPath;
   if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
                                                         iconPath))) {
     Unused << SendSetAudioSessionData(id, sessionName, iconPath);
   }
 #endif
 
-  RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
-  MOZ_ASSERT(swr);
-
-  nsTArray<ServiceWorkerRegistrationData> registrations;
-  swr->GetRegistrations(registrations);
-
-  Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+  {
+    RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(swr);
+
+    nsTArray<ServiceWorkerRegistrationData> registrations;
+    swr->GetRegistrations(registrations);
+    Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+  }
+
+  {
+    nsTArray<BlobURLRegistrationData> registrations;
+    if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations,
+                                                          this)) {
+      Unused << SendInitBlobURLs(registrations);
+    }
+  }
 }
 
 bool
 ContentParent::IsAlive() const
 {
   return mIsAlive;
 }
 
@@ -3205,24 +3227,16 @@ ContentParent::AllocPAPZParent(const Tab
 }
 
 bool
 ContentParent::DeallocPAPZParent(PAPZParent* aActor)
 {
   return true;
 }
 
-PCompositorBridgeParent*
-ContentParent::AllocPCompositorBridgeParent(mozilla::ipc::Transport* aTransport,
-                                            base::ProcessId aOtherProcess)
-{
-  return GPUProcessManager::Get()->CreateTabCompositorBridge(
-    aTransport, aOtherProcess);
-}
-
 gfx::PVRManagerParent*
 ContentParent::AllocPVRManagerParent(Transport* aTransport,
                                      ProcessId aOtherProcess)
 {
   return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess);
 }
 
 PImageBridgeParent*
@@ -5691,16 +5705,83 @@ bool
 ContentParent::RecvNotifyLowMemory()
 {
 #ifdef MOZ_CRASHREPORTER
   nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
 #endif
   return true;
 }
 
+/* static */ void
+ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
+                                            BlobImpl* aBlobImpl,
+                                            nsIPrincipal* aPrincipal,
+                                            ContentParent* aIgnoreThisCP)
+{
+  nsCString uri(aURI);
+  IPC::Principal principal(aPrincipal);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    if (cp != aIgnoreThisCP) {
+      PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl);
+      if (blobParent) {
+        Unused << cp->SendBlobURLRegistration(uri, blobParent, principal);
+      }
+    }
+  }
+}
+
+/* static */ void
+ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI,
+                                              ContentParent* aIgnoreThisCP)
+{
+  nsCString uri(aURI);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    if (cp != aIgnoreThisCP) {
+      Unused << cp->SendBlobURLUnregistration(uri);
+    }
+  }
+}
+
+bool
+ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+                                                        PBlobParent* aBlobParent,
+                                                        const Principal& aPrincipal)
+{
+  RefPtr<BlobImpl> blobImpl =
+    static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
+  if (NS_WARN_IF(!blobImpl)) {
+    return false;
+  }
+
+  if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl,
+                                                             aPrincipal))) {
+    BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
+
+    // We want to store this blobURL, so we can unregister it if the child
+    // crashes.
+    mBlobURLs.AppendElement(aURI);
+  }
+
+  BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
+  return true;
+}
+
+bool
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI)
+{
+  nsHostObjectProtocolHandler::RemoveDataEntry(aURI,
+                                               false /* Don't broadcast */);
+  BroadcastBlobURLUnregistration(aURI, this);
+  mBlobURLs.RemoveElement(aURI);
+
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -61,17 +61,16 @@ class URIParams;
 class TestShellParent;
 } // namespace ipc
 
 namespace jsipc {
 class PJavaScriptParent;
 } // namespace jsipc
 
 namespace layers {
-class PCompositorBridgeParent;
 class PSharedBufferManagerParent;
 struct TextureFactoryIdentifier;
 } // namespace layers
 
 namespace layout {
 class PRenderFrameParent;
 } // namespace layout
 
@@ -570,16 +569,34 @@ public:
                                 bool* aWindowIsNew,
                                 InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                 nsCString* aURLToLoad,
                                 layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                 uint64_t* aLayersId) override;
 
   static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
 
+  static void
+  BroadcastBlobURLRegistration(const nsACString& aURI,
+                               BlobImpl* aBlobImpl,
+                               nsIPrincipal* aPrincipal,
+                               ContentParent* aIgnoreThisCP = nullptr);
+
+  static void
+  BroadcastBlobURLUnregistration(const nsACString& aURI,
+                                 ContentParent* aIgnoreThisCP = nullptr);
+
+  virtual bool
+  RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+                                           PBlobParent* aBlobParent,
+                                           const Principal& aPrincipal) override;
+
+  virtual bool
+  RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
+
 protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   void OnNuwaForkTimeout();
 
   bool ShouldContinueFromReplyTimeout() override;
@@ -723,20 +740,16 @@ private:
   AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
                          base::ProcessId aOtherProcess) override;
 
   PAPZParent*
   AllocPAPZParent(const TabId& aTabId) override;
   bool
   DeallocPAPZParent(PAPZParent* aActor) override;
 
-  PCompositorBridgeParent*
-  AllocPCompositorBridgeParent(mozilla::ipc::Transport* aTransport,
-                               base::ProcessId aOtherProcess) override;
-
   PImageBridgeParent*
   AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) override;
 
   PSharedBufferManagerParent*
   AllocPSharedBufferManagerParent(mozilla::ipc::Transport* aTranport,
                                    base::ProcessId aOtherProcess) override;
 
@@ -1276,16 +1289,18 @@ private:
 
 #ifdef NS_PRINTING
   RefPtr<embedding::PrintingParent> mPrintingParent;
 #endif
 
   // This hashtable is used to run GetFilesHelper objects in the parent process.
   // GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest.
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;
+
+  nsTArray<nsCString> mBlobURLs;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver
 {
   friend class mozilla::dom::ContentParent;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -368,21 +368,27 @@ struct GetFilesResponseFailure
 };
 
 union GetFilesResponseResult
 {
   GetFilesResponseSuccess;
   GetFilesResponseFailure;
 };
 
+struct BlobURLRegistrationData
+{
+    nsCString url;
+    PBlob blob;
+    Principal principal;
+};
+
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
-    parent opens PCompositorBridge;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     parent opens PGMPService;
     parent opens PVRManager;
     child opens PBackground;
 
     manages PAPZ;
@@ -459,16 +465,19 @@ both:
     // For parent->child, aBrowser must be non-null; aOuterWindowID can
     // be 0 to indicate the browser's current root document, or nonzero
     // to persist a subdocument.  For child->parent, arguments are
     // ignored and should be null/zero.
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
+    // Give the content process its endpoint to the compositor.
+    async InitCompositor(Endpoint<PCompositorBridgeChild> compositor);
+
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
     async PMemoryReportRequest(uint32_t generation, bool anonymize,
@@ -542,16 +551,21 @@ child:
                   nsCString ID, nsCString vendor);
     async AppInit();
 
     /**
      * Send ServiceWorkerRegistrationData to child process.
      */
     async InitServiceWorkers(ServiceWorkerConfiguration aConfig);
 
+    /**
+     * Send BlobURLRegistrationData to child process.
+     */
+    async InitBlobURLs(BlobURLRegistrationData[] registrations);
+
     // Notify child that last-pb-context-exited notification was observed
     async LastPrivateDocShellDestroyed();
 
     async FilePathUpdate(nsString storageType, nsString storageName, nsString filepath,
                          nsCString reasons);
 
     // Note: Any changes to this structure should also be changed in
     // VolumeInfo above.
@@ -671,16 +685,21 @@ child:
      * audio session.
      */
     async SetAudioSessionData(nsID aID,
                               nsString aDisplayName,
                               nsString aIconPath);
 
     async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
 
+    async BlobURLRegistration(nsCString aURI, PBlob aBlob,
+                              Principal aPrincipal);
+
+    async BlobURLUnregistration(nsCString aURI);
+
 parent:
     /**
      * Tell the content process some attributes of itself.  This is
      * among the first information queried by content processes after
      * startup.  (The message is sync to allow the content process to
      * control when it receives the information.)
      *
      * |id| is a unique ID among all subprocesses.  When |isForApp &&
@@ -1183,16 +1202,21 @@ parent:
      * allows the parent process to save a memory report that can potentially be
      * sent with a crash report from the content process.
      */
      async NotifyLowMemory();
 
      async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
      async DeleteGetFilesRequest(nsID aID);
 
+     async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
+                                                Principal principal);
+
+     async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
+
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/ipc/PPluginWidget.ipdl
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -42,12 +42,22 @@ parent:
    */
   sync GetNativePluginPort() returns (uintptr_t value);
 
   /**
    * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window
    * on the chrome side. This is only currently used on Windows.
    */
   sync SetNativeChildWindow(uintptr_t childWindow);
+
+child:
+  /**
+   * Used to set the ID of a scroll capture container from the parent process,
+   * so that we can create a proxy container in the layer tree.
+   * @param aScrollCaptureId async container ID of the parent container
+   * @param aPluginInstanceId plugin ID on which to set the scroll capture ID
+   */
+  async SetScrollCaptureId(uint64_t aScrollCaptureId,
+                           uintptr_t aPluginInstanceId);
 };
 
 }
 }
--- a/dom/ipc/PermissionMessageUtils.h
+++ b/dom/ipc/PermissionMessageUtils.h
@@ -23,19 +23,23 @@ public:
   {}
 
   explicit Principal(nsIPrincipal* aPrincipal)
     : mPrincipal(aPrincipal)
   {}
 
   operator nsIPrincipal*() const { return mPrincipal.get(); }
 
+  Principal& operator=(const Principal& aOther)
+  {
+    mPrincipal = aOther.mPrincipal;
+    return *this;
+  }
+
 private:
-  // Unimplemented
-  Principal& operator=(Principal&);
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 template <>
 struct ParamTraits<Principal>
 {
   typedef Principal paramType;
   static void Write(Message* aMsg, const paramType& aParam);
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -75,17 +75,17 @@ public:
   static ReaderQueue& Instance()
   {
     static StaticMutex sMutex;
     StaticMutexAutoLock lock(sMutex);
 
     if (!sInstance) {
       sInstance = new ReaderQueue;
       sInstance->MaxNumActive(MediaPrefs::MediaDecoderLimit());
-      ClearOnShutdown(&sInstance);
+      ClearOnShutdown(&sInstance, ShutdownPhase::Shutdown);
     }
     MOZ_ASSERT(sInstance);
     return *sInstance;
   }
 
   void MaxNumActive(int32_t aNumActive)
   {
     MutexAutoLock lock(mMutex);
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -322,21 +322,16 @@ PluginPRLibrary::EndUpdateBackground(NPP
 }
 
 #if defined(XP_WIN)
 nsresult
 PluginPRLibrary::GetScrollCaptureContainer(NPP aInstance, ImageContainer** aContainer)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
-nsresult
-PluginPRLibrary::UpdateScrollState(NPP aInstance, bool aIsScrolling)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
 #endif
 
 nsresult
 PluginPRLibrary::HandledWindowedPluginKeyEvent(
                    NPP aInstance,
                    const NativeEventData& aNativeKeyData,
                    bool aIsConsumed)
 {
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -122,17 +122,16 @@ public:
     virtual nsresult EndUpdateBackground(NPP instance,
                                          const nsIntRect&) override;
     virtual void DidComposite(NPP aInstance) override { }
     virtual void GetLibraryPath(nsACString& aPath) { aPath.Assign(mFilePath); }
     virtual nsresult GetRunID(uint32_t* aRunID) override { return NS_ERROR_NOT_IMPLEMENTED; }
     virtual void SetHasLocalInstance() override { }
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
-    virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) override;
 #endif
     virtual nsresult HandledWindowedPluginKeyEvent(
                        NPP aInstance,
                        const mozilla::NativeEventData& aNativeKeyData,
                        bool aIsCOnsumed) override;
 
 private:
     NP_InitializeFunc mNP_Initialize;
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1156,25 +1156,16 @@ nsNPAPIPluginInstance::GetScrollCaptureC
   *aContainer = nullptr;
 
   if (RUNNING != mRunning)
     return NS_OK;
 
   AutoPluginLibraryCall library(this);
   return !library ? NS_ERROR_FAILURE : library->GetScrollCaptureContainer(&mNPP, aContainer);
 }
-nsresult
-nsNPAPIPluginInstance::UpdateScrollState(bool aIsScrolling)
-{
-  if (RUNNING != mRunning)
-    return NS_OK;
-
-  AutoPluginLibraryCall library(this);
-  return !library ? NS_ERROR_FAILURE : library->UpdateScrollState(&mNPP, aIsScrolling);
-}
 #endif
 
 nsresult
 nsNPAPIPluginInstance::HandledWindowedPluginKeyEvent(
                          const NativeEventData& aKeyEventData,
                          bool aIsConsumed)
 {
   if (NS_WARN_IF(!mPlugin)) {
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -117,17 +117,16 @@ public:
   nsresult PushPopupsEnabledState(bool aEnabled);
   nsresult PopPopupsEnabledState();
   nsresult GetPluginAPIVersion(uint16_t* version);
   nsresult InvalidateRect(NPRect *invalidRect);
   nsresult InvalidateRegion(NPRegion invalidRegion);
   nsresult GetMIMEType(const char* *result);
 #if defined(XP_WIN)
   nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer **aContainer);
-  nsresult UpdateScrollState(bool aIsScrolling);
 #endif
   nsresult HandledWindowedPluginKeyEvent(
              const mozilla::NativeEventData& aKeyEventData,
              bool aIsConsumed);
   nsPluginInstanceOwner* GetOwner();
   void SetOwner(nsPluginInstanceOwner *aOwner);
   void DidComposite();
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -317,31 +317,16 @@ nsPluginInstanceOwner::GetCurrentImageSi
 {
   nsIntSize size(0,0);
   if (mInstance) {
     mInstance->GetImageSize(&size);
   }
   return size;
 }
 
-bool
-nsPluginInstanceOwner::UpdateScrollState(bool aIsScrolling)
-{
-#if defined(XP_WIN)
-  if (!mInstance) {
-    return false;
-  }
-  mScrollState = aIsScrolling;
-  nsresult rv = mInstance->UpdateScrollState(aIsScrolling);
-  return NS_SUCCEEDED(rv);
-#else
-  return false;
-#endif
-}
-
 nsPluginInstanceOwner::nsPluginInstanceOwner()
   : mPluginWindow(nullptr)
 {
   // create nsPluginNativeWindow object, it is derived from NPWindow
   // struct and allows to manipulate native window procedure
   nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
   mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (mPluginHost)
@@ -379,17 +364,16 @@ nsPluginInstanceOwner::nsPluginInstanceO
   mFullScreen = false;
   mJavaView = nullptr;
 #endif
 
 #ifdef XP_WIN
   mGotCompositionData = false;
   mSentStartComposition = false;
   mPluginDidNotHandleIMEComposition = false;
-  mScrollState = false;
 #endif
 }
 
 nsPluginInstanceOwner::~nsPluginInstanceOwner()
 {
   if (mWaitingForPaint) {
     nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
     if (content) {
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -224,18 +224,16 @@ public:
   
   void NotifyPaintWaiter(nsDisplayListBuilder* aBuilder);
 
   // Returns the image container that has our currently displayed image.
   already_AddRefed<mozilla::layers::ImageContainer> GetImageContainer();
   // Returns true if this is windowed plugin that can return static captures
   // for scroll operations.
   bool NeedsScrollImageLayer();
-  // Notification we receive from nsPluginFrame about scroll state.
-  bool UpdateScrollState(bool aIsScrolling);
 
   void DidComposite();
 
   /**
    * Returns the bounds of the current async-rendered surface. This can only
    * change in response to messages received by the event loop (i.e. not during
    * painting).
    */
@@ -406,15 +404,12 @@ private:
     NPWindow* mWindow;
     nsPluginInstanceOwner* mInstanceOwner;
     const nsIntSize& mPluginSize;
     const nsIntRect& mDirtyRect;
   };
 #endif
 
   bool mWaitingForPaint;
-#if defined(XP_WIN)
-  bool mScrollState;
-#endif
 };
 
 #endif // nsPluginInstanceOwner_h_
 
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -56,36 +56,28 @@
 #include <windowsx.h>
 #include "gfxWindowsPlatform.h"
 #include "mozilla/plugins/PluginSurfaceParent.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIWidget.h"
 #include "nsPluginNativeWindow.h"
 #include "PluginQuirks.h"
-#include "nsWindowsHelpers.h"
 extern const wchar_t* kFlashFullscreenClass;
 #elif defined(MOZ_WIDGET_GTK)
 #include "mozilla/dom/ContentChild.h"
 #include <gdk/gdk.h>
 #elif defined(XP_MACOSX)
 #include <ApplicationServices/ApplicationServices.h>
 #endif // defined(XP_MACOSX)
 
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
-#if defined(XP_WIN)
-// Delays associated with attempting an e10s window capture for scrolling.
-const int kScrollCaptureDelayMs = 100;
-const int kInitScrollCaptureDelayMs = 1000;
-const uint32_t kScrollCaptureFillColor = 0xFFa0a0a0; // gray
-#endif
-
 void
 StreamNotifyParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Implement me! Bug 1005162
 }
 
 bool
 StreamNotifyParent::RecvRedirectNotifyResponse(const bool& allow)
@@ -138,20 +130,16 @@ PluginInstanceParent::PluginInstancePare
     , mPluginWndProc(nullptr)
     , mNestedEventState(false)
 #endif // defined(XP_WIN)
 #if defined(XP_MACOSX)
     , mShWidth(0)
     , mShHeight(0)
     , mShColorSpace(nullptr)
 #endif
-#if defined(XP_WIN)
-    , mValidFirstCapture(false)
-    , mIsScrolling(false)
-#endif
 {
 #if defined(OS_WIN)
     if (!sPluginInstanceList) {
         sPluginInstanceList = new nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>();
     }
 #endif
 }
 
@@ -166,19 +154,16 @@ PluginInstanceParent::~PluginInstancePar
 #endif
 #if defined(MOZ_WIDGET_COCOA)
     if (mShWidth != 0 && mShHeight != 0) {
         DeallocShmem(mShSurface);
     }
     if (mShColorSpace)
         ::CGColorSpaceRelease(mShColorSpace);
 #endif
-#if defined(XP_WIN)
-    CancelScheduledScrollCapture();
-#endif
 }
 
 bool
 PluginInstanceParent::InitMetadata(const nsACString& aMimeType,
                                    const nsACString& aSrcAttribute)
 {
     if (aSrcAttribute.IsEmpty()) {
         return false;
@@ -443,22 +428,16 @@ bool
 PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow(
     const bool& windowed, NPError* result)
 {
     // Yes, we are passing a boolean as a void*.  We have to cast to intptr_t
     // first to avoid gcc warnings about casting to a pointer from a
     // non-pointer-sized integer.
     *result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool,
                                   (void*)(intptr_t)windowed);
-
-#if defined(XP_WIN)
-    if (windowed) {
-        ScheduleScrollCapture(kScrollCaptureDelayMs);
-    }
-#endif
     return true;
 }
 
 bool
 PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent(
     const bool& transparent, NPError* result)
 {
     *result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool,
@@ -1202,214 +1181,37 @@ PluginInstanceParent::EndUpdateBackgroun
 #endif
 
     Unused << SendUpdateBackground(BackgroundDescriptor(), aRect);
 
     return NS_OK;
 }
 
 #if defined(XP_WIN)
-//#define CAPTURE_LOG(...) printf_stderr("CAPTURE [%X]: ", this);printf_stderr(__VA_ARGS__);printf_stderr("\n");
-#define CAPTURE_LOG(...)
-
-void
-PluginInstanceParent::ScheduleScrollCapture(int aTimeout)
-{
-    if (mCaptureRefreshTask) {
-        return;
-    }
-    CAPTURE_LOG("delayed scroll capture requested.");
-    mCaptureRefreshTask =
-        NewNonOwningCancelableRunnableMethod(this, &PluginInstanceParent::ScheduledUpdateScrollCaptureCallback);
-    RefPtr<Runnable> addrefedTask = mCaptureRefreshTask;
-    MessageLoop::current()->PostDelayedTask(addrefedTask.forget(),
-                                            kScrollCaptureDelayMs);
-}
-
-void
-PluginInstanceParent::ScheduledUpdateScrollCaptureCallback()
-{
-    CAPTURE_LOG("taking delayed scrollcapture.");
-    mCaptureRefreshTask = nullptr;
-    bool retrigger = false;
-    UpdateScrollCapture(retrigger);
-    if (retrigger) {
-        // reset the async request
-        ScheduleScrollCapture(kScrollCaptureDelayMs);
-    }
-}
-
-void
-PluginInstanceParent::CancelScheduledScrollCapture()
+nsresult
+PluginInstanceParent::SetScrollCaptureId(uint64_t aScrollCaptureId)
 {
-    CAPTURE_LOG("delayed scroll capture cancelled.");
-    if (mCaptureRefreshTask) {
-        mCaptureRefreshTask->Cancel();
-        mCaptureRefreshTask = nullptr;
-    }
-}
-
-bool
-PluginInstanceParent::UpdateScrollCapture(bool& aRequestNewCapture)
-{
-    aRequestNewCapture = false;
-    if (!::IsWindow(mChildPluginHWND)) {
-        CAPTURE_LOG("invalid window");
-        aRequestNewCapture = true;
-        return false;
-    }
-
-    nsAutoHDC windowDC(::GetDC(mChildPluginHWND));
-    if (!windowDC) {
-        CAPTURE_LOG("no windowdc");
-        aRequestNewCapture = true;
-        return false;
-    }
-
-    RECT bounds = {0};
-    ::GetWindowRect(mChildPluginHWND, &bounds);
-    if ((bounds.left == bounds.right && bounds.top == bounds.bottom) ||
-        mWindowSize.IsEmpty()) {
-        CAPTURE_LOG("empty bounds");
-        // Lots of null window plugins in content, don't capture.
-        return false;
-    }
-
-    // If we need to init mScrollCapture do so, also reset it if the size of the
-    // plugin window changes.
-    if (!mScrollCapture || mScrollCapture->GetSize() != mWindowSize) {
-        mValidFirstCapture = false;
-        mScrollCapture =
-            gfxPlatform::GetPlatform()->CreateOffscreenSurface(mWindowSize,
-                                                               SurfaceFormat::X8R8G8B8_UINT32);
-    }
+  if (aScrollCaptureId == ImageContainer::sInvalidAsyncContainerId) {
+    return NS_ERROR_FAILURE;
+  }
 
-    // Check clipping, we don't want to capture windows that are clipped by
-    // the viewport.
-    RECT clip = {0};
-    int rgnType = ::GetWindowRgnBox(mPluginHWND, &clip);
-    bool clipCorrect = !clip.left && !clip.top &&
-                       clip.right == mWindowSize.width &&
-                       clip.bottom == mWindowSize.height;
-
-    bool isVisible = ::IsWindowVisible(mChildPluginHWND);
-
-    CAPTURE_LOG("validcap=%d visible=%d region=%d clip=%d:%dx%dx%dx%d",
-                mValidFirstCapture, isVisible, rgnType, clipCorrect,
-                clip.left, clip.top, clip.right, clip.bottom);
-
-    // We have a good capture and can't update so keep using the existing
-    // capture image. Otherwise fall through so we paint the fill color to
-    // the layer.
-    if (mValidFirstCapture && (!isVisible || !clipCorrect)) {
-        return true;
-    }
-
-    // On Windows we'll need a native bitmap for BitBlt.
-    RefPtr<gfxWindowsSurface> nativeScrollCapture;
-
-    // Copy the plugin window if it's visible and there's no clipping, otherwise
-    // use a default fill color.
-    if (isVisible && clipCorrect) {
-        CAPTURE_LOG("capturing window");
-        nativeScrollCapture =
-            new gfxWindowsSurface(mWindowSize, SurfaceFormat::X8R8G8B8_UINT32);
-        if (!::BitBlt(nativeScrollCapture->GetDC(), 0, 0, mWindowSize.width,
-                      mWindowSize.height, windowDC, 0, 0, SRCCOPY)) {
-            CAPTURE_LOG("blt failure??");
-            return false;
-        }
-        ::GdiFlush();
-        mValidFirstCapture = true;
-    }
-
-    IntSize targetSize = mScrollCapture->GetSize();
-
-    if (targetSize.IsEmpty()) {
-        return false;
-    }
-
-    RefPtr<gfx::DrawTarget> dt =
-        gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mScrollCapture,
-                                                               targetSize);
-
-    if (nativeScrollCapture) {
-        // Copy the native capture image over to a remotable gfx surface.
-        RefPtr<gfx::SourceSurface> sourceSurface =
-            gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
-                                                                   nativeScrollCapture);
-        dt->CopySurface(sourceSurface,
-                        IntRect(0, 0, targetSize.width, targetSize.height),
-                        IntPoint());
-    } else {
-        CAPTURE_LOG("using fill color");
-        dt->FillRect(gfx::Rect(0, 0, targetSize.width, targetSize.height),
-                     gfx::ColorPattern(gfx::Color::FromABGR(kScrollCaptureFillColor)),
-                     gfx::DrawOptions(1.f, CompositionOp::OP_SOURCE));
-        aRequestNewCapture = true;
-    }
-    dt->Flush();
-
-    // Get a source for mScrollCapture and load it into the image container.
-    RefPtr<gfx::SourceSurface> cachedSource =
-        gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt,
-                                                               mScrollCapture);
-    RefPtr<SourceSurfaceImage> image =
-        new SourceSurfaceImage(cachedSource->GetSize(), cachedSource);
-
-    ImageContainer::NonOwningImage holder(image);
-    holder.mFrameID = ++mFrameID;
-
-    AutoTArray<ImageContainer::NonOwningImage,1> imageList;
-    imageList.AppendElement(holder);
-
-    // inits mImageContainer
-    ImageContainer *container = GetImageContainer();
-    container->SetCurrentImages(imageList);
-
-    // Invalidate our area in the page so the image gets flushed.
-    NPRect nprect = {0, 0, targetSize.width, targetSize.height};
-    RecvNPN_InvalidateRect(nprect);
-
-    return true;
+  mImageContainer = new ImageContainer(aScrollCaptureId);
+  return NS_OK;
 }
 
 nsresult
 PluginInstanceParent::GetScrollCaptureContainer(ImageContainer** aContainer)
 {
-    if (!aContainer || !::IsWindow(mPluginHWND)) {
-        return NS_ERROR_FAILURE;
-    }
-
-    if (!mImageContainer) {
-        ScheduleScrollCapture(kInitScrollCaptureDelayMs);
-        return NS_ERROR_FAILURE;
-    }
-
-    ImageContainer *container = GetImageContainer();
-    NS_IF_ADDREF(container);
-    *aContainer = container;
+  if (!aContainer || !mImageContainer) {
+    return NS_ERROR_FAILURE;
+  }
 
-    return NS_OK;
-}
+  RefPtr<ImageContainer> container = GetImageContainer();
+  container.forget(aContainer);
 
-nsresult
-PluginInstanceParent::UpdateScrollState(bool aIsScrolling)
-{
-  bool scrollStateChanged = (mIsScrolling != aIsScrolling);
-  mIsScrolling = aIsScrolling;
-  if (scrollStateChanged && !aIsScrolling) {
-      // At the end of a dom scroll operation capturing now will attempt to
-      // capture a window that is still hidden due to the current scroll
-      // operation. (The browser process will update visibility after layer
-      // updates get pushed over.) So we delay our attempt for a bit. This
-      // shouldn't hurt our chances of capturing with APZ scroll since the
-      // delay is short.
-      ScheduleScrollCapture(kScrollCaptureDelayMs);
-  }
   return NS_OK;
 }
 #endif // XP_WIN
 
 PluginAsyncSurrogate*
 PluginInstanceParent::GetAsyncSurrogate()
 {
     return mSurrogate;
@@ -1556,19 +1358,16 @@ PluginInstanceParent::NPP_SetWindow(cons
     window.x = aWindow->x;
     window.y = aWindow->y;
     window.width = aWindow->width;
     window.height = aWindow->height;
     window.clipRect = aWindow->clipRect; // MacOS specific
     window.type = aWindow->type;
 #endif
 
-    mWindowSize.width = window.width;
-    mWindowSize.height = window.height;
-
 #if defined(XP_MACOSX)
     double floatScaleFactor = 1.0;
     mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
     int scaleFactor = ceil(floatScaleFactor);
     window.contentsScaleFactor = floatScaleFactor;
 
     if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
         if (mDrawingModel == NPDrawingModelCoreAnimation ||
@@ -1603,22 +1402,16 @@ PluginInstanceParent::NPP_SetWindow(cons
     window.colormap = ws_info->colormap;
 #endif
 
     if (!CallNPP_SetWindow(window)) {
         return NPERR_GENERIC_ERROR;
     }
 
     RecordDrawingModel();
-
-#if defined(XP_WIN)
-    if (!mCaptureRefreshTask) {
-        ScheduleScrollCapture(kScrollCaptureDelayMs);
-    }
-#endif
     return NPERR_NO_ERROR;
 }
 
 NPError
 PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
                                    void* _retval)
 {
     switch (aVariable) {
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -326,18 +326,18 @@ public:
     nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
     nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
 #endif
     nsresult SetBackgroundUnknown();
     nsresult BeginUpdateBackground(const nsIntRect& aRect,
                                    DrawTarget** aDrawTarget);
     nsresult EndUpdateBackground(const nsIntRect& aRect);
 #if defined(XP_WIN)
+    nsresult SetScrollCaptureId(uint64_t aScrollCaptureId);
     nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer** aContainer);
-    nsresult UpdateScrollState(bool aIsScrolling);
 #endif
     void DidComposite();
 
     bool IsUsingDirectDrawing();
 
     virtual PluginAsyncSurrogate* GetAsyncSurrogate() override;
 
     virtual PluginInstanceParent* GetInstance() override { return this; }
@@ -395,17 +395,16 @@ private:
     PluginModuleParent* mParent;
     RefPtr<PluginAsyncSurrogate> mSurrogate;
     bool mUseSurrogate;
     NPP mNPP;
     const NPNetscapeFuncs* mNPNIface;
     nsCString mSrcAttribute;
     NPWindowType mWindowType;
     int16_t mDrawingModel;
-    IntSize mWindowSize;
 
     // Since plugins may request different drawing models to find a compatible
     // one, we only record the drawing model after a SetWindow call and if the
     // drawing model has changed.
     int mLastRecordedDrawingModel;
 
     nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects;
 
@@ -460,27 +459,15 @@ private:
     // the browser, but a "read-only" reference is sent to the plugin.
     //
     // We have explicitly chosen not to provide any guarantees about
     // the consistency of the pixels in |mBackground|.  A plugin may
     // be able to observe partial updates to the background.
     RefPtr<gfxASurface>    mBackground;
 
     RefPtr<ImageContainer> mImageContainer;
-
-#if defined(XP_WIN)
-    void ScheduleScrollCapture(int aTimeout);
-    void ScheduledUpdateScrollCaptureCallback();
-    bool UpdateScrollCapture(bool& aRequestNewCapture);
-    void CancelScheduledScrollCapture();
-
-    RefPtr<gfxASurface> mScrollCapture;
-    RefPtr<CancelableRunnable> mCaptureRefreshTask;
-    bool mValidFirstCapture;
-    bool mIsScrolling;
-#endif
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceParent_h
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -84,17 +84,16 @@ public:
   virtual void DidComposite(NPP instance) = 0;
   virtual bool IsOOP() = 0;
 #if defined(XP_MACOSX)
   virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
   virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
 #endif
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) = 0;
-    virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) = 0;
 #endif
   virtual nsresult HandledWindowedPluginKeyEvent(
                      NPP aInstance,
                      const mozilla::NativeEventData& aNativeKeyData,
                      bool aIsCOnsumed) = 0;
 
   /**
    * The next three methods are the third leg in the trip to
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2028,22 +2028,16 @@ PluginModuleParent::EndUpdateBackground(
 #if defined(XP_WIN)
 nsresult
 PluginModuleParent::GetScrollCaptureContainer(NPP aInstance,
                                               mozilla::layers::ImageContainer** aContainer)
 {
     PluginInstanceParent* inst = PluginInstanceParent::Cast(aInstance);
     return !inst ? NS_ERROR_FAILURE : inst->GetScrollCaptureContainer(aContainer);
 }
-nsresult
-PluginModuleParent::UpdateScrollState(NPP aInstance, bool aIsScrolling)
-{
-    PluginInstanceParent* inst = PluginInstanceParent::Cast(aInstance);
-    return !inst ? NS_ERROR_FAILURE : inst->UpdateScrollState(aIsScrolling);
-}
 #endif
 
 nsresult
 PluginModuleParent::HandledWindowedPluginKeyEvent(
                         NPP aInstance,
                         const NativeEventData& aNativeKeyData,
                         bool aIsConsumed)
 {
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -258,17 +258,16 @@ protected:
     virtual nsresult BeginUpdateBackground(NPP instance,
                                            const nsIntRect& aRect,
                                            DrawTarget** aDrawTarget) override;
     virtual nsresult EndUpdateBackground(NPP instance,
                                          const nsIntRect& aRect) override;
 
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
-    virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling);
 #endif
 
     virtual nsresult HandledWindowedPluginKeyEvent(
                        NPP aInstance,
                        const mozilla::NativeEventData& aNativeKeyData,
                        bool aIsConsumed) override;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
--- a/dom/plugins/ipc/PluginWidgetChild.cpp
+++ b/dom/plugins/ipc/PluginWidgetChild.cpp
@@ -7,16 +7,20 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "PluginWidgetProxy.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
 
+#if defined(XP_WIN)
+#include "mozilla/plugins/PluginInstanceParent.h"
+#endif
+
 #define PWLOG(...)
 //#define PWLOG(...) printf_stderr(__VA_ARGS__)
 
 namespace mozilla {
 namespace plugins {
 
 PluginWidgetChild::PluginWidgetChild() :
   mWidget(nullptr)
@@ -26,16 +30,35 @@ PluginWidgetChild::PluginWidgetChild() :
 }
 
 PluginWidgetChild::~PluginWidgetChild()
 {
   PWLOG("PluginWidgetChild::~PluginWidgetChild()\n");
   MOZ_COUNT_DTOR(PluginWidgetChild);
 }
 
+bool
+PluginWidgetChild::RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
+                                          const uintptr_t& aPluginInstanceId)
+{
+#if defined(XP_WIN)
+  PluginInstanceParent* instance =
+    PluginInstanceParent::LookupPluginInstanceByID(aPluginInstanceId);
+  if (instance) {
+    NS_WARN_IF(NS_FAILED(instance->SetScrollCaptureId(aScrollCaptureId)));
+  }
+
+  return true;
+#else
+  MOZ_ASSERT_UNREACHABLE(
+    "PluginWidgetChild::RecvSetScrollCaptureId calls not expected.");
+  return false;
+#endif
+}
+
 // Called by the proxy widget when it is destroyed by layout. Only gets
 // called once.
 void
 PluginWidgetChild::ProxyShutdown()
 {
   PWLOG("PluginWidgetChild::ProxyShutdown()\n");
   if (mWidget) {
     mWidget = nullptr;
--- a/dom/plugins/ipc/PluginWidgetChild.h
+++ b/dom/plugins/ipc/PluginWidgetChild.h
@@ -14,16 +14,19 @@ class PluginWidgetProxy;
 namespace plugins {
 
 class PluginWidgetChild : public PPluginWidgetChild
 {
 public:
   PluginWidgetChild();
   virtual ~PluginWidgetChild();
 
+  bool RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
+                              const uintptr_t& aPluginInstanceId) override;
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void SetWidget(mozilla::widget::PluginWidgetProxy* aWidget) {
     mWidget = aWidget;
   }
   void ProxyShutdown();
 
 private:
--- a/dom/plugins/ipc/PluginWidgetParent.cpp
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -138,16 +138,21 @@ PluginWidgetParent::RecvCreate(nsresult*
   mWrapper->SetAllocation();
   PWLOG("Plugin XID=%p\n", (void*)mWrapper->window);
 #elif defined(XP_WIN)
   DebugOnly<DWORD> winres =
     ::SetPropW((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW),
                mozilla::dom::kPluginWidgetContentParentProperty,
                GetTabParent()->Manager()->AsContentParent());
   NS_ASSERTION(winres, "SetPropW call failure");
+
+  uint64_t scrollCaptureId = mWidget->CreateScrollCaptureContainer();
+  uintptr_t pluginId =
+    reinterpret_cast<uintptr_t>(mWidget->GetNativeData(NS_NATIVE_PLUGIN_ID));
+  Unused << SendSetScrollCaptureId(scrollCaptureId, pluginId);
 #endif
 
   // This is a special call we make to nsBaseWidget to register this
   // window as a remote plugin window which is expected to receive
   // visibility updates from the compositor, which ships this data
   // over with corresponding layer updates.
   mWidget->RegisterPluginWindowForRemoteUpdates();
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1899,18 +1899,21 @@ public:
     JSContext* cx = jsapi.cx();
 
     JS::Rooted<JS::Value> value(cx, aValue);
     JS::Rooted<JSObject*> values(cx, mValues);
     if (!JS_WrapValue(cx, &value) ||
         !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) {
       MOZ_ASSERT(JS_IsExceptionPending(cx));
       JS::Rooted<JS::Value> exn(cx);
-      jsapi.StealException(&exn);
-      mPromise->MaybeReject(cx, exn);
+      if (!jsapi.StealException(&exn)) {
+        mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+      } else {
+        mPromise->MaybeReject(cx, exn);
+      }
     }
 
     --mCountdown;
     if (mCountdown == 0) {
       JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues));
       mPromise->MaybeResolve(cx, result);
     }
   }
@@ -2756,17 +2759,17 @@ Promise::Settle(JS::Handle<JS::Value> aV
   }
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
   // If the Promise was rejected, and there is no reject handler already setup,
   // watch for thread shutdown.
   if (aState == PromiseState::Rejected &&
       !mHadRejectCallback &&
       !NS_IsMainThread()) {
-    workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
     worker->AssertIsOnWorkerThread();
 
     mWorkerHolder = new PromiseReportRejectWorkerHolder(this);
     if (NS_WARN_IF(!mWorkerHolder->HoldWorker(worker))) {
       mWorkerHolder = nullptr;
       // Worker is shutting down, report rejection immediately since it is
       // unlikely that reject callbacks will be added after this point.
@@ -2820,19 +2823,19 @@ Promise::RemoveWorkerHolder()
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
   // The DTOR of this WorkerHolder will release the worker for us.
   mWorkerHolder = nullptr;
 }
 
 bool
-PromiseReportRejectWorkerHolder::Notify(workers::Status aStatus)
+PromiseReportRejectWorkerHolder::Notify(Status aStatus)
 {
-  MOZ_ASSERT(aStatus > workers::Running);
+  MOZ_ASSERT(aStatus > Running);
   mPromise->MaybeReportRejectedOnce();
   // After this point, `this` has been deleted by RemoveWorkerHolder!
   return true;
 }
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
 bool
 Promise::CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget)
@@ -2874,32 +2877,32 @@ Promise::GetDependentPromises(nsTArray<R
     }
   }
 }
 
 #endif // SPIDERMONKEY_PROMISE
 
 // A WorkerRunnable to resolve/reject the Promise on the worker thread.
 // Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
-class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
+class PromiseWorkerProxyRunnable : public WorkerRunnable
 {
 public:
   PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
                              PromiseWorkerProxy::RunCallbackFunc aFunc)
     : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
                      WorkerThreadUnchangedBusyCount)
     , mPromiseWorkerProxy(aPromiseWorkerProxy)
     , mFunc(aFunc)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mPromiseWorkerProxy);
   }
 
   virtual bool
-  WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate)
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
 
     MOZ_ASSERT(mPromiseWorkerProxy);
     RefPtr<Promise> workerPromise = mPromiseWorkerProxy->WorkerPromise();
 
@@ -2922,19 +2925,42 @@ protected:
 
 private:
   RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   PromiseWorkerProxy::RunCallbackFunc mFunc;
 };
 
+class PromiseWorkerHolder final : public WorkerHolder
+{
+  // RawPointer because this proxy keeps alive the holder.
+  PromiseWorkerProxy* mProxy;
+
+public:
+  explicit PromiseWorkerHolder(PromiseWorkerProxy* aProxy)
+    : mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  bool
+  Notify(Status aStatus) override
+  {
+    if (aStatus >= Canceling) {
+      mProxy->CleanUp();
+    }
+
+    return true;
+  }
+};
+
 /* static */
 already_AddRefed<PromiseWorkerProxy>
-PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate,
+PromiseWorkerProxy::Create(WorkerPrivate* aWorkerPrivate,
                            Promise* aWorkerPromise,
                            const PromiseWorkerProxyStructuredCloneCallbacks* aCb)
 {
   MOZ_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPromise);
   MOZ_ASSERT_IF(aCb, !!aCb->Write && !!aCb->Read);
 
@@ -2950,43 +2976,40 @@ PromiseWorkerProxy::Create(workers::Work
     return nullptr;
   }
 
   return proxy.forget();
 }
 
 NS_IMPL_ISUPPORTS0(PromiseWorkerProxy)
 
-PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
+PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
                                        Promise* aWorkerPromise,
                                        const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks)
   : mWorkerPrivate(aWorkerPrivate)
   , mWorkerPromise(aWorkerPromise)
   , mCleanedUp(false)
   , mCallbacks(aCallbacks)
   , mCleanUpLock("cleanUpLock")
-#ifdef DEBUG
-  , mWorkerHolderAdded(false)
-#endif
 {
 }
 
 PromiseWorkerProxy::~PromiseWorkerProxy()
 {
   MOZ_ASSERT(mCleanedUp);
-  MOZ_ASSERT(!mWorkerHolderAdded);
+  MOZ_ASSERT(!mWorkerHolder);
   MOZ_ASSERT(!mWorkerPromise);
   MOZ_ASSERT(!mWorkerPrivate);
 }
 
 void
 PromiseWorkerProxy::CleanProperties()
 {
 #ifdef DEBUG
-  workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 #endif
   // Ok to do this unprotected from Create().
   // CleanUp() holds the lock before calling this.
   mCleanedUp = true;
   mWorkerPromise = nullptr;
   mWorkerPrivate = nullptr;
@@ -2995,51 +3018,51 @@ PromiseWorkerProxy::CleanProperties()
   Clear();
 }
 
 bool
 PromiseWorkerProxy::AddRefObject()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mWorkerHolderAdded);
-  if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
+
+  MOZ_ASSERT(!mWorkerHolder);
+  mWorkerHolder.reset(new PromiseWorkerHolder(this));
+  if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate))) {
+    mWorkerHolder = nullptr;
     return false;
   }
 
-#ifdef DEBUG
-  mWorkerHolderAdded = true;
-#endif
   // Maintain a reference so that we have a valid object to clean up when
   // removing the feature.
   AddRef();
   return true;
 }
 
-workers::WorkerPrivate*
+WorkerPrivate*
 PromiseWorkerProxy::GetWorkerPrivate() const
 {
 #ifdef DEBUG
   if (NS_IsMainThread()) {
     mCleanUpLock.AssertCurrentThreadOwns();
   }
 #endif
   // Safe to check this without a lock since we assert lock ownership on the
   // main thread above.
   MOZ_ASSERT(!mCleanedUp);
-  MOZ_ASSERT(mWorkerHolderAdded);
+  MOZ_ASSERT(mWorkerHolder);
 
   return mWorkerPrivate;
 }
 
 Promise*
 PromiseWorkerProxy::WorkerPromise() const
 {
 #ifdef DEBUG
-  workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 #endif
   MOZ_ASSERT(mWorkerPromise);
   return mWorkerPromise;
 }
 
 void
@@ -3086,26 +3109,16 @@ PromiseWorkerProxy::ResolvedCallback(JSC
 
 void
 PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
                                      JS::Handle<JS::Value> aValue)
 {
   RunCallback(aCx, aValue, &Promise::MaybeReject);
 }
 
-bool
-PromiseWorkerProxy::Notify(Status aStatus)
-{
-  if (aStatus >= Canceling) {
-    CleanUp();
-  }
-
-  return true;
-}
-
 void
 PromiseWorkerProxy::CleanUp()
 {
   // Can't release Mutex while it is still locked, so scope the lock.
   {
     MutexAutoLock lock(Lock());
 
     // |mWorkerPrivate| is not safe to use anymore if we have already
@@ -3113,24 +3126,22 @@ PromiseWorkerProxy::CleanUp()
     // first.
     if (CleanedUp()) {
       return;
     }
 
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
-    // Release the Promise and remove the PromiseWorkerProxy from the features of
+    // Release the Promise and remove the PromiseWorkerProxy from the holders of
     // the worker thread since the Promise has been resolved/rejected or the
     // worker thread has been cancelled.
-    MOZ_ASSERT(mWorkerHolderAdded);
-    ReleaseWorker();
-#ifdef DEBUG
-    mWorkerHolderAdded = false;
-#endif
+    MOZ_ASSERT(mWorkerHolder);
+    mWorkerHolder = nullptr;
+
     CleanProperties();
   }
   Release();
 }
 
 JSObject*
 PromiseWorkerProxy::CustomReadHandler(JSContext* aCx,
                                       JSStructuredCloneReader* aReader,
--- a/dom/promise/PromiseDebugging.h
+++ b/dom/promise/PromiseDebugging.h
@@ -85,17 +85,17 @@ public:
 #endif // SPIDERMONKEY_PROMISE
   // Propagate the informations from AddUncaughtRejection
   // and AddConsumedRejection to observers.
   static void FlushUncaughtRejections();
 
 protected:
   static void FlushUncaughtRejectionsInternal();
   friend class FlushRejections;
-  friend class WorkerPrivate;
+  friend class mozilla::dom::workers::WorkerPrivate;
 private:
   // Identity of the process.
   // This property is:
   // - set during initialization of the layout module,
   // prior to any Worker using it;
   // - read by both the main thread and the Workers;
   // - unset during shutdown of the layout module,
   // after any Worker has been shutdown.
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -105,17 +105,16 @@ class WorkerPrivate;
 //        }
 //
 // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
 // can happen if the main thread Promise is never fulfilled - it will
 // stay alive till the worker reaches a Canceling state, even if all external
 // references to it are dropped.
 
 class PromiseWorkerProxy : public PromiseNativeHandler
-                         , public workers::WorkerHolder
                          , public StructuredCloneHolderBase
 {
   friend class PromiseWorkerProxyRunnable;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 public:
   typedef JSObject* (*ReadCallbackOp)(JSContext* aCx,
@@ -180,18 +179,16 @@ public:
 
 protected:
   virtual void ResolvedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
-  virtual bool Notify(workers::Status aStatus) override;
-
 private:
   PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
                      Promise* aWorkerPromise,
                      const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
 
   virtual ~PromiseWorkerProxy();
 
   bool AddRefObject();
@@ -222,17 +219,14 @@ private:
 
   // Aimed to keep objects alive when doing the structured-clone read/write,
   // which can be added by calling StoreISupports() on the main thread.
   nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
 
   // Ensure the worker and the main thread won't race to access |mCleanedUp|.
   Mutex mCleanUpLock;
 
-#ifdef DEBUG
-  // Maybe get rid of this entirely and rely on mCleanedUp
-  bool mWorkerHolderAdded;
-#endif
+  UniquePtr<workers::WorkerHolder> mWorkerHolder;
 };
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseWorkerProxy_h
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -316,16 +316,24 @@ interface WebGL2RenderingContext : WebGL
     const GLenum MAX_CLIENT_WAIT_TIMEOUT_WEBGL                 = 0x9247;
 
     /* Buffer objects */
     void copyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset,
                            GLintptr writeOffset, GLsizeiptr size);
     void getBufferSubData(GLenum target, GLintptr offset, ArrayBuffer? returnedData);
     void getBufferSubData(GLenum target, GLintptr offset, SharedArrayBuffer returnedData);
 
+    [Throws] /* Throws on readback in a write-only context. */
+    void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                    GLenum type, GLintptr offset);
+    /* Include our WebGL 1 function override(s) */
+    [Throws]
+    void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                    GLenum type, ArrayBufferView? pixels);
+
     /* Framebuffer objects */
     void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
                          GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
     void framebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level, GLint layer);
 
     [Throws]
     void invalidateFramebuffer(GLenum target, sequence<GLenum> attachments);
 
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -536,20 +536,22 @@ private:
   ~LoaderListener() {}
 
   RefPtr<ScriptLoaderRunnable> mRunnable;
   uint32_t mIndex;
 };
 
 NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
 
-class ScriptLoaderRunnable final : public WorkerHolder
-                                 , public nsIRunnable
+class ScriptLoaderHolder;
+
+class ScriptLoaderRunnable final : public nsIRunnable
 {
   friend class ScriptExecutorRunnable;
+  friend class ScriptLoaderHolder;
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
   friend class LoaderListener;
 
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   nsTArray<ScriptLoadInfo> mLoadInfos;
   RefPtr<CacheCreator> mCacheCreator;
@@ -713,18 +715,18 @@ private:
     cachePromise->AppendNativeHandler(promiseHandler);
 
     loadInfo.mCachePromise.swap(cachePromise);
     loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
 
     return NS_OK;
   }
 
-  virtual bool
-  Notify(Status aStatus) override
+  bool
+  Notify(Status aStatus)
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (aStatus >= Terminating && !mCanceled) {
       mCanceled = true;
 
       MOZ_ALWAYS_SUCCEEDS(
         NS_DispatchToMainThread(NewRunnableMethod(this,
@@ -1034,17 +1036,17 @@ private:
       principal = parentWorker->GetPrincipal();
     }
 
     // We don't mute the main worker script becase we've already done
     // same-origin checks on them so we should be able to see their errors.
     // Note that for data: url, where we allow it through the same-origin check
     // but then give it a different origin.
     aLoadInfo.mMutedErrorFlag.emplace(IsMainWorkerScript()
-                                        ? false 
+                                        ? false
                                         : !principal->Subsumes(channelPrincipal));
 
     // Make sure we're not seeing the result of a 404 or something by checking
     // the 'requestSucceeded' attribute on the http channel.
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
     nsAutoCString tCspHeaderValue, tCspROHeaderValue;
 
     if (httpChannel) {
@@ -1351,16 +1353,36 @@ private:
         MOZ_ASSERT(false, "This should never fail!");
       }
     }
   }
 };
 
 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable)
 
+class MOZ_STACK_CLASS ScriptLoaderHolder final : public WorkerHolder
+{
+  // Raw pointer because this holder object follows the mRunnable life-time.
+  ScriptLoaderRunnable* mRunnable;
+
+public:
+  explicit ScriptLoaderHolder(ScriptLoaderRunnable* aRunnable)
+    : mRunnable(aRunnable)
+  {
+    MOZ_ASSERT(aRunnable);
+  }
+
+  virtual bool
+  Notify(Status aStatus) override
+  {
+    mRunnable->Notify(aStatus);
+    return true;
+  }
+};
+
 NS_IMETHODIMP
 LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
                                  nsresult aStatus, uint32_t aStringLen,
                                  const uint8_t* aString)
 {
   return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen, aString);
 }
 
@@ -2003,17 +2025,16 @@ ScriptExecutorRunnable::ShutdownScriptLo
         LogExceptionToConsole(aCx, aWorkerPrivate);
         mScriptLoader.mRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
       }
     } else {
       mScriptLoader.mRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     }
   }
 
-  mScriptLoader.ReleaseWorker();
   aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, aResult);
 }
 
 void
 ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx,
                                               WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
@@ -2055,25 +2076,25 @@ LoadAllScripts(WorkerPrivate* aWorkerPri
 
   RefPtr<ScriptLoaderRunnable> loader =
     new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(),
                              aLoadInfos, aIsMainScript, aWorkerScriptType,
                              aRv);
 
   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
 
-  if (NS_WARN_IF(!loader->HoldWorker(aWorkerPrivate))) {
+  ScriptLoaderHolder workerHolder(loader);
+
+  if (NS_WARN_IF(!workerHolder.HoldWorker(aWorkerPrivate))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (NS_FAILED(NS_DispatchToMainThread(loader))) {
     NS_ERROR("Failed to dispatch!");
-
-    loader->ReleaseWorker();
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   syncLoop.Run();
 }
 
 } /* anonymous namespace */
--- a/dom/workers/WorkerHolder.cpp
+++ b/dom/workers/WorkerHolder.cpp
@@ -11,44 +11,50 @@ BEGIN_WORKERS_NAMESPACE
 
 WorkerHolder::WorkerHolder()
   : mWorkerPrivate(nullptr)
 {
 }
 
 WorkerHolder::~WorkerHolder()
 {
+  NS_ASSERT_OWNINGTHREAD(WorkerHolder);
   ReleaseWorkerInternal();
   MOZ_ASSERT(mWorkerPrivate == nullptr);
 }
 
 bool
 WorkerHolder::HoldWorker(WorkerPrivate* aWorkerPrivate)
 {
+  NS_ASSERT_OWNINGTHREAD(WorkerHolder);
   MOZ_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!aWorkerPrivate->AddHolder(this)) {
     return false;
   }
 
   mWorkerPrivate = aWorkerPrivate;
   return true;
 }
 
 void
 WorkerHolder::ReleaseWorker()
 {
+  NS_ASSERT_OWNINGTHREAD(WorkerHolder);
   MOZ_ASSERT(mWorkerPrivate);
+
   ReleaseWorkerInternal();
 }
 
 void
 WorkerHolder::ReleaseWorkerInternal()
 {
+  NS_ASSERT_OWNINGTHREAD(WorkerHolder);
+
   if (mWorkerPrivate) {
     mWorkerPrivate->AssertIsOnWorkerThread();
     mWorkerPrivate->RemoveHolder(this);
     mWorkerPrivate = nullptr;
   }
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerHolder.h
+++ b/dom/workers/WorkerHolder.h
@@ -67,25 +67,30 @@ enum Status
 
   // The close handler has run and the worker is effectively dead.
   Dead
 };
 
 class WorkerHolder
 {
 public:
+  NS_DECL_OWNINGTHREAD
+
   WorkerHolder();
   virtual ~WorkerHolder();
 
   bool HoldWorker(WorkerPrivate* aWorkerPrivate);
   void ReleaseWorker();
 
   virtual bool Notify(Status aStatus) = 0;
 
 protected:
   void ReleaseWorkerInternal();
 
   WorkerPrivate* MOZ_NON_OWNING_REF mWorkerPrivate;
+
+private:
+  void AssertIsOwningThread() const;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_WorkerHolder_h */
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -723,17 +723,18 @@ WorkerProxyToMainThreadRunnable::PostDis
     virtual bool
     WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
     {
       MOZ_ASSERT(aWorkerPrivate);
       aWorkerPrivate->AssertIsOnWorkerThread();
 
       mRunnable->RunBackOnWorkerThread();
 
-      aWorkerPrivate->ModifyBusyCountFromWorker(true);
+      // Let's release the worker thread.
+      aWorkerPrivate->ModifyBusyCountFromWorker(false);
       return true;
     }
 
   private:
     ~ReleaseRunnable()
     {}
   };
 
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -898,19 +898,23 @@ GLBlitHelper::BlitImageToFramebuffer(lay
     mGL->fViewport(0, 0, destSize.width, destSize.height);
 
     switch (type) {
 #ifdef MOZ_WIDGET_GONK
     case ConvertGralloc:
         return BlitGrallocImage(static_cast<layers::GrallocImage*>(srcImage));
 #endif
 
-    case ConvertPlanarYCbCr:
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
-        return BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage));
+    case ConvertPlanarYCbCr: {
+            const auto saved = mGL->GetIntAs<GLint>(LOCAL_GL_UNPACK_ALIGNMENT);
+            mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
+            const auto ret = BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage));
+            mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, saved);
+            return ret;
+        }
 
 #ifdef MOZ_WIDGET_ANDROID
     case ConvertSurfaceTexture:
         return BlitSurfaceTextureImage(static_cast<layers::SurfaceTextureImage*>(srcImage));
 
     case ConvertEGLImage:
         return BlitEGLImageImage(static_cast<layers::EGLImageImage*>(srcImage));
 #endif
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -14,20 +14,16 @@
 #include <map>
 #include <queue>
 #include <stack>
 
 #ifdef DEBUG
 #include <string.h>
 #endif
 
-#ifdef WIN32
-#include <windows.h>
-#endif
-
 #ifdef GetClassName
 #undef GetClassName
 #endif
 
 // Define MOZ_GL_DEBUG unconditionally to enable GL debugging in opt
 // builds.
 #ifdef DEBUG
 #define MOZ_GL_DEBUG 1
@@ -37,17 +33,16 @@
 #include "../../mfbt/RefPtr.h"
 #include "../../mfbt/UniquePtr.h"
 
 #include "GLDefs.h"
 #include "GLLibraryLoader.h"
 #include "nsISupportsImpl.h"
 #include "plstr.h"
 #include "GLContextTypes.h"
-//#include "GLTextureImage.h"
 #include "SurfaceTypes.h"
 #include "GLContextSymbols.h"
 #include "base/platform_thread.h"       // for PlatformThreadId
 #include "mozilla/GenericRefCounted.h"
 #include "gfx2DGlue.h"
 #include "GeckoProfiler.h"
 
 class nsIWidget;
@@ -1223,16 +1218,24 @@ private:
 public:
 
     void fGetIntegerv(GLenum pname, GLint* params);
 
     void GetUIntegerv(GLenum pname, GLuint* params) {
         fGetIntegerv(pname, reinterpret_cast<GLint*>(params));
     }
 
+    template<typename T>
+    T GetIntAs(GLenum pname) {
+        static_assert(sizeof(T) == sizeof(GLint), "Invalid T.");
+        T ret = 0;
+        fGetIntegerv(pname, (GLint*)&ret);
+        return ret;
+    }
+
     void fGetFloatv(GLenum pname, GLfloat* params) {
         BEFORE_GL_CALL;
         mSymbols.fGetFloatv(pname, params);
         AFTER_GL_CALL;
     }
 
     void fGetBooleanv(GLenum pname, realGLboolean* params) {
         BEFORE_GL_CALL;
--- a/gfx/gl/GLLibraryLoader.cpp
+++ b/gfx/gl/GLLibraryLoader.cpp
@@ -1,16 +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/. */
 
 #include "GLLibraryLoader.h"
 
 #include "nsDebug.h"
 
+#ifdef WIN32
+#include <windows.h>
+#endif
+
 namespace mozilla {
 namespace gl {
 
 bool
 GLLibraryLoader::OpenLibrary(const char* library)
 {
     PRLibSpec lspec;
     lspec.type = PR_LibSpec_Pathname;
--- a/gfx/gl/GLLibraryLoader.h
+++ b/gfx/gl/GLLibraryLoader.h
@@ -2,20 +2,16 @@
  * 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 GLLIBRARYLOADER_H_
 #define GLLIBRARYLOADER_H_
 
 #include <stdio.h>
 
-#ifdef WIN32
-#include <windows.h>
-#endif
-
 #include "GLDefs.h"
 #include "nscore.h"
 #include "prlink.h"
 
 namespace mozilla {
 namespace gl {
 
 class GLLibraryLoader
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -388,32 +388,28 @@ ReadPixelsIntoDataSurface(GLContext* gl,
             return;
         }
 
         readSurf = tempSurf;
     }
     MOZ_ASSERT(readAlignment);
     MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
 
-    GLint currentPackAlignment = 0;
-    gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &currentPackAlignment);
-
-    if (currentPackAlignment != readAlignment)
-        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
-
     GLsizei width = dest->GetSize().width;
     GLsizei height = dest->GetSize().height;
 
-    gl->fReadPixels(0, 0,
-                    width, height,
-                    readFormat, readType,
-                    readSurf->GetData());
+    {
+        ScopedPackState safePackState(gl);
+        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
 
-    if (currentPackAlignment != readAlignment)
-        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
+        gl->fReadPixels(0, 0,
+                        width, height,
+                        readFormat, readType,
+                        readSurf->GetData());
+    }
 
     if (readSurf != dest) {
         MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
         MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
         gfx::Factory::CopyDataSourceSurface(readSurf, dest);
     }
 
     // Check if GL is giving back 1.0 alpha for
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -81,19 +81,16 @@ ScopedBindFramebuffer::ScopedBindFramebu
 {
     Init();
     mGL->BindFB(aNewFB);
 }
 
 void
 ScopedBindFramebuffer::UnwrapImpl()
 {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
     if (mOldReadFB == mOldDrawFB) {
         mGL->BindFB(mOldDrawFB);
     } else {
         mGL->BindDrawFB(mOldDrawFB);
         mGL->BindReadFB(mOldReadFB);
     }
 }
 
@@ -105,76 +102,60 @@ ScopedBindTextureUnit::ScopedBindTexture
 {
     MOZ_ASSERT(aTexUnit >= LOCAL_GL_TEXTURE0);
     mGL->GetUIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &mOldTexUnit);
     mGL->fActiveTexture(aTexUnit);
 }
 
 void
 ScopedBindTextureUnit::UnwrapImpl() {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
     mGL->fActiveTexture(mOldTexUnit);
 }
 
 
 /* ScopedTexture **************************************************************/
 
 ScopedTexture::ScopedTexture(GLContext* aGL)
     : ScopedGLWrapper<ScopedTexture>(aGL)
 {
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fGenTextures(1, &mTexture);
 }
 
 void
 ScopedTexture::UnwrapImpl()
 {
-    // Check that we're not falling out of scope after
-    // the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fDeleteTextures(1, &mTexture);
 }
 
-
 /* ScopedFramebuffer **************************************************************/
 
 ScopedFramebuffer::ScopedFramebuffer(GLContext* aGL)
     : ScopedGLWrapper<ScopedFramebuffer>(aGL)
 {
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fGenFramebuffers(1, &mFB);
 }
 
 void
 ScopedFramebuffer::UnwrapImpl()
 {
-    // Check that we're not falling out of scope after
-    // the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fDeleteFramebuffers(1, &mFB);
 }
 
 
 /* ScopedRenderbuffer **************************************************************/
 
 ScopedRenderbuffer::ScopedRenderbuffer(GLContext* aGL)
     : ScopedGLWrapper<ScopedRenderbuffer>(aGL)
 {
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fGenRenderbuffers(1, &mRB);
 }
 
 void
 ScopedRenderbuffer::UnwrapImpl()
 {
-    // Check that we're not falling out of scope after
-    // the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fDeleteRenderbuffers(1, &mRB);
 }
 
 /* ScopedBindTexture **********************************************************/
 
 static GLuint
 GetBoundTexture(GLContext* gl, GLenum texTarget)
 {
@@ -246,20 +227,18 @@ ScopedBindRenderbuffer::ScopedBindRender
 ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL, GLuint aNewRB)
     : ScopedGLWrapper<ScopedBindRenderbuffer>(aGL)
 {
     Init();
     mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, aNewRB);
 }
 
 void
-ScopedBindRenderbuffer::UnwrapImpl() {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
+ScopedBindRenderbuffer::UnwrapImpl()
+{
     mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOldRB);
 }
 
 
 /* ScopedFramebufferForTexture ************************************************/
 ScopedFramebufferForTexture::ScopedFramebufferForTexture(GLContext* aGL,
                                                          GLuint aTexture,
                                                          GLenum aTarget)
@@ -450,25 +429,23 @@ ScopedGLDrawState::ScopedGLDrawState(GLC
     , depthTest   (aGL, LOCAL_GL_DEPTH_TEST, false)
     , dither      (aGL, LOCAL_GL_DITHER,     false)
     , polyOffsFill(aGL, LOCAL_GL_POLYGON_OFFSET_FILL,      false)
     , sampleAToC  (aGL, LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)
     , sampleCover (aGL, LOCAL_GL_SAMPLE_COVERAGE, false)
     , scissor     (aGL, LOCAL_GL_SCISSOR_TEST,    false)
     , stencil     (aGL, LOCAL_GL_STENCIL_TEST,    false)
     , mGL(aGL)
-    , packAlign(4)
 {
-    mGL->GetUIntegerv(LOCAL_GL_UNPACK_ALIGNMENT, &packAlign);
     mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &boundProgram);
     mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &boundBuffer);
     mGL->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &maxAttrib);
     attrib_enabled = MakeUnique<GLint[]>(maxAttrib);
 
-    for (unsigned int i = 0; i < maxAttrib; i++) {
+    for (GLuint i = 0; i < maxAttrib; i++) {
         mGL->fGetVertexAttribiv(i, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &attrib_enabled[i]);
         mGL->fDisableVertexAttribArray(i);
     }
     // Only Attrib0's client side state affected
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib0_size);
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib0_stride);
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, &attrib0_type);
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &attrib0_normalized);
@@ -489,18 +466,16 @@ ScopedGLDrawState::~ScopedGLDrawState()
     mGL->fViewport(viewport[0], viewport[1],
                    viewport[2], viewport[3]);
 
     mGL->fColorMask(colorMask[0],
                     colorMask[1],
                     colorMask[2],
                     colorMask[3]);
 
-    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, packAlign);
-
     for (unsigned int i = 0; i < maxAttrib; i++) {
         if (attrib_enabled[i])
             mGL->fEnableVertexAttribArray(i);
         else
             mGL->fDisableVertexAttribArray(i);
     }
 
 
@@ -513,66 +488,53 @@ ScopedGLDrawState::~ScopedGLDrawState()
                               attrib0_pointer);
 
     mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, boundBuffer);
 
     mGL->fUseProgram(boundProgram);
 }
 
 ////////////////////////////////////////////////////////////////////////
-// ScopedPackAlignment
+// ScopedPackState
 
-ScopedPackAlignment::ScopedPackAlignment(GLContext* gl, GLint scopedVal)
-    : ScopedGLWrapper<ScopedPackAlignment>(gl)
+static bool
+HasPBOState(const GLContext* gl)
+{
+    return (!gl->IsGLES() || gl->Version() >= 300);
+}
+
+ScopedPackState::ScopedPackState(GLContext* gl)
+    : ScopedGLWrapper<ScopedPackState>(gl)
 {
-    MOZ_ASSERT(scopedVal == 1 ||
-               scopedVal == 2 ||
-               scopedVal == 4 ||
-               scopedVal == 8);
+    mGL->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &mAlignment);
 
-    gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &mOldVal);
+    if (mAlignment != 4) mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
+
+    if (!HasPBOState(mGL))
+        return;
 
-    if (scopedVal != mOldVal) {
-        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, scopedVal);
-    } else {
-      // Don't try to re-set it during unwrap.
-        mOldVal = 0;
-    }
+    mGL->fGetIntegerv(LOCAL_GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&mPixelBuffer);
+    mGL->fGetIntegerv(LOCAL_GL_PACK_ROW_LENGTH, &mRowLength);
+    mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_PIXELS, &mSkipPixels);
+    mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_ROWS, &mSkipRows);
+
+    if (mPixelBuffer != 0) mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
+    if (mRowLength != 0)   mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
+    if (mSkipPixels != 0)  mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
+    if (mSkipRows != 0)    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
 }
 
 void
-ScopedPackAlignment::UnwrapImpl() {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
-    if (mOldVal) {
-        mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mOldVal);
-    }
-}
-
-////////////////////////////////////////////////////////////////////////
-// ScopedUnpackAlignment
-
-ScopedUnpackAlignment::ScopedUnpackAlignment(GLContext* gl, GLint scopedVal)
-    : ScopedGLWrapper<ScopedUnpackAlignment>(gl)
+ScopedPackState::UnwrapImpl()
 {
-    MOZ_ASSERT(scopedVal == 1 ||
-               scopedVal == 2 ||
-               scopedVal == 4 ||
-               scopedVal == 8);
+    mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mAlignment);
 
-    gl->fGetIntegerv(LOCAL_GL_UNPACK_ALIGNMENT, &mOldVal);
+    if (!HasPBOState(mGL))
+        return;
 
-    if (scopedVal != mOldVal) {
-        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, scopedVal);
-    }
-}
-
-void
-ScopedUnpackAlignment::UnwrapImpl() {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
-    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mOldVal);
+    mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mPixelBuffer);
+    mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mRowLength);
+    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mSkipPixels);
+    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mSkipRows);
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -304,17 +304,18 @@ public:
                               GLsizei stride, GLuint buffer, const GLvoid* pointer);
     explicit ScopedVertexAttribPointer(GLContext* aGL, GLuint index);
 
 protected:
     void WrapImpl(GLuint index);
     void UnwrapImpl();
 };
 
-struct ScopedGLDrawState {
+struct ScopedGLDrawState
+{
     explicit ScopedGLDrawState(GLContext* gl);
     ~ScopedGLDrawState();
 
     GLuint boundProgram;
     GLuint boundBuffer;
 
     ScopedGLState blend;
     ScopedGLState cullFace;
@@ -334,45 +335,33 @@ struct ScopedGLDrawState {
     GLint attrib0_normalized;
     GLint attrib0_bufferBinding;
     void* attrib0_pointer;
 
     realGLboolean colorMask[4];
     GLint viewport[4];
     GLint scissorBox[4];
     GLContext* const mGL;
-    GLuint packAlign;
 };
 
-struct ScopedPackAlignment
-    : public ScopedGLWrapper<ScopedPackAlignment>
+struct ScopedPackState
+    : public ScopedGLWrapper<ScopedPackState>
 {
-    friend struct ScopedGLWrapper<ScopedPackAlignment>;
-
-protected:
-    GLint mOldVal;
-
-public:
-    ScopedPackAlignment(GLContext* aGL, GLint scopedVal);
+    friend struct ScopedGLWrapper<ScopedPackState>;
 
 protected:
-    void UnwrapImpl();
-};
-
+    GLint mAlignment;
 
-struct ScopedUnpackAlignment
-    : public ScopedGLWrapper<ScopedUnpackAlignment>
-{
-    friend struct ScopedGLWrapper<ScopedUnpackAlignment>;
-
-protected:
-    GLint mOldVal;
+    GLuint mPixelBuffer;
+    GLint mRowLength;
+    GLint mSkipPixels;
+    GLint mSkipRows;
 
 public:
-    ScopedUnpackAlignment(GLContext* gl, GLint scopedVal);
+    explicit ScopedPackState(GLContext* gl);
 
 protected:
     void UnwrapImpl();
 };
 
 } /* namespace gl */
 } /* namespace mozilla */
 
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -564,17 +564,21 @@ ReadbackSharedSurface(SharedSurface* src
                    readGLFormat == LOCAL_GL_BGRA);
         MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
 
         // ReadPixels from the current FB into lockedBits.
         {
             size_t alignment = 8;
             if (dstStride % 4 == 0)
                 alignment = 4;
-            ScopedPackAlignment autoAlign(gl, alignment);
+
+            ScopedPackState scopedPackState(gl);
+            if (alignment != 4) {
+                gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, alignment);
+            }
 
             gl->raw_fReadPixels(0, 0, width, height, readGLFormat, readType,
                                 dstBytes);
         }
     }
 
     const bool isReadRGBA = readGLFormat == LOCAL_GL_RGBA;
 
@@ -596,17 +600,17 @@ uint32_t
 ReadPixel(SharedSurface* src)
 {
     GLContext* gl = src->mGL;
 
     uint32_t pixel;
 
     ScopedReadbackFB a(src);
     {
-        ScopedPackAlignment autoAlign(gl, 4);
+        ScopedPackState scopedPackState(gl);
 
         UniquePtr<uint8_t[]> bytes(new uint8_t[4]);
         gl->raw_fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
                             bytes.get());
         memcpy(&pixel, bytes.get(), 4);
     }
 
     return pixel;
--- a/gfx/ipc/CompositorSession.cpp
+++ b/gfx/ipc/CompositorSession.cpp
@@ -1,133 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "CompositorSession.h"
+#include "base/process_util.h"
+#include "GPUChild.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/GPUProcessHost.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
-#include "mozilla/widget/PlatformWidgetTypes.h"
-#include "base/process_util.h"
 
 namespace mozilla {
 namespace layers {
 
+using namespace gfx;
 using namespace widget;
 
-class InProcessCompositorSession final : public CompositorSession
-{
-public:
-  InProcessCompositorSession(
-    nsIWidget* aWidget,
-    ClientLayerManager* aLayerManager,
-    CSSToLayoutDeviceScale aScale,
-    bool aUseAPZ,
-    bool aUseExternalSurfaceSize,
-    const gfx::IntSize& aSurfaceSize);
 
-  CompositorBridgeParent* GetInProcessBridge() const override;
-  void SetContentController(GeckoContentController* aController) override;
-  uint64_t RootLayerTreeId() const override;
-  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
-  void Shutdown() override;
-
-private:
-  RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
-  RefPtr<CompositorWidget> mCompositorWidget;
-};
-
-already_AddRefed<CompositorSession>
-CompositorSession::CreateInProcess(nsIWidget* aWidget,
-                                   ClientLayerManager* aLayerManager,
-                                   CSSToLayoutDeviceScale aScale,
-                                   bool aUseAPZ,
-                                   bool aUseExternalSurfaceSize,
-                                   const gfx::IntSize& aSurfaceSize)
-{
-  RefPtr<InProcessCompositorSession> session = new InProcessCompositorSession(
-    aWidget,
-    aLayerManager,
-    aScale,
-    aUseAPZ,
-    aUseExternalSurfaceSize,
-    aSurfaceSize);
-  return session.forget();
-}
-
-CompositorSession::CompositorSession()
- : mCompositorWidgetDelegate(nullptr)
+CompositorSession::CompositorSession(CompositorWidgetDelegate* aDelegate,
+                                     CompositorBridgeChild* aChild,
+                                     const uint64_t& aRootLayerTreeId)
+ : mCompositorWidgetDelegate(aDelegate),
+   mCompositorBridgeChild(aChild),
+   mRootLayerTreeId(aRootLayerTreeId)
 {
 }
 
 CompositorSession::~CompositorSession()
 {
 }
 
 CompositorBridgeChild*
 CompositorSession::GetCompositorBridgeChild()
 {
   return mCompositorBridgeChild;
 }
 
-InProcessCompositorSession::InProcessCompositorSession(nsIWidget* aWidget,
-                                                       ClientLayerManager* aLayerManager,
-                                                       CSSToLayoutDeviceScale aScale,
-                                                       bool aUseAPZ,
-                                                       bool aUseExternalSurfaceSize,
-                                                       const gfx::IntSize& aSurfaceSize)
-{
-  CompositorWidgetInitData initData;
-  aWidget->GetCompositorWidgetInitData(&initData);
-  mCompositorWidget = CompositorWidget::CreateLocal(initData, aWidget);
-  mCompositorWidgetDelegate = mCompositorWidget->AsDelegate();
-
-  mCompositorBridgeParent = new CompositorBridgeParent(
-    mCompositorWidget,
-    aScale,
-    aUseAPZ,
-    aUseExternalSurfaceSize,
-    aSurfaceSize);
-  mCompositorBridgeChild = new CompositorBridgeChild(aLayerManager);
-  mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent);
-  mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId());
-}
-
-CompositorBridgeParent*
-InProcessCompositorSession::GetInProcessBridge() const
-{
-  return mCompositorBridgeParent;
-}
-
-void
-InProcessCompositorSession::SetContentController(GeckoContentController* aController)
-{
-  mCompositorBridgeParent->SetControllerForLayerTree(RootLayerTreeId(), aController);
-}
-
-uint64_t
-InProcessCompositorSession::RootLayerTreeId() const
-{
-  return mCompositorBridgeParent->RootLayerTreeId();
-}
-
-already_AddRefed<APZCTreeManager>
-InProcessCompositorSession::GetAPZCTreeManager() const
-{
-  return mCompositorBridgeParent->GetAPZCTreeManager(RootLayerTreeId());
-}
-
-void
-InProcessCompositorSession::Shutdown()
-{
-  // Destroy will synchronously wait for the parent to acknowledge shutdown,
-  // at which point CBP will defer a Release on the compositor thread. We
-  // can safely release our reference now, and let the destructor run on either
-  // thread.
-  mCompositorBridgeChild->Destroy();
-  mCompositorBridgeChild = nullptr;
-  mCompositorBridgeParent = nullptr;
-  mCompositorWidget = nullptr;
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -2,28 +2,27 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef _include_mozilla_gfx_ipc_CompositorSession_h_
 #define _include_mozilla_gfx_ipc_CompositorSession_h_
 
 #include "base/basictypes.h"
-#include "Units.h"
 #include "nsISupportsImpl.h"
-#include "mozilla/gfx/Point.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace widget {
 class CompositorWidget;
 class CompositorWidgetDelegate;
 } // namespace widget
 namespace gfx {
+class GPUProcessHost;
 class GPUProcessManager;
 } // namespace gfx
 namespace layers {
 
 class GeckoContentController;
 class APZCTreeManager;
 class CompositorBridgeParent;
 class CompositorBridgeChild;
@@ -31,59 +30,57 @@ class ClientLayerManager;
 
 // A CompositorSession provides access to a compositor without exposing whether
 // or not it's in-process or out-of-process.
 class CompositorSession
 {
   friend class gfx::GPUProcessManager;
 
 protected:
+  typedef gfx::GPUProcessHost GPUProcessHost;
   typedef widget::CompositorWidget CompositorWidget;
   typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate;
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession)
 
   virtual void Shutdown() = 0;
 
   // This returns a CompositorBridgeParent if the compositor resides in the same process.
   virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
 
   // Set the GeckoContentController for the root of the layer tree.
   virtual void SetContentController(GeckoContentController* aController) = 0;
 
-  // Return the id of the root layer tree.
-  virtual uint64_t RootLayerTreeId() const = 0;
-
   // Return the Async Pan/Zoom Tree Manager for this compositor.
   virtual already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const = 0;
 
   // Return the child end of the compositor IPC bridge.
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   // Return the proxy for accessing the compositor's widget.
   CompositorWidgetDelegate* GetCompositorWidgetDelegate() {
     return mCompositorWidgetDelegate;
   }
 
+  // Return the id of the root layer tree.
+  uint64_t RootLayerTreeId() const {
+    return mRootLayerTreeId;
+  }
+
 protected:
-  CompositorSession();
+  CompositorSession(CompositorWidgetDelegate* aDelegate,
+                    CompositorBridgeChild* aChild,
+                    const uint64_t& aRootLayerTreeId);
   virtual ~CompositorSession();
 
-  static already_AddRefed<CompositorSession> CreateInProcess(
-    nsIWidget* aWidget,
-    ClientLayerManager* aLayerManager,
-    CSSToLayoutDeviceScale aScale,
-    bool aUseAPZ,
-    bool aUseExternalSurfaceSize,
-    const gfx::IntSize& aSurfaceSize);
-
 protected:
+  CompositorWidgetDelegate* mCompositorWidgetDelegate;
   RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
-  CompositorWidgetDelegate* mCompositorWidgetDelegate;
+  uint64_t mRootLayerTreeId;
 
 private:
   DISALLOW_COPY_AND_ASSIGN(CompositorSession);
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -4,21 +4,24 @@
  * 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 "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
+using namespace layers;
 
 GPUParent::GPUParent()
 {
 }
 
 GPUParent::~GPUParent()
 {
 }
@@ -29,17 +32,17 @@ GPUParent::Init(base::ProcessId aParentP
                 IPC::Channel* aChannel)
 {
   if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
     return false;
   }
 
   // Ensure gfxPrefs are initialized.
   gfxPrefs::GetSingleton();
-
+  CompositorThreadHolder::Start();
   return true;
 }
 
 bool
 GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs)
 {
   for (auto setting : prefs) {
     gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
@@ -52,28 +55,58 @@ GPUParent::RecvInit(nsTArray<GfxPrefSett
 bool
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
   return true;
 }
 
+static void
+OpenParent(RefPtr<CompositorBridgeParent> aParent,
+           Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+  if (!aParent->Bind(Move(aEndpoint))) {
+    MOZ_CRASH("Failed to bind compositor");
+  }
+}
+
+bool
+GPUParent::RecvNewWidgetCompositor(Endpoint<layers::PCompositorBridgeParent>&& aEndpoint,
+                                   const CSSToLayoutDeviceScale& aScale,
+                                   const bool& aUseExternalSurfaceSize,
+                                   const IntSize& aSurfaceSize)
+{
+  RefPtr<CompositorBridgeParent> cbp =
+    new CompositorBridgeParent(aScale, aUseExternalSurfaceSize, aSurfaceSize);
+
+  MessageLoop* loop = CompositorThreadHolder::Loop();
+  loop->PostTask(NewRunnableFunction(OpenParent, cbp, Move(aEndpoint)));
+  return true;
+}
+
+bool
+GPUParent::RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+  return CompositorBridgeParent::CreateForContent(Move(aEndpoint));
+}
+
 void
 GPUParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Shutting down GPU process early due to a crash!");
     ProcessChild::QuickExit();
   }
 
 #ifndef NS_FREE_PERMANENT_DATA
   // No point in going through XPCOM shutdown because we don't keep persistent
   // state. Currently we quick-exit in RecvBeginShutdown so this should be
   // unreachable.
   ProcessChild::QuickExit();
-#else
+#endif
+
+  CompositorThreadHolder::Shutdown();
   XRE_ShutdownChildProcess();
-#endif
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -18,16 +18,22 @@ public:
   ~GPUParent();
 
   bool Init(base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
 
   bool RecvInit(nsTArray<GfxPrefSetting>&& prefs) override;
   bool RecvUpdatePref(const GfxPrefSetting& pref) override;
+  bool RecvNewWidgetCompositor(
+    Endpoint<PCompositorBridgeParent>&& aEndpoint,
+    const CSSToLayoutDeviceScale& aScale,
+    const bool& aUseExternalSurface,
+    const IntSize& aSurfaceSize) override;
+  bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_gfx_ipc_GPUParent_h__
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -13,17 +13,19 @@
 namespace mozilla {
 namespace gfx {
 
 GPUProcessHost::GPUProcessHost(Listener* aListener)
  : GeckoChildProcessHost(GeckoProcessType_GPU),
    mListener(aListener),
    mTaskFactory(this),
    mLaunchPhase(LaunchPhase::Unlaunched),
-   mShutdownRequested(false)
+   mProcessToken(0),
+   mShutdownRequested(false),
+   mChannelClosed(false)
 {
   MOZ_COUNT_CTOR(GPUProcessHost);
 }
 
 GPUProcessHost::~GPUProcessHost()
 {
   MOZ_COUNT_DTOR(GPUProcessHost);
 }
@@ -104,25 +106,28 @@ GPUProcessHost::OnChannelConnectedTask()
 void
 GPUProcessHost::OnChannelErrorTask()
 {
   if (mLaunchPhase == LaunchPhase::Waiting) {
     InitAfterConnect(false);
   }
 }
 
+static uint64_t sProcessTokenCounter = 0;
+
 void
 GPUProcessHost::InitAfterConnect(bool aSucceeded)
 {
   MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
   MOZ_ASSERT(!mGPUChild);
 
   mLaunchPhase = LaunchPhase::Complete;
 
   if (aSucceeded) {
+    mProcessToken = ++sProcessTokenCounter;
     mGPUChild = MakeUnique<GPUChild>(this);
     DebugOnly<bool> rv =
       mGPUChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
     MOZ_ASSERT(rv);
 
     mGPUChild->Init();
   }
 
@@ -139,35 +144,44 @@ GPUProcessHost::Shutdown()
   mListener = nullptr;
 
   if (mGPUChild) {
     // OnChannelClosed uses this to check if the shutdown was expected or
     // unexpected.
     mShutdownRequested = true;
 
 #ifdef NS_FREE_PERMANENT_DATA
-    mGPUChild->Close();
+    // The channel might already be closed if we got here unexpectedly.
+    if (!mChannelClosed) {
+      mGPUChild->Close();
+    }
 #else
     // No need to communicate shutdown, the GPU process doesn't need to
     // communicate anything back.
     KillHard("NormalShutdown");
 #endif
 
-    // Wait for ActorDestroy.
+    // If we're shutting down unexpectedly, we're in the middle of handling an
+    // ActorDestroy for PGPUChild, which is still on the stack. We'll return
+    // back to OnChannelClosed.
+    //
+    // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
+    // acknowledges shutdown.
     return;
   }
 
   DestroyProcess();
 }
 
 void
 GPUProcessHost::OnChannelClosed()
 {
   if (!mShutdownRequested) {
     // This is an unclean shutdown. Notify our listener that we're going away.
+    mChannelClosed = true;
     if (mListener) {
       mListener->OnProcessUnexpectedShutdown(this);
     }
   }
 
   // Release the actor.
   GPUChild::Destroy(Move(mGPUChild));
   MOZ_ASSERT(!mGPUChild);
@@ -189,16 +203,22 @@ GPUProcessHost::KillHard(const char* aRe
     NS_WARNING("failed to kill subprocess!");
   }
 
   SetAlreadyDead();
   XRE_GetIOMessageLoop()->PostTask(
     NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, handle, /*force=*/true));
 }
 
+uint64_t
+GPUProcessHost::GetProcessToken() const
+{
+  return mProcessToken;
+}
+
 static void
 DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
 {
   XRE_GetIOMessageLoop()->
     PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
 }
 
 void
--- a/gfx/ipc/GPUProcessHost.h
+++ b/gfx/ipc/GPUProcessHost.h
@@ -69,16 +69,20 @@ public:
   void Shutdown();
 
   // Return the actor for the top-level actor of the process. If the process
   // has not connected yet, this returns null.
   GPUChild* GetActor() const {
     return mGPUChild.get();
   }
 
+  // Return a unique id for this process, guaranteed not to be shared with any
+  // past or future instance of GPUProcessHost.
+  uint64_t GetProcessToken() const;
+
   bool IsConnected() const {
     return !!mGPUChild;
   }
 
   // Called on the IO thread.
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
 
@@ -109,17 +113,18 @@ private:
   enum class LaunchPhase {
     Unlaunched,
     Waiting,
     Complete
   };
   LaunchPhase mLaunchPhase;
 
   UniquePtr<GPUChild> mGPUChild;
-  Listener* listener_;
+  uint64_t mProcessToken;
 
   bool mShutdownRequested;
+  bool mChannelClosed;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -1,17 +1,23 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "GPUProcessManager.h"
 #include "GPUProcessHost.h"
-#include "mozilla/layers/CompositorSession.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/InProcessCompositorSession.h"
+#include "mozilla/layers/RemoteCompositorSession.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
@@ -31,17 +37,19 @@ GPUProcessManager::Initialize()
 
 void
 GPUProcessManager::Shutdown()
 {
   sSingleton = nullptr;
 }
 
 GPUProcessManager::GPUProcessManager()
- : mProcess(nullptr),
+ : mTaskFactory(this),
+   mNextLayerTreeId(0),
+   mProcess(nullptr),
    mGPUChild(nullptr)
 {
   mObserver = new Observer(this);
   nsContentUtils::RegisterShutdownObserver(mObserver);
 }
 
 GPUProcessManager::~GPUProcessManager()
 {
@@ -122,72 +130,200 @@ GPUProcessManager::OnProcessLaunchComple
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   if (!mProcess->IsConnected()) {
     DisableGPUProcess("Failed to launch GPU process");
     return;
   }
 
   mGPUChild = mProcess->GetActor();
+  mProcessToken = mProcess->GetProcessToken();
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 }
 
 void
+GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
+{
+  if (!NS_IsMainThread()) {
+    RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
+      &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
+    NS_DispatchToMainThread(task.forget());
+    return;
+  }
+
+  if (mProcessToken != aProcessToken) {
+    // This token is for an older process; we can safely ignore it.
+    return;
+  }
+
+  // One of the bridged top-level actors for the GPU process has been
+  // prematurely terminated, and we're receiving a notification. This
+  // can happen if the ActorDestroy for a bridged protocol fires
+  // before the ActorDestroy for PGPUChild.
+  DestroyProcess();
+}
+
+void
 GPUProcessManager::DestroyProcess()
 {
   if (!mProcess) {
     return;
   }
 
   mProcess->Shutdown();
+  mProcessToken = 0;
   mProcess = nullptr;
   mGPUChild = nullptr;
 }
 
-already_AddRefed<CompositorSession>
+RefPtr<CompositorSession>
 GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
                                             ClientLayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
                                             bool aUseAPZ,
                                             bool aUseExternalSurfaceSize,
                                             const gfx::IntSize& aSurfaceSize)
 {
-  return CompositorSession::CreateInProcess(
+  uint64_t layerTreeId = AllocateLayerTreeId();
+
+  if (mGPUChild) {
+    RefPtr<CompositorSession> session = CreateRemoteSession(
+      aWidget,
+      aLayerManager,
+      layerTreeId,
+      aScale,
+      aUseAPZ,
+      aUseExternalSurfaceSize,
+      aSurfaceSize);
+    if (session) {
+      return session;
+    }
+
+    // We couldn't create a remote compositor, so abort the process.
+    DisableGPUProcess("Failed to create remote compositor");
+  }
+
+  return InProcessCompositorSession::Create(
     aWidget,
     aLayerManager,
+    layerTreeId,
     aScale,
     aUseAPZ,
     aUseExternalSurfaceSize,
     aSurfaceSize);
 }
 
-PCompositorBridgeParent*
-GPUProcessManager::CreateTabCompositorBridge(ipc::Transport* aTransport,
-                                             base::ProcessId aOtherProcess)
+RefPtr<CompositorSession>
+GPUProcessManager::CreateRemoteSession(nsIWidget* aWidget,
+                                       ClientLayerManager* aLayerManager,
+                                       const uint64_t& aRootLayerTreeId,
+                                       CSSToLayoutDeviceScale aScale,
+                                       bool aUseAPZ,
+                                       bool aUseExternalSurfaceSize,
+                                       const gfx::IntSize& aSurfaceSize)
 {
-  return CompositorBridgeParent::Create(aTransport, aOtherProcess);
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+  ipc::Endpoint<PCompositorBridgeParent> parentPipe;
+  ipc::Endpoint<PCompositorBridgeChild> childPipe;
+
+  nsresult rv = PCompositorBridge::CreateEndpoints(
+    mGPUChild->OtherPid(),
+    base::GetCurrentProcId(),
+    &parentPipe,
+    &childPipe);
+  if (NS_FAILED(rv)) {
+    gfxCriticalNote << "Failed to create PCompositorBridge endpoints: " << hexa(int(rv));
+    return nullptr;
+  }
+
+  RefPtr<CompositorBridgeChild> child = CompositorBridgeChild::CreateRemote(
+    mProcessToken,
+    aLayerManager,
+    Move(childPipe));
+  if (!child) {
+    gfxCriticalNote << "Failed to create CompositorBridgeChild";
+    return nullptr;
+  }
+
+  CompositorWidgetInitData initData;
+  aWidget->GetCompositorWidgetInitData(&initData);
+
+  bool ok = mGPUChild->SendNewWidgetCompositor(
+    Move(parentPipe),
+    aScale,
+    aUseExternalSurfaceSize,
+    aSurfaceSize);
+  if (!ok)
+    return nullptr;
+
+  CompositorWidgetChild* widget = new CompositorWidgetChild(aWidget);
+  if (!child->SendPCompositorWidgetConstructor(widget, initData))
+    return nullptr;
+  if (!child->SendInitialize(aRootLayerTreeId))
+    return nullptr;
+
+  RefPtr<RemoteCompositorSession> session =
+    new RemoteCompositorSession(child, widget, aRootLayerTreeId);
+  return session.forget();
+#else
+  gfxCriticalNote << "Platform does not support out-of-process compositing";
+  return nullptr;
+#endif
+}
+
+bool
+GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
+                                                 ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint)
+{
+  ipc::Endpoint<PCompositorBridgeParent> parentPipe;
+  ipc::Endpoint<PCompositorBridgeChild> childPipe;
+
+  base::ProcessId gpuPid = mGPUChild
+                           ? mGPUChild->OtherPid()
+                           : base::GetCurrentProcId();
+
+  nsresult rv = PCompositorBridge::CreateEndpoints(
+    gpuPid,
+    aOtherProcess,
+    &parentPipe,
+    &childPipe);
+  if (NS_FAILED(rv)) {
+    gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv));
+    return false;
+  }
+
+  if (mGPUChild) {
+    mGPUChild->SendNewContentCompositorBridge(Move(parentPipe));
+  } else {
+    if (!CompositorBridgeParent::CreateForContent(Move(parentPipe)))
+      return false;
+  }
+
+  *aOutEndpoint = Move(childPipe);
+  return true;
 }
 
 already_AddRefed<APZCTreeManager>
 GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
 {
   return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
 }
 
 uint64_t
 GPUProcessManager::AllocateLayerTreeId()
 {
-  return CompositorBridgeParent::AllocateLayerTreeId();
+  MOZ_ASSERT(NS_IsMainThread());
+  return ++mNextLayerTreeId;
 }
 
 void
 GPUProcessManager::DeallocateLayerTreeId(uint64_t aLayersId)
 {
   CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
 }
 
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -7,25 +7,29 @@
 #define _include_mozilla_gfx_ipc_GPUProcessManager_h_
 
 #include "base/basictypes.h"
 #include "base/process.h"
 #include "Units.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/GPUProcessHost.h"
 #include "mozilla/gfx/Point.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/TaskFactory.h"
 #include "mozilla/ipc/Transport.h"
 #include "nsIObserverService.h"
 
+
 namespace mozilla {
 namespace layers {
 class APZCTreeManager;
 class CompositorSession;
 class ClientLayerManager;
 class CompositorUpdateObserver;
+class PCompositorBridgeChild;
 class PCompositorBridgeParent;
 } // namespace layers
 namespace widget {
 class CompositorWidget;
 } // namespace widget
 namespace dom {
 class ContentParent;
 class TabParent;
@@ -38,44 +42,46 @@ namespace gfx {
 class GPUChild;
 
 // The GPUProcessManager is a singleton responsible for creating GPU-bound
 // objects that may live in another process. Currently, it provides access
 // to the compositor via CompositorBridgeParent.
 class GPUProcessManager final : public GPUProcessHost::Listener
 {
   typedef layers::APZCTreeManager APZCTreeManager;
+  typedef layers::ClientLayerManager ClientLayerManager;
+  typedef layers::CompositorSession CompositorSession;
   typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
+  typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
 
 public:
   static void Initialize();
   static void Shutdown();
   static GPUProcessManager* Get();
 
   ~GPUProcessManager();
 
   // If not using a GPU process, launch a new GPU process asynchronously.
   void EnableGPUProcess();
 
   // Ensure that GPU-bound methods can be used. If no GPU process is being
   // used, or one is launched and ready, this function returns immediately.
   // Otherwise it blocks until the GPU process has finished launching.
   void EnsureGPUReady();
 
-  already_AddRefed<layers::CompositorSession> CreateTopLevelCompositor(
+  RefPtr<CompositorSession> CreateTopLevelCompositor(
     nsIWidget* aWidget,
-    layers::ClientLayerManager* aLayerManager,
+    ClientLayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
-  layers::PCompositorBridgeParent* CreateTabCompositorBridge(
-    ipc::Transport* aTransport,
-    base::ProcessId aOtherProcess);
+  bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
+                                     ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
   already_AddRefed<APZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Allocate an ID that can be used to refer to a layer tree and
   // associated resources that live only on the compositor thread.
   //
@@ -101,16 +107,20 @@ public:
   bool UpdateRemoteContentController(uint64_t aLayersId,
                                      dom::ContentParent* aContentParent,
                                      const dom::TabId& aTabId,
                                      dom::TabParent* aBrowserParent);
 
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
 
+  // Notify the GPUProcessManager that a top-level PGPU protocol has been
+  // terminated. This may be called from any thread.
+  void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
+
   // Returns access to the PGPU protocol if a GPU process is present.
   GPUChild* GetGPUChild() {
     return mGPUChild;
   }
 
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
@@ -119,16 +129,25 @@ private:
   GPUProcessManager();
 
   // Permanently disable the GPU process and record a message why.
   void DisableGPUProcess(const char* aMessage);
 
   // Shutdown the GPU process.
   void DestroyProcess();
 
+  RefPtr<CompositorSession> CreateRemoteSession(
+    nsIWidget* aWidget,
+    ClientLayerManager* aLayerManager,
+    const uint64_t& aRootLayerTreeId,
+    CSSToLayoutDeviceScale aScale,
+    bool aUseAPZ,
+    bool aUseExternalSurfaceSize,
+    const gfx::IntSize& aSurfaceSize);
+
   DISALLOW_COPY_AND_ASSIGN(GPUProcessManager);
 
   class Observer final : public nsIObserver {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
     explicit Observer(GPUProcessManager* aManager);
 
@@ -136,16 +155,20 @@ private:
     ~Observer() {}
 
     GPUProcessManager* mManager;
   };
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
+  ipc::TaskFactory<GPUProcessManager> mTaskFactory;
+  uint64_t mNextLayerTreeId;
+
   GPUProcessHost* mProcess;
+  uint64_t mProcessToken;
   GPUChild* mGPUChild;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_
copy from gfx/ipc/CompositorSession.cpp
copy to gfx/ipc/InProcessCompositorSession.cpp
--- a/gfx/ipc/CompositorSession.cpp
+++ b/gfx/ipc/InProcessCompositorSession.cpp
@@ -1,124 +1,64 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "CompositorSession.h"
-#include "mozilla/layers/CompositorBridgeChild.h"
-#include "mozilla/layers/CompositorBridgeParent.h"
-#include "mozilla/widget/PlatformWidgetTypes.h"
-#include "base/process_util.h"
+
+#include "InProcessCompositorSession.h"
 
 namespace mozilla {
 namespace layers {
 
-using namespace widget;
-
-class InProcessCompositorSession final : public CompositorSession
+InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidget* aWidget,
+                                                       CompositorBridgeChild* aChild,
+                                                       CompositorBridgeParent* aParent)
+ : CompositorSession(aWidget->AsDelegate(), aChild, aParent->RootLayerTreeId()),
+   mCompositorBridgeParent(aParent),
+   mCompositorWidget(aWidget)
 {
-public:
-  InProcessCompositorSession(
-    nsIWidget* aWidget,
-    ClientLayerManager* aLayerManager,
-    CSSToLayoutDeviceScale aScale,
-    bool aUseAPZ,
-    bool aUseExternalSurfaceSize,
-    const gfx::IntSize& aSurfaceSize);
+}
 
-  CompositorBridgeParent* GetInProcessBridge() const override;
-  void SetContentController(GeckoContentController* aController) override;
-  uint64_t RootLayerTreeId() const override;
-  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
-  void Shutdown() override;
-
-private:
-  RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
-  RefPtr<CompositorWidget> mCompositorWidget;
-};
-
-already_AddRefed<CompositorSession>
-CompositorSession::CreateInProcess(nsIWidget* aWidget,
+/* static */ RefPtr<InProcessCompositorSession>
+InProcessCompositorSession::Create(nsIWidget* aWidget,
                                    ClientLayerManager* aLayerManager,
+                                   const uint64_t& aRootLayerTreeId,
                                    CSSToLayoutDeviceScale aScale,
                                    bool aUseAPZ,
                                    bool aUseExternalSurfaceSize,
                                    const gfx::IntSize& aSurfaceSize)
 {
-  RefPtr<InProcessCompositorSession> session = new InProcessCompositorSession(
-    aWidget,
-    aLayerManager,
-    aScale,
-    aUseAPZ,
-    aUseExternalSurfaceSize,
-    aSurfaceSize);
-  return session.forget();
-}
-
-CompositorSession::CompositorSession()
- : mCompositorWidgetDelegate(nullptr)
-{
-}
-
-CompositorSession::~CompositorSession()
-{
-}
-
-CompositorBridgeChild*
-CompositorSession::GetCompositorBridgeChild()
-{
-  return mCompositorBridgeChild;
-}
-
-InProcessCompositorSession::InProcessCompositorSession(nsIWidget* aWidget,
-                                                       ClientLayerManager* aLayerManager,
-                                                       CSSToLayoutDeviceScale aScale,
-                                                       bool aUseAPZ,
-                                                       bool aUseExternalSurfaceSize,
-                                                       const gfx::IntSize& aSurfaceSize)
-{
   CompositorWidgetInitData initData;
   aWidget->GetCompositorWidgetInitData(&initData);
-  mCompositorWidget = CompositorWidget::CreateLocal(initData, aWidget);
-  mCompositorWidgetDelegate = mCompositorWidget->AsDelegate();
 
-  mCompositorBridgeParent = new CompositorBridgeParent(
-    mCompositorWidget,
-    aScale,
-    aUseAPZ,
-    aUseExternalSurfaceSize,
-    aSurfaceSize);
-  mCompositorBridgeChild = new CompositorBridgeChild(aLayerManager);
-  mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent);
-  mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId());
+  RefPtr<CompositorWidget> widget = CompositorWidget::CreateLocal(initData, aWidget);
+  RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager);
+  RefPtr<CompositorBridgeParent> parent =
+    child->InitSameProcess(widget, aRootLayerTreeId, aScale, aUseAPZ, aUseExternalSurfaceSize, aSurfaceSize);
+
+  return new InProcessCompositorSession(widget, child, parent);
 }
 
 CompositorBridgeParent*
 InProcessCompositorSession::GetInProcessBridge() const
 {
   return mCompositorBridgeParent;
 }
 
 void
 InProcessCompositorSession::SetContentController(GeckoContentController* aController)
 {
-  mCompositorBridgeParent->SetControllerForLayerTree(RootLayerTreeId(), aController);
-}
-
-uint64_t
-InProcessCompositorSession::RootLayerTreeId() const
-{
-  return mCompositorBridgeParent->RootLayerTreeId();
+  mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController);
 }
 
 already_AddRefed<APZCTreeManager>
 InProcessCompositorSession::GetAPZCTreeManager() const
 {
-  return mCompositorBridgeParent->GetAPZCTreeManager(RootLayerTreeId());
+  return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
 }
 
 void
 InProcessCompositorSession::Shutdown()
 {
   // Destroy will synchronously wait for the parent to acknowledge shutdown,
   // at which point CBP will defer a Release on the compositor thread. We
   // can safely release our reference now, and let the destructor run on either
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/InProcessCompositorSession.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
+#define _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
+
+#include "CompositorSession.h"
+#include "mozilla/gfx/Point.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+// A CompositorSession where both the child and parent CompositorBridge reside
+// in the same process.
+class InProcessCompositorSession final : public CompositorSession
+{
+public:
+  static RefPtr<InProcessCompositorSession> Create(
+    nsIWidget* aWidget,
+    ClientLayerManager* aLayerManager,
+    const uint64_t& aRootLayerTreeId,
+    CSSToLayoutDeviceScale aScale,
+    bool aUseAPZ,
+    bool aUseExternalSurfaceSize,
+    const gfx::IntSize& aSurfaceSize);
+
+  CompositorBridgeParent* GetInProcessBridge() const override;
+  void SetContentController(GeckoContentController* aController) override;
+  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
+  void Shutdown() override;
+
+private:
+  InProcessCompositorSession(widget::CompositorWidget* aWidget,
+                             CompositorBridgeChild* aChild,
+                             CompositorBridgeParent* aParent);
+
+private:
+  RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+  RefPtr<CompositorWidget> mCompositorWidget;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -1,14 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; 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 protocol PContent;
+include protocol PCompositorBridge;
+
+using mozilla::CSSToLayoutDeviceScale from "Units.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 
 namespace mozilla {
 namespace gfx {
 
 union GfxPrefValue {
   bool;
   int32_t;
   uint32_t;
@@ -23,12 +26,21 @@ struct GfxPrefSetting {
 sync protocol PGPU
 {
 parent:
   // Sent by the UI process to initiate core settings.
   async Init(GfxPrefSetting[] prefs);
 
   // Called to update a gfx preference.
   async UpdatePref(GfxPrefSetting pref);
+
+  // Create a new top-level compositor.
+  async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
+                            CSSToLayoutDeviceScale scale,
+                            bool useExternalSurface,
+                            IntSize surfaceSize);
+
+  // Create a new content-process compositor bridge.
+  async NewContentCompositorBridge(Endpoint<PCompositorBridgeParent> endpoint);
 };
 
 } // namespace gfx
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "RemoteCompositorSession.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace widget;
+
+RemoteCompositorSession::RemoteCompositorSession(CompositorBridgeChild* aChild,
+                                                 CompositorWidgetDelegate* aWidgetDelegate,
+                                                 const uint64_t& aRootLayerTreeId)
+ : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId)
+{
+}
+
+CompositorBridgeParent*
+RemoteCompositorSession::GetInProcessBridge() const
+{
+  return nullptr;
+}
+
+void
+RemoteCompositorSession::SetContentController(GeckoContentController* aController)
+{
+  MOZ_CRASH("NYI");
+}
+
+already_AddRefed<APZCTreeManager>
+RemoteCompositorSession::GetAPZCTreeManager() const
+{
+  return nullptr;
+}
+
+void
+RemoteCompositorSession::Shutdown()
+{
+  mCompositorBridgeChild->Destroy();
+  mCompositorBridgeChild = nullptr;
+  mCompositorWidgetDelegate = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_mozilla_gfx_ipc_RemoteCompositorSession_h
+#define include_mozilla_gfx_ipc_RemoteCompositorSession_h
+
+#include "CompositorSession.h"
+#include "mozilla/gfx/Point.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+class RemoteCompositorSession final : public CompositorSession
+{
+public:
+  RemoteCompositorSession(CompositorBridgeChild* aChild,
+                          CompositorWidgetDelegate* aWidgetDelegate,
+                          const uint64_t& aRootLayerTreeId);
+
+  CompositorBridgeParent* GetInProcessBridge() const override;
+  void SetContentController(GeckoContentController* aController) override;
+  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
+  void Shutdown() override;
+
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -15,16 +15,18 @@ EXPORTS.mozilla.gfx += [
     'GPUProcessHost.h',
     'GPUProcessImpl.h',
     'GPUProcessManager.h',
     'SharedDIB.h',
 ]
 
 EXPORTS.mozilla.layers += [
     'CompositorSession.h',
+    'InProcessCompositorSession.h',
+    'RemoteCompositorSession.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXPORTS.mozilla.gfx += [
         'SharedDIBSurface.h',
         'SharedDIBWin.h',
     ]
     UNIFIED_SOURCES += [
@@ -35,16 +37,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
 UNIFIED_SOURCES += [
     'CompositorSession.cpp',
     'D3DMessageUtils.cpp',
     'GPUChild.cpp',
     'GPUParent.cpp',
     'GPUProcessHost.cpp',
     'GPUProcessImpl.cpp',
     'GPUProcessManager.cpp',
+    'InProcessCompositorSession.cpp',
+    'RemoteCompositorSession.cpp',
     'SharedDIB.cpp',
 ]
 
 IPDL_SOURCES = [
     'GraphicsMessages.ipdlh',
     'PGPU.ipdl',
 ]
 
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -203,21 +203,38 @@ ImageContainer::ImageContainer(Mode flag
         mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take();
         MOZ_ASSERT(mImageClient);
         break;
       default:
         MOZ_ASSERT(false, "This flag is invalid.");
         break;
     }
   }
+  mAsyncContainerID = mImageClient ? mImageClient->GetAsyncID()
+                                   : sInvalidAsyncContainerId;
+}
+
+ImageContainer::ImageContainer(uint64_t aAsyncContainerID)
+  : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+  mGenerationCounter(++sGenerationCounter),
+  mPaintCount(0),
+  mDroppedImageCount(0),
+  mImageFactory(nullptr),
+  mRecycleBin(nullptr),
+  mImageClient(nullptr),
+  mAsyncContainerID(aAsyncContainerID),
+  mCurrentProducerID(-1),
+  mIPDLChild(nullptr)
+{
+  MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
 }
 
 ImageContainer::~ImageContainer()
 {
-  if (IsAsync()) {
+  if (mIPDLChild) {
     mIPDLChild->ForgetImageContainer();
     ImageBridgeChild::DispatchReleaseImageClient(mImageClient, mIPDLChild);
   }
 }
 
 RefPtr<PlanarYCbCrImage>
 ImageContainer::CreatePlanarYCbCrImage()
 {
@@ -327,26 +344,26 @@ ImageContainer::ClearImagesFromImageBrid
   SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
 ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
 {
   MOZ_ASSERT(!aImages.IsEmpty());
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  if (IsAsync()) {
+  if (mImageClient) {
     ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
   }
   SetCurrentImageInternal(aImages);
 }
 
 void
 ImageContainer::ClearAllImages()
 {
-  if (IsAsync()) {
+  if (mImageClient) {
     // Let ImageClient release all TextureClients. This doesn't return
     // until ImageBridge has called ClearCurrentImageFromImageBridge.
     ImageBridgeChild::FlushAllImages(mImageClient, this);
     return;
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   SetCurrentImageInternal(nsTArray<NonOwningImage>());
@@ -380,27 +397,23 @@ ImageContainer::SetCurrentImagesInTransa
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
 
   SetCurrentImageInternal(aImages);
 }
 
 bool ImageContainer::IsAsync() const
 {
-  return mImageClient != nullptr;
+  return mAsyncContainerID != sInvalidAsyncContainerId;
 }
 
 uint64_t ImageContainer::GetAsyncContainerID() const
 {
   NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
-  if (IsAsync()) {
-    return mImageClient->GetAsyncID();
-  } else {
-    return 0; // zero is always an invalid AsyncID
-  }
+  return mAsyncContainerID;
 }
 
 bool
 ImageContainer::HasCurrentImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   return !mCurrentImages.IsEmpty();
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -354,18 +354,27 @@ protected:
  */
 class ImageContainer final : public SupportsWeakPtr<ImageContainer> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
 
   enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 };
 
+  static const uint64_t sInvalidAsyncContainerId = 0;
+
   explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
 
+  /**
+   * Create ImageContainer just to hold another ASYNCHRONOUS ImageContainer's
+   * async container ID.
+   * @param aAsyncContainerID async container ID for which we are a proxy
+   */
+  explicit ImageContainer(uint64_t aAsyncContainerID);
+
   typedef uint32_t FrameID;
   typedef uint32_t ProducerID;
 
   RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
 
   // Factory methods for shared image types.
   RefPtr<SharedRGBImage> CreateSharedRGBImage();
 
@@ -622,16 +631,18 @@ private:
   // sucessfully created with ENABLE_ASYNC, or points to null otherwise.
   // 'unsuccessful' in this case only means that the ImageClient could not
   // be created, most likely because off-main-thread compositing is not enabled.
   // In this case the ImageContainer is perfectly usable, but it will forward
   // frames to the compositor through transactions in the main thread rather than
   // asynchronusly using the ImageBridge IPDL protocol.
   ImageClient* mImageClient;
 
+  uint64_t mAsyncContainerID;
+
   nsTArray<FrameID> mFrameIDsNotYetComposited;
   // ProducerID for last current image(s), including the frames in
   // mFrameIDsNotYetComposited
   ProducerID mCurrentProducerID;
 
   // Object must be released on the ImageBridge thread. Field is immutable
   // after creation of the ImageContainer.
   ImageContainerChild* mIPDLChild;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3652,25 +3652,25 @@ void AsyncPanZoomController::DispatchSta
 
   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
     if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::eTransformBegin);
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
       // Let the compositor know about scroll state changes so it can manage
       // windowed plugins.
-      if (mCompositorBridgeParent) {
+      if (gfxPrefs::HidePluginsForScroll() && mCompositorBridgeParent) {
         mCompositorBridgeParent->ScheduleHideAllPluginWindows();
       }
 #endif
     } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::eTransformEnd);
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
-      if (mCompositorBridgeParent) {
+      if (gfxPrefs::HidePluginsForScroll() && mCompositorBridgeParent) {
         mCompositorBridgeParent->ScheduleShowAllPluginWindows();
       }
 #endif
     }
   }
 }
 
 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -304,32 +304,32 @@ TexClientFromReadback(SharedSurface* src
       // [RR, GG, BB, AA]
       texClient = factory.CreateR8G8B8AX8();
     } else {
       MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
     }
 
     MOZ_ASSERT(texClient);
     if (!texClient)
-        return nullptr;
+      return nullptr;
 
     // With a texClient, we can lock for writing.
     TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
     DebugOnly<bool> succeeded = autoLock.Succeeded();
     MOZ_ASSERT(succeeded, "texture should have locked");
 
     MappedTextureData mapped;
     texClient->BorrowMappedData(mapped);
 
     // ReadPixels from the current FB into mapped.data.
     auto width = src->mSize.width;
     auto height = src->mSize.height;
 
     {
-      ScopedPackAlignment autoAlign(gl, 4);
+      ScopedPackState scopedPackState(gl);
 
       MOZ_ASSERT(mapped.stride/4 == mapped.size.width);
       gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data);
     }
 
     // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
     // RB_SWAPPED doesn't work with Basic. (bug ???????)
     // RB_SWAPPED doesn't work with D3D9. (bug ???????)
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -155,16 +155,19 @@ CompositableClient::IsConnected() const
 
 void
 CompositableClient::Destroy()
 {
   if (!IsConnected()) {
     return;
   }
 
+  if (mTextureClientRecycler) {
+    mTextureClientRecycler->Destroy();
+  }
   mCompositableChild->mCompositableClient = nullptr;
   mCompositableChild->Destroy(mForwarder);
   mCompositableChild = nullptr;
 }
 
 bool
 CompositableClient::DestroyFallback(PCompositableChild* aActor)
 {
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -670,17 +670,17 @@ public:
    * When CompositableForwarder forwards the TextureClient with
    * TextureFlags::RECYCLE, it holds TextureClient's ref until host side
    * releases it. The host side sends TextureClient release message.
    * The id is used to check if the message is for the last TextureClient
    * forwarding.
    */
   void SetLastFwdTransactionId(uint64_t aTransactionId)
   {
-    MOZ_ASSERT(mFwdTransactionId < aTransactionId);
+    MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
     mFwdTransactionId = aTransactionId;
   }
 
   uint64_t GetLastFwdTransactionId() { return mFwdTransactionId; }
 
   void EnableReadLock();
 
   TextureReadLock* GetReadLock() { return mReadLock; }
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -109,16 +109,17 @@ YCbCrTextureClientAllocationHelper::Allo
                                        mData.mStereoMode,
                                        mTextureFlags);
 }
 
 TextureClientRecycleAllocator::TextureClientRecycleAllocator(CompositableForwarder* aAllocator)
   : mSurfaceAllocator(aAllocator)
   , mMaxPooledSize(kMaxPooledSized)
   , mLock("TextureClientRecycleAllocatorImp.mLock")
+  , mIsDestroyed(false)
 {
 }
 
 TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
 {
   MutexAutoLock lock(mLock);
   while (!mPooledClients.empty()) {
     mPooledClients.pop();
@@ -159,16 +160,19 @@ TextureClientRecycleAllocator::CreateOrR
              aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT ||
              aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
   MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
 
   RefPtr<TextureClientHolder> textureHolder;
 
   {
     MutexAutoLock lock(mLock);