Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 18 Jul 2016 17:12:23 +0200
changeset 330550 ec19b4cb42d5355d336967e00059ddee3053f924
parent 330549 10ef4e6ed0a3dfa4f1523069647c7d93c2b28e7a (current diff)
parent 330458 cde56ead650fd302be1d440507485b9abf7c163a (diff)
child 330551 cd99b54daeb13fb8a15278caad6800be85ddbc04
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
Merge mozilla-central to fx-team
dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-array-buffer.html
dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/biv_array_oob/biv_array_oob_001_to_001.html
dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/biv_array_oob/gl_Color.frag
dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/biv_array_oob/input.run.txt
dom/canvas/test/webgl-conf/checkout/conformance/resources/1-channel.jpg
dom/canvas/test/webgl-conf/checkout/conformance/resources/3x3.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/blue-1x1.jpg
dom/canvas/test/webgl-conf/checkout/conformance/resources/boolUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/bug-32888-texture.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/floatUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/fragmentShader.frag
dom/canvas/test/webgl-conf/checkout/conformance/resources/glsl-conformance-test.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/glsl-constructor-tests-generator.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/glsl-feature-tests.css
dom/canvas/test/webgl-conf/checkout/conformance/resources/glsl-generator.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-256-with-128-alpha.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-256.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-default-gamma.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-gamma0.1.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-gamma1.0.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-gamma2.0.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-gamma4.0.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp-gamma9.0.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/gray-ramp.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/green-2x2-16bit.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/intArrayUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/intUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/iterable-test.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/matUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/noopUniformShader.frag
dom/canvas/test/webgl-conf/checkout/conformance/resources/noopUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/npot-video.mp4
dom/canvas/test/webgl-conf/checkout/conformance/resources/npot-video.theora.ogv
dom/canvas/test/webgl-conf/checkout/conformance/resources/npot-video.webmvp8.webm
dom/canvas/test/webgl-conf/checkout/conformance/resources/oes-texture-float-and-half-float-linear.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/ogles-tests.css
dom/canvas/test/webgl-conf/checkout/conformance/resources/opengl_logo.jpg
dom/canvas/test/webgl-conf/checkout/conformance/resources/out-of-bounds-test.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/pnglib.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-green.mp4
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-green.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-green.svg
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-green.theora.ogv
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-green.webmvp8.webm
dom/canvas/test/webgl-conf/checkout/conformance/resources/red-indexed.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/samplerUniformShader.frag
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-cie-rgb-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-colormatch-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-colorspin-profile.jpg
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-colorspin-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-e-srgb-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-smpte-c-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/small-square-with-srgb-iec61966-2.1-profile.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/structUniformShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-canvas.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-image-data.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-image.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-svg-image.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-video.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/tex-image-and-sub-image-2d-with-webgl-canvas.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/thunderbird-logo-64x64.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/transparent-on-left-indexed.png
dom/canvas/test/webgl-conf/checkout/conformance/resources/vertexShader.vert
dom/canvas/test/webgl-conf/checkout/conformance/resources/webgl-test-utils.js
dom/canvas/test/webgl-conf/checkout/conformance/resources/zero-alpha.png
dom/canvas/test/webgl-conf/checkout/conformance/textures/compressed-tex-image.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/copy-tex-image-2d-formats.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/copy-tex-image-and-sub-image-2d.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/default-texture.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/gl-get-tex-parameter.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/gl-pixelstorei.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/gl-teximage.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/mipmap-fbo.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/origin-clean-conformance.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-array-buffer-view.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgb565.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba4444.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba5551.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-canvas.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgb565.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba4444.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba5551.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-data.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-rgb565.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-rgba4444.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image-rgba5551.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-image.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-svg-image.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-video-rgb565.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-video-rgba4444.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-video-rgba5551.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-video.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgb565.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgba4444.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgba5551.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-and-uniform-binding-bugs.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-canvas-corruption.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-webgl.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-with-format-and-type.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-image-with-invalid-data.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-input-validation.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-sub-image-2d-bad-args.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/tex-sub-image-2d.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texparameter-test.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-active-bind-2.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-active-bind.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-attachment-formats.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-clear.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-complete.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-copying-feedback-loops.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-draw-with-2d-and-cube.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-fakeblack.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-formats-test.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-hd-dpi.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-mips.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-npot-video.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-npot.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-size-cube-maps.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-size-limit.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-size.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-sub-image-cube-maps.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-transparent-pixels-initialized.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-upload-cube-maps.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/texture-upload-size.html
dom/canvas/test/webgl-conf/checkout/conformance/typedarrays/resources/typed-array-test-cases.js
dom/canvas/test/webgl-conf/checkout/conformance/typedarrays/resources/typed-array-worker.js
dom/canvas/test/webgl-conf/checkout/misc/program-test-1.html
dom/canvas/test/webgl-conf/checkout/resources/cookies.js
dom/canvas/test/webgl-conf/checkout/resources/desktop-gl-constants.js
dom/canvas/test/webgl-conf/checkout/resources/js-test-post.js
dom/canvas/test/webgl-conf/checkout/resources/js-test-pre.js
dom/canvas/test/webgl-conf/checkout/resources/test-eval.js
dom/canvas/test/webgl-conf/checkout/resources/webgl-test-harness.js
dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py
dom/canvas/test/webgl-conf/generated/test_conformance__buffers__buffer-data-array-buffer.html
dom/canvas/test/webgl-conf/generated/test_conformance__ogles__GL__biv_array_oob__biv_array_oob_001_to_001.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__compressed-tex-image.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__copy-tex-image-2d-formats.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__copy-tex-image-and-sub-image-2d.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__default-texture.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__gl-get-tex-parameter.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__gl-pixelstorei.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__gl-teximage.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__mipmap-fbo.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__origin-clean-conformance.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-array-buffer-view.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-canvas-rgb565.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-canvas-rgba4444.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-canvas-rgba5551.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-canvas.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-data-rgb565.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-data-rgba4444.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-data-rgba5551.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-data.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-rgb565.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-rgba4444.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image-rgba5551.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-image.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-svg-image.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-video-rgb565.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-video-rgba4444.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-video-rgba5551.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-video.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-webgl-canvas-rgb565.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-webgl-canvas-rgba4444.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-webgl-canvas-rgba5551.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-sub-image-2d-with-webgl-canvas.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-and-uniform-binding-bugs.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-canvas-corruption.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-webgl.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-with-format-and-type.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-image-with-invalid-data.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-input-validation.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-sub-image-2d-bad-args.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__tex-sub-image-2d.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texparameter-test.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-active-bind-2.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-active-bind.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-attachment-formats.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-clear.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-complete.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-copying-feedback-loops.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-draw-with-2d-and-cube.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-fakeblack.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-formats-test.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-hd-dpi.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-mips.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-npot-video.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-npot.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-size-cube-maps.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-size-limit.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-size.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-sub-image-cube-maps.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-transparent-pixels-initialized.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-upload-cube-maps.html
dom/canvas/test/webgl-conf/generated/test_conformance__textures__texture-upload-size.html
--- 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/script.js
+++ b/devtools/server/actors/script.js
@@ -14,16 +14,17 @@ const { EnvironmentActor } = require("de
 const { FrameActor } = require("devtools/server/actors/frame");
 const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
 const { SourceActor, getSourceURL } = require("devtools/server/actors/source");
 const { DebuggerServer } = require("devtools/server/main");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, dumpn, update, fetch } = DevToolsUtils;
 const promise = require("promise");
+const PromiseDebugging = require("PromiseDebugging");
 const xpcInspector = require("xpcInspector");
 const ScriptStore = require("./utils/ScriptStore");
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const object = require("sdk/util/object");
 const { threadSpec } = require("devtools/shared/specs/script");
 
 const { defer, resolve, reject, all } = promise;
 
--- 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/devtools/shared/worker/loader.js
+++ b/devtools/shared/worker/loader.js
@@ -312,16 +312,22 @@ function WorkerDebuggerLoader(options) {
 }
 
 this.WorkerDebuggerLoader = WorkerDebuggerLoader;
 
 // The following APIs rely on the use of Components, and the worker debugger
 // does not provide alternative definitions for them. Consequently, they are
 // stubbed out both on the main thread and worker threads.
 
+var PromiseDebugging = {
+  getState: function () {
+    throw new Error("PromiseDebugging is not available in workers!");
+  }
+};
+
 var chrome = {
   CC: undefined,
   Cc: undefined,
   ChromeWorker: undefined,
   Cm: undefined,
   Ci: undefined,
   Cu: undefined,
   Cr: undefined,
@@ -485,16 +491,17 @@ this.worker = new WorkerDebuggerLoader({
     "reportError": reportError,
     "rpc": rpc,
     "setImmediate": setImmediate,
     "URL": URL,
   },
   loadSubScript: loadSubScript,
   modules: {
     "Debugger": Debugger,
+    "PromiseDebugging": PromiseDebugging,
     "Services": Object.create(null),
     "chrome": chrome,
     "xpcInspector": xpcInspector
   },
   paths: {
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "": "resource://gre/modules/commonjs/",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
--- 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/docshell/test/browser/browser_timelineMarkers-frame-04.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js
@@ -45,21 +45,17 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      let asyncFrame = frame.asyncParent;
-      // Skip over self-hosted parts of our Promise implementation.
-      while (asyncFrame.source === 'self-hosted')
-        asyncFrame = asyncFrame.parent;
-      is(asyncFrame.functionDisplayName, "do_promise",
+      is(frame.asyncParent.functionDisplayName, "do_promise",
          "Async parent has correct function name");
     }
   }, {
     desc: "Async stack trace on Javascript marker with script",
     searchFor: (markers) => {
       return markers.some(m => (m.name == "Javascript" &&
                                 m.causeName == "promise callback"));
     },
@@ -70,19 +66,15 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      let asyncFrame = frame.asyncParent;
-      // Skip over self-hosted parts of our Promise implementation.
-      while (asyncFrame.source === 'self-hosted')
-        asyncFrame = asyncFrame.parent;
-      is(asyncFrame.functionDisplayName, "do_promise_script",
+      is(frame.asyncParent.functionDisplayName, "do_promise_script",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/docshell/test/browser/browser_timelineMarkers-frame-05.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js
@@ -86,32 +86,20 @@ if (Services.prefs.getBoolPref("javascri
     searchFor: "ConsoleTime",
     setup: function(docShell) {
       let resolver = makePromise();
       resolvePromise(resolver);
     },
     check: function(markers) {
       markers = markers.filter(m => m.name == "ConsoleTime");
       ok(markers.length > 0, "Promise marker includes stack");
-      ok(markers[0].stack.functionDisplayName == "testConsoleTime",
-         "testConsoleTime is on the stack");
+
       let frame = markers[0].endStack;
-      ok(frame.functionDisplayName == "testConsoleTimeEnd",
-         "testConsoleTimeEnd is on the stack");
-
-      frame = frame.parent;
-      ok(frame.functionDisplayName == "makePromise/<",
-         "makePromise/< is on the stack");
-      let asyncFrame = frame.asyncParent;
-      ok(asyncFrame !== null, "Frame has async parent");
-      is(asyncFrame.asyncCause, "promise callback",
+      ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
+      is(frame.parent.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      // Skip over self-hosted parts of our Promise implementation.
-      while (asyncFrame.source === 'self-hosted') {
-        asyncFrame = asyncFrame.parent;
-      }
-      is(asyncFrame.functionDisplayName, "makePromise",
+      is(frame.parent.asyncParent.functionDisplayName, "makePromise",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1526,29 +1526,29 @@ Element::BindToTree(nsIDocument* aDocume
     ClearSubtreeRootPointer();
 
     // Being added to a document.
     SetIsInDocument();
 
     // Unset this flag since we now really are in a document.
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
                // And clear the lazy frame construction bits.
-               NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
-               // And the restyle bits
-               ELEMENT_ALL_RESTYLE_FLAGS);
+               NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
+    // And the restyle bits
+    UnsetRestyleFlagsIfGecko();
   } else if (IsInShadowTree()) {
     // We're not in a document, but we did get inserted into a shadow tree.
     // Since we won't have any restyle data in the document's restyle trackers,
     // don't let us get inserted with restyle bits set incorrectly.
     //
     // Also clear all the other flags that are cleared above when we do get
     // inserted into a document.
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
-               NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
-               ELEMENT_ALL_RESTYLE_FLAGS);
+               NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
+    UnsetRestyleFlagsIfGecko();
   } else {
     // If we're not in the doc and not in a shadow tree,
     // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
   nsIDocument* composedDoc = GetComposedDoc();
   if (composedDoc) {
--- 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/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1290,17 +1290,19 @@ class IDLInterfaceOrNamespace(IDLObjectW
         if (self.getExtendedAttribute("Pref") and
             self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
             raise WebIDLError("[Pref] used on an interface that is not %s-only" %
                               self.parentScope.primaryGlobalName,
                               [self.location])
 
         # Conditional exposure makes no sense for interfaces with no
         # interface object, unless they're navigator properties.
-        if (self.isExposedConditionally() and
+        # And SecureContext makes sense for interfaces with no interface object,
+        # since it is also propagated to interface members.
+        if (self.isExposedConditionally(exclusions=["SecureContext"]) and
             not self.hasInterfaceObject() and
             not self.isNavigatorProperty()):
             raise WebIDLError("Interface with no interface object is "
                               "exposed conditionally",
                               [self.location])
 
         # Value iterators are only allowed on interfaces with indexed getters,
         # and pair iterators are only allowed on interfaces without indexed
@@ -1528,18 +1530,18 @@ class IDLInterfaceOrNamespace(IDLObjectW
 
     def hasMembersInSlots(self):
         return self._ownMembersInSlots != 0
 
     conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
                                     "SecureContext",
                                     "CheckAnyPermissions",
                                     "CheckAllPermissions" ]
-    def isExposedConditionally(self):
-        return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
+    def isExposedConditionally(self, exclusions=[]):
+        return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes)
 
 class IDLInterface(IDLInterfaceOrNamespace):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial):
         IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
                                          parent, members, isKnownNonPartial)
 
     def __str__(self):
--- a/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
+++ b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
@@ -311,8 +311,22 @@ def WebIDLTest(parser, harness):
                "[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface")
     harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
                "Constants copied from non-[SecureContext] interface should not be [SecureContext]")
     harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
                "Attributes copied from non-[SecureContext] interface should not be [SecureContext]")
     harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
                "Methods copied from non-[SecureContext] interface should not be [SecureContext]")
  
+    # Test SecureContext and NoInterfaceObject
+    parser = parser.reset()
+    parser.parse("""
+        [NoInterfaceObject, SecureContext]
+        interface TestSecureContextNoInterfaceObject {
+          void testSecureMethod(byte foo);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 1, "TestSecureContextNoInterfaceObject should have only one member")
+    harness.ok(results[0].getExtendedAttribute("SecureContext"),
+      "Interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+      "Interface member should have [SecureContext] extended attribute")
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -20,114 +20,105 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(exn.name, name,
        "Should have the right exception name in test " + testNumber);
     is("filename" in exn ? exn.filename : exn.fileName, filename,
        "Should have the right file name in test " + testNumber);
     is(exn.message, message,
        "Should have the right message in test " + testNumber);
     is(exn.code, code, "Should have the right .code in test " + testNumber);
     if (message === "") {
-      is(exn.name, "InternalError",
+      is(exn.name, "NS_ERROR_UNEXPECTED",
          "Should have one of our synthetic exceptions in test " + testNumber);
     }
     is(exn.stack, stack, "Should have the right stack in test " + testNumber);
   }
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
     /* Async parent frames from pushPrefEnv don't show up in e10s.  */
     var isE10S = !SpecialPowers.isMainProcess();
     var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
     var ourFile = location.href;
-    var unwrapError = "Promise rejection value is a non-unwrappable cross-compartment wrapper.";
-    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:130:3
+    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:121:3
 ` : "";
 
     Promise.all([
       t.testPromiseWithThrowingChromePromiseInit().then(
           ensurePromiseFail.bind(null, 1),
-          checkExn.bind(null, 49, "InternalError", unwrapError,
-                        undefined, ourFile, 1,
-                        `doTest@${ourFile}:49:7
+          checkExn.bind(null, 48, "NS_ERROR_UNEXPECTED", "", undefined,
+                        ourFile, 1,
+                        `doTest@${ourFile}:48:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingContentPromiseInit(function() {
           thereIsNoSuchContentFunction1();
         }).then(
           ensurePromiseFail.bind(null, 2),
-          checkExn.bind(null, 57, "ReferenceError",
+          checkExn.bind(null, 56, "ReferenceError",
                         "thereIsNoSuchContentFunction1 is not defined",
                         undefined, ourFile, 2,
-                        `doTest/<@${ourFile}:57:11
-doTest@${ourFile}:56:7
+                        `doTest/<@${ourFile}:56:11
+doTest@${ourFile}:55:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenFunction().then(
           ensurePromiseFail.bind(null, 3),
-          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 3, asyncStack ? (`Async*doTest@${ourFile}:67:7
-` +
-                        parentFrame) : "")),
+          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 3, "")),
       t.testPromiseWithThrowingContentThenFunction(function() {
           thereIsNoSuchContentFunction2();
         }).then(
           ensurePromiseFail.bind(null, 4),
-          checkExn.bind(null, 73, "ReferenceError",
+          checkExn.bind(null, 70, "ReferenceError",
                         "thereIsNoSuchContentFunction2 is not defined",
                         undefined, ourFile, 4,
-                        `doTest/<@${ourFile}:73:11
+                        `doTest/<@${ourFile}:70:11
 ` +
-                        (asyncStack ? `Async*doTest@${ourFile}:72:7
+                        (asyncStack ? `Async*doTest@${ourFile}:69:7
 ` : "") +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenable().then(
           ensurePromiseFail.bind(null, 5),
-          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 5, asyncStack ? (`Async*doTest@${ourFile}:84:7
-` +
-                        parentFrame) : "")),
+          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 5, "")),
       t.testPromiseWithThrowingContentThenable({
             then: function() { thereIsNoSuchContentFunction3(); }
         }).then(
           ensurePromiseFail.bind(null, 6),
-          checkExn.bind(null, 90, "ReferenceError",
+          checkExn.bind(null, 85, "ReferenceError",
                         "thereIsNoSuchContentFunction3 is not defined",
                         undefined, ourFile, 6,
-                        `doTest/<.then@${ourFile}:90:32
-` + (asyncStack ? `Async*doTest@${ourFile}:89:7\n` + parentFrame : ""))),
+                        `doTest/<.then@${ourFile}:85:32
+`)),
       t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
           ensurePromiseFail.bind(null, 7),
-          checkExn.bind(null, 98, "NotFoundError",
+          checkExn.bind(null, 93, "NotFoundError",
                         "We are a second DOMException",
                         DOMException.NOT_FOUND_ERR, ourFile, 7,
-                        `doTest@${ourFile}:98:7
+                        `doTest@${ourFile}:93:7
 ` +
                         parentFrame)),
       t.testPromiseWithDOMExceptionThrowingThenFunction().then(
           ensurePromiseFail.bind(null, 8),
-          checkExn.bind(null, asyncStack ? 106 : 0, "NetworkError",
+          checkExn.bind(null, asyncStack ? 101 : 0, "NetworkError",
                          "We are a third DOMException",
                         DOMException.NETWORK_ERR, asyncStack ? ourFile : "", 8,
-                        (asyncStack ? `Async*doTest@${ourFile}:106:7
+                        (asyncStack ? `Async*doTest@${ourFile}:101:7
 ` +
                          parentFrame : ""))),
       t.testPromiseWithDOMExceptionThrowingThenable().then(
           ensurePromiseFail.bind(null, 9),
-          checkExn.bind(null, asyncStack ? 114 : 0, "TypeMismatchError",
+          checkExn.bind(null, 0, "TypeMismatchError",
                         "We are a fourth DOMException",
-                        DOMException.TYPE_MISMATCH_ERR,
-                        asyncStack ? ourFile : "", 9,
-                        (asyncStack ? `Async*doTest@${ourFile}:114:7
-` +
-                         parentFrame : ""))),
+                         DOMException.TYPE_MISMATCH_ERR, "", 9, "")),
     ]).then(SimpleTest.finish,
-            function(err) {
-              ok(false, "One of our catch statements totally failed with err" + err + ', stack: ' + (err ? err.stack : ''));
+            function() {
+              ok(false, "One of our catch statements totally failed");
               SimpleTest.finish();
             });
   }
 
   SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
                             doTest);
   </script>
 </head>
--- 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/always-fail.html
+++ b/dom/canvas/test/webgl-conf/always-fail.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
   <meta charset='utf-8'>
   <link rel='stylesheet' href='checkout/resources/js-test-style.css'/>
-  <script src='checkout/resources/js-test-pre.js'></script>
+  <script src='checkout/js/js-test-pre.js'></script>
 </head>
 <body>
   <div id='description'></div>
   <div id='console'></div>
 
   <script>
     description('Deliberately fail so as to test our harness.');
 
--- a/dom/canvas/test/webgl-conf/checkout/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/00_test_list.txt
@@ -1,6 +1,8 @@
 // files that end in .txt list other tests
 // other lines are assumed to be .html files
 
 conformance/00_test_list.txt
 conformance/more/00_test_list.txt
-
+// Disable deqp tests temporarily
+//deqp/00_test_list.txt
+--min-version 2.0.0 conformance2/00_test_list.txt
--- a/dom/canvas/test/webgl-conf/checkout/CONFORMANCE_RULES.txt
+++ b/dom/canvas/test/webgl-conf/checkout/CONFORMANCE_RULES.txt
@@ -58,20 +58,18 @@ 2. Conformance across multiple operating
 
 A WebGL implementation will be considered to conform to a particular
 version of the conformance suite if it passes rule (1) on all of the
 OSs on which the WebGL implementation is intended to be supported.
 
 3. Conformance as the web browser is upgraded
 
 WebGL conformance results submitted for an earlier version of the
-browser carry forward to later versions of the browser, unless the
-WebGL implementation changes substantially enough that it is expected
-that conformance may have been affected. In that case, the browser
-implementer should submit new conformance results.
+browser carry forward to later versions of the browser that do not
+cause any previously passing test to fail.
 
 4. Conformance as the operating system is upgraded
 
 If a new version is released of one of the OSs on which a WebGL
 implementation is intended to run, then WebGL conformance results
 submitted for earlier versions of that OS carry forward. Future
 conformance results must be submitted against the new version of the
 OS. If it is anticipated that the older OS version will be supported
@@ -83,25 +81,22 @@ separately for both the old and new vers
 
 A GPU vendor submitting conformance results for a WebGL implementation
 typically does so because the device containing the GPU includes a
 built-in web browser. In this case the following rules apply:
 
 1. Conformance results must be submitted for each GPU and operating
 system combination to be certified. It is not required to submit
 results for different devices containing the same GPU and running the
-same operating system.
-
-2. Conformance results may be submitted up to three months in advance
-of the product reaching initial shipment.
+same operating system that do not cause any previously passing test to
+fail.
 
-3. Conformance results carry forward for a given GPU as the operating
-system and graphics driver are upgraded, unless there is an
-expectation that conformance may have been affected. In that case, the
-GPU vendor should submit new conformance results.
+2. Conformance results carry forward for a given GPU as the operating
+system and graphics driver are upgraded but do not cause any previously
+passing test to fail.
 
 Discussion
 ==========
 
 A WebGL implementation intended to ship on three OSs may reach
 conformance on two of them, but due to graphics driver bugs, may be
 unable to reach conformance on the third. In this situation the
 implementation is not yet considered to be conformant.
--- a/dom/canvas/test/webgl-conf/checkout/README.md
+++ b/dom/canvas/test/webgl-conf/checkout/README.md
@@ -64,14 +64,16 @@ There are various URL options you can pa
 
                  Example: webgl-conformance-tests.html?frames=8
 
                  Note the tests are not required to run with anything other than frames = 1.
 
 History
 -------
 
+The dates below are when work on the conformance suite version was started.
+
 - 2011/02/24: Version 1.0.0
 - 2012/02/23: Version 1.0.1
 - 2012/03/20: Version 1.0.2
 - 2013/02/14: Version 1.0.3
 - 2013/10/11: Version 2.0.0 (beta)
-
+- 2014/11/14: Version 1.0.4
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/AUTHORS
@@ -0,0 +1,19 @@
+# This is a list of contributors to the Closure Library.
+
+# Names should be added to this file like so:
+# Name or Organization <email address>
+
+Google Inc.
+Stellar Science Ltd.
+Mohamed Mansour <hello@mohamedmansour.com>
+Bjorn Tipling <bjorn.tipling@gmail.com>
+SameGoal LLC <help@samegoal.com>
+Guido Tapia <guido.tapia@gmail.com>
+Andrew Mattie <amattie@gmail.com>
+Ilia Mirkin <ibmirkin@gmail.com>
+Ivan Kozik <ivan.kozik@gmail.com>
+Rich Dougherty <rich@rd.gen.nz>
+Chad Killingsworth <chadkillingsworth@missouristate.edu>
+Dan Pupius <dan.pupius@gmail.com>
+Mike Dunn <dunn74@gmail.com>
+Kengo Toda <skypencil@gmail.com>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/CONTRIBUTING
@@ -0,0 +1,48 @@
+Closure Library welcomes patches/pulls for features and bugfixes.
+
+For contributors inside Google, follow the instructions given here:
+http://go/closure-contributors
+
+For contributors external to Google, follow the instructions given here:
+
+Notes on Contributions to Closure Library
+
+Google Individual Contributor License
+
+In all cases, contributors must sign a contributor license agreement,
+either for an individual or corporation, before a patch can be
+accepted. Please fill out the agreement for an individual or a
+corporation, as appropriate.
+
+https://developers.google.com/open-source/cla/individual
+https://developers.google.com/open-source/cla/corporate
+
+If you or your organization is not listed there already, you should
+add an entry to the AUTHORS file as part of your patch.
+
+If you plan to add a significant component or large chunk of code, it
+is recommended to bring it up on the discussion list for a design
+discussion before writing code.
+
+If appropriate, write a unit test that demonstrates your patch. Tests are the
+best way to ensure that future contributors do not break your code
+accidentally.
+
+To change the Closure Library source, you must submit a pull request
+in GitHub. See the GitHub documentation here:
+
+https://help.github.com/categories/63/articles
+
+Closure Library developers monitor outstanding pull requests. They may
+request changes on the pull request before accepting. They will also
+verify that the CLA has been signed.
+
+Oftentimes, the pull request will not be directly merged, but patched to
+the internal Google codebase to verify that unit and integration tests
+will Closure pass before submitting (and optionally make changes to
+the patch to match style, fix text, or to make the code or comments
+clearer). In this case, the issue associated with the pull request
+will be closed when the patch pushed to the repository via the MOE
+(Make Open Easy) system.
+
+https://code.google.com/p/moe-java/
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/LICENSE
@@ -0,0 +1,176 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/README-Khronos.txt
@@ -0,0 +1,20 @@
+This is a partial snapshot of the Closure library workspace from:
+
+  https://github.com/google/closure-library
+
+It contains only the portions needed to type check the ported dEQP
+tests, namely:
+
+  closure/goog/base.js
+  closure/goog/deps.js
+
+and supporting scripts in closure/bin/ .
+
+The current version snapshotted here is:
+
+-----
+commit 57bdfe0093cc158fb3a58d2c5f7d75ece8c4b45b
+Author: Nathan Naze <nanaze@gmail.com>
+Date:   Fri Apr 24 18:38:26 2015 -0400
+
+    fix bad merge
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/README.md
@@ -0,0 +1,9 @@
+# Closure Library
+
+Closure Library is a powerful, low-level JavaScript library designed
+for building complex and scalable web applications. It is used by many
+Google web applications, such as Gmail and Google Docs.
+
+For more information, visit the
+[Google Developers](https://developers.google.com/closure/library) or
+[GitHub](https://github.com/google/closure-library) sites.
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/closurebuilder.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utility for Closure Library dependency calculation.
+
+ClosureBuilder scans source files to build dependency info.  From the
+dependencies, the script can produce a manifest in dependency order,
+a concatenated script, or compiled output from the Closure Compiler.
+
+Paths to files can be expressed as individual arguments to the tool (intended
+for use with find and xargs).  As a convenience, --root can be used to specify
+all JS files below a directory.
+
+usage: %prog [options] [file1.js file2.js ...]
+"""
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+import logging
+import optparse
+import os
+import sys
+
+import depstree
+import jscompiler
+import source
+import treescan
+
+
+def _GetOptionsParser():
+  """Get the options parser."""
+
+  parser = optparse.OptionParser(__doc__)
+  parser.add_option('-i',
+                    '--input',
+                    dest='inputs',
+                    action='append',
+                    default=[],
+                    help='One or more input files to calculate dependencies '
+                    'for.  The namespaces in this file will be combined with '
+                    'those given with the -n flag to form the set of '
+                    'namespaces to find dependencies for.')
+  parser.add_option('-n',
+                    '--namespace',
+                    dest='namespaces',
+                    action='append',
+                    default=[],
+                    help='One or more namespaces to calculate dependencies '
+                    'for.  These namespaces will be combined with those given '
+                    'with the -i flag to form the set of namespaces to find '
+                    'dependencies for.  A Closure namespace is a '
+                    'dot-delimited path expression declared with a call to '
+                    'goog.provide() (e.g. "goog.array" or "foo.bar").')
+  parser.add_option('--root',
+                    dest='roots',
+                    action='append',
+                    default=[],
+                    help='The paths that should be traversed to build the '
+                    'dependencies.')
+  parser.add_option('-o',
+                    '--output_mode',
+                    dest='output_mode',
+                    type='choice',
+                    action='store',
+                    choices=['list', 'script', 'compiled'],
+                    default='list',
+                    help='The type of output to generate from this script. '
+                    'Options are "list" for a list of filenames, "script" '
+                    'for a single script containing the contents of all the '
+                    'files, or "compiled" to produce compiled output with '
+                    'the Closure Compiler.  Default is "list".')
+  parser.add_option('-c',
+                    '--compiler_jar',
+                    dest='compiler_jar',
+                    action='store',
+                    help='The location of the Closure compiler .jar file.')
+  parser.add_option('-f',
+                    '--compiler_flags',
+                    dest='compiler_flags',
+                    default=[],
+                    action='append',
+                    help='Additional flags to pass to the Closure compiler. '
+                    'To pass multiple flags, --compiler_flags has to be '
+                    'specified multiple times.')
+  parser.add_option('-j',
+                    '--jvm_flags',
+                    dest='jvm_flags',
+                    default=[],
+                    action='append',
+                    help='Additional flags to pass to the JVM compiler. '
+                    'To pass multiple flags, --jvm_flags has to be '
+                    'specified multiple times.')
+  parser.add_option('--output_file',
+                    dest='output_file',
+                    action='store',
+                    help=('If specified, write output to this path instead of '
+                          'writing to standard output.'))
+
+  return parser
+
+
+def _GetInputByPath(path, sources):
+  """Get the source identified by a path.
+
+  Args:
+    path: str, A path to a file that identifies a source.
+    sources: An iterable collection of source objects.
+
+  Returns:
+    The source from sources identified by path, if found.  Converts to
+    real paths for comparison.
+  """
+  for js_source in sources:
+    # Convert both to real paths for comparison.
+    if os.path.realpath(path) == os.path.realpath(js_source.GetPath()):
+      return js_source
+
+
+def _GetClosureBaseFile(sources):
+  """Given a set of sources, returns the one base.js file.
+
+  Note that if zero or two or more base.js files are found, an error message
+  will be written and the program will be exited.
+
+  Args:
+    sources: An iterable of _PathSource objects.
+
+  Returns:
+    The _PathSource representing the base Closure file.
+  """
+  base_files = [
+      js_source for js_source in sources if _IsClosureBaseFile(js_source)]
+
+  if not base_files:
+    logging.error('No Closure base.js file found.')
+    sys.exit(1)
+  if len(base_files) > 1:
+    logging.error('More than one Closure base.js files found at these paths:')
+    for base_file in base_files:
+      logging.error(base_file.GetPath())
+    sys.exit(1)
+  return base_files[0]
+
+
+def _IsClosureBaseFile(js_source):
+  """Returns true if the given _PathSource is the Closure base.js source."""
+  return (os.path.basename(js_source.GetPath()) == 'base.js' and
+          js_source.provides == set(['goog']))
+
+
+class _PathSource(source.Source):
+  """Source file subclass that remembers its file path."""
+
+  def __init__(self, path):
+    """Initialize a source.
+
+    Args:
+      path: str, Path to a JavaScript file.  The source string will be read
+        from this file.
+    """
+    super(_PathSource, self).__init__(source.GetFileContents(path))
+
+    self._path = path
+
+  def __str__(self):
+    return 'PathSource %s' % self._path
+
+  def GetPath(self):
+    """Returns the path."""
+    return self._path
+
+
+def _WrapGoogModuleSource(src):
+  return ('goog.loadModule(function(exports) {{'
+          '"use strict";'
+          '{0}'
+          '\n'  # terminate any trailing single line comment.
+          ';return exports'
+          '}});\n').format(src)
+
+
+def main():
+  logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
+                      level=logging.INFO)
+  options, args = _GetOptionsParser().parse_args()
+
+  # Make our output pipe.
+  if options.output_file:
+    out = open(options.output_file, 'w')
+  else:
+    out = sys.stdout
+
+  sources = set()
+
+  logging.info('Scanning paths...')
+  for path in options.roots:
+    for js_path in treescan.ScanTreeForJsFiles(path):
+      sources.add(_PathSource(js_path))
+
+  # Add scripts specified on the command line.
+  for js_path in args:
+    sources.add(_PathSource(js_path))
+
+  logging.info('%s sources scanned.', len(sources))
+
+  # Though deps output doesn't need to query the tree, we still build it
+  # to validate dependencies.
+  logging.info('Building dependency tree..')
+  tree = depstree.DepsTree(sources)
+
+  input_namespaces = set()
+  inputs = options.inputs or []
+  for input_path in inputs:
+    js_input = _GetInputByPath(input_path, sources)
+    if not js_input:
+      logging.error('No source matched input %s', input_path)
+      sys.exit(1)
+    input_namespaces.update(js_input.provides)
+
+  input_namespaces.update(options.namespaces)
+
+  if not input_namespaces:
+    logging.error('No namespaces found. At least one namespace must be '
+                  'specified with the --namespace or --input flags.')
+    sys.exit(2)
+
+  # The Closure Library base file must go first.
+  base = _GetClosureBaseFile(sources)
+  deps = [base] + tree.GetDependencies(input_namespaces)
+
+  output_mode = options.output_mode
+  if output_mode == 'list':
+    out.writelines([js_source.GetPath() + '\n' for js_source in deps])
+  elif output_mode == 'script':
+    for js_source in deps:
+      src = js_source.GetSource()
+      if js_source.is_goog_module:
+        src = _WrapGoogModuleSource(src)
+      out.write(src + '\n')
+  elif output_mode == 'compiled':
+    logging.warning("""\
+Closure Compiler now natively understands and orders Closure dependencies and
+is prefererred over using this script for performing JavaScript compilation.
+
+Please migrate your codebase.
+
+See:
+https://github.com/google/closure-compiler/wiki/Manage-Closure-Dependencies
+""")
+
+    # Make sure a .jar is specified.
+    if not options.compiler_jar:
+      logging.error('--compiler_jar flag must be specified if --output is '
+                    '"compiled"')
+      sys.exit(2)
+
+    # Will throw an error if the compilation fails.
+    compiled_source = jscompiler.Compile(
+        options.compiler_jar,
+        [js_source.GetPath() for js_source in deps],
+        jvm_flags=options.jvm_flags,
+        compiler_flags=options.compiler_flags)
+
+    logging.info('JavaScript compilation succeeded.')
+    out.write(compiled_source)
+
+  else:
+    logging.error('Invalid value for --output flag.')
+    sys.exit(2)
+
+
+if __name__ == '__main__':
+  main()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/depstree.py
@@ -0,0 +1,189 @@
+# Copyright 2009 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Class to represent a full Closure Library dependency tree.
+
+Offers a queryable tree of dependencies of a given set of sources.  The tree
+will also do logical validation to prevent duplicate provides and circular
+dependencies.
+"""
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+class DepsTree(object):
+  """Represents the set of dependencies between source files."""
+
+  def __init__(self, sources):
+    """Initializes the tree with a set of sources.
+
+    Args:
+      sources: A set of JavaScript sources.
+
+    Raises:
+      MultipleProvideError: A namespace is provided by muplitple sources.
+      NamespaceNotFoundError: A namespace is required but never provided.
+    """
+
+    self._sources = sources
+    self._provides_map = dict()
+
+    # Ensure nothing was provided twice.
+    for source in sources:
+      for provide in source.provides:
+        if provide in self._provides_map:
+          raise MultipleProvideError(
+              provide, [self._provides_map[provide], source])
+
+        self._provides_map[provide] = source
+
+    # Check that all required namespaces are provided.
+    for source in sources:
+      for require in source.requires:
+        if require not in self._provides_map:
+          raise NamespaceNotFoundError(require, source)
+
+  def GetDependencies(self, required_namespaces):
+    """Get source dependencies, in order, for the given namespaces.
+
+    Args:
+      required_namespaces: A string (for one) or list (for one or more) of
+        namespaces.
+
+    Returns:
+      A list of source objects that provide those namespaces and all
+      requirements, in dependency order.
+
+    Raises:
+      NamespaceNotFoundError: A namespace is requested but doesn't exist.
+      CircularDependencyError: A cycle is detected in the dependency tree.
+    """
+    if isinstance(required_namespaces, str):
+      required_namespaces = [required_namespaces]
+
+    deps_sources = []
+
+    for namespace in required_namespaces:
+      for source in DepsTree._ResolveDependencies(
+          namespace, [], self._provides_map, []):
+        if source not in deps_sources:
+          deps_sources.append(source)
+
+    return deps_sources
+
+  @staticmethod
+  def _ResolveDependencies(required_namespace, deps_list, provides_map,
+                           traversal_path):
+    """Resolve dependencies for Closure source files.
+
+    Follows the dependency tree down and builds a list of sources in dependency
+    order.  This function will recursively call itself to fill all dependencies
+    below the requested namespaces, and then append its sources at the end of
+    the list.
+
+    Args:
+      required_namespace: String of required namespace.
+      deps_list: List of sources in dependency order.  This function will append
+        the required source once all of its dependencies are satisfied.
+      provides_map: Map from namespace to source that provides it.
+      traversal_path: List of namespaces of our path from the root down the
+        dependency/recursion tree.  Used to identify cyclical dependencies.
+        This is a list used as a stack -- when the function is entered, the
+        current namespace is pushed and popped right before returning.
+        Each recursive call will check that the current namespace does not
+        appear in the list, throwing a CircularDependencyError if it does.
+
+    Returns:
+      The given deps_list object filled with sources in dependency order.
+
+    Raises:
+      NamespaceNotFoundError: A namespace is requested but doesn't exist.
+      CircularDependencyError: A cycle is detected in the dependency tree.
+    """
+
+    source = provides_map.get(required_namespace)
+    if not source:
+      raise NamespaceNotFoundError(required_namespace)
+
+    if required_namespace in traversal_path:
+      traversal_path.append(required_namespace)  # do this *after* the test
+
+      # This must be a cycle.
+      raise CircularDependencyError(traversal_path)
+
+    # If we don't have the source yet, we'll have to visit this namespace and
+    # add the required dependencies to deps_list.
+    if source not in deps_list:
+      traversal_path.append(required_namespace)
+
+      for require in source.requires:
+
+        # Append all other dependencies before we append our own.
+        DepsTree._ResolveDependencies(require, deps_list, provides_map,
+                                      traversal_path)
+      deps_list.append(source)
+
+      traversal_path.pop()
+
+    return deps_list
+
+
+class BaseDepsTreeError(Exception):
+  """Base DepsTree error."""
+
+  def __init__(self):
+    Exception.__init__(self)
+
+
+class CircularDependencyError(BaseDepsTreeError):
+  """Raised when a dependency cycle is encountered."""
+
+  def __init__(self, dependency_list):
+    BaseDepsTreeError.__init__(self)
+    self._dependency_list = dependency_list
+
+  def __str__(self):
+    return ('Encountered circular dependency:\n%s\n' %
+            '\n'.join(self._dependency_list))
+
+
+class MultipleProvideError(BaseDepsTreeError):
+  """Raised when a namespace is provided more than once."""
+
+  def __init__(self, namespace, sources):
+    BaseDepsTreeError.__init__(self)
+    self._namespace = namespace
+    self._sources = sources
+
+  def __str__(self):
+    source_strs = map(str, self._sources)
+
+    return ('Namespace "%s" provided more than once in sources:\n%s\n' %
+            (self._namespace, '\n'.join(source_strs)))
+
+
+class NamespaceNotFoundError(BaseDepsTreeError):
+  """Raised when a namespace is requested but not provided."""
+
+  def __init__(self, namespace, source=None):
+    BaseDepsTreeError.__init__(self)
+    self._namespace = namespace
+    self._source = source
+
+  def __str__(self):
+    msg = 'Namespace "%s" never provided.' % self._namespace
+    if self._source:
+      msg += ' Required in %s' % self._source
+    return msg
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/depswriter.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Generates out a Closure deps.js file given a list of JavaScript sources.
+
+Paths can be specified as arguments or (more commonly) specifying trees
+with the flags (call with --help for descriptions).
+
+Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...]
+"""
+
+import logging
+import optparse
+import os
+import posixpath
+import shlex
+import sys
+
+import source
+import treescan
+
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+def MakeDepsFile(source_map):
+  """Make a generated deps file.
+
+  Args:
+    source_map: A dict map of the source path to source.Source object.
+
+  Returns:
+    str, A generated deps file source.
+  """
+
+  # Write in path alphabetical order
+  paths = sorted(source_map.keys())
+
+  lines = []
+
+  for path in paths:
+    js_source = source_map[path]
+
+    # We don't need to add entries that don't provide anything.
+    if js_source.provides:
+      lines.append(_GetDepsLine(path, js_source))
+
+  return ''.join(lines)
+
+
+def _GetDepsLine(path, js_source):
+  """Get a deps.js file string for a source."""
+
+  provides = sorted(js_source.provides)
+  requires = sorted(js_source.requires)
+  module = 'true' if js_source.is_goog_module else 'false'
+
+  return 'goog.addDependency(\'%s\', %s, %s, %s);\n' % (
+      path, provides, requires, module)
+
+
+def _GetOptionsParser():
+  """Get the options parser."""
+
+  parser = optparse.OptionParser(__doc__)
+
+  parser.add_option('--output_file',
+                    dest='output_file',
+                    action='store',
+                    help=('If specified, write output to this path instead of '
+                          'writing to standard output.'))
+  parser.add_option('--root',
+                    dest='roots',
+                    default=[],
+                    action='append',
+                    help='A root directory to scan for JS source files. '
+                    'Paths of JS files in generated deps file will be '
+                    'relative to this path.  This flag may be specified '
+                    'multiple times.')
+  parser.add_option('--root_with_prefix',
+                    dest='roots_with_prefix',
+                    default=[],
+                    action='append',
+                    help='A root directory to scan for JS source files, plus '
+                    'a prefix (if either contains a space, surround with '
+                    'quotes).  Paths in generated deps file will be relative '
+                    'to the root, but preceded by the prefix.  This flag '
+                    'may be specified multiple times.')
+  parser.add_option('--path_with_depspath',
+                    dest='paths_with_depspath',
+                    default=[],
+                    action='append',
+                    help='A path to a source file and an alternate path to '
+                    'the file in the generated deps file (if either contains '
+                    'a space, surround with whitespace). This flag may be '
+                    'specified multiple times.')
+  return parser
+
+
+def _NormalizePathSeparators(path):
+  """Replaces OS-specific path separators with POSIX-style slashes.
+
+  Args:
+    path: str, A file path.
+
+  Returns:
+    str, The path with any OS-specific path separators (such as backslash on
+      Windows) replaced with URL-compatible forward slashes. A no-op on systems
+      that use POSIX paths.
+  """
+  return path.replace(os.sep, posixpath.sep)
+
+
+def _GetRelativePathToSourceDict(root, prefix=''):
+  """Scans a top root directory for .js sources.
+
+  Args:
+    root: str, Root directory.
+    prefix: str, Prefix for returned paths.
+
+  Returns:
+    dict, A map of relative paths (with prefix, if given), to source.Source
+      objects.
+  """
+  # Remember and restore the cwd when we're done. We work from the root so
+  # that paths are relative from the root.
+  start_wd = os.getcwd()
+  os.chdir(root)
+
+  path_to_source = {}
+  for path in treescan.ScanTreeForJsFiles('.'):
+    prefixed_path = _NormalizePathSeparators(os.path.join(prefix, path))
+    path_to_source[prefixed_path] = source.Source(source.GetFileContents(path))
+
+  os.chdir(start_wd)
+
+  return path_to_source
+
+
+def _GetPair(s):
+  """Return a string as a shell-parsed tuple.  Two values expected."""
+  try:
+    # shlex uses '\' as an escape character, so they must be escaped.
+    s = s.replace('\\', '\\\\')
+    first, second = shlex.split(s)
+    return (first, second)
+  except:
+    raise Exception('Unable to parse input line as a pair: %s' % s)
+
+
+def main():
+  """CLI frontend to MakeDepsFile."""
+  logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
+                      level=logging.INFO)
+  options, args = _GetOptionsParser().parse_args()
+
+  path_to_source = {}
+
+  # Roots without prefixes
+  for root in options.roots:
+    path_to_source.update(_GetRelativePathToSourceDict(root))
+
+  # Roots with prefixes
+  for root_and_prefix in options.roots_with_prefix:
+    root, prefix = _GetPair(root_and_prefix)
+    path_to_source.update(_GetRelativePathToSourceDict(root, prefix=prefix))
+
+  # Source paths
+  for path in args:
+    path_to_source[path] = source.Source(source.GetFileContents(path))
+
+  # Source paths with alternate deps paths
+  for path_with_depspath in options.paths_with_depspath:
+    srcpath, depspath = _GetPair(path_with_depspath)
+    path_to_source[depspath] = source.Source(source.GetFileContents(srcpath))
+
+  # Make our output pipe.
+  if options.output_file:
+    out = open(options.output_file, 'w')
+  else:
+    out = sys.stdout
+
+  out.write('// This file was autogenerated by %s.\n' % sys.argv[0])
+  out.write('// Please do not edit.\n')
+
+  out.write(MakeDepsFile(path_to_source))
+
+
+if __name__ == '__main__':
+  main()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/jscompiler.py
@@ -0,0 +1,135 @@
+# Copyright 2010 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utility to use the Closure Compiler CLI from Python."""
+
+
+import logging
+import os
+import re
+import subprocess
+
+
+# Pulls just the major and minor version numbers from the first line of
+# 'java -version'. Versions are in the format of [0-9]+\.[0-9]+\..* See:
+# http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
+_VERSION_REGEX = re.compile(r'"([0-9]+)\.([0-9]+)')
+
+
+class JsCompilerError(Exception):
+  """Raised if there's an error in calling the compiler."""
+  pass
+
+
+def _GetJavaVersionString():
+  """Get the version string from the Java VM."""
+  return subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)
+
+
+def _ParseJavaVersion(version_string):
+  """Returns a 2-tuple for the current version of Java installed.
+
+  Args:
+    version_string: String of the Java version (e.g. '1.7.2-ea').
+
+  Returns:
+    The major and minor versions, as a 2-tuple (e.g. (1, 7)).
+  """
+  match = _VERSION_REGEX.search(version_string)
+  if match:
+    version = tuple(int(x, 10) for x in match.groups())
+    assert len(version) == 2
+    return version
+
+
+def _JavaSupports32BitMode():
+  """Determines whether the JVM supports 32-bit mode on the platform."""
+  # Suppresses process output to stderr and stdout from showing up in the
+  # console as we're only trying to determine 32-bit JVM support.
+  supported = False
+  try:
+    devnull = open(os.devnull, 'wb')
+    return subprocess.call(
+        ['java', '-d32', '-version'], stdout=devnull, stderr=devnull) == 0
+  except IOError:
+    pass
+  else:
+    devnull.close()
+  return supported
+
+
+def _GetJsCompilerArgs(compiler_jar_path, java_version, source_paths,
+                       jvm_flags, compiler_flags):
+  """Assembles arguments for call to JsCompiler."""
+
+  if java_version < (1, 7):
+    raise JsCompilerError('Closure Compiler requires Java 1.7 or higher. '
+                          'Please visit http://www.java.com/getjava')
+
+  args = ['java']
+
+  # Add JVM flags we believe will produce the best performance.  See
+  # https://groups.google.com/forum/#!topic/closure-library-discuss/7w_O9-vzlj4
+
+  # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit
+  # mode, for example).
+  if _JavaSupports32BitMode():
+    args += ['-d32']
+
+  # Prefer the "client" VM.
+  args += ['-client']
+
+  # Add JVM flags, if any
+  if jvm_flags:
+    args += jvm_flags
+
+  # Add the application JAR.
+  args += ['-jar', compiler_jar_path]
+
+  for path in source_paths:
+    args += ['--js', path]
+
+  # Add compiler flags, if any.
+  if compiler_flags:
+    args += compiler_flags
+
+  return args
+
+
+def Compile(compiler_jar_path, source_paths,
+            jvm_flags=None,
+            compiler_flags=None):
+  """Prepares command-line call to Closure Compiler.
+
+  Args:
+    compiler_jar_path: Path to the Closure compiler .jar file.
+    source_paths: Source paths to build, in order.
+    jvm_flags: A list of additional flags to pass on to JVM.
+    compiler_flags: A list of additional flags to pass on to Closure Compiler.
+
+  Returns:
+    The compiled source, as a string, or None if compilation failed.
+  """
+
+  java_version = _ParseJavaVersion(_GetJavaVersionString())
+
+  args = _GetJsCompilerArgs(
+      compiler_jar_path, java_version, source_paths, jvm_flags, compiler_flags)
+
+  logging.info('Compiling with the following command: %s', ' '.join(args))
+
+  try:
+    return subprocess.check_output(args)
+  except subprocess.CalledProcessError:
+    raise JsCompilerError('JavaScript compilation failed.')
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/source.py
@@ -0,0 +1,127 @@
+# Copyright 2009 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Scans a source JS file for its provided and required namespaces.
+
+Simple class to scan a JavaScript file and express its dependencies.
+"""
+
+__author__ = 'nnaze@google.com'
+
+
+import re
+
+_BASE_REGEX_STRING = r'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)'
+_MODULE_REGEX = re.compile(_BASE_REGEX_STRING % 'module')
+_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide')
+
+_REQUIRE_REGEX_STRING = (r'^\s*(?:(?:var|let|const)\s+[a-zA-Z_$][a-zA-Z0-9$_]*'
+                         r'\s*=\s*)?goog\.require\(\s*[\'"](.+)[\'"]\s*\)')
+_REQUIRES_REGEX = re.compile(_REQUIRE_REGEX_STRING)
+
+
+class Source(object):
+  """Scans a JavaScript source for its provided and required namespaces."""
+
+  # Matches a "/* ... */" comment.
+  # Note: We can't definitively distinguish a "/*" in a string literal without a
+  # state machine tokenizer. We'll assume that a line starting with whitespace
+  # and "/*" is a comment.
+  _COMMENT_REGEX = re.compile(
+      r"""
+      ^\s*   # Start of a new line and whitespace
+      /\*    # Opening "/*"
+      .*?    # Non greedy match of any characters (including newlines)
+      \*/    # Closing "*/""",
+      re.MULTILINE | re.DOTALL | re.VERBOSE)
+
+  def __init__(self, source):
+    """Initialize a source.
+
+    Args:
+      source: str, The JavaScript source.
+    """
+
+    self.provides = set()
+    self.requires = set()
+    self.is_goog_module = False
+
+    self._source = source
+    self._ScanSource()
+
+  def GetSource(self):
+    """Get the source as a string."""
+    return self._source
+
+  @classmethod
+  def _StripComments(cls, source):
+    return cls._COMMENT_REGEX.sub('', source)
+
+  @classmethod
+  def _HasProvideGoogFlag(cls, source):
+    """Determines whether the @provideGoog flag is in a comment."""
+    for comment_content in cls._COMMENT_REGEX.findall(source):
+      if '@provideGoog' in comment_content:
+        return True
+
+    return False
+
+  def _ScanSource(self):
+    """Fill in provides and requires by scanning the source."""
+
+    stripped_source = self._StripComments(self.GetSource())
+
+    source_lines = stripped_source.splitlines()
+    for line in source_lines:
+      match = _PROVIDE_REGEX.match(line)
+      if match:
+        self.provides.add(match.group(1))
+      match = _MODULE_REGEX.match(line)
+      if match:
+        self.provides.add(match.group(1))
+        self.is_goog_module = True
+      match = _REQUIRES_REGEX.match(line)
+      if match:
+        self.requires.add(match.group(1))
+
+    # Closure's base file implicitly provides 'goog'.
+    # This is indicated with the @provideGoog flag.
+    if self._HasProvideGoogFlag(self.GetSource()):
+
+      if len(self.provides) or len(self.requires):
+        raise Exception(
+            'Base file should not provide or require namespaces.')
+
+      self.provides.add('goog')
+
+
+def GetFileContents(path):
+  """Get a file's contents as a string.
+
+  Args:
+    path: str, Path to file.
+
+  Returns:
+    str, Contents of file.
+
+  Raises:
+    IOError: An error occurred opening or reading the file.
+
+  """
+  fileobj = open(path)
+  try:
+    return fileobj.read()
+  finally:
+    fileobj.close()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/build/treescan.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Shared utility functions for scanning directory trees."""
+
+import os
+import re
+
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+# Matches a .js file path.
+_JS_FILE_REGEX = re.compile(r'^.+\.js$')
+
+
+def ScanTreeForJsFiles(root):
+  """Scans a directory tree for JavaScript files.
+
+  Args:
+    root: str, Path to a root directory.
+
+  Returns:
+    An iterable of paths to JS files, relative to cwd.
+  """
+  return ScanTree(root, path_filter=_JS_FILE_REGEX)
+
+
+def ScanTree(root, path_filter=None, ignore_hidden=True):
+  """Scans a directory tree for files.
+
+  Args:
+    root: str, Path to a root directory.
+    path_filter: A regular expression filter.  If set, only paths matching
+      the path_filter are returned.
+    ignore_hidden: If True, do not follow or return hidden directories or files
+      (those starting with a '.' character).
+
+  Yields:
+    A string path to files, relative to cwd.
+  """
+
+  def OnError(os_error):
+    raise os_error
+
+  for dirpath, dirnames, filenames in os.walk(root, onerror=OnError):
+    # os.walk allows us to modify dirnames to prevent decent into particular
+    # directories.  Avoid hidden directories.
+    for dirname in dirnames:
+      if ignore_hidden and dirname.startswith('.'):
+        dirnames.remove(dirname)
+
+    for filename in filenames:
+
+      # nothing that starts with '.'
+      if ignore_hidden and filename.startswith('.'):
+        continue
+
+      fullpath = os.path.join(dirpath, filename)
+
+      if path_filter and not path_filter.match(fullpath):
+        continue
+
+      yield os.path.normpath(fullpath)
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/calcdeps.py
@@ -0,0 +1,590 @@
+#!/usr/bin/env python
+#
+# Copyright 2006 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Calculates JavaScript dependencies without requiring Google's build system.
+
+This tool is deprecated and is provided for legacy users.
+See build/closurebuilder.py and build/depswriter.py for the current tools.
+
+It iterates over a number of search paths and builds a dependency tree.  With
+the inputs provided, it walks the dependency tree and outputs all the files
+required for compilation.
+"""
+
+
+
+
+
+try:
+  import distutils.version
+except ImportError:
+  # distutils is not available in all environments
+  distutils = None
+
+import logging
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+
+_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)'
+req_regex = re.compile(_BASE_REGEX_STRING % 'require')
+prov_regex = re.compile(_BASE_REGEX_STRING % 'provide')
+ns_regex = re.compile('^ns:((\w+\.)*(\w+))$')
+version_regex = re.compile('[\.0-9]+')
+
+
+def IsValidFile(ref):
+  """Returns true if the provided reference is a file and exists."""
+  return os.path.isfile(ref)
+
+
+def IsJsFile(ref):
+  """Returns true if the provided reference is a Javascript file."""
+  return ref.endswith('.js')
+
+
+def IsNamespace(ref):
+  """Returns true if the provided reference is a namespace."""
+  return re.match(ns_regex, ref) is not None
+
+
+def IsDirectory(ref):
+  """Returns true if the provided reference is a directory."""
+  return os.path.isdir(ref)
+
+
+def ExpandDirectories(refs):
+  """Expands any directory references into inputs.
+
+  Description:
+    Looks for any directories in the provided references.  Found directories
+    are recursively searched for .js files, which are then added to the result
+    list.
+
+  Args:
+    refs: a list of references such as files, directories, and namespaces
+
+  Returns:
+    A list of references with directories removed and replaced by any
+    .js files that are found in them. Also, the paths will be normalized.
+  """
+  result = []
+  for ref in refs:
+    if IsDirectory(ref):
+      # Disable 'Unused variable' for subdirs
+      # pylint: disable=unused-variable
+      for (directory, subdirs, filenames) in os.walk(ref):
+        for filename in filenames:
+          if IsJsFile(filename):
+            result.append(os.path.join(directory, filename))
+    else:
+      result.append(ref)
+  return map(os.path.normpath, result)
+
+
+class DependencyInfo(object):
+  """Represents a dependency that is used to build and walk a tree."""
+
+  def __init__(self, filename):
+    self.filename = filename
+    self.provides = []
+    self.requires = []
+
+  def __str__(self):
+    return '%s Provides: %s Requires: %s' % (self.filename,
+                                             repr(self.provides),
+                                             repr(self.requires))
+
+
+def BuildDependenciesFromFiles(files):
+  """Build a list of dependencies from a list of files.
+
+  Description:
+    Takes a list of files, extracts their provides and requires, and builds
+    out a list of dependency objects.
+
+  Args:
+    files: a list of files to be parsed for goog.provides and goog.requires.
+
+  Returns:
+    A list of dependency objects, one for each file in the files argument.
+  """
+  result = []
+  filenames = set()
+  for filename in files:
+    if filename in filenames:
+      continue
+
+    # Python 3 requires the file encoding to be specified
+    if (sys.version_info[0] < 3):
+      file_handle = open(filename, 'r')
+    else:
+      file_handle = open(filename, 'r', encoding='utf8')
+
+    try:
+      dep = CreateDependencyInfo(filename, file_handle)
+      result.append(dep)
+    finally:
+      file_handle.close()
+
+    filenames.add(filename)
+
+  return result
+
+
+def CreateDependencyInfo(filename, source):
+  """Create dependency info.
+
+  Args:
+    filename: Filename for source.
+    source: File-like object containing source.
+
+  Returns:
+    A DependencyInfo object with provides and requires filled.
+  """
+  dep = DependencyInfo(filename)
+  for line in source:
+    if re.match(req_regex, line):
+      dep.requires.append(re.search(req_regex, line).group(1))
+    if re.match(prov_regex, line):
+      dep.provides.append(re.search(prov_regex, line).group(1))
+  return dep
+
+
+def BuildDependencyHashFromDependencies(deps):
+  """Builds a hash for searching dependencies by the namespaces they provide.
+
+  Description:
+    Dependency objects can provide multiple namespaces.  This method enumerates
+    the provides of each dependency and adds them to a hash that can be used
+    to easily resolve a given dependency by a namespace it provides.
+
+  Args:
+    deps: a list of dependency objects used to build the hash.
+
+  Raises:
+    Exception: If a multiple files try to provide the same namepace.
+
+  Returns:
+    A hash table { namespace: dependency } that can be used to resolve a
+    dependency by a namespace it provides.
+  """
+  dep_hash = {}
+  for dep in deps:
+    for provide in dep.provides:
+      if provide in dep_hash:
+        raise Exception('Duplicate provide (%s) in (%s, %s)' % (
+            provide,
+            dep_hash[provide].filename,
+            dep.filename))
+      dep_hash[provide] = dep
+  return dep_hash
+
+
+def CalculateDependencies(paths, inputs):
+  """Calculates the dependencies for given inputs.
+
+  Description:
+    This method takes a list of paths (files, directories) and builds a
+    searchable data structure based on the namespaces that each .js file
+    provides.  It then parses through each input, resolving dependencies
+    against this data structure.  The final output is a list of files,
+    including the inputs, that represent all of the code that is needed to
+    compile the given inputs.
+
+  Args:
+    paths: the references (files, directories) that are used to build the
+      dependency hash.
+    inputs: the inputs (files, directories, namespaces) that have dependencies
+      that need to be calculated.
+
+  Raises:
+    Exception: if a provided input is invalid.
+
+  Returns:
+    A list of all files, including inputs, that are needed to compile the given
+    inputs.
+  """
+  deps = BuildDependenciesFromFiles(paths + inputs)
+  search_hash = BuildDependencyHashFromDependencies(deps)
+  result_list = []
+  seen_list = []
+  for input_file in inputs:
+    if IsNamespace(input_file):
+      namespace = re.search(ns_regex, input_file).group(1)
+      if namespace not in search_hash:
+        raise Exception('Invalid namespace (%s)' % namespace)
+      input_file = search_hash[namespace].filename
+    if not IsValidFile(input_file) or not IsJsFile(input_file):
+      raise Exception('Invalid file (%s)' % input_file)
+    seen_list.append(input_file)
+    file_handle = open(input_file, 'r')
+    try:
+      for line in file_handle:
+        if re.match(req_regex, line):
+          require = re.search(req_regex, line).group(1)
+          ResolveDependencies(require, search_hash, result_list, seen_list)
+    finally:
+      file_handle.close()
+    result_list.append(input_file)
+
+  # All files depend on base.js, so put it first.
+  base_js_path = FindClosureBasePath(paths)
+  if base_js_path:
+    result_list.insert(0, base_js_path)
+  else:
+    logging.warning('Closure Library base.js not found.')
+
+  return result_list
+
+
+def FindClosureBasePath(paths):
+  """Given a list of file paths, return Closure base.js path, if any.
+
+  Args:
+    paths: A list of paths.
+
+  Returns:
+    The path to Closure's base.js file including filename, if found.
+  """
+
+  for path in paths:
+    pathname, filename = os.path.split(path)
+
+    if filename == 'base.js':
+      f = open(path)
+
+      is_base = False
+
+      # Sanity check that this is the Closure base file.  Check that this
+      # is where goog is defined.  This is determined by the @provideGoog
+      # flag.
+      for line in f:
+        if '@provideGoog' in line:
+          is_base = True
+          break
+
+      f.close()
+
+      if is_base:
+        return path
+
+def ResolveDependencies(require, search_hash, result_list, seen_list):
+  """Takes a given requirement and resolves all of the dependencies for it.
+
+  Description:
+    A given requirement may require other dependencies.  This method
+    recursively resolves all dependencies for the given requirement.
+
+  Raises:
+    Exception: when require does not exist in the search_hash.
+
+  Args:
+    require: the namespace to resolve dependencies for.
+    search_hash: the data structure used for resolving dependencies.
+    result_list: a list of filenames that have been calculated as dependencies.
+      This variable is the output for this function.
+    seen_list: a list of filenames that have been 'seen'.  This is required
+      for the dependency->dependant ordering.
+  """
+  if require not in search_hash:
+    raise Exception('Missing provider for (%s)' % require)
+
+  dep = search_hash[require]
+  if not dep.filename in seen_list:
+    seen_list.append(dep.filename)
+    for sub_require in dep.requires:
+      ResolveDependencies(sub_require, search_hash, result_list, seen_list)
+    result_list.append(dep.filename)
+
+
+def GetDepsLine(dep, base_path):
+  """Returns a JS string for a dependency statement in the deps.js file.
+
+  Args:
+    dep: The dependency that we're printing.
+    base_path: The path to Closure's base.js including filename.
+  """
+  return 'goog.addDependency("%s", %s, %s);' % (
+      GetRelpath(dep.filename, base_path), dep.provides, dep.requires)
+
+
+def GetRelpath(path, start):
+  """Return a relative path to |path| from |start|."""
+  # NOTE: Python 2.6 provides os.path.relpath, which has almost the same
+  # functionality as this function. Since we want to support 2.4, we have
+  # to implement it manually. :(
+  path_list = os.path.abspath(os.path.normpath(path)).split(os.sep)
+  start_list = os.path.abspath(
+      os.path.normpath(os.path.dirname(start))).split(os.sep)
+
+  common_prefix_count = 0
+  for i in range(0, min(len(path_list), len(start_list))):
+    if path_list[i] != start_list[i]:
+      break
+    common_prefix_count += 1
+
+  # Always use forward slashes, because this will get expanded to a url,
+  # not a file path.
+  return '/'.join(['..'] * (len(start_list) - common_prefix_count) +
+                  path_list[common_prefix_count:])
+
+
+def PrintLine(msg, out):
+  out.write(msg)
+  out.write('\n')
+
+
+def PrintDeps(source_paths, deps, out):
+  """Print out a deps.js file from a list of source paths.
+
+  Args:
+    source_paths: Paths that we should generate dependency info for.
+    deps: Paths that provide dependency info. Their dependency info should
+        not appear in the deps file.
+    out: The output file.
+
+  Returns:
+    True on success, false if it was unable to find the base path
+    to generate deps relative to.
+  """
+  base_path = FindClosureBasePath(source_paths + deps)
+  if not base_path:
+    return False
+
+  PrintLine('// This file was autogenerated by calcdeps.py', out)
+  excludesSet = set(deps)
+
+  for dep in BuildDependenciesFromFiles(source_paths + deps):
+    if not dep.filename in excludesSet:
+      PrintLine(GetDepsLine(dep, base_path), out)
+
+  return True
+
+
+def PrintScript(source_paths, out):
+  for index, dep in enumerate(source_paths):
+    PrintLine('// Input %d' % index, out)
+    f = open(dep, 'r')
+    PrintLine(f.read(), out)
+    f.close()
+
+
+def GetJavaVersion():
+  """Returns the string for the current version of Java installed."""
+  proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE)
+  proc.wait()
+  version_line = proc.stderr.read().splitlines()[0]
+  return version_regex.search(version_line).group()
+
+
+def FilterByExcludes(options, files):
+  """Filters the given files by the exlusions specified at the command line.
+
+  Args:
+    options: The flags to calcdeps.
+    files: The files to filter.
+  Returns:
+    A list of files.
+  """
+  excludes = []
+  if options.excludes:
+    excludes = ExpandDirectories(options.excludes)
+
+  excludesSet = set(excludes)
+  return [i for i in files if not i in excludesSet]
+
+
+def GetPathsFromOptions(options):
+  """Generates the path files from flag options.
+
+  Args:
+    options: The flags to calcdeps.
+  Returns:
+    A list of files in the specified paths. (strings).
+  """
+
+  search_paths = options.paths
+  if not search_paths:
+    search_paths = ['.']  # Add default folder if no path is specified.
+
+  search_paths = ExpandDirectories(search_paths)
+  return FilterByExcludes(options, search_paths)
+
+
+def GetInputsFromOptions(options):
+  """Generates the inputs from flag options.
+
+  Args:
+    options: The flags to calcdeps.
+  Returns:
+    A list of inputs (strings).
+  """
+  inputs = options.inputs
+  if not inputs:  # Parse stdin
+    logging.info('No inputs specified. Reading from stdin...')
+    inputs = filter(None, [line.strip('\n') for line in sys.stdin.readlines()])
+
+  logging.info('Scanning files...')
+  inputs = ExpandDirectories(inputs)
+
+  return FilterByExcludes(options, inputs)
+
+
+def Compile(compiler_jar_path, source_paths, out, flags=None):
+  """Prepares command-line call to Closure compiler.
+
+  Args:
+    compiler_jar_path: Path to the Closure compiler .jar file.
+    source_paths: Source paths to build, in order.
+    flags: A list of additional flags to pass on to Closure compiler.
+  """
+  args = ['java', '-jar', compiler_jar_path]
+  for path in source_paths:
+    args += ['--js', path]
+
+  if flags:
+    args += flags
+
+  logging.info('Compiling with the following command: %s', ' '.join(args))
+  proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+  (stdoutdata, stderrdata) = proc.communicate()
+  if proc.returncode != 0:
+    logging.error('JavaScript compilation failed.')
+    sys.exit(1)
+  else:
+    out.write(stdoutdata)
+
+
+def main():
+  """The entrypoint for this script."""
+
+  logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO)
+
+  usage = 'usage: %prog [options] arg'
+  parser = optparse.OptionParser(usage)
+  parser.add_option('-i',
+                    '--input',
+                    dest='inputs',
+                    action='append',
+                    help='The inputs to calculate dependencies for. Valid '
+                    'values can be files, directories, or namespaces '
+                    '(ns:goog.net.XhrIo).  Only relevant to "list" and '
+                    '"script" output.')
+  parser.add_option('-p',
+                    '--path',
+                    dest='paths',
+                    action='append',
+                    help='The paths that should be traversed to build the '
+                    'dependencies.')
+  parser.add_option('-d',
+                    '--dep',
+                    dest='deps',
+                    action='append',
+                    help='Directories or files that should be traversed to '
+                    'find required dependencies for the deps file. '
+                    'Does not generate dependency information for names '
+                    'provided by these files. Only useful in "deps" mode.')
+  parser.add_option('-e',
+                    '--exclude',
+                    dest='excludes',
+                    action='append',
+                    help='Files or directories to exclude from the --path '
+                    'and --input flags')
+  parser.add_option('-o',
+                    '--output_mode',
+                    dest='output_mode',
+                    action='store',
+                    default='list',
+                    help='The type of output to generate from this script. '
+                    'Options are "list" for a list of filenames, "script" '
+                    'for a single script containing the contents of all the '
+                    'file, "deps" to generate a deps.js file for all '
+                    'paths, or "compiled" to produce compiled output with '
+                    'the Closure compiler.')
+  parser.add_option('-c',
+                    '--compiler_jar',
+                    dest='compiler_jar',
+                    action='store',
+                    help='The location of the Closure compiler .jar file.')
+  parser.add_option('-f',
+                    '--compiler_flag',
+                    '--compiler_flags', # for backwards compatability
+                    dest='compiler_flags',
+                    action='append',
+                    help='Additional flag to pass to the Closure compiler. '
+                    'May be specified multiple times to pass multiple flags.')
+  parser.add_option('--output_file',
+                    dest='output_file',
+                    action='store',
+                    help=('If specified, write output to this path instead of '
+                          'writing to standard output.'))
+
+  (options, args) = parser.parse_args()
+
+  search_paths = GetPathsFromOptions(options)
+
+  if options.output_file:
+    out = open(options.output_file, 'w')
+  else:
+    out = sys.stdout
+
+  if options.output_mode == 'deps':
+    result = PrintDeps(search_paths, ExpandDirectories(options.deps or []), out)
+    if not result:
+      logging.error('Could not find Closure Library in the specified paths')
+      sys.exit(1)
+
+    return
+
+  inputs = GetInputsFromOptions(options)
+
+  logging.info('Finding Closure dependencies...')
+  deps = CalculateDependencies(search_paths, inputs)
+  output_mode = options.output_mode
+
+  if output_mode == 'script':
+    PrintScript(deps, out)
+  elif output_mode == 'list':
+    # Just print out a dep per line
+    for dep in deps:
+      PrintLine(dep, out)
+  elif output_mode == 'compiled':
+    # Make sure a .jar is specified.
+    if not options.compiler_jar:
+      logging.error('--compiler_jar flag must be specified if --output is '
+                    '"compiled"')
+      sys.exit(1)
+
+    # User friendly version check.
+    if distutils and not (distutils.version.LooseVersion(GetJavaVersion()) >
+        distutils.version.LooseVersion('1.6')):
+      logging.error('Closure Compiler requires Java 1.6 or higher.')
+      logging.error('Please visit http://www.java.com/getjava')
+      sys.exit(1)
+
+    Compile(options.compiler_jar, deps, out, options.compiler_flags)
+
+  else:
+    logging.error('Invalid value for --output flag.')
+    sys.exit(1)
+
+if __name__ == '__main__':
+  main()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/closure-library/closure/bin/scopify.py
@@ -0,0 +1,221 @@
+#!/usr/bin/python
+#
+# Copyright 2010 The Closure Library Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Automatically converts codebases over to goog.scope.
+
+Usage:
+cd path/to/my/dir;
+../../../../javascript/closure/bin/scopify.py
+
+Scans every file in this directory, recursively. Looks for existing
+goog.scope calls, and goog.require'd symbols. If it makes sense to
+generate a goog.scope call for the file, then we will do so, and
+try to auto-generate some aliases based on the goog.require'd symbols.
+
+Known Issues:
+
+  When a file is goog.scope'd, the file contents will be indented +2.
+  This may put some lines over 80 chars. These will need to be fixed manually.
+
+  We will only try to create aliases for capitalized names. We do not check
+  to see if those names will conflict with any existing locals.
+
+  This creates merge conflicts for every line of every outstanding change.
+  If you intend to run this on your codebase, make sure your team members
+  know. Better yet, send them this script so that they can scopify their
+  outstanding changes and "accept theirs".
+
+  When an alias is "captured", it can no longer be stubbed out for testing.
+  Run your tests.
+
+"""
+
+__author__ = 'nicksantos@google.com (Nick Santos)'
+
+import os.path
+import re
+import sys
+
+REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)")
+
+# Edit this manually if you want something to "always" be aliased.
+# TODO(nicksantos): Add a flag for this.
+DEFAULT_ALIASES = {}
+
+def Transform(lines):
+  """Converts the contents of a file into javascript that uses goog.scope.
+
+  Arguments:
+    lines: A list of strings, corresponding to each line of the file.
+  Returns:
+    A new list of strings, or None if the file was not modified.
+  """
+  requires = []
+
+  # Do an initial scan to be sure that this file can be processed.
+  for line in lines:
+    # Skip this file if it has already been scopified.
+    if line.find('goog.scope') != -1:
+      return None
+
+    # If there are any global vars or functions, then we also have
+    # to skip the whole file. We might be able to deal with this
+    # more elegantly.
+    if line.find('var ') == 0 or line.find('function ') == 0:
+      return None
+
+    for match in REQUIRES_RE.finditer(line):
+      requires.append(match.group(1))
+