merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 04 Aug 2015 13:01:07 +0200
changeset 287694 5cf4d2f7f2f2b3df2f1edd31b8bdce7882f3875c
parent 287601 e1a40a9057e17afdec87257456055ab23fc607b2 (current diff)
parent 287693 909e4b1913a9ce70d5aead33e13261f288d87904 (diff)
child 287695 54a3cfc16a110044bc387adbb8f18637dac49698
child 287705 25eeaecb471d9f6dcc35a873f5f7b473220404f3
child 287716 256463501f11564afd5a23ec9d36648c3545529b
child 287757 b3f61169f7273904ce5d36e13e11c7694e32ad75
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/components/moz.build
browser/config/tooltool-manifests/linux32/valgrind.manifest
browser/config/tooltool-manifests/linux64/valgrind.manifest
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
mobile/android/tests/browser/robocop/desktopmode_user_agent.sjs
mobile/android/tests/browser/robocop/devicesearch.xml
mobile/android/tests/browser/robocop/testAccounts.java
mobile/android/tests/browser/robocop/testAccounts.js
mobile/android/tests/browser/robocop/testAndroidLog.java
mobile/android/tests/browser/robocop/testAndroidLog.js
mobile/android/tests/browser/robocop/testDebuggerServer.java
mobile/android/tests/browser/robocop/testDebuggerServer.js
mobile/android/tests/browser/robocop/testDesktopUserAgent.java
mobile/android/tests/browser/robocop/testDesktopUserAgent.js
mobile/android/tests/browser/robocop/testDeviceSearchEngine.java
mobile/android/tests/browser/robocop/testDeviceSearchEngine.js
mobile/android/tests/browser/robocop/testResourceSubstitutions.java
mobile/android/tests/browser/robocop/testResourceSubstitutions.js
security/manager/ssl/tests/unit/test_client_cert.js
security/manager/ssl/tests/unit/test_client_cert/cert_dialog.js
security/manager/ssl/tests/unit/test_client_cert/cert_dialog.manifest
security/manager/ssl/tests/unit/test_client_cert/client-cert.p12
security/manager/ssl/tests/unit/test_client_cert/generate.py
security/manager/ssl/tests/unit/tlsserver/cmd/ClientAuthServer.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1190180 - need clobber for backouts
+Bug 1186748 needed a CLOBBER again
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -687,17 +687,18 @@ DocAccessible::OnPivotChanged(nsIAccessi
 NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(DocAccessible)
 
 void
 DocAccessible::AttributeWillChange(nsIDocument* aDocument,
                                    dom::Element* aElement,
                                    int32_t aNameSpaceID,
-                                   nsIAtom* aAttribute, int32_t aModType)
+                                   nsIAtom* aAttribute, int32_t aModType,
+                                   const nsAttrValue* aNewValue)
 {
   Accessible* accessible = GetAccessible(aElement);
   if (!accessible) {
     if (aElement != mContent)
       return;
 
     accessible = this;
   }
@@ -728,17 +729,18 @@ DocAccessible::AttributeWillChange(nsIDo
       aAttribute == nsGkAtoms::disabled)
     mStateBitWasOn = accessible->Unavailable();
 }
 
 void
 DocAccessible::AttributeChanged(nsIDocument* aDocument,
                                 dom::Element* aElement,
                                 int32_t aNameSpaceID, nsIAtom* aAttribute,
-                                int32_t aModType)
+                                int32_t aModType,
+                                const nsAttrValue* aOldValue)
 {
   NS_ASSERTION(!IsDefunct(),
                "Attribute changed called on defunct document accessible!");
 
   // Proceed even if the element is not accessible because element may become
   // accessible if it gets certain attribute.
   if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
     return;
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This content script contains code that requires a tab browser. */
 
 let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ExtensionContent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
   "resource:///modules/E10SUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
   "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AboutReader",
@@ -651,8 +652,13 @@ let DOMFullscreenHandler = {
         removeEventListener("MozAfterPaint", this);
         sendAsyncMessage("DOMFullscreen:Painted");
         break;
       }
     }
   }
 };
 DOMFullscreenHandler.init();
+
+ExtensionContent.init(this);
+addEventListener("unload", () => {
+  ExtensionContent.uninit(this);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/bootstrap.js
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/Extension.jsm");
+
+let extension;
+
+function startup(data, reason)
+{
+  extension = new Extension(data);
+  extension.startup();
+}
+
+function shutdown(data, reason)
+{
+  extension.shutdown();
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-browserAction.js
@@ -0,0 +1,326 @@
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
+                                  "resource:///modules/CustomizableUI.jsm");
+
+Cu.import("resource://gre/modules/devtools/event-emitter.js");
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+let {
+  EventManager,
+  DefaultWeakMap,
+  ignoreEvent,
+  runSafe,
+} = ExtensionUtils;
+
+// WeakMap[Extension -> BrowserAction]
+let browserActionMap = new WeakMap();
+
+function browserActionOf(extension)
+{
+  return browserActionMap.get(extension);
+}
+
+function makeWidgetId(id)
+{
+  id = id.toLowerCase();
+  return id.replace(/[^a-z0-9_-]/g, "_");
+}
+
+let nextActionId = 0;
+
+// Responsible for the browser_action section of the manifest as well
+// as the associated popup.
+function BrowserAction(options, extension)
+{
+  this.extension = extension;
+  this.id = makeWidgetId(extension.id) + "-browser-action";
+  this.widget = null;
+
+  this.title = new DefaultWeakMap(extension.localize(options.default_title));
+  this.badgeText = new DefaultWeakMap();
+  this.badgeBackgroundColor = new DefaultWeakMap();
+  this.icon = new DefaultWeakMap(options.default_icon);
+  this.popup = new DefaultWeakMap(options.default_popup);
+
+  // Make the default something that won't compare equal to anything.
+  this.prevPopups = new DefaultWeakMap({});
+
+  this.context = null;
+}
+
+BrowserAction.prototype = {
+  build() {
+    let widget = CustomizableUI.createWidget({
+      id: this.id,
+      type: "custom",
+      removable: true,
+      defaultArea: CustomizableUI.AREA_NAVBAR,
+      onBuild: document => {
+        let node = document.createElement("toolbarbutton");
+        node.id = this.id;
+        node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button");
+        node.setAttribute("constrain-size", "true");
+
+        this.updateTab(null, node);
+
+        let tabbrowser = document.defaultView.gBrowser;
+        tabbrowser.ownerDocument.addEventListener("TabSelect", () => {
+          this.updateTab(tabbrowser.selectedTab, node);
+        });
+
+        node.addEventListener("command", event => {
+          if (node.getAttribute("type") != "panel") {
+            this.emit("click");
+          }
+        });
+
+        return node;
+      },
+    });
+    this.widget = widget;
+  },
+
+  // Initialize the toolbar icon and popup given that |tab| is the
+  // current tab and |node| is the CustomizableUI node. Note: |tab|
+  // will be null if we don't know the current tab yet (during
+  // initialization).
+  updateTab(tab, node) {
+    let window = node.ownerDocument.defaultView;
+
+    let title = this.getProperty(tab, "title");
+    if (title) {
+      node.setAttribute("tooltiptext", title);
+      node.setAttribute("label", title);
+    } else {
+      node.removeAttribute("tooltiptext");
+      node.removeAttribute("label");
+    }
+
+    let badgeText = this.badgeText.get(tab);
+    if (badgeText) {
+      node.setAttribute("badge", badgeText);
+    } else {
+      node.removeAttribute("badge");
+    }
+
+    function toHex(n) {
+      return Math.floor(n / 16).toString(16) + (n % 16).toString(16);
+    }
+
+    let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
+                                        'class', 'toolbarbutton-badge');
+    if (badgeNode) {
+      let color = this.badgeBackgroundColor.get(tab);
+      if (Array.isArray(color)) {
+        color = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+      }
+      badgeNode.style.backgroundColor = color;
+    }
+
+    let iconURL = this.getIcon(tab, node);
+    node.setAttribute("image", iconURL);
+
+    let popup = this.getProperty(tab, "popup");
+
+    if (popup != this.prevPopups.get(window)) {
+      this.prevPopups.set(window, popup);
+
+      let panel = node.querySelector("panel");
+      if (panel) {
+        panel.remove();
+      }
+
+      if (popup) {
+        let popupURL = this.extension.baseURI.resolve(popup);
+        node.setAttribute("type", "panel");
+
+        let document = node.ownerDocument;
+        let panel = document.createElement("panel");
+        panel.setAttribute("class", "browser-action-panel");
+        panel.setAttribute("type", "arrow");
+        panel.setAttribute("flip", "slide");
+        node.appendChild(panel);
+
+        let browser = document.createElementNS(XUL_NS, "browser");
+        browser.setAttribute("type", "content");
+        browser.setAttribute("disableglobalhistory", "true");
+        browser.setAttribute("width", "500");
+        browser.setAttribute("height", "500");
+        panel.appendChild(browser);
+
+        let loadListener = () => {
+          panel.removeEventListener("load", loadListener);
+
+          if (this.context) {
+            this.context.unload();
+          }
+
+          this.context = new ExtensionPage(this.extension, {
+            type: "popup",
+            contentWindow: browser.contentWindow,
+            uri: Services.io.newURI(popupURL, null, null),
+            docShell: browser.docShell,
+          });
+          GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context);
+          browser.setAttribute("src", popupURL);
+        };
+        panel.addEventListener("load", loadListener);
+      } else {
+        node.removeAttribute("type");
+      }
+    }
+  },
+
+  // Note: tab is allowed to be null here.
+  getIcon(tab, node) {
+    let icon = this.icon.get(tab);
+
+    let url;
+    if (typeof(icon) != "object") {
+      url = icon;
+    } else {
+      let window = node.ownerDocument.defaultView;
+      let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
+      let res = {value: 1}
+      utils.getResolution(res);
+
+      let size = res.value == 1 ? 19 : 38;
+      url = icon[size];
+    }
+
+    if (url) {
+      return this.extension.baseURI.resolve(url);
+    } else {
+      return "chrome://browser/content/extension.svg";
+    }
+  },
+
+  // Update the toolbar button for a given window.
+  updateWindow(window) {
+    let tab = window.gBrowser ? window.gBrowser.selectedTab : null;
+    let node = CustomizableUI.getWidget(this.id).forWindow(window).node;
+    this.updateTab(tab, node);
+  },
+
+  // Update the toolbar button when the extension changes the icon,
+  // title, badge, etc. If it only changes a parameter for a single
+  // tab, |tab| will be that tab. Otherwise it will be null.
+  updateOnChange(tab) {
+    if (tab) {
+      if (tab.selected) {
+        this.updateWindow(tab.ownerDocument.defaultView);
+      }
+    } else {
+      let e = Services.wm.getEnumerator("navigator:browser");
+      while (e.hasMoreElements()) {
+        let window = e.getNext();
+        if (window.gBrowser) {
+          this.updateWindow(window);
+        }
+      }
+    }
+  },
+
+  // tab is allowed to be null.
+  // prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor".
+  setProperty(tab, prop, value) {
+    this[prop].set(tab, value);
+    this.updateOnChange(tab);
+  },
+
+  // tab is allowed to be null.
+  // prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor".
+  getProperty(tab, prop) {
+    return this[prop].get(tab);
+  },
+
+  shutdown() {
+    CustomizableUI.destroyWidget(this.id);
+  },
+};
+
+EventEmitter.decorate(BrowserAction.prototype);
+
+extensions.on("manifest_browser_action", (type, directive, extension, manifest) => {
+  let browserAction = new BrowserAction(manifest.browser_action, extension);
+  browserAction.build();
+  browserActionMap.set(extension, browserAction);
+});
+
+extensions.on("shutdown", (type, extension) => {
+  if (browserActionMap.has(extension)) {
+    browserActionMap.get(extension).shutdown();
+    browserActionMap.delete(extension);
+  }
+});
+
+extensions.registerAPI((extension, context) => {
+  return {
+    browserAction: {
+      onClicked: new EventManager(context, "browserAction.onClicked", fire => {
+        let listener = () => {
+          let tab = TabManager.activeTab;
+          fire(TabManager.convert(extension, tab));
+        };
+        browserActionOf(extension).on("click", listener);
+        return () => {
+          browserActionOf(extension).off("click", listener);
+        };
+      }).api(),
+
+      setTitle: function(details) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        browserActionOf(extension).setProperty(tab, "title", details.title);
+      },
+
+      getTitle: function(details, callback) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        let title = browserActionOf(extension).getProperty(tab, "title");
+        runSafe(context, callback, title);
+      },
+
+      setIcon: function(details, callback) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        if (details.imageData) {
+          // FIXME: Support the imageData attribute.
+          return;
+        }
+        browserActionOf(extension).setProperty(tab, "icon", details.path);
+      },
+
+      setBadgeText: function(details) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        browserActionOf(extension).setProperty(tab, "badgeText", details.text);
+      },
+
+      getBadgeText: function(details, callback) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        let text = browserActionOf(extension).getProperty(tab, "badgeText");
+        runSafe(context, callback, text);
+      },
+
+      setPopup: function(details) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        browserActionOf(extension).setProperty(tab, "popup", details.popup);
+      },
+
+      getPopup: function(details, callback) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        let popup = browserActionOf(extension).getProperty(tab, "popup");
+        runSafe(context, callback, popup);
+      },
+
+      setBadgeBackgroundColor: function(details) {
+        let color = details.color;
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color);
+      },
+
+      getBadgeBackgroundColor: function(details, callback) {
+        let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
+        let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor");
+        runSafe(context, callback, color);
+      },
+    }
+  };
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -0,0 +1,8 @@
+extensions.registerPrivilegedAPI("contextMenus", (extension, context) => {
+  return {
+    contextMenus: {
+      create() {},
+      removeAll() {},
+    },
+  };
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-tabs.js
@@ -0,0 +1,486 @@
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL",
+                                  "resource:///modules/NewTabURL.jsm");
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+let {
+  EventManager,
+  ignoreEvent,
+  runSafe,
+} = ExtensionUtils;
+
+// This function is pretty tightly tied to Extension.jsm.
+// Its job is to fill in the |tab| property of the sender.
+function getSender(context, target, sender)
+{
+  // The message was sent from a content script to a <browser> element.
+  // We can just get the |tab| from |target|.
+  if (target instanceof Ci.nsIDOMXULElement) {
+    // The message came from a content script.
+    let tabbrowser = target.ownerDocument.defaultView.gBrowser;
+    if (!tabbrowser) {
+      return;
+    }
+    let tab = tabbrowser.getTabForBrowser(target);
+
+    sender.tab = TabManager.convert(context.extension, tab);
+  } else {
+    // The message came from an ExtensionPage. In that case, it should
+    // include a tabId property (which is filled in by the page-open
+    // listener below).
+    if ("tabId" in sender) {
+      sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
+      delete sender.tabId;
+    }
+  }
+}
+
+// WeakMap[ExtensionPage -> {tab, parentWindow}]
+let pageDataMap = new WeakMap();
+
+// This listener fires whenever an extension page opens in a tab
+// (either initiated by the extension or the user). Its job is to fill
+// in some tab-specific details and keep data around about the
+// ExtensionPage.
+extensions.on("page-load", (type, page, params, sender, delegate) => {
+  if (params.type == "tab") {
+    let browser = params.docShell.chromeEventHandler;
+    let parentWindow = browser.ownerDocument.defaultView;
+    let tab = parentWindow.gBrowser.getTabForBrowser(browser);
+    sender.tabId = TabManager.getId(tab);
+
+    pageDataMap.set(page, {tab, parentWindow});
+  }
+
+  delegate.getSender = getSender;
+});
+
+extensions.on("page-unload", (type, page) => {
+  pageDataMap.delete(page);
+});
+
+extensions.on("page-shutdown", (type, page) => {
+  if (pageDataMap.has(page)) {
+    let {tab, parentWindow} = pageDataMap.get(page);
+    pageDataMap.delete(page);
+
+    parentWindow.gBrowser.removeTab(tab);
+  }
+});
+
+extensions.on("fill-browser-data", (type, browser, data, result) => {
+  let tabId = TabManager.getBrowserId(browser);
+  if (tabId == -1) {
+    result.cancel = true;
+    return;
+  }
+
+  data.tabId = tabId;
+});
+
+// TODO: activeTab permission
+
+extensions.registerAPI((extension, context) => {
+  let self = {
+    tabs: {
+      onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
+        let tab = event.originalTarget;
+        let tabId = TabManager.getId(tab);
+        let windowId = WindowManager.getId(tab.ownerDocument.defaultView);
+        fire({tabId, windowId});
+      }).api(),
+
+      onCreated: new EventManager(context, "tabs.onCreated", fire => {
+        let listener = event => {
+          let tab = event.originalTarget;
+          fire({tab: TabManager.convert(extension, tab)});
+        };
+
+        let windowListener = window => {
+          for (let tab of window.gBrowser.tabs) {
+            fire({tab: TabManager.convert(extension, tab)});
+          }
+        };
+
+        WindowListManager.addOpenListener(windowListener, false);
+        AllWindowEvents.addListener("TabOpen", listener);
+        return () => {
+          WindowListManager.removeOpenListener(windowListener);
+          AllWindowEvents.removeListener("TabOpen", listener);
+        };
+      }).api(),
+
+      onUpdated: new EventManager(context, "tabs.onUpdated", fire => {
+        function sanitize(extension, changeInfo) {
+          let result = {};
+          let nonempty = false;
+          for (let prop in changeInfo) {
+            if ((prop != "favIconUrl" && prop != "url") || extension.hasPermission("tabs")) {
+              nonempty = true;
+              result[prop] = changeInfo[prop];
+            }
+          }
+          return [nonempty, result];
+        }
+
+        let listener = event => {
+          let tab = event.originalTarget;
+          let window = tab.ownerDocument.defaultView;
+          let tabId = TabManager.getId(tab);
+
+          let changeInfo = {};
+          let needed = false;
+          if (event.type == "TabAttrModified") {
+            if (event.detail.changed.indexOf("image") != -1) {
+              changeInfo.favIconUrl = window.gBrowser.getIcon(tab);
+              needed = true;
+            }
+          } else if (event.type == "TabPinned") {
+            changeInfo.pinned = true;
+            needed = true;
+          } else if (event.type == "TabUnpinned") {
+            changeInfo.pinned = false;
+            needed = true;
+          }
+
+          [needed, changeInfo] = sanitize(extension, changeInfo);
+          if (needed) {
+            fire(tabId, changeInfo, TabManager.convert(extension, tab));
+          }
+        };
+        let progressListener = {
+          onStateChange(browser, webProgress, request, stateFlags, statusCode) {
+            if (!webProgress.isTopLevel) {
+              return;
+            }
+
+            let status;
+            if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
+              if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
+                status = "loading";
+              } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+                status = "complete";
+              }
+            } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+                       statusCode == Cr.NS_BINDING_ABORTED) {
+              status = "complete";
+            }
+
+            let gBrowser = browser.ownerDocument.defaultView.gBrowser;
+            let tab = gBrowser.getTabForBrowser(browser);
+            let tabId = TabManager.getId(tab);
+            let [needed, changeInfo] = sanitize(extension, {status});
+            fire(tabId, changeInfo, TabManager.convert(extension, tab));
+          },
+
+          onLocationChange(browser, webProgress, request, locationURI, flags) {
+            let gBrowser = browser.ownerDocument.defaultView.gBrowser;
+            let tab = gBrowser.getTabForBrowser(browser);
+            let tabId = TabManager.getId(tab);
+            let [needed, changeInfo] = sanitize(extension, {url: locationURI.spec});
+            if (needed) {
+              fire(tabId, changeInfo, TabManager.convert(extension, tab));
+            }
+          },
+        };
+
+        AllWindowEvents.addListener("progress", progressListener);
+        AllWindowEvents.addListener("TabAttrModified", listener);
+        AllWindowEvents.addListener("TabPinned", listener);
+        AllWindowEvents.addListener("TabUnpinned", listener);
+        return () => {
+          AllWindowEvents.removeListener("progress", progressListener);
+          AllWindowEvents.addListener("TabAttrModified", listener);
+          AllWindowEvents.addListener("TabPinned", listener);
+          AllWindowEvents.addListener("TabUnpinned", listener);
+        };
+      }).api(),
+
+      onReplaced: ignoreEvent(),
+
+      onRemoved: new EventManager(context, "tabs.onRemoved", fire => {
+        let tabListener = event => {
+          let tab = event.originalTarget;
+          let tabId = TabManager.getId(tab);
+          let windowId = WindowManager.getId(tab.ownerDocument.defaultView);
+          let removeInfo = {windowId, isWindowClosing: false};
+          fire(tabId, removeInfo);
+        };
+
+        let windowListener = window => {
+          for (let tab of window.gBrowser.tabs) {
+            let tabId = TabManager.getId(tab);
+            let windowId = WindowManager.getId(window);
+            let removeInfo = {windowId, isWindowClosing: true};
+            fire(tabId, removeInfo);
+          }
+        };
+
+        WindowListManager.addCloseListener(windowListener);
+        AllWindowEvents.addListener("TabClose", tabListener);
+        return () => {
+          WindowListManager.removeCloseListener(windowListener);
+          AllWindowEvents.removeListener("TabClose", tabListener);
+        };
+      }).api(),
+
+      create: function(createProperties, callback) {
+        if (!createProperties) {
+          createProperties = {};
+        }
+
+        let url = createProperties.url || NewTabURL.get();
+        url = extension.baseURI.resolve(url);
+
+        function createInWindow(window) {
+          let tab = window.gBrowser.addTab(url);
+
+          let active = true;
+          if ("active" in createProperties) {
+            active = createProperties.active;
+          } else if ("selected" in createProperties) {
+            active = createProperties.selected;
+          }
+          if (active) {
+            window.gBrowser.selectedTab = tab;
+          }
+
+          if ("index" in createProperties) {
+            window.gBrowser.moveTabTo(tab, createProperties.index);
+          }
+
+          if (createProperties.pinned) {
+            window.gBrowser.pinTab(tab);
+          }
+
+          if (callback) {
+            runSafe(context, callback, TabManager.convert(extension, tab));
+          }
+        }
+
+        let window = createProperties.windowId ?
+          WindowManager.getWindow(createProperties.windowId) :
+          WindowManager.topWindow;
+        if (!window.gBrowser) {
+          let obs = (finishedWindow, topic, data) => {
+            if (finishedWindow != window) {
+              return;
+            }
+            Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
+            createInWindow(window);
+          };
+          Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
+        } else {
+          createInWindow(window);
+        }
+      },
+
+      remove: function(tabs, callback) {
+        if (!Array.isArray(tabs)) {
+          tabs = [tabs];
+        }
+
+        for (let tabId of tabs) {
+          let tab = TabManager.getTab(tabId);
+          tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
+        }
+
+        if (callback) {
+          runSafe(context, callback);
+        }
+      },
+
+      update: function(...args) {
+        let tabId, updateProperties, callback;
+        if (args.length == 1) {
+          updateProperties = args[0];
+        } else {
+          [tabId, updateProperties, callback] = args;
+        }
+
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+        let tabbrowser = tab.ownerDocument.gBrowser;
+        if ("url" in updateProperties) {
+          tab.linkedBrowser.loadURI(updateProperties.url);
+        }
+        if ("active" in updateProperties) {
+          if (updateProperties.active) {
+            tabbrowser.selectedTab = tab;
+          } else {
+            // Not sure what to do here? Which tab should we select?
+          }
+        }
+        if ("pinned" in updateProperties) {
+          if (updateProperties.pinned) {
+            tabbrowser.pinTab(tab);
+          } else {
+            tabbrowser.unpinTab(tab);
+          }
+        }
+        // FIXME: highlighted/selected, openerTabId
+
+        if (callback) {
+          runSafe(context, callback, TabManager.convert(extension, tab));
+        }
+      },
+
+      reload: function(tabId, reloadProperties, callback) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+        let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+        if (reloadProperties && reloadProperties.bypassCache) {
+          flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
+        }
+        tab.linkedBrowser.reloadWithFlags(flags);
+
+        if (callback) {
+          runSafe(context, callback);
+        }
+      },
+
+      get: function(tabId, callback) {
+        let tab = TabManager.getTab(tabId);
+        runSafe(context, callback, TabManager.convert(extension, tab));
+      },
+
+      getAllInWindow: function(...args) {
+        let window, callback;
+        if (args.length == 1) {
+          callbacks = args[0];
+        } else {
+          window = WindowManager.getWindow(args[0]);
+          callback = args[1];
+        }
+
+        if (!window) {
+          window = WindowManager.topWindow;
+        }
+
+        return self.tabs.query({windowId: WindowManager.getId(window)}, callback);
+      },
+
+      query: function(queryInfo, callback) {
+        if (!queryInfo) {
+          queryInfo = {};
+        }
+
+        function matches(window, tab) {
+          let props = ["active", "pinned", "highlighted", "status", "title", "url", "index"];
+          for (let prop of props) {
+            if (prop in queryInfo && queryInfo[prop] != tab[prop]) {
+              return false;
+            }
+          }
+
+          let lastFocused = window == WindowManager.topWindow;
+          if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) {
+            return false;
+          }
+
+          let windowType = WindowManager.windowType(window);
+          if ("windowType" in queryInfo && queryInfo.windowType != windowType) {
+            return false;
+          }
+
+          if ("windowId" in queryInfo) {
+            if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) {
+              if (context.contentWindow != window) {
+                return false;
+              }
+            } else {
+              if (queryInfo.windowId != tab.windowId) {
+                return false;
+              }
+            }
+          }
+
+          if ("currentWindow" in queryInfo) {
+            let eq = window == context.contentWindow;
+            if (queryInfo.currentWindow != eq) {
+              return false;
+            }
+          }
+
+          return true;
+        }
+
+        let result = [];
+        let e = Services.wm.getEnumerator("navigator:browser");
+        while (e.hasMoreElements()) {
+          let window = e.getNext();
+          let tabs = TabManager.getTabs(extension, window);
+          for (let tab of tabs) {
+            if (matches(window, tab)) {
+              result.push(tab);
+            }
+          }
+        }
+        runSafe(context, callback, result);
+      },
+
+      _execute: function(tabId, details, kind, callback) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+        let mm = tab.linkedBrowser.messageManager;
+
+        let options = {js: [], css: []};
+        if (details.code) {
+          options[kind + 'Code'] = details.code;
+        }
+        if (details.file) {
+          options[kind].push(extension.baseURI.resolve(details.file));
+        }
+        if (details.allFrames) {
+          options.all_frames = details.allFrames;
+        }
+        if (details.matchAboutBlank) {
+          options.match_about_blank = details.matchAboutBlank;
+        }
+        if (details.runAt) {
+          options.run_at = details.runAt;
+        }
+        mm.sendAsyncMessage("Extension:Execute",
+                            {extensionId: extension.id, options});
+
+        // TODO: Call the callback with the result (which is what???).
+      },
+
+      executeScript: function(...args) {
+        if (args.length == 1) {
+          self.tabs._execute(undefined, args[0], 'js', undefined);
+        } else {
+          self.tabs._execute(args[0], args[1], 'js', args[2]);
+        }
+      },
+
+      insertCss: function(tabId, details, callback) {
+        if (args.length == 1) {
+          self.tabs._execute(undefined, args[0], 'css', undefined);
+        } else {
+          self.tabs._execute(args[0], args[1], 'css', args[2]);
+        }
+      },
+
+      connect: function(tabId, connectInfo) {
+        let tab = TabManager.getTab(tabId);
+        let mm = tab.linkedBrowser.messageManager;
+
+        let name = connectInfo.name || "";
+        let recipient = {extensionId: extension.id};
+        if ("frameId" in connectInfo) {
+          recipient.frameId = connectInfo.frameId;
+        }
+        return context.messenger.connect(mm, name, recipient);
+      },
+
+      sendMessage: function(tabId, message, options, responseCallback) {
+        let tab = TabManager.getTab(tabId);
+        let mm = tab.linkedBrowser.messageManager;
+
+        let recipient = {extensionId: extension.id};
+        if (options && "frameId" in options) {
+          recipient.frameId = options.frameId;
+        }
+        return context.messenger.sendMessage(mm, message, recipient, responseCallback);
+      },
+    },
+  };
+  return self;
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-utils.js
@@ -0,0 +1,324 @@
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+let {
+  EventManager,
+} = ExtensionUtils;
+
+// This file provides some useful code for the |tabs| and |windows|
+// modules. All of the code is installed on |global|, which is a scope
+// shared among the different ext-*.js scripts.
+
+// Manages mapping between XUL tabs and extension tab IDs.
+global.TabManager = {
+  _tabs: new WeakMap(),
+  _nextId: 1,
+
+  getId(tab) {
+    if (this._tabs.has(tab)) {
+      return this._tabs.get(tab);
+    }
+    let id = this._nextId++;
+    this._tabs.set(tab, id);
+    return id;
+  },
+
+  getBrowserId(browser) {
+    let gBrowser = browser.ownerDocument.defaultView.gBrowser;
+    // Some non-browser windows have gBrowser but not
+    // getTabForBrowser!
+    if (gBrowser && gBrowser.getTabForBrowser) {
+      let tab = gBrowser.getTabForBrowser(browser);
+      if (tab) {
+        return this.getId(tab);
+      }
+    }
+    return -1;
+  },
+
+  getTab(tabId) {
+    // FIXME: Speed this up without leaking memory somehow.
+    let e = Services.wm.getEnumerator("navigator:browser");
+    while (e.hasMoreElements()) {
+      let window = e.getNext();
+      if (!window.gBrowser) {
+        continue;
+      }
+      for (let tab of window.gBrowser.tabs) {
+        if (this.getId(tab) == tabId) {
+          return tab;
+        }
+      }
+    }
+    return null;
+  },
+
+  get activeTab() {
+    let window = WindowManager.topWindow;
+    if (window && window.gBrowser) {
+      return window.gBrowser.selectedTab;
+    }
+    return null;
+  },
+
+  getStatus(tab) {
+    return tab.getAttribute("busy") == "true" ? "loading" : "complete";
+  },
+
+  convert(extension, tab) {
+    let window = tab.ownerDocument.defaultView;
+    let windowActive = window == WindowManager.topWindow;
+    let result = {
+      id: this.getId(tab),
+      index: tab._tPos,
+      windowId: WindowManager.getId(window),
+      selected: tab.selected,
+      highlighted: tab.selected,
+      active: tab.selected,
+      pinned: tab.pinned,
+      status: this.getStatus(tab),
+      incognito: PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser),
+      width: tab.linkedBrowser.clientWidth,
+      height: tab.linkedBrowser.clientHeight,
+    };
+
+    if (extension.hasPermission("tabs")) {
+      result.url = tab.linkedBrowser.currentURI.spec;
+      if (tab.linkedBrowser.contentTitle) {
+        result.title = tab.linkedBrowser.contentTitle;
+      }
+      let icon = window.gBrowser.getIcon(tab);
+      if (icon) {
+        result.favIconUrl = icon;
+      }
+    }
+
+    return result;
+  },
+
+  getTabs(extension, window) {
+    if (!window.gBrowser) {
+      return [];
+    }
+    return [ for (tab of window.gBrowser.tabs) this.convert(extension, tab) ];
+  },
+};
+
+// Manages mapping between XUL windows and extension window IDs.
+global.WindowManager = {
+  _windows: new WeakMap(),
+  _nextId: 0,
+
+  WINDOW_ID_NONE: -1,
+  WINDOW_ID_CURRENT: -2,
+
+  get topWindow() {
+    return Services.wm.getMostRecentWindow("navigator:browser");
+  },
+
+  windowType(window) {
+    // TODO: Make this work.
+    return "normal";
+  },
+
+  getId(window) {
+    if (this._windows.has(window)) {
+      return this._windows.get(window);
+    }
+    let id = this._nextId++;
+    this._windows.set(window, id);
+    return id;
+  },
+
+  getWindow(id) {
+    let e = Services.wm.getEnumerator("navigator:browser");
+    while (e.hasMoreElements()) {
+      let window = e.getNext();
+      if (this.getId(window) == id) {
+        return window;
+      }
+    }
+    return null;
+  },
+
+  convert(extension, window, getInfo) {
+    let result = {
+      id: this.getId(window),
+      focused: window == WindowManager.topWindow,
+      top: window.screenY,
+      left: window.screenX,
+      width: window.outerWidth,
+      height: window.outerHeight,
+      incognito: PrivateBrowsingUtils.isWindowPrivate(window),
+
+      // We fudge on these next two.
+      type: this.windowType(window),
+      state: window.fullScreen ? "fullscreen" : "normal",
+    };
+
+    if (getInfo && getInfo.populate) {
+      results.tabs = TabManager.getTabs(extension, window);
+    }
+
+    return result;
+  },
+};
+
+// Manages listeners for window opening and closing. A window is
+// considered open when the "load" event fires on it. A window is
+// closed when a "domwindowclosed" notification fires for it.
+global.WindowListManager = {
+  _openListeners: new Set(),
+  _closeListeners: new Set(),
+
+  addOpenListener(listener, fireOnExisting = true) {
+    if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
+      Services.ww.registerNotification(this);
+    }
+    this._openListeners.add(listener);
+
+    let e = Services.wm.getEnumerator("navigator:browser");
+    while (e.hasMoreElements()) {
+      let window = e.getNext();
+      if (window.document.readyState != "complete") {
+        window.addEventListener("load", this);
+      } else if (fireOnExisting) {
+        listener(window);
+      }
+    }
+  },
+
+  removeOpenListener(listener) {
+    this._openListeners.delete(listener);
+    if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
+      Services.ww.unregisterNotification(this);
+    }
+  },
+
+  addCloseListener(listener) {
+    if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
+      Services.ww.registerNotification(this);
+    }
+    this._closeListeners.add(listener);
+  },
+
+  removeCloseListener(listener) {
+    this._closeListeners.delete(listener);
+    if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
+      Services.ww.unregisterNotification(this);
+    }
+  },
+
+  handleEvent(event) {
+    let window = event.target.defaultView;
+    window.removeEventListener("load", this.loadListener);
+    if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
+      return;
+    }
+
+    for (let listener of this._openListeners) {
+      listener(window);
+    }
+  },
+
+  queryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+
+  observe(window, topic, data) {
+    if (topic == "domwindowclosed") {
+      if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
+        return;
+      }
+
+      window.removeEventListener("load", this);
+      for (let listener of this._closeListeners) {
+        listener(window);
+      }
+    } else {
+      window.addEventListener("load", this);
+    }
+  },
+};
+
+// Provides a facility to listen for DOM events across all XUL windows.
+global.AllWindowEvents = {
+  _listeners: new Map(),
+
+  // If |type| is a normal event type, invoke |listener| each time
+  // that event fires in any open window. If |type| is "progress", add
+  // a web progress listener that covers all open windows.
+  addListener(type, listener) {
+    if (type == "domwindowopened") {
+      return WindowListManager.addOpenListener(listener);
+    } else if (type == "domwindowclosed") {
+      return WindowListManager.addCloseListener(listener);
+    }
+
+    let needOpenListener = this._listeners.size == 0;
+
+    if (!this._listeners.has(type)) {
+      this._listeners.set(type, new Set());
+    }
+    let list = this._listeners.get(type);
+    list.add(listener);
+
+    if (needOpenListener) {
+      WindowListManager.addOpenListener(this.openListener);
+    }
+  },
+
+  removeListener(type, listener) {
+    if (type == "domwindowopened") {
+      return WindowListManager.removeOpenListener(listener);
+    } else if (type == "domwindowclosed") {
+      return WindowListManager.removeCloseListener(listener);
+    }
+
+    let listeners = this._listeners.get(type);
+    listeners.delete(listener);
+    if (listeners.length == 0) {
+      this._listeners.delete(type);
+      if (this._listeners.size == 0) {
+        WindowListManager.removeOpenListener(this.openListener);
+      }
+    }
+
+    let e = Services.wm.getEnumerator("navigator:browser");
+    while (e.hasMoreElements()) {
+      let window = e.getNext();
+      if (type == "progress") {
+        window.gBrowser.removeTabsProgressListener(listener);
+      } else {
+        window.removeEventListener(type, listener);
+      }
+    }
+  },
+
+  // Runs whenever the "load" event fires for a new window.
+  openListener(window) {
+    for (let [eventType, listeners] of AllWindowEvents._listeners) {
+      for (let listener of listeners) {
+        if (eventType == "progress") {
+          window.gBrowser.addTabsProgressListener(listener);
+        } else {
+          window.addEventListener(eventType, listener);
+        }
+      }
+    }
+  },
+};
+
+// Subclass of EventManager where we just need to call
+// add/removeEventListener on each XUL window.
+global.WindowEventManager = function(context, name, event, listener)
+{
+  EventManager.call(this, context, name, fire => {
+    let listener2 = (...args) => listener(fire, ...args);
+    AllWindowEvents.addListener(event, listener2);
+    return () => {
+      AllWindowEvents.removeListener(event, listener2);
+    }
+  });
+}
+
+WindowEventManager.prototype = Object.create(EventManager.prototype);
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-windows.js
@@ -0,0 +1,151 @@
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL",
+                                  "resource:///modules/NewTabURL.jsm");
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+let {
+  EventManager,
+  ignoreEvent,
+  runSafe,
+} = ExtensionUtils;
+
+extensions.registerAPI((extension, context) => {
+  return {
+    windows: {
+      WINDOW_ID_CURRENT: WindowManager.WINDOW_ID_CURRENT,
+      WINDOW_ID_NONE: WindowManager.WINDOW_ID_NONE,
+
+      onCreated:
+      new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
+        fire(WindowManager.convert(extension, window));
+      }).api(),
+
+      onRemoved:
+      new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
+        fire(WindowManager.getId(window));
+      }).api(),
+
+      onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => {
+        // FIXME: This will send multiple messages for a single focus change.
+        let listener = event => {
+          let window = WindowManager.topWindow;
+          let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE;
+          fire(windowId);
+        };
+        AllWindowEvents.addListener("focus", listener);
+        AllWindowEvents.addListener("blur", listener);
+        return () => {
+          AllWindowEvents.removeListener("focus", listener);
+          AllWindowEvents.removeListener("blur", listener);
+        };
+      }).api(),
+
+      get: function(windowId, getInfo, callback) {
+        let window = WindowManager.getWindow(windowId);
+        runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
+      },
+
+      getCurrent: function(getInfo, callback) {
+        let window = context.contentWindow;
+        runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
+      },
+
+      getLastFocused: function(...args) {
+        let getInfo, callback;
+        if (args.length == 1) {
+          callback = args[0];
+        } else {
+          [getInfo, callback] = args;
+        }
+        let window = WindowManager.topWindow;
+        runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
+      },
+
+      getAll: function(getAll, callback) {
+        let e = Services.wm.getEnumerator("navigator:browser");
+        let windows = [];
+        while (e.hasMoreElements()) {
+          let window = e.getNext();
+          windows.push(WindowManager.convert(extension, window, getInfo));
+        }
+        runSafe(context, callback, windows);
+      },
+
+      create: function(createData, callback) {
+        function mkstr(s) {
+          let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+          result.data = s;
+          return result;
+        }
+
+        let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
+        if ("url" in createData) {
+          if (Array.isArray(createData.url)) {
+            let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
+            for (let url of createData.url) {
+              array.AppendElement(mkstr(url));
+            }
+            args.AppendElement(array);
+          } else {
+            args.AppendElement(mkstr(createData.url));
+          }
+        } else {
+          args.AppendElement(mkstr(NewTabURL.get()));
+        }
+
+        let extraFeatures = "";
+        if ("incognito" in createData) {
+          if (createData.incognito) {
+            extraFeatures += ",private";
+          } else {
+            extraFeatures += ",non-private";
+          }
+        }
+
+        let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
+                                            "chrome,dialog=no,all" + extraFeatures, args);
+
+        if ("left" in createData || "top" in createData) {
+          let left = "left" in createData ? createData.left : window.screenX;
+          let top = "top" in createData ? createData.top : window.screenY;
+          window.moveTo(left, top);
+        }
+        if ("width" in createData || "height" in createData) {
+          let width = "width" in createData ? createData.width : window.outerWidth;
+          let height = "height" in createData ? createData.height : window.outerHeight;
+          window.resizeTo(width, height);
+        }
+
+        // TODO: focused, type, state
+
+        window.addEventListener("load", function listener() {
+          window.removeEventListener("load", listener);
+          if (callback) {
+            runSafe(context, callback, WindowManager.convert(extension, window));
+          }
+        });
+      },
+
+      update: function(windowId, updateInfo, callback) {
+        let window = WindowManager.getWindow(windowId);
+        if (updateInfo.focused) {
+          Services.focus.activeWindow = window;
+        }
+        // TODO: All the other properties...
+        runSafe(context, callback, WindowManager.convert(extension, window));
+      },
+
+      remove: function(windowId, callback) {
+        let window = WindowManager.getWindow(windowId);
+        window.close();
+
+        let listener = () => {
+          AllWindowEvents.removeListener("domwindowclosed", listener);
+          if (callback) {
+            runSafe(context, callback);
+          }
+        };
+        AllWindowEvents.addListener("domwindowclosed", listener);
+      },
+    },
+  };
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/extension.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="64" height="64" viewBox="0 0 64 64">
+  <defs>
+    <style>
+      .style-puzzle-piece {
+        fill: url('#gradient-linear-puzzle-piece');
+      }
+    </style>
+    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
+      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
+      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
+    </linearGradient>
+  </defs>
+  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/jar.mn
@@ -0,0 +1,11 @@
+# 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/.
+
+browser.jar:
+    content/browser/extension.svg (extension.svg)
+    content/browser/ext-utils.js (ext-utils.js)
+    content/browser/ext-contextMenus.js (ext-contextMenus.js)
+    content/browser/ext-browserAction.js (ext-browserAction.js)
+    content/browser/ext-tabs.js (ext-tabs.js)
+    content/browser/ext-windows.js (ext-windows.js)
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/moz.build
@@ -0,0 +1,7 @@
+# -*- 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/.
+
+JAR_MANIFESTS += ['jar.mn']
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/prepare.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import argparse
+import json
+import uuid
+import sys
+import os.path
+
+parser = argparse.ArgumentParser(description='Create install.rdf from manifest.json')
+parser.add_argument('--locale')
+parser.add_argument('--profile')
+parser.add_argument('--uuid')
+parser.add_argument('dir')
+args = parser.parse_args()
+
+manifestFile = os.path.join(args.dir, 'manifest.json')
+manifest = json.load(open(manifestFile))
+
+locale = args.locale
+if not locale:
+    locale = manifest.get('default_locale', 'en-US')
+
+def process_locale(s):
+    if s.startswith('__MSG_') and s.endswith('__'):
+        tag = s[6:-2]
+        path = os.path.join(args.dir, '_locales', locale, 'messages.json')
+        data = json.load(open(path))
+        return data[tag]['message']
+    else:
+        return s
+
+id = args.uuid
+if not id:
+    id = '{' + str(uuid.uuid4()) + '}'
+
+name = process_locale(manifest['name'])
+desc = process_locale(manifest['description'])
+version = manifest['version']
+
+installFile = open(os.path.join(args.dir, 'install.rdf'), 'w')
+print >>installFile, '<?xml version="1.0"?>'
+print >>installFile, '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
+print >>installFile, '     xmlns:em="http://www.mozilla.org/2004/em-rdf#">'
+print >>installFile
+print >>installFile, '  <Description about="urn:mozilla:install-manifest">'
+print >>installFile, '    <em:id>{}</em:id>'.format(id)
+print >>installFile, '    <em:type>2</em:type>'
+print >>installFile, '    <em:name>{}</em:name>'.format(name)
+print >>installFile, '    <em:description>{}</em:description>'.format(desc)
+print >>installFile, '    <em:version>{}</em:version>'.format(version)
+print >>installFile, '    <em:bootstrap>true</em:bootstrap>'
+
+print >>installFile, '    <em:targetApplication>'
+print >>installFile, '      <Description>'
+print >>installFile, '        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>'
+print >>installFile, '        <em:minVersion>4.0</em:minVersion>'
+print >>installFile, '        <em:maxVersion>50.0</em:maxVersion>'
+print >>installFile, '      </Description>'
+print >>installFile, '    </em:targetApplication>'
+
+print >>installFile, '  </Description>'
+print >>installFile, '</RDF>'
+installFile.close()
+
+bootstrapPath = os.path.join(os.path.dirname(sys.argv[0]), 'bootstrap.js')
+data = open(bootstrapPath).read()
+boot = open(os.path.join(args.dir, 'bootstrap.js'), 'w')
+boot.write(data)
+boot.close()
+
+if args.profile:
+    os.system('mkdir -p {}/extensions'.format(args.profile))
+    output = open(args.profile + '/extensions/' + id, 'w')
+    print >>output, os.path.realpath(args.dir)
+    output.close()
+else:
+    dir = os.path.realpath(args.dir)
+    if dir[-1] == os.sep:
+        dir = dir[:-1]
+    os.system('cd "{}"; zip ../"{}".xpi -r *'.format(args.dir, os.path.basename(dir)))
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'about',
     'customizableui',
     'dirprovider',
     'downloads',
+    'extensions',
     'feeds',
     'loop',
     'migration',
     'places',
     'pocket',
     'preferences',
     'privatebrowsing',
     'search',
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -164,16 +164,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/ReaderParent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
                                   "resource://gre/modules/AddonWatcher.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
+                                  "resource://gre/modules/ExtensionManagement.jsm");
+
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
@@ -596,16 +599,22 @@ BrowserGlue.prototype = {
 #endif
     os.addObserver(this, "browser-search-engine-modified", false);
     os.addObserver(this, "browser-search-service", false);
     os.addObserver(this, "restart-in-safe-mode", false);
     os.addObserver(this, "flash-plugin-hang", false);
     os.addObserver(this, "xpi-signature-changed", false);
     os.addObserver(this, "autocomplete-did-enter-text", false);
 
+    ExtensionManagement.registerScript("chrome://browser/content/ext-utils.js");
+    ExtensionManagement.registerScript("chrome://browser/content/ext-browserAction.js");
+    ExtensionManagement.registerScript("chrome://browser/content/ext-contextMenus.js");
+    ExtensionManagement.registerScript("chrome://browser/content/ext-tabs.js");
+    ExtensionManagement.registerScript("chrome://browser/content/ext-windows.js");
+
     this._flashHangCount = 0;
   },
 
   // cleanup (called on application shutdown)
   _dispose: function BG__dispose() {
     let os = Services.obs;
     os.removeObserver(this, "prefservice:after-app-defaults");
     os.removeObserver(this, "final-ui-startup");
--- a/browser/config/mozconfigs/linux64/hazards
+++ b/browser/config/mozconfigs/linux64/hazards
@@ -21,12 +21,13 @@ mk_add_options MOZ_OBJDIR=obj-analyzed
 ac_add_options --enable-debug
 ac_add_options --enable-tests
 ac_add_options --enable-optimize
 
 CFLAGS="$CFLAGS -Wno-attributes"
 CPPFLAGS="$CPPFLAGS -Wno-attributes"
 CXXFLAGS="$CXXFLAGS -Wno-attributes"
 
+TOOLTOOL_DIR="$(dirname $topsrcdir)"
 export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
 . $topsrcdir/build/unix/mozconfig.gtk
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -2,20 +2,21 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 4079256,
-"digest": "bb5238558bcf6db2ca395513c8dccaa15dd61b3c375598eb6a685356b0c1a2d9840e3bf81bc00242b872fd798541f53d723777c754412abf0e772b7cc284937c",
+"size": 11179576,
+"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux32/valgrind.manifest
+++ /dev/null
@@ -1,16 +0,0 @@
-[
-{
-"size": 80458572,
-"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
-"algorithm": "sha512", 
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-}
-]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -3,10 +3,18 @@
 "clang_version": "r241406"
 }, 
 {
 "size": 100307285, 
 "digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"size": 12057960,
+"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"setup": "setup.sh",
+"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -2,20 +2,21 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 4431740,
-"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"size": 12057960,
+"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
--- a/browser/config/tooltool-manifests/linux64/tsan.manifest
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -5,15 +5,16 @@
 {
 "size": 89690541, 
 "digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
-"size": 4431740,
-"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"size": 12057960,
+"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/valgrind.manifest
+++ /dev/null
@@ -1,16 +0,0 @@
-[
-{
-"size": 80458572,
-"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
-"algorithm": "sha512", 
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-}
-]
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -54,16 +54,25 @@ this.E10SUtils = {
     if (aURL.startsWith("chrome:")) {
       let url = Services.io.newURI(aURL, null, null);
       let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
                       getService(Ci.nsIXULChromeRegistry);
       canLoadRemote = chromeReg.canLoadURLRemotely(url);
       mustLoadRemote = chromeReg.mustLoadURLRemotely(url);
     }
 
+    if (aURL.startsWith("moz-extension:")) {
+      canLoadRemote = false;
+      mustLoadRemote = false;
+    }
+
+    if (aURL.startsWith("view-source:")) {
+      return this.canLoadURIInProcess(aURL.substr("view-source:".length), aProcess);
+    }
+
     if (mustLoadRemote)
       return processIsRemote;
 
     if (!canLoadRemote && processIsRemote)
       return false;
 
     return true;
   },
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1939,8 +1939,12 @@ chatbox {
 %include ../shared/contextmenu.inc.css
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left {
   visibility: visible;
   /* override toolkit/themes/linux/global/menu.css */
   -moz-padding-end: 0 !important;
   -moz-margin-end: 0 !important;
 }
+
+.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3680,8 +3680,12 @@ window > chatbox {
 }
 
 %include ../shared/contextmenu.inc.css
 
 #context-navigation > .menuitem-iconic {
   padding-left: 0;
   padding-right: 0;
 }
+
+.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2899,8 +2899,12 @@ chatbox {
   -moz-margin-start: -28px;
   margin-top: -4px;
 }
 
 
 @media not all and (-moz-os-version: windows-xp) {
 %include browser-aero.css
 }
+
+.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -236,16 +236,17 @@ bool isInterestingDeclForImplicitConvers
 
 class CustomTypeAnnotation {
   enum ReasonKind {
     RK_None,
     RK_Direct,
     RK_ArrayElement,
     RK_BaseClass,
     RK_Field,
+    RK_TemplateInherited,
   };
   struct AnnotationReason {
     QualType Type;
     ReasonKind Kind;
     const FieldDecl *Field;
 
     bool valid() const { return Kind != RK_None; }
   };
@@ -837,16 +838,25 @@ void CustomTypeAnnotation::dumpAnnotatio
         Diag.Report(Decl->getLocation(), InheritsID)
           << Pretty << T << Reason.Type;
         break;
       }
     case RK_Field:
       Diag.Report(Reason.Field->getLocation(), MemberID)
         << Pretty << T << Reason.Field << Reason.Type;
       break;
+    case RK_TemplateInherited:
+      {
+        const CXXRecordDecl *Decl = T->getAsCXXRecordDecl();
+        assert(Decl && "This type should be a C++ class");
+
+        Diag.Report(Decl->getLocation(), TemplID)
+          << Pretty << T << Reason.Type;
+        break;
+      }
     default:
       return;
     }
 
     T = Reason.Type;
     Reason = directAnnotationReason(T);
   }
 }
@@ -900,16 +910,39 @@ CustomTypeAnnotation::AnnotationReason C
       // Recurse into members
       for (const FieldDecl *Field : Decl->fields()) {
         if (hasEffectiveAnnotation(Field->getType())) {
           AnnotationReason Reason = { Field->getType(), RK_Field, Field };
           Cache[Key] = Reason;
           return Reason;
         }
       }
+
+      // Recurse into template arguments if the annotation
+      // MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present
+      if (MozChecker::hasCustomAnnotation(
+            Decl, "moz_inherit_type_annotations_from_template_args")) {
+        const ClassTemplateSpecializationDecl *Spec =
+          dyn_cast<ClassTemplateSpecializationDecl>(Decl);
+        if (Spec) {
+          const TemplateArgumentList &Args = Spec->getTemplateArgs();
+
+          for (const TemplateArgument &Arg : Args.asArray()) {
+            if (Arg.getKind() == TemplateArgument::Type) {
+              QualType Type = Arg.getAsType();
+
+              if (hasEffectiveAnnotation(Type)) {
+                AnnotationReason Reason = { Type, RK_TemplateInherited, nullptr };
+                Cache[Key] = Reason;
+                return Reason;
+              }
+            }
+          }
+        }
+      }
     }
   }
 
   AnnotationReason Reason = { QualType(), RK_None, nullptr };
   Cache[Key] = Reason;
   return Reason;
 }
 
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp
@@ -0,0 +1,17 @@
+#define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS                 \
+  __attribute__((annotate("moz_inherit_type_annotations_from_template_args")))
+#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
+
+class Normal {};
+class MOZ_STACK_CLASS Stack {};
+class IndirectStack : Stack {}; // expected-note {{'IndirectStack' is a stack type because it inherits from a stack type 'Stack'}}
+
+template<class T>
+class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Template {}; // expected-note 2 {{'Template<Stack>' is a stack type because it has a template argument stack type 'Stack'}} expected-note {{'Template<IndirectStack>' is a stack type because it has a template argument stack type 'IndirectStack'}}
+class IndirectTemplate : Template<Stack> {}; // expected-note {{'IndirectTemplate' is a stack type because it inherits from a stack type 'Template<Stack>'}}
+
+static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}}
+static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}}
+static Template<Normal> c;
+static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}}
+
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'TestBadImplicitConversionCtor.cpp',
     'TestCustomHeap.cpp',
     'TestExplicitOperatorBool.cpp',
     'TestGlobalClass.cpp',
+    'TestInheritTypeAnnotationsFromTemplateArgs.cpp',
     'TestMultipleAnnotations.cpp',
     'TestMustOverride.cpp',
     'TestMustUse.cpp',
     'TestNANTestingExpr.cpp',
     'TestNANTestingExprC.c',
     'TestNeedsNoVTableType.cpp',
     'TestNoAddRefReleaseOnReturn.cpp',
     'TestNoArithmeticExprInArgument.cpp',
--- a/build/sanitizers/lsan_suppressions.txt
+++ b/build/sanitizers/lsan_suppressions.txt
@@ -42,16 +42,23 @@ leak:nr_reg_set
 leak:mozilla::TransportLayerDtls::Setup
 
 # Bug 1187424 - DesktopApplication does not free any of its string members.
 leak:webrtc::DesktopApplication::
 
 # Bug 1187518 - SCTP leaks in child process while running WebRTC tests.
 leak:recv_function_udp
 
+# Bug 1074310 - Very large e10s vp8 leaks, maybe in WebRTC tests.
+leak:set_noise_sensitivity
+leak:vp8_alloc_compressor_data
+leak:vp8_change_config
+leak:vp8_lookahead_init
+leak:vpx_codec_enc_init_ver
+
 
 ###
 ### Many leaks only affect some test suites.  The suite annotations are not checked.
 ###
 
 # Bug 981195 - Small leak in the parser. m4
 leak:ObjectGroup::fixPlainObjectGroup
 
@@ -102,16 +109,24 @@ leak:IPC::Channel::Channel
 leak:base::MessagePumpLibevent::WatchFileDescriptor
 
 # Bug 1122045 - Leaks in MessageLoop::MessageLoop()
 leak:MessageLoop::MessageLoop
 # This may not actually be related to MessageLoop.
 leak:base::WaitableEvent::TimedWait
 leak:MessageLoop::PostTask_Helper
 
+# Bug 1189430 - DNS leaks in mochitest-chrome.
+leak:nsDNSService::AsyncResolveExtended
+leak:_GetAddrInfo_Portable
+
+# Bug 1189568 - Indirect leaks of IMContextWrapper and nsIntRect.
+leak:nsWindow::Create
+leak:nsBaseWidget::StoreWindowClipRegion
+
 
 ###
 ### Leaks with system libraries in their stacks. These show up across a number of tests.
 ### Better symbols and disabling fast stackwalking may help diagnose these.
 ###
 
 leak:libcairo.so
 leak:libdl.so
--- a/build/unix/build-gtk3/build-gtk3.sh
+++ b/build/unix/build-gtk3/build-gtk3.sh
@@ -52,17 +52,17 @@ build() {
 	version=$(eval echo \$${pkg}_version)
 	url=$(eval echo \$${pkg}_url)
 	wget -c -P $TMPDIR $url
 	tar -axf $TMPDIR/$name-$version.tar.*
 	mkdir -p build/$name
 	cd build/$name
 	eval ../../$name-$version/configure --disable-static $* $configure_args
 	make $make_flags
-	make install-strip DESTDIR=$root_dir/gtk3
+	make install DESTDIR=$root_dir/gtk3
 	find $root_dir/gtk3 -name \*.la -delete
 	cd ../..
 }
 
 case "$1" in
 32)
 	configure_args='--host=i686-pc-linux --build=i686-pc-linux CC="gcc -m32" CXX="g++ -m32"'
         lib=lib
@@ -98,10 +98,49 @@ build cairo --enable-tee
 build pango
 build atk
 make_flags="$make_flags GLIB_COMPILE_SCHEMAS=glib-compile-schemas"
 build gtk+
 
 rm -rf $root_dir/gtk3/usr/local/share/gtk-doc
 rm -rf $root_dir/gtk3/usr/local/share/locale
 
+# mock build environment doesn't have fonts in /usr/share/fonts, but
+# has some in /usr/share/X11/fonts. Add this directory to the
+# fontconfig configuration without changing the gtk3 tooltool package.
+cat << EOF > $root_dir/gtk3/usr/local/etc/fonts/local.conf
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+  <dir>/usr/share/X11/fonts</dir>
+</fontconfig>
+EOF
+
+cat <<EOF > $root_dir/gtk3/setup.sh
+#!/bin/sh
+
+cd \$(dirname \$0)
+
+# pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary...
+LD_LIBRARY_PATH=./usr/local/lib \
+PANGO_SYSCONFDIR=./usr/local/etc \
+PANGO_LIBDIR=./usr/local/lib \
+./usr/local/bin/pango-querymodules > ./usr/local/etc/pango/pango.modules
+
+# same with gdb-pixbuf and loaders.cache
+LD_LIBRARY_PATH=./usr/local/lib \
+GDK_PIXBUF_MODULE_FILE=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \
+GDK_PIXBUF_MODULEDIR=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
+./usr/local/bin/gdk-pixbuf-query-loaders > ./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
+
+# The fontconfig version in the tooltool package has known uses of
+# uninitialized memory when creating its cache, and while most users
+# will already have an existing cache, running Firefox on automation
+# will create it. Combined with valgrind, this generates irrelevant
+# errors.
+# So create the fontconfig cache beforehand.
+./usr/local/bin/fc-cache
+EOF
+
+chmod +x $root_dir/gtk3/setup.sh
+
 cd $cwd
 tar -C $root_dir -Jcf gtk3.tar.xz gtk3
--- a/build/unix/mozconfig.gtk
+++ b/build/unix/mozconfig.gtk
@@ -1,48 +1,28 @@
-# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it.
-if [ -d "$topsrcdir/gtk3" ]; then
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+# $TOOLTOOL_DIR/gtk3 comes from tooltool, when the tooltool manifest contains it.
+if [ -d "$TOOLTOOL_DIR/gtk3" ]; then
   if [ -z "$PKG_CONFIG_LIBDIR" ]; then
     echo PKG_CONFIG_LIBDIR must be set >&2
     exit 1
   fi
-  export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3"
-  export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig"
-  export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}"
+  export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3"
+  export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig"
+  export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}"
   # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
-  LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib ${LDFLAGS}"
-  mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
+  LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}"
   ac_add_options --enable-default-toolkit=cairo-gtk3
 
   # Set things up to use Gtk+3 from the tooltool package
-  mk_add_options "export FONTCONFIG_PATH=$topsrcdir/gtk3/usr/local/etc/fonts"
-  mk_add_options "export PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc"
-  mk_add_options "export PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib"
-  mk_add_options "export GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
-  mk_add_options "export GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
-  mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
-
-  # pango expects absolute paths in pango.modules, and topsrcdir may vary...
-  LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
-  PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc \
-  PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib \
-  $topsrcdir/gtk3/usr/local/bin/pango-querymodules > $topsrcdir/gtk3/usr/local/etc/pango/pango.modules
+  mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts"
+  mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc"
+  mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib"
+  mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
+  mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
+  mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib"
 
-  # same with gdb-pixbuf and loaders.cache
-  LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
-  GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \
-  GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
-  $topsrcdir/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
-
-  # mock build environment doesn't have fonts in /usr/share/fonts, but
-  # has some in /usr/share/X11/fonts. Add this directory to the
-  # fontconfig configuration without changing the gtk3 tooltool package.
-  cat << EOF > $topsrcdir/gtk3/usr/local/etc/fonts/local.conf
-<?xml version="1.0"?>
-<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-<fontconfig>
-  <dir>/usr/share/X11/fonts</dir>
-</fontconfig>
-EOF
-
+  # Until a tooltool with bug 1188571 landed is available everywhere
+  $TOOLTOOL_DIR/gtk3/setup.sh
 else
   ac_add_options --enable-default-toolkit=cairo-gtk2
 fi
--- a/build/valgrind/mach_commands.py
+++ b/build/valgrind/mach_commands.py
@@ -106,16 +106,23 @@ class MachCommands(MachCommandBase):
                 '--smc-check=all-non-file',
                 '--vex-iropt-register-updates=allregs-at-mem-access',
                 '--gen-suppressions=all',
                 '--num-callers=36',
                 '--leak-check=full',
                 '--show-possibly-lost=no',
                 '--track-origins=yes',
                 '--trace-children=yes',
+                # The gstreamer plugin scanner can run as part of executing
+                # firefox, but is an external program. In some weird cases,
+                # valgrind finds errors while executing __libc_freeres when
+                # it runs, but those are not relevant, as it's related to
+                # executing third party code. So don't trace
+                # gst-plugin-scanner.
+                '--trace-children-skip=*/gst-plugin-scanner',
                 '-v',  # Enable verbosity to get the list of used suppressions
             ]
 
             for s in suppressions:
                 valgrind_args.append('--suppressions=' + s)
 
             supps_dir = os.path.join(build_dir, 'valgrind')
             supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
--- a/build/valgrind/valgrind.sh
+++ b/build/valgrind/valgrind.sh
@@ -20,17 +20,17 @@ fi
 cd $objdir
 
 if [ "`uname -m`" = "x86_64" ]; then
     _arch=64
 else
     _arch=32
 fi
 
-TOOLTOOL_MANIFEST=browser/config/tooltool-manifests/linux${_arch}/valgrind.manifest
+TOOLTOOL_MANIFEST=browser/config/tooltool-manifests/linux${_arch}/releng.manifest
 TOOLTOOL_SERVER=https://api.pub.build.mozilla.org/tooltool/
 (cd $srcdir; python /builds/tooltool.py --url $TOOLTOOL_SERVER --overwrite -m $TOOLTOOL_MANIFEST fetch ${TOOLTOOL_CACHE:+ -c ${TOOLTOOL_CACHE}}) || exit 2
 
 # Note: an exit code of 2 turns the job red on TBPL.
 MOZCONFIG=$srcdir/browser/config/mozconfigs/linux${_arch}/valgrind make -f $srcdir/client.mk configure || exit 2
 make -j4 || exit 2
 make package || exit 2
 
--- a/docshell/shistory/nsSHEntryShared.cpp
+++ b/docshell/shistory/nsSHEntryShared.cpp
@@ -322,26 +322,28 @@ nsSHEntryShared::CharacterDataChanged(ns
   RemoveFromBFCacheAsync();
 }
 
 void
 nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
                                      dom::Element* aContent,
                                      int32_t aNameSpaceID,
                                      nsIAtom* aAttribute,
-                                     int32_t aModType)
+                                     int32_t aModType,
+                                     const nsAttrValue* aNewValue)
 {
 }
 
 void
 nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
                                   dom::Element* aElement,
                                   int32_t aNameSpaceID,
                                   nsIAtom* aAttribute,
-                                  int32_t aModType)
+                                  int32_t aModType,
+                                  const nsAttrValue* aOldValue)
 {
   RemoveFromBFCacheAsync();
 }
 
 void
 nsSHEntryShared::ContentAppended(nsIDocument* aDocument,
                                  nsIContent* aContainer,
                                  nsIContent* aFirstNewContent,
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2176,26 +2176,33 @@ Element::SetAttr(int32_t aNamespaceID, n
 
   if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
                              oldValue, &modType, &hasListeners)) {
     return NS_OK;
   }
 
   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
+  nsAttrValue* preparsedAttrValue = value.GetStoredAttrValue();
 
   if (aNotify) {
-    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
+    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType,
+                                     preparsedAttrValue);
   }
 
   // Hold a script blocker while calling ParseAttribute since that can call
   // out to id-observers
   nsAutoScriptBlocker scriptBlocker;
 
   nsAttrValue attrValue;
+  if (preparsedAttrValue) {
+    attrValue.SwapValueWith(*preparsedAttrValue);
+  }
+  // Even the value was pre-parsed in BeforeSetAttr, we still need to call
+  // ParseAttribute because it can have side effects.
   if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
     attrValue.SetTo(aValue);
   }
 
   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
                           attrValue, modType, hasListeners, aNotify,
                           kCallAfterSetAttr);
 }
@@ -2225,17 +2232,18 @@ Element::SetParsedAttr(int32_t aNamespac
                              oldValue, &modType, &hasListeners)) {
     return NS_OK;
   }
 
   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNotify) {
-    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
+    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType,
+                                     &aParsedValue);
   }
 
   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
                           aParsedValue, modType, hasListeners, aNotify,
                           kCallAfterSetAttr);
 }
 
 nsresult
@@ -2252,83 +2260,92 @@ Element::SetAttrAndNotify(int32_t aNames
   nsresult rv;
 
   nsIDocument* document = GetComposedDoc();
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   nsMutationGuard::DidMutate();
 
   // Copy aParsedValue for later use since it will be lost when we call
-  // SetAndTakeMappedAttr below
-  nsAttrValue aValueForAfterSetAttr;
+  // SetAndSwapMappedAttr below
+  nsAttrValue valueForAfterSetAttr;
   if (aCallAfterSetAttr) {
-    aValueForAfterSetAttr.SetTo(aParsedValue);
+    valueForAfterSetAttr.SetTo(aParsedValue);
   }
 
   bool hadValidDir = false;
   bool hadDirAuto = false;
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::dir) {
       hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
       hadDirAuto = HasDirAuto(); // already takes bdi into account
     }
 
     // XXXbz Perhaps we should push up the attribute mapping function
     // stuff to Element?
     if (!IsAttributeMapped(aName) ||
         !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
-      rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
+      rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue);
     }
   }
   else {
     nsRefPtr<mozilla::dom::NodeInfo> ni;
     ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix,
                                                    aNamespaceID,
                                                    nsIDOMNode::ATTRIBUTE_NODE);
 
-    rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue);
+    rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue);
   }
+
+  // If the old value owns its own data, we know it is OK to keep using it.
+  const nsAttrValue* oldValue =
+      aParsedValue.StoresOwnData() ? &aParsedValue : &aOldValue;
+
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
     }
   }
 
   UpdateState(aNotify);
 
   nsIDocument* ownerDoc = OwnerDoc();
   if (ownerDoc && GetCustomElementData()) {
-    nsCOMPtr<nsIAtom> oldValueAtom = aOldValue.GetAsAtom();
-    nsCOMPtr<nsIAtom> newValueAtom = aValueForAfterSetAttr.GetAsAtom();
+    nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom();
+    nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
     LifecycleCallbackArgs args = {
       nsDependentAtomString(aName),
       aModType == nsIDOMMutationEvent::ADDITION ?
         NullString() : nsDependentAtomString(oldValueAtom),
       nsDependentAtomString(newValueAtom)
     };
 
     ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
   }
 
   if (aCallAfterSetAttr) {
-    rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
+    rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
-      OnSetDirAttr(this, &aValueForAfterSetAttr,
+      OnSetDirAttr(this, &valueForAfterSetAttr,
                    hadValidDir, hadDirAuto, aNotify);
     }
   }
 
   if (aNotify) {
-    nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
+    // Don't pass aOldValue to AttributeChanged since it may not be reliable.
+    // Callers only compute aOldValue under certain conditions which may not
+    // be triggered by all nsIMutationObservers.
+    nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType,
+        oldValue == &aParsedValue ? &aParsedValue : nullptr);
   }
 
   if (aFireMutation) {
     InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     nsAutoString ns;
     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
     Attr* attrNode =
@@ -2336,38 +2353,57 @@ Element::SetAttrAndNotify(int32_t aNames
     mutation.mRelatedNode = attrNode;
 
     mutation.mAttrName = aName;
     nsAutoString newValue;
     GetAttr(aNamespaceID, aName, newValue);
     if (!newValue.IsEmpty()) {
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
-    if (!aOldValue.IsEmptyString()) {
-      mutation.mPrevAttrValue = aOldValue.GetAsAtom();
+    if (!oldValue->IsEmptyString()) {
+      mutation.mPrevAttrValue = oldValue->GetAsAtom();
     }
     mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(OwnerDoc(), this);
     (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
 
+nsresult
+Element::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
+                       nsAttrValueOrString* aValue, bool aNotify)
+{
+  if (aNamespaceID == kNameSpaceID_None) {
+    if (aName == nsGkAtoms::_class) {
+      // aValue->GetAttrValue will only be non-null here when this is called
+      // via Element::SetParsedAttr. This shouldn't happen for "class", but
+      // this will handle it.
+      if (aValue && !aValue->GetAttrValue()) {
+        nsAttrValue attr;
+        attr.ParseAtomArray(aValue->String());
+        aValue->TakeParsedValue(attr);
+      }
+    }
+  }
+  return NS_OK;
+}
+
 bool
 Element::ParseAttribute(int32_t aNamespaceID,
                         nsIAtom* aAttribute,
                         const nsAString& aValue,
                         nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::_class) {
       SetFlags(NODE_MAY_HAVE_CLASS);
-      aResult.ParseAtomArray(aValue);
+      // Result should have been preparsed above.
       return true;
     }
     if (aAttribute == nsGkAtoms::id) {
       // Store id as an atom.  id="" means that the element has no id,
       // not that it has an emptystring as the id.
       RemoveFromIdTable();
       if (aValue.IsEmpty()) {
         ClearHasID();
@@ -2464,17 +2500,18 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIDocument *document = GetComposedDoc();
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   if (aNotify) {
     nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
-                                     nsIDOMMutationEvent::REMOVAL);
+                                     nsIDOMMutationEvent::REMOVAL,
+                                     nullptr);
   }
 
   bool hasMutationListeners = aNotify &&
     nsContentUtils::HasMutationListeners(this,
                                          NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
                                          this);
 
   // Grab the attr node if needed before we remove it from the attr map
@@ -2530,18 +2567,20 @@ Element::UnsetAttr(int32_t aNameSpaceID,
       nsDependentAtomString(oldValueAtom),
       NullString()
     };
 
     ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
   }
 
   if (aNotify) {
+    // We can always pass oldValue here since there is no new value which could
+    // have corrupted it.
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
-                                  nsIDOMMutationEvent::REMOVAL);
+                                  nsIDOMMutationEvent::REMOVAL, &oldValue);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
     OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify);
   }
@@ -3489,9 +3528,9 @@ Element::GetReferrerPolicy()
   if (Preferences::GetBool("network.http.enablePerElementReferrer", false) &&
       IsHTMLElement()) {
     const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrer);
     if (referrerValue && referrerValue->Type() == nsAttrValue::eEnum) {
       return net::ReferrerPolicy(referrerValue->GetEnumValue());
     }
   }
   return net::RP_Unset;
-}
\ No newline at end of file
+}
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -472,16 +472,18 @@ public:
   bool OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
                               nsIAtom* aPrefix,
                               const nsAttrValueOrString& aValue,
                               bool aNotify, nsAttrValue& aOldValue,
                               uint8_t* aModType, bool* aHasListeners);
 
   virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
                            const nsAString& aValue, bool aNotify) override;
+  // aParsedValue receives the old value of the attribute. That's useful if
+  // either the input or output value of aParsedValue is StoresOwnData.
   nsresult SetParsedAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
                          nsAttrValue& aParsedValue, bool aNotify);
   // GetAttr is not inlined on purpose, to keep down codesize from all
   // the inlined nsAttrValue bits for C++ callers.
   bool GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                nsAString& aResult) const;
   inline bool HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const;
   // aCaseSensitive == eIgnoreCaase means ASCII case-insensitive matching.
@@ -1082,20 +1084,25 @@ protected:
    * in particular before it has been parsed.
    *
    * For the boolean parameters, consider using the named bools above to aid
    * code readability.
    *
    * @param aNamespaceID  namespace of attribute
    * @param aAttribute    local-name of attribute
    * @param aPrefix       aPrefix of attribute
-   * @param aOldValue     previous value of attribute. Only needed if
-   *                      aFireMutation is true or if the element is a
-   *                      custom element (in web components).
-   * @param aParsedValue  parsed new value of attribute
+   * @param aOldValue     The old value of the attribute to use as a fallback
+   *                      in the cases where the actual old value (i.e.
+   *                      its current value) is !StoresOwnData() --- in which
+   *                      case the current value is probably already useless.
+   *                      If the current value is StoresOwnData() (or absent),
+   *                      aOldValue will not be used.
+   * @param aParsedValue  parsed new value of attribute. Replaced by the
+   *                      old value of the attribute. This old value is only
+   *                      useful if either it or the new value is StoresOwnData.
    * @param aModType      nsIDOMMutationEvent::MODIFICATION or ADDITION.  Only
    *                      needed if aFireMutation or aNotify is true.
    * @param aFireMutation should mutation-events be fired?
    * @param aNotify       should we notify document-observers?
    * @param aCallAfterSetAttr should we call AfterSetAttr?
    */
   nsresult SetAttrAndNotify(int32_t aNamespaceID,
                             nsIAtom* aName,
@@ -1158,27 +1165,26 @@ protected:
    * we're actually doing an attr set and will be called before
    * AttributeWillChange and before ParseAttribute and hence before we've set
    * the new value.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aValue the value it's being set to represented as either a string or
    *        a parsed nsAttrValue. Alternatively, if the attr is being removed it
-   *        will be null.
+   *        will be null. BeforeSetAttr is allowed to modify aValue by parsing
+   *        the string to an nsAttrValue (to avoid having to reparse it in
+   *        ParseAttribute).
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
   // inlined.  Those calls don't go through a vtable.
   virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
-                                 const nsAttrValueOrString* aValue,
-                                 bool aNotify)
-  {
-    return NS_OK;
-  }
+                                 nsAttrValueOrString* aValue,
+                                 bool aNotify);
 
   /**
    * Hook that is called by Element::SetAttr to allow subclasses to
    * deal with attribute sets.  This will only be called after we have called
    * SetAndTakeAttr and AttributeChanged (that is, after we have actually set
    * the attr).  It will always be called under a scriptblocker.
    *
    * @param aNamespaceID the namespace of the attr being set
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -602,17 +602,18 @@ ShadowRoot::IsPooledNode(nsIContent* aCo
   return false;
 }
 
 void
 ShadowRoot::AttributeChanged(nsIDocument* aDocument,
                              Element* aElement,
                              int32_t aNameSpaceID,
                              nsIAtom* aAttribute,
-                             int32_t aModType)
+                             int32_t aModType,
+                             const nsAttrValue* aOldValue)
 {
   if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) {
     return;
   }
 
   // Attributes may change insertion point matching, find its new distribution.
   RemoveDistributedNode(aElement);
   DistributeSingleNode(aElement);
--- a/dom/base/nsAttrAndChildArray.cpp
+++ b/dom/base/nsAttrAndChildArray.cpp
@@ -382,24 +382,22 @@ nsAttrAndChildArray::AttrAt(uint32_t aPo
   if (aPos < mapped) {
     return mImpl->mMappedAttrs->AttrAt(aPos);
   }
 
   return &ATTRS(mImpl)[aPos - mapped].mValue;
 }
 
 nsresult
-nsAttrAndChildArray::SetAndTakeAttr(nsIAtom* aLocalName, nsAttrValue& aValue)
+nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue)
 {
   uint32_t i, slotCount = AttrSlotCount();
   for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
     if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
-      ATTRS(mImpl)[i].mValue.Reset();
       ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
-
       return NS_OK;
     }
   }
 
   NS_ENSURE_TRUE(i < ATTRCHILD_ARRAY_MAX_ATTR_COUNT,
                  NS_ERROR_FAILURE);
 
   if (i == slotCount && !AddAttrSlot()) {
@@ -409,22 +407,22 @@ nsAttrAndChildArray::SetAndTakeAttr(nsIA
   new (&ATTRS(mImpl)[i].mName) nsAttrName(aLocalName);
   new (&ATTRS(mImpl)[i].mValue) nsAttrValue();
   ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
 
   return NS_OK;
 }
 
 nsresult
-nsAttrAndChildArray::SetAndTakeAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue)
+nsAttrAndChildArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue)
 {
   int32_t namespaceID = aName->NamespaceID();
   nsIAtom* localName = aName->NameAtom();
   if (namespaceID == kNameSpaceID_None) {
-    return SetAndTakeAttr(localName, aValue);
+    return SetAndSwapAttr(localName, aValue);
   }
 
   uint32_t i, slotCount = AttrSlotCount();
   for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
     if (ATTRS(mImpl)[i].mName.Equals(localName, namespaceID)) {
       ATTRS(mImpl)[i].mName.SetTo(aName);
       ATTRS(mImpl)[i].mValue.Reset();
       ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
--- a/dom/base/nsAttrAndChildArray.h
+++ b/dom/base/nsAttrAndChildArray.h
@@ -83,18 +83,19 @@ public:
   // As above but using a string attr name and always using
   // kNameSpaceID_None.  This is always case-sensitive.
   const nsAttrValue* GetAttr(const nsAString& aName) const;
   // Get an nsAttrValue by qualified name.  Can optionally do
   // ASCII-case-insensitive name matching.
   const nsAttrValue* GetAttr(const nsAString& aName,
                              nsCaseTreatment aCaseSensitive) const;
   const nsAttrValue* AttrAt(uint32_t aPos) const;
-  nsresult SetAndTakeAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
-  nsresult SetAndTakeAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue);
+  // SetAndSwapAttr swaps the current attribute value with aValue.
+  nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
+  nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue);
 
   // Remove the attr at position aPos.  The value of the attr is placed in
   // aValue; any value that was already in aValue is destroyed.
   nsresult RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
 
   // Returns attribute name at given position, *not* out-of-bounds safe
   const nsAttrName* AttrNameAt(uint32_t aPos) const;
 
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -122,16 +122,22 @@ public:
   ~nsAttrValue();
 
   inline const nsAttrValue& operator=(const nsAttrValue& aOther);
 
   static nsresult Init();
   static void Shutdown();
 
   ValueType Type() const;
+  // Returns true when this value is self-contained and does not depend on
+  // the state of its associated element.
+  // Returns false when this value depends on the state of its associated
+  // element and may be invalid if that state has been changed by changes to
+  // that element state outside of attribute setting.
+  inline bool StoresOwnData() const;
 
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
   void SetTo(nsIAtom* aValue);
   void SetTo(int16_t aInt);
   void SetTo(int32_t aInt, const nsAString* aSerialized);
--- a/dom/base/nsAttrValueInlines.h
+++ b/dom/base/nsAttrValueInlines.h
@@ -186,16 +186,26 @@ nsAttrValue::GetIntMarginValue(nsIntMarg
 }
 
 inline bool
 nsAttrValue::IsSVGType(ValueType aType) const
 {
   return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd;
 }
 
+inline bool
+nsAttrValue::StoresOwnData() const
+{
+  if (BaseType() != eOtherBase) {
+    return true;
+  }
+  ValueType t = Type();
+  return t != eCSSStyleRule && !IsSVGType(t);
+}
+
 inline void
 nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
 {
   NS_ASSERTION(!(NS_PTR_TO_INT32(aValue) & ~NS_ATTRVALUE_POINTERVALUE_MASK),
                "pointer not properly aligned, this will crash");
   mBits = reinterpret_cast<intptr_t>(aValue) | aType;
 }
 
--- a/dom/base/nsAttrValueOrString.h
+++ b/dom/base/nsAttrValueOrString.h
@@ -44,16 +44,31 @@ public:
   { }
 
   explicit nsAttrValueOrString(const nsAttrValue* aValue)
     : mAttrValue(aValue)
     , mStringPtr(nullptr)
     , mCheapString(nullptr)
   { }
 
+  void TakeParsedValue(nsAttrValue& aValue)
+  {
+    mStoredAttrValue.SwapValueWith(aValue);
+    mAttrValue = &mStoredAttrValue;
+    mStringPtr = nullptr;
+  }
+  /**
+   * If TakeParsedValue has been called, returns the value that it set.
+   */
+  nsAttrValue* GetStoredAttrValue()
+  {
+    return mAttrValue == &mStoredAttrValue ? &mStoredAttrValue : nullptr;
+  }
+  const nsAttrValue* GetAttrValue() { return mAttrValue; }
+
   /**
    * Returns a reference to the string value of the contents of this object.
    *
    * When this object points to a string or an nsAttrValue of string or atom
    * type this should be fairly cheap. Other nsAttrValue types will be
    * serialized the first time this is called and cached from thereon.
    */
   const nsAString& String() const;
@@ -69,11 +84,12 @@ public:
     }
     return aOther.EqualsAsStrings(*mAttrValue);
   }
 
 protected:
   const nsAttrValue*       mAttrValue;
   mutable const nsAString* mStringPtr;
   mutable nsCheapString    mCheapString;
+  nsAttrValue              mStoredAttrValue;
 };
 
 #endif // nsAttrValueOrString_h___
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -654,17 +654,18 @@ nsIContent*
 nsContentList::Item(uint32_t aIndex)
 {
   return GetElementAt(aIndex);
 }
 
 void
 nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement,
                                 int32_t aNameSpaceID, nsIAtom* aAttribute,
-                                int32_t aModType)
+                                int32_t aModType,
+                                const nsAttrValue* aOldValue)
 {
   NS_PRECONDITION(aElement, "Must have a content node to work with");
   
   if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
       !MayContainRelevantNodes(aElement->GetParentNode()) ||
       !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
     // Either we're already dirty or this notification doesn't affect
     // whether we might match aElement.
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -551,30 +551,24 @@ nsDOMAttributeMap::Count() const
 uint32_t
 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
                              void *aUserArg) const
 {
   return mAttributeCache.EnumerateRead(aFunc, aUserArg);
 }
 
 size_t
-AttrCacheSizeEnumerator(const nsAttrKey& aKey,
-                        const nsRefPtr<Attr>& aValue,
-                        MallocSizeOf aMallocSizeOf,
-                        void* aUserArg)
-{
-  return aMallocSizeOf(aValue.get());
-}
-
-size_t
 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
-  n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
-                                           aMallocSizeOf);
+
+  n += mAttributeCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  for (auto iter = mAttributeCache.ConstIter(); !iter.Done(); iter.Next()) {
+    n += aMallocSizeOf(iter.Data().get());
+  }
 
   // NB: mContent is non-owning and thus not counted.
   return n;
 }
 
 /* virtual */ JSObject*
 nsDOMAttributeMap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -112,17 +112,18 @@ nsMutationReceiver::Disconnect(bool aRem
   }
 }
 
 void
 nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
                                         mozilla::dom::Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsIAtom* aAttribute,
-                                        int32_t aModType)
+                                        int32_t aModType,
+                                        const nsAttrValue* aNewValue)
 {
   if (nsAutoMutationBatch::IsBatching() ||
       !ObservesAttr(RegisterTarget(), aElement, aNameSpaceID, aAttribute)) {
     return;
   }
 
   nsDOMMutationRecord* m =
     Observer()->CurrentRecord(nsGkAtoms::attributes);
--- a/dom/base/nsDOMMutationObserver.h
+++ b/dom/base/nsDOMMutationObserver.h
@@ -369,17 +369,17 @@ public:
 
   virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
                                           mozilla::dom::Element* aElement,
                                           int32_t aNameSpaceID,
                                           nsIAtom* aAttribute) override
   {
     // We can reuse AttributeWillChange implementation.
     AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
-                        nsIDOMMutationEvent::MODIFICATION);
+                        nsIDOMMutationEvent::MODIFICATION, nullptr);
   }
 
 protected:
   nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
 
   nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
   : nsMutationReceiverBase(aRegisterTarget, aParent)
   {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12630,17 +12630,17 @@ nsDocument::DocAddSizeOfExcludingThis(ns
 
   aWindowSizes->mDOMOtherSize +=
     mSVGAttrAnimationRuleProcessor ?
     mSVGAttrAnimationRuleProcessor->DOMSizeOfIncludingThis(
                                       aWindowSizes->mMallocSizeOf) :
     0;
 
   aWindowSizes->mDOMOtherSize +=
-    mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
+    mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 
   aWindowSizes->mDOMOtherSize +=
     mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -151,16 +151,17 @@ nsFrameLoader::nsFrameLoader(Element* aO
   , mRemoteBrowserShown(false)
   , mRemoteFrame(false)
   , mClipSubdocument(true)
   , mClampScrollPosition(true)
   , mObservingOwnerContent(false)
   , mVisible(true)
 {
   ResetPermissionManagerStatus();
+  mRemoteFrame = ShouldUseRemoteProcess();
 }
 
 nsFrameLoader::~nsFrameLoader()
 {
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
   MOZ_RELEASE_ASSERT(mDestroyCalled);
@@ -296,40 +297,36 @@ nsFrameLoader::ReallyStartLoading()
 nsresult
 nsFrameLoader::ReallyStartLoadingInternal()
 {
   NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc());
 
   PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading",
     js::ProfileEntry::Category::OTHER);
 
-  nsresult rv = MaybeCreateDocShell();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (mRemoteFrame) {
+  if (IsRemoteFrame()) {
     if (!mRemoteBrowser && !TryRemoteBrowser()) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
     }
 
-    // Execute pending frame scripts before loading URL
-    EnsureMessageManager();
-
     // FIXME get error codes from child
     mRemoteBrowser->LoadURL(mURIToLoad);
     
     if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
       NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
     }
 
     return NS_OK;
   }
 
+  nsresult rv = MaybeCreateDocShell();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
   NS_ASSERTION(mDocShell,
                "MaybeCreateDocShell succeeded with a null mDocShell");
 
   // Just to be safe, recheck uri.
   rv = CheckURILoad(mURIToLoad);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
@@ -439,40 +436,38 @@ nsFrameLoader::CheckURILoad(nsIURI* aURI
   nsresult rv =
     secMan->CheckLoadURIWithPrincipal(principal, aURI,
                                       nsIScriptSecurityManager::STANDARD);
   if (NS_FAILED(rv)) {
     return rv; // We're not
   }
 
   // Bail out if this is an infinite recursion scenario
-  rv = MaybeCreateDocShell();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (mRemoteFrame) {
+  if (IsRemoteFrame()) {
     return NS_OK;
   }
   return CheckForRecursiveLoad(aURI);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetDocShell(nsIDocShell **aDocShell)
 {
   *aDocShell = nullptr;
   nsresult rv = NS_OK;
 
+  if (IsRemoteFrame()) {
+    return rv;
+  }
+
   // If we have an owner, make sure we have a docshell and return
   // that. If not, we're most likely in the middle of being torn down,
   // then we just return null.
   if (mOwnerContent) {
     nsresult rv = MaybeCreateDocShell();
-    if (NS_FAILED(rv))
-      return rv;
-    if (mRemoteFrame) {
+    if (NS_FAILED(rv)) {
       return rv;
     }
     NS_ASSERTION(mDocShell,
                  "MaybeCreateDocShell succeeded, but null mDocShell");
   }
 
   *aDocShell = mDocShell;
   NS_IF_ADDREF(*aDocShell);
@@ -620,52 +615,52 @@ nsFrameLoader::Show(int32_t marginWidth,
 {
   if (mInShow) {
     return false;
   }
   // Reset mInShow if we exit early.
   AutoResetInShow resetInShow(this);
   mInShow = true;
 
+  ScreenIntSize size = frame->GetSubdocumentSize();
+  if (IsRemoteFrame()) {
+    return ShowRemoteFrame(size, frame);
+  }
+
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return false;
   }
-
-  if (!mRemoteFrame) {
-    if (!mDocShell)
-      return false;
-
-    mDocShell->SetMarginWidth(marginWidth);
-    mDocShell->SetMarginHeight(marginHeight);
-
-    nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
-    if (sc) {
-      sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
-                                         scrollbarPrefX);
-      sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
-                                         scrollbarPrefY);
+  NS_ASSERTION(mDocShell,
+               "MaybeCreateDocShell succeeded, but null mDocShell");
+  if (!mDocShell) {
+    return false;
+  }
+
+  mDocShell->SetMarginWidth(marginWidth);
+  mDocShell->SetMarginHeight(marginHeight);
+
+  nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
+  if (sc) {
+    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
+                                       scrollbarPrefX);
+    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
+                                       scrollbarPrefY);
+  }
+
+  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
+  if (presShell) {
+    // Ensure root scroll frame is reflowed in case scroll preferences or
+    // margins have changed
+    nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+    if (rootScrollFrame) {
+      presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
+                                  NS_FRAME_IS_DIRTY);
     }
-
-    nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
-    if (presShell) {
-      // Ensure root scroll frame is reflowed in case scroll preferences or
-      // margins have changed
-      nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
-      if (rootScrollFrame) {
-        presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
-                                    NS_FRAME_IS_DIRTY);
-      }
-      return true;
-    }
-  }
-
-  ScreenIntSize size = frame->GetSubdocumentSize();
-  if (mRemoteFrame) {
-    return ShowRemoteFrame(size, frame);
+    return true;
   }
 
   nsView* view = frame->EnsureInnerView();
   if (!view)
     return false;
 
   nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell);
   NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
@@ -677,17 +672,17 @@ nsFrameLoader::Show(int32_t marginWidth,
   baseWindow->Create();
   baseWindow->SetVisibility(true);
   NS_ENSURE_TRUE(mDocShell, false);
 
   // Trigger editor re-initialization if midas is turned on in the
   // sub-document. This shouldn't be necessary, but given the way our
   // editor works, it is. See
   // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
-  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
+  presShell = mDocShell->GetPresShell();
   if (presShell) {
     nsCOMPtr<nsIDOMHTMLDocument> doc =
       do_QueryInterface(presShell->GetDocument());
 
     if (doc) {
       nsAutoString designMode;
       doc->GetDesignMode(designMode);
 
@@ -724,17 +719,17 @@ nsFrameLoader::Show(int32_t marginWidth,
   return true;
 }
 
 void
 nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
                               uint32_t aMarginHeight)
 {
   // We assume that the margins are always zero for remote frames.
-  if (mRemoteFrame)
+  if (IsRemoteFrame())
     return;
 
   // If there's no docshell, we're probably not up and running yet.
   // nsFrameLoader::Show() will take care of setting the right
   // margins.
   if (!mDocShell)
     return;
 
@@ -749,17 +744,17 @@ nsFrameLoader::MarginsChanged(uint32_t a
   if (presContext)
     presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
 }
 
 bool
 nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
                                nsSubDocumentFrame *aFrame)
 {
-  NS_ASSERTION(mRemoteFrame, "ShowRemote only makes sense on remote frames.");
+  NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames.");
 
   if (!mRemoteBrowser && !TryRemoteBrowser()) {
     NS_ERROR("Couldn't create child process.");
     return false;
   }
 
   // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   // cross-process layers; need to figure out what behavior we really
@@ -785,19 +780,16 @@ nsFrameLoader::ShowRemoteFrame(const Scr
       if (windowRoot) {
         nsPIDOMWindow* topWin = windowRoot->GetWindow();
         parentIsActive = topWin && topWin->IsActive();
       }
     }
     mRemoteBrowser->Show(size, parentIsActive);
     mRemoteBrowserShown = true;
 
-    EnsureMessageManager();
-
-    InitializeBrowserAPI();
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                           "remote-browser-shown", nullptr);
     }
   } else {
     nsIntRect dimensions;
     NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
@@ -962,21 +954,21 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
                                    nsRefPtr<nsFrameLoader>& aFirstToSwap,
                                    nsRefPtr<nsFrameLoader>& aSecondToSwap)
 {
   NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
                   (aFirstToSwap == aOther && aSecondToSwap == this),
                   "Swapping some sort of random loaders?");
   NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
 
-  if (mRemoteFrame && aOther->mRemoteFrame) {
+  if (IsRemoteFrame() && aOther->IsRemoteFrame()) {
     return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
   }
 
-  if (mRemoteFrame || aOther->mRemoteFrame) {
+  if (IsRemoteFrame() || aOther->IsRemoteFrame()) {
     NS_WARNING("Swapping remote and non-remote frames is not currently supported");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   Element* ourContent = mOwnerContent;
   Element* otherContent = aOther->mOwnerContent;
 
   if (!ourContent || !otherContent) {
@@ -1636,32 +1628,37 @@ nsFrameLoader::ShouldUseRemoteProcess()
   return (OwnerIsBrowserOrAppFrame() ||
           mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) &&
          mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                     nsGkAtoms::Remote,
                                     nsGkAtoms::_true,
                                     eCaseMatters);
 }
 
+bool
+nsFrameLoader::IsRemoteFrame()
+{
+  if (mRemoteFrame) {
+    MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell");
+    return true;
+  }
+  return false;
+}
+
 nsresult
 nsFrameLoader::MaybeCreateDocShell()
 {
   if (mDocShell) {
     return NS_OK;
   }
-  if (mRemoteFrame) {
+  if (IsRemoteFrame()) {
     return NS_OK;
   }
   NS_ENSURE_STATE(!mDestroyCalled);
 
-  if (ShouldUseRemoteProcess()) {
-    mRemoteFrame = true;
-    return NS_OK;
-  }
-
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->OwnerDoc();
   if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) {
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -1784,18 +1781,16 @@ nsFrameLoader::MaybeCreateDocShell()
     nsCOMPtr<nsISHistory> sessionHistory =
       do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
     webNav->SetSessionHistory(sessionHistory);
   }
 
-  EnsureMessageManager();
-
   if (OwnerIsAppFrame()) {
     // You can't be both an app and a browser frame.
     MOZ_ASSERT(!OwnerIsBrowserFrame());
 
     nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
     MOZ_ASSERT(ownApp);
     uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (ownApp) {
@@ -1813,28 +1808,17 @@ nsFrameLoader::MaybeCreateDocShell()
     uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (containingApp) {
       NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
                         NS_ERROR_FAILURE);
     }
     mDocShell->SetIsBrowserInsideApp(containingAppId);
   }
 
-  InitializeBrowserAPI();
-  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-  if (os) {
-    os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                        "inprocess-browser-shown", nullptr);
-  }
-
-  if (OwnerIsBrowserOrAppFrame() && mMessageManager) {
-    mMessageManager->LoadFrameScript(
-      NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
-      /* allowDelayedLoad = */ true,
-      /* aRunInGlobalScope */ true);
+  if (OwnerIsBrowserOrAppFrame()) {
     // For inproc frames, set the docshell properties.
     nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
     nsAutoString name;
     if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
       item->SetName(name);
     }
     mDocShell->SetFullscreenAllowed(
       mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
@@ -1851,16 +1835,25 @@ nsFrameLoader::MaybeCreateDocShell()
           nullptr);
       } else {
         nsCOMPtr<nsILoadContext> context = do_GetInterface(mDocShell);
         context->SetUsePrivateBrowsing(true);
       }
     }
   }
 
+  ReallyLoadFrameScripts();
+  InitializeBrowserAPI();
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
+                        "inprocess-browser-shown", nullptr);
+  }
+
   return NS_OK;
 }
 
 void
 nsFrameLoader::GetURL(nsString& aURI)
 {
   aURI.Truncate();
 
@@ -1871,23 +1864,26 @@ nsFrameLoader::GetURL(nsString& aURI)
   }
 }
 
 nsresult
 nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
 {
   nsresult rv;
 
+  MOZ_ASSERT(!IsRemoteFrame(),
+             "Shouldn't call CheckForRecursiveLoad on remote frames.");
+
   mDepthTooGreat = false;
   rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
-  NS_ASSERTION(!mRemoteFrame,
-               "Shouldn't call CheckForRecursiveLoad on remote frames.");
+  NS_ASSERTION(mDocShell,
+               "MaybeCreateDocShell succeeded, but null mDocShell");
   if (!mDocShell) {
     return NS_ERROR_FAILURE;
   }
 
   // Check that we're still in the docshell tree.
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
   NS_WARN_IF_FALSE(treeOwner,
@@ -1999,17 +1995,17 @@ nsFrameLoader::GetWindowDimensions(nsInt
   treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y);
   treeOwnerAsWin->GetSize(&aRect.width, &aRect.height);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
 {
-  if (mRemoteFrame) {
+  if (IsRemoteFrame()) {
     if (mRemoteBrowser) {
       ScreenIntSize size = aIFrame->GetSubdocumentSize();
       nsIntRect dimensions;
       NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
       mRemoteBrowser->UpdateDimensions(dimensions, size);
     }
     return NS_OK;
   }
@@ -2227,17 +2223,20 @@ nsFrameLoader::TryRemoteBrowser()
 
   if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                  nsGkAtoms::mozpasspointerevents,
                                  nsGkAtoms::_true,
                                  eCaseMatters)) {
     unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
   }
 
-  return true;
+  ReallyLoadFrameScripts();
+  InitializeBrowserAPI();
+
+ return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser() const
 {
   return mRemoteBrowser;
 }
 
@@ -2464,39 +2463,30 @@ nsFrameLoader::GetMessageManager(nsIMess
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
 
-  nsresult rv = MaybeCreateDocShell();
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (mMessageManager) {
+    return NS_OK;
   }
 
   if (!mIsTopLevelContent &&
       !OwnerIsBrowserOrAppFrame() &&
-      !mRemoteFrame &&
+      !IsRemoteFrame() &&
       !(mOwnerContent->IsXULElement() &&
         mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::forcemessagemanager,
                                    nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
-  bool useRemoteProcess = ShouldUseRemoteProcess();
-  if (mMessageManager) {
-    if (useRemoteProcess && mRemoteBrowser) {
-      mMessageManager->InitWithCallback(this);
-    }
-    return NS_OK;
-  }
-
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
     do_QueryInterface(GetOwnerDoc()->GetWindow());
   nsCOMPtr<nsIMessageBroadcaster> parentManager;
 
   if (chromeWindow) {
     nsAutoString messagemanagergroup;
     if (mOwnerContent->IsXULElement() &&
         mOwnerContent->GetAttr(kNameSpaceID_None,
@@ -2505,28 +2495,43 @@ nsFrameLoader::EnsureMessageManager()
       chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager));
     }
 
     if (!parentManager) {
       chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
     }
   }
 
-  if (useRemoteProcess) {
-    mMessageManager = new nsFrameMessageManager(mRemoteBrowser ? this : nullptr,
-                                                static_cast<nsFrameMessageManager*>(parentManager.get()),
-                                                MM_CHROME);
-  } else {
-    mMessageManager = new nsFrameMessageManager(nullptr,
-                                                static_cast<nsFrameMessageManager*>(parentManager.get()),
-                                                MM_CHROME);
-
+  mMessageManager = new nsFrameMessageManager(nullptr,
+                                              static_cast<nsFrameMessageManager*>(parentManager.get()),
+                                              MM_CHROME);
+  if (!IsRemoteFrame()) {
+    nsresult rv = MaybeCreateDocShell();
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    NS_ASSERTION(mDocShell,
+                 "MaybeCreateDocShell succeeded, but null mDocShell");
+    if (!mDocShell) {
+      return NS_ERROR_FAILURE;
+    }
     mChildMessageManager =
       new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager);
-    // Force pending frame scripts to be loaded.
+  }
+  return NS_OK;
+}
+
+nsresult
+nsFrameLoader::ReallyLoadFrameScripts()
+{
+  nsresult rv = EnsureMessageManager();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (mMessageManager) {
     mMessageManager->InitWithCallback(this);
   }
   return NS_OK;
 }
 
 EventTarget*
 nsFrameLoader::GetTabChildGlobalAsEventTarget()
 {
@@ -2550,16 +2555,18 @@ nsFrameLoader::GetChildID(uint64_t* aChi
 
 void
 nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
 {
   MOZ_ASSERT(!mRemoteBrowser);
   mRemoteFrame = true;
   mRemoteBrowser = TabParent::GetFrom(aTabParent);
   mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0;
+  ReallyLoadFrameScripts();
+  InitializeBrowserAPI();
   ShowRemoteFrame(ScreenIntSize(0, 0));
 }
 
 void
 nsFrameLoader::SetDetachedSubdocView(nsView* aDetachedViews,
                                      nsIDocument* aContainerDoc)
 {
   mDetachedSubdocViews = aDetachedViews;
@@ -2585,17 +2592,18 @@ nsFrameLoader::ApplySandboxFlags(uint32_
   }
 }
 
 /* virtual */ void
 nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
                                 mozilla::dom::Element* aElement,
                                 int32_t      aNameSpaceID,
                                 nsIAtom*     aAttribute,
-                                int32_t      aModType)
+                                int32_t      aModType,
+                                const nsAttrValue* aOldValue)
 {
   MOZ_ASSERT(mObservingOwnerContent);
   // TODO: Implement ContentShellAdded for remote browsers (bug 658304)
   MOZ_ASSERT(!mRemoteBrowser);
 
   if (aNameSpaceID != kNameSpaceID_None || aAttribute != TypeAttrName()) {
     return;
   }
@@ -2815,13 +2823,27 @@ nsFrameLoader::GetLoadContext(nsILoadCon
   }
   loadContext.forget(aLoadContext);
   return NS_OK;
 }
 
 void
 nsFrameLoader::InitializeBrowserAPI()
 {
-  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
-  if (browserFrame) {
-    browserFrame->InitializeBrowserAPI();
+  if (OwnerIsBrowserOrAppFrame()) {
+    if (!IsRemoteFrame()) {
+      nsresult rv = EnsureMessageManager();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+      if (mMessageManager) {
+        mMessageManager->LoadFrameScript(
+          NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
+          /* allowDelayedLoad = */ true,
+          /* aRunInGlobalScope */ true);
+      }
+    }
+    nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
+    if (browserFrame) {
+      browserFrame->InitializeBrowserAPI();
+    }
   }
 }
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -218,16 +218,21 @@ private:
   nsFrameLoader(mozilla::dom::Element* aOwner, bool aNetworkCreated);
   ~nsFrameLoader();
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
+   * Return true if the frame is a remote frame. Return false otherwise
+   */
+  bool IsRemoteFrame();
+
+  /**
    * Is this a frameloader for a bona fide <iframe mozbrowser> or
    * <iframe mozapp>?  (I.e., does the frame return true for
    * nsIMozBrowserFrame::GetReallyIsBrowserOrApp()?)
    */
   bool OwnerIsBrowserOrAppFrame();
 
   /**
    * Is this a frameloader for a bona fide <iframe mozwidget>?  (I.e., does the
@@ -265,16 +270,17 @@ private:
   already_AddRefed<mozIApplication> GetContainingApp();
 
   /**
    * If we are an IPC frame, set mRemoteFrame. Otherwise, create and
    * initialize mDocShell.
    */
   nsresult MaybeCreateDocShell();
   nsresult EnsureMessageManager();
+  nsresult ReallyLoadFrameScripts();
 
   // Updates the subdocument position and size. This gets called only
   // when we have our own in-process DocShell.
   void UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);
   nsresult CheckURILoad(nsIURI* aURI);
   void FireErrorEvent();
   nsresult ReallyStartLoadingInternal();
 
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1427,16 +1427,24 @@ nsFrameMessageManager::GetInitialProcess
     if (!obj) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     mInitialProcessData.setObject(*obj);
     init.setObject(*obj);
   }
 
+  if (!mChrome && XRE_IsParentProcess()) {
+    // This is the cpmm in the parent process. We should use the same object as the ppmm.
+    nsCOMPtr<nsIGlobalProcessScriptLoader> ppmm =
+      do_GetService("@mozilla.org/parentprocessmessagemanager;1");
+    ppmm->GetInitialProcessData(aCx, &init);
+    mInitialProcessData = init;
+  }
+
   if (!JS_WrapValue(aCx, &init)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   aResult.set(init);
   return NS_OK;
 }
 
 namespace {
@@ -2159,17 +2167,16 @@ NS_NewChildProcessMessageManager(nsISync
   nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   nsFrameMessageManager::SetChildProcessManager(mm);
   nsRefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
   NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
   global.forget(aResult);
   return NS_OK;
-
 }
 
 static PLDHashOperator
 CycleCollectorMarkListeners(const nsAString& aKey,
                             nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
                             void* aData)
 {
   uint32_t count = aListeners->Length();
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13593,22 +13593,18 @@ nsGlobalWindow::AddSizeOfIncludingThis(n
     }
   }
 
   if (mNavigator) {
     aWindowSizes->mDOMOtherSize +=
       mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
   }
 
-  // The things pointed to by the entries will be measured below, so we
-  // use nullptr for the callback here.
   aWindowSizes->mDOMEventTargetsSize +=
-    mEventTargetObjects.SizeOfExcludingThis(nullptr,
-                                            aWindowSizes->mMallocSizeOf);
-
+    mEventTargetObjects.ShallowSizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 
   for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
     DOMEventTargetHelper* et = iter.Get()->GetKey();
     if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
       aWindowSizes->mDOMEventTargetsSize +=
         iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf);
     }
     if (EventListenerManager* elm = et->GetExistingListenerManager()) {
--- a/dom/base/nsIMutationObserver.h
+++ b/dom/base/nsIMutationObserver.h
@@ -4,30 +4,31 @@
  * 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 nsIMutationObserver_h
 #define nsIMutationObserver_h
 
 #include "nsISupports.h"
 
+class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIDocument;
 class nsINode;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IMUTATION_OBSERVER_IID \
-{ 0x16fe5e3e, 0xeadc, 0x4312, \
-  { 0x9d, 0x44, 0xb6, 0xbe, 0xdd, 0x6b, 0x54, 0x74 } }
+{ 0xdd74f0cc, 0x2849, 0x4d05, \
+  { 0x9c, 0xe3, 0xb0, 0x95, 0x3e, 0xc2, 0xfd, 0x44 } }
 
 /**
  * Information details about a characterdata change.  Basically, we
  * view all changes as replacements of a length of text at some offset
  * with some other text (of possibly some other length).
  */
 struct CharacterDataChangeInfo
 {
@@ -152,51 +153,57 @@ public:
    *
    * @param aDocument    The owner-document of aContent. Can be null.
    * @param aContent     The element whose attribute will change
    * @param aNameSpaceID The namespace id of the changing attribute
    * @param aAttribute   The name of the changing attribute
    * @param aModType     Whether or not the attribute will be added, changed, or
    *                     removed. The constants are defined in
    *                     nsIDOMMutationEvent.h.
+   * @param aNewValue    The new value, IF it has been preparsed by
+   *                     BeforeSetAttr, otherwise null.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
   virtual void AttributeWillChange(nsIDocument* aDocument,
                                    mozilla::dom::Element* aElement,
                                    int32_t      aNameSpaceID,
                                    nsIAtom*     aAttribute,
-                                   int32_t      aModType) = 0;
+                                   int32_t      aModType,
+                                   const nsAttrValue* aNewValue) = 0;
 
   /**
    * Notification that an attribute of an element has changed.
    *
    * @param aDocument    The owner-document of aContent. Can be null.
    * @param aElement     The element whose attribute changed
    * @param aNameSpaceID The namespace id of the changed attribute
    * @param aAttribute   The name of the changed attribute
    * @param aModType     Whether or not the attribute was added, changed, or
    *                     removed. The constants are defined in
    *                     nsIDOMMutationEvent.h.
+   * @param aOldValue    The old value, if either the old value or the new
+   *                     value are StoresOwnData() (or absent); null otherwise.
    *
    * @note Callers of this method might not hold a strong reference to the
    *       observer.  The observer is responsible for making sure it stays
    *       alive for the duration of the call as needed.  The observer may
    *       assume that this call will happen when there are script blockers on
    *       the stack.
    */
   virtual void AttributeChanged(nsIDocument* aDocument,
                                 mozilla::dom::Element* aElement,
                                 int32_t      aNameSpaceID,
                                 nsIAtom*     aAttribute,
-                                int32_t      aModType) = 0;
+                                int32_t      aModType,
+                                const nsAttrValue* aOldValue) = 0;
 
   /**
    * Notification that an attribute of an element has been
    * set to the value it already had.
    *
    * @param aDocument    The owner-document of aContent.
    * @param aElement     The element whose attribute changed
    * @param aNameSpaceID The namespace id of the changed attribute
@@ -331,24 +338,26 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutatio
                                       nsIContent* aContent,                  \
                                       CharacterDataChangeInfo* aInfo) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE                      \
     virtual void AttributeWillChange(nsIDocument* aDocument,                 \
                                      mozilla::dom::Element* aElement,        \
                                      int32_t aNameSpaceID,                   \
                                      nsIAtom* aAttribute,                    \
-                                     int32_t aModType) override;
+                                     int32_t aModType,                       \
+                                     const nsAttrValue* aNewValue) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED                         \
     virtual void AttributeChanged(nsIDocument* aDocument,                    \
                                   mozilla::dom::Element* aElement,           \
                                   int32_t aNameSpaceID,                      \
                                   nsIAtom* aAttribute,                       \
-                                  int32_t aModType) override;
+                                  int32_t aModType,                          \
+                                  const nsAttrValue* aOldValue) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED                          \
     virtual void ContentAppended(nsIDocument* aDocument,                     \
                                  nsIContent* aContainer,                     \
                                  nsIContent* aFirstNewContent,               \
                                  int32_t aNewIndexInContainer) override;
 
 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED                          \
@@ -400,25 +409,27 @@ void                                    
                              CharacterDataChangeInfo* aInfo)              \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::AttributeWillChange(nsIDocument* aDocument,                       \
                             mozilla::dom::Element* aElement,              \
                             int32_t aNameSpaceID,                         \
                             nsIAtom* aAttribute,                          \
-                            int32_t aModType)                             \
+                            int32_t aModType,                             \
+                            const nsAttrValue* aNewValue)                 \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::AttributeChanged(nsIDocument* aDocument,                          \
                          mozilla::dom::Element* aElement,                 \
                          int32_t aNameSpaceID,                            \
                          nsIAtom* aAttribute,                             \
-                         int32_t aModType)                                \
+                         int32_t aModType,                                \
+                         const nsAttrValue* aOldValue)                    \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::ContentAppended(nsIDocument* aDocument,                           \
                         nsIContent* aContainer,                           \
                         nsIContent* aFirstNewContent,                     \
                         int32_t aNewIndexInContainer)                     \
 {                                                                         \
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -118,34 +118,36 @@ nsNodeUtils::CharacterDataChanged(nsICon
   IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
                              (doc, aContent, aInfo));
 }
 
 void
 nsNodeUtils::AttributeWillChange(Element* aElement,
                                  int32_t aNameSpaceID,
                                  nsIAtom* aAttribute,
-                                 int32_t aModType)
+                                 int32_t aModType,
+                                 const nsAttrValue* aNewValue)
 {
   nsIDocument* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
                              (doc, aElement, aNameSpaceID, aAttribute,
-                              aModType));
+                              aModType, aNewValue));
 }
 
 void
 nsNodeUtils::AttributeChanged(Element* aElement,
                               int32_t aNameSpaceID,
                               nsIAtom* aAttribute,
-                              int32_t aModType)
+                              int32_t aModType,
+                              const nsAttrValue* aOldValue)
 {
   nsIDocument* doc = aElement->OwnerDoc();
   IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement,
                              (doc, aElement, aNameSpaceID, aAttribute,
-                              aModType));
+                              aModType, aOldValue));
 }
 
 void
 nsNodeUtils::AttributeSetToCurrentValue(Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsIAtom* aAttribute)
 {
   nsIDocument* doc = aElement->OwnerDoc();
--- a/dom/base/nsNodeUtils.h
+++ b/dom/base/nsNodeUtils.h
@@ -43,35 +43,42 @@ public:
                                    CharacterDataChangeInfo* aInfo);
 
   /**
    * Send AttributeWillChange notifications to nsIMutationObservers.
    * @param aElement      Element whose data will change
    * @param aNameSpaceID  Namespace of changing attribute
    * @param aAttribute    Local-name of changing attribute
    * @param aModType      Type of change (add/change/removal)
+   * @param aNewValue     The parsed new value, but only if BeforeSetAttr
+   *                      preparsed it!!!
    * @see nsIMutationObserver::AttributeWillChange
    */
   static void AttributeWillChange(mozilla::dom::Element* aElement,
                                   int32_t aNameSpaceID,
                                   nsIAtom* aAttribute,
-                                  int32_t aModType);
+                                  int32_t aModType,
+                                  const nsAttrValue* aNewValue);
 
   /**
    * Send AttributeChanged notifications to nsIMutationObservers.
    * @param aElement      Element whose data changed
    * @param aNameSpaceID  Namespace of changed attribute
    * @param aAttribute    Local-name of changed attribute
    * @param aModType      Type of change (add/change/removal)
+   * @param aOldValue     If the old value was StoresOwnData() (or absent),
+   *                      that value, otherwise null
    * @see nsIMutationObserver::AttributeChanged
    */
   static void AttributeChanged(mozilla::dom::Element* aElement,
                                int32_t aNameSpaceID,
                                nsIAtom* aAttribute,
-                               int32_t aModType);
+                               int32_t aModType,
+                               const nsAttrValue* aOldValue);
+
   /**
    * Send AttributeSetToCurrentValue notifications to nsIMutationObservers.
    * @param aElement      Element whose data changed
    * @param aNameSpaceID  Namespace of the attribute
    * @param aAttribute    Local-name of the attribute
    * @see nsIMutationObserver::AttributeSetToCurrentValue
    */
   static void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
--- a/dom/base/nsScriptElement.cpp
+++ b/dom/base/nsScriptElement.cpp
@@ -78,17 +78,18 @@ nsScriptElement::CharacterDataChanged(ns
   MaybeProcessScript();
 }
 
 void
 nsScriptElement::AttributeChanged(nsIDocument* aDocument,
                                   Element* aElement,
                                   int32_t aNameSpaceID,
                                   nsIAtom* aAttribute,
-                                  int32_t aModType)
+                                  int32_t aModType,
+                                  const nsAttrValue* aOldValue)
 {
   MaybeProcessScript();
 }
 
 void
 nsScriptElement::ContentAppended(nsIDocument* aDocument,
                                  nsIContent* aContainer,
                                  nsIContent* aFirstNewContent,
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -133,17 +133,17 @@ nsStyledElementNotElementCSSInlineStyle:
   
   if (oldVal && oldVal->Type() != nsAttrValue::eCSSStyleRule) {
     nsAttrValue attrValue;
     nsAutoString stringValue;
     oldVal->ToString(stringValue);
     ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc);
     // Don't bother going through SetInlineStyleRule, we don't want to fire off
     // mutation events or document notifications anyway
-    nsresult rv = mAttrsAndChildren.SetAndTakeAttr(nsGkAtoms::style, attrValue);
+    nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   
   return NS_OK;
 }
 
 void
 nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue,
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -263,17 +263,18 @@ nsAttributeTextNode::UnbindFromTree(bool
   nsTextNode::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
 nsAttributeTextNode::AttributeChanged(nsIDocument* aDocument,
                                       Element* aElement,
                                       int32_t aNameSpaceID,
                                       nsIAtom* aAttribute,
-                                      int32_t aModType)
+                                      int32_t aModType,
+                                      const nsAttrValue* aOldValue)
 {
   if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName &&
       aElement == mGrandparent) {
     // Since UpdateText notifies, do it when it's safe to run script.  Note
     // that if we get unbound while the event is up that's ok -- we'll just
     // have no grandparent when it fires, and will do nothing.
     void (nsAttributeTextNode::*update)() = &nsAttributeTextNode::UpdateText;
     nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, update);
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -80,17 +80,17 @@ function CameraTestSuite() {
   this.rejectTakePicture = this._rejectTakePicture.bind(this);
   this.rejectStartRecording = this._rejectStartRecording.bind(this);
   this.rejectStopRecording = this._rejectStopRecording.bind(this);
   this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
 
   var self = this;
   this._window.addEventListener('beforeunload', function() {
     if (isDefinedObj(self.viewfinder)) {
-      self.viewfinder.mozSrcObject = null;
+      self.viewfinder.srcObject = null;
     }
 
     self.hw = null;
     if (isDefinedObj(self.camera)) {
       ok(false, 'window unload triggered camera release instead of test completion');
       self.camera.release();
       self.camera = null;
     }
@@ -206,17 +206,17 @@ CameraTestSuite.prototype = {
 
       function resetLowMem() {
         return self.setLowMemoryPlatform(false);
       }
 
       function postTest(pass) {
         ok(pass, test.name + ' finished');
         var camera = self.camera;
-        self.viewfinder.mozSrcObject = null;
+        self.viewfinder.srcObject = null;
         self.camera = null;
 
         if (!isDefinedObj(camera)) {
           return Promise.resolve();
         }
 
         function handler(e) {
           ok(typeof(e) === 'undefined', 'camera released');
@@ -320,17 +320,17 @@ CameraTestSuite.prototype = {
         }
       }
 
       if (!isDefinedObj(self.viewfinder)) {
         reject(new Error('no viewfinder object'));
         return;
       }
 
-      self.viewfinder.mozSrcObject = self.camera;
+      self.viewfinder.srcObject = self.camera;
       self.viewfinder.play();
       self.camera.addEventListener('previewstatechange', onPreviewStateChange);
     });
   },
 
   /* Returns a promise which resolves when the camera hardware
      has received a push parameters request. This is useful
      when setting camera parameters from the application and
--- a/dom/camera/test/test_bug1104913.html
+++ b/dom/camera/test/test_bug1104913.html
@@ -37,17 +37,17 @@ var Camera = {
     return document.getElementById('viewfinder');
   },
 
   start: function test_start() {
     function getCamera_onSuccess(d) {
       var camera = d.camera;
       var cfg = d.configuration;
       Camera.cameraObj = camera;
-      Camera.viewfinder.mozSrcObject = camera;
+      Camera.viewfinder.srcObject = camera;
       Camera.viewfinder.play();
 
       // Check the default configuration
       ok(cfg.mode === config.mode, "Initial mode = " + cfg.mode);
       ok(cfg.previewSize.width === config.previewSize.width &&
          cfg.previewSize.height === config.previewSize.height,
          "Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
       ok(cfg.pictureSize.width === config.pictureSize.width &&
@@ -61,17 +61,17 @@ var Camera = {
 
     navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
+  Camera.viewfinder.srcObject = null;
   if (Camera.cameraObj) {
     Camera.cameraObj.release();
     Camera.cameraObj = null;
   }
 });
 
 Camera.start();
 
--- a/dom/camera/test/test_camera.html
+++ b/dom/camera/test/test_camera.html
@@ -227,30 +227,30 @@ var Camera = {
   },
   setUp: function setup_tests() {
     function onSuccess(d) {
       Camera.cameraObj = d.camera;
       Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
       Camera.cameraObj.addEventListener('configurationchanged', Camera.onConfigChange);
       Camera.cameraObj.addEventListener('shutter', Camera.shutter);
       Camera.cameraObj.addEventListener('picture', Camera.takePictureEvent.bind(Camera));
-      Camera.viewfinder.mozSrcObject = d.camera;
+      Camera.viewfinder.srcObject = d.camera;
       Camera.viewfinder.play();
       SimpleTest.expectAssertions(0);
       ok(true, "Camera Control object has been successfully initialized");
       Camera.cameraObj.setConfiguration(options).then(Camera.onConfigChange, onError);
     };
     navigator.mozCameras.getCamera(whichCamera, null).then(onSuccess, onError);
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
+  Camera.viewfinder.srcObject = null;
   Camera.cameraObj.release();
   Camera.cameraObj = null;
 });
 
 Camera.setUp();
 
 </script>
 </body>
--- a/dom/camera/test/test_camera_2.html
+++ b/dom/camera/test/test_camera_2.html
@@ -174,28 +174,28 @@ var Camera = {
       ok(config.mode === options.mode, "configuration mode = " + config.mode);
       ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
       ok(config.previewSize.width === options.previewSize.width &&
         config.previewSize.height === options.previewSize.height,
         "preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
       Camera.cameraObj = d.camera;
       Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
       Camera.cameraObj.addEventListener('shutter', Camera.shutter);
-      Camera.viewfinder.mozSrcObject = d.camera;
+      Camera.viewfinder.srcObject = d.camera;
       Camera.viewfinder.play();
       SimpleTest.expectAssertions(0);
     };
     navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
+  Camera.viewfinder.srcObject = null;
   Camera.cameraObj.release();
   Camera.cameraObj = null;
 });
 
 Camera.setUp();
 
 </script>
 </body>
--- a/dom/camera/test/test_camera_3.html
+++ b/dom/camera/test/test_camera_3.html
@@ -49,27 +49,27 @@ var Camera = {
   },
   release: function release() {
     cameraObj = null;
   },
   start: function run_test() {
     function onSuccess(d) {
       Camera.cameraObj = d.camera;
       Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
-      Camera.viewfinder.mozSrcObject = d.camera;
+      Camera.viewfinder.srcObject = d.camera;
       Camera.viewfinder.play();
     };
     navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
+  Camera.viewfinder.srcObject = null;
   Camera.cameraObj.release();
   Camera.cameraObj = null;
 });
 
 Camera.start();
 
 </script>
 </body>
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -352,25 +352,20 @@ public:
     void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
     bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
     void BindVertexArray(WebGLVertexArrayObject* vertexArray);
 */
 
 private:
     WebGL2Context();
 
-    JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname) override;
+    virtual bool IsTexParamValid(GLenum pname) const override;
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
-    bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
-    bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
-                                GLsizei width, GLsizei height, GLsizei depth,
-                                const char* info);
-
     // CreateVertexArrayImpl is assumed to be infallible.
     virtual WebGLVertexArray* CreateVertexArrayImpl() override;
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -5,490 +5,131 @@
 
 #include "GLContext.h"
 #include "WebGL2Context.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
-bool
-WebGL2Context::ValidateSizedInternalFormat(GLenum internalformat, const char* info)
+void
+WebGL2Context::TexStorage2D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height)
 {
-    switch (internalformat) {
-        // Sized Internal Formats
-        // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
-    case LOCAL_GL_R8:
-    case LOCAL_GL_R8_SNORM:
-    case LOCAL_GL_R16F:
-    case LOCAL_GL_R32F:
-    case LOCAL_GL_R8UI:
-    case LOCAL_GL_R8I:
-    case LOCAL_GL_R16UI:
-    case LOCAL_GL_R16I:
-    case LOCAL_GL_R32UI:
-    case LOCAL_GL_R32I:
-    case LOCAL_GL_RG8:
-    case LOCAL_GL_RG8_SNORM:
-    case LOCAL_GL_RG16F:
-    case LOCAL_GL_RG32F:
-    case LOCAL_GL_RG8UI:
-    case LOCAL_GL_RG8I:
-    case LOCAL_GL_RG16UI:
-    case LOCAL_GL_RG16I:
-    case LOCAL_GL_RG32UI:
-    case LOCAL_GL_RG32I:
-    case LOCAL_GL_RGB8:
-    case LOCAL_GL_SRGB8:
-    case LOCAL_GL_RGB565:
-    case LOCAL_GL_RGB8_SNORM:
-    case LOCAL_GL_R11F_G11F_B10F:
-    case LOCAL_GL_RGB9_E5:
-    case LOCAL_GL_RGB16F:
-    case LOCAL_GL_RGB32F:
-    case LOCAL_GL_RGB8UI:
-    case LOCAL_GL_RGB8I:
-    case LOCAL_GL_RGB16UI:
-    case LOCAL_GL_RGB16I:
-    case LOCAL_GL_RGB32UI:
-    case LOCAL_GL_RGB32I:
-    case LOCAL_GL_RGBA8:
-    case LOCAL_GL_SRGB8_ALPHA8:
-    case LOCAL_GL_RGBA8_SNORM:
-    case LOCAL_GL_RGB5_A1:
-    case LOCAL_GL_RGBA4:
-    case LOCAL_GL_RGB10_A2:
-    case LOCAL_GL_RGBA16F:
-    case LOCAL_GL_RGBA32F:
-    case LOCAL_GL_RGBA8UI:
-    case LOCAL_GL_RGBA8I:
-    case LOCAL_GL_RGB10_A2UI:
-    case LOCAL_GL_RGBA16UI:
-    case LOCAL_GL_RGBA16I:
-    case LOCAL_GL_RGBA32I:
-    case LOCAL_GL_RGBA32UI:
-    case LOCAL_GL_DEPTH_COMPONENT16:
-    case LOCAL_GL_DEPTH_COMPONENT24:
-    case LOCAL_GL_DEPTH_COMPONENT32F:
-    case LOCAL_GL_DEPTH24_STENCIL8:
-    case LOCAL_GL_DEPTH32F_STENCIL8:
-        return true;
-    }
-
-    if (IsCompressedTextureFormat(internalformat))
-        return true;
-
-    nsCString name;
-    EnumName(internalformat, &name);
-    ErrorInvalidEnum("%s: invalid internal format %s", info, name.get());
-
-    return false;
-}
-
-/** Validates parameters to texStorage{2D,3D} */
-bool
-WebGL2Context::ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
-                                      GLsizei width, GLsizei height, GLsizei depth,
-                                      const char* info)
-{
-    // GL_INVALID_OPERATION is generated if the default texture object is curently bound to target.
-    WebGLTexture* tex = ActiveBoundTextureForTarget(target);
-    if (!tex) {
-        ErrorInvalidOperation("%s: no texture is bound to target %s", info, EnumName(target));
-        return false;
-    }
-
-    // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has
-    // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE.
-    if (tex->IsImmutable()) {
-        ErrorInvalidOperation("%s: texture bound to target %s is already immutable", info, EnumName(target));
-        return false;
-    }
-
-    // GL_INVALID_ENUM is generated if internalformat is not a valid sized internal format.
-    if (!ValidateSizedInternalFormat(internalformat, info))
-        return false;
-
-    // GL_INVALID_VALUE is generated if width, height or levels are less than 1.
-    if (width < 1)  { ErrorInvalidValue("%s: width is < 1", info);  return false; }
-    if (height < 1) { ErrorInvalidValue("%s: height is < 1", info); return false; }
-    if (depth < 1)  { ErrorInvalidValue("%s: depth is < 1", info);  return false; }
-    if (levels < 1) { ErrorInvalidValue("%s: levels is < 1", info); return false; }
-
-    // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1.
-    if (FloorLog2(std::max(std::max(width, height), depth)) + 1 < levels) {
-        ErrorInvalidOperation("%s: too many levels for given texture dimensions", info);
-        return false;
-    }
-
-    return true;
-}
-
-// -------------------------------------------------------------------------
-// Texture objects
-
-void
-WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
-{
-    if (IsContextLost())
+    const char funcName[] = "TexStorage2D";
+    TexTarget texTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
         return;
 
-    // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
-    if (target != LOCAL_GL_TEXTURE_2D && target != LOCAL_GL_TEXTURE_CUBE_MAP)
-        return ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP");
-
-    if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D"))
-        return;
-
-    GetAndFlushUnderlyingGLErrors();
-    gl->fTexStorage2D(target, levels, internalformat, width, height);
-    GLenum error = GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        return GenerateWarning("texStorage2D generated error %s", ErrorName(error));
-    }
-
-    WebGLTexture* tex = ActiveBoundTextureForTarget(target);
-    tex->SetImmutable();
-
-    const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
-    GLsizei w = width;
-    GLsizei h = height;
-    for (size_t l = 0; l < size_t(levels); l++) {
-        for (size_t f = 0; f < facesCount; f++) {
-            tex->SetImageInfo(TexImageTargetForTargetAndFace(target, f),
-                              l, w, h, 1,
-                              internalformat,
-                              WebGLImageDataStatus::UninitializedImageData);
-        }
-        w = std::max(1, w / 2);
-        h = std::max(1, h / 2);
-    }
+    tex->TexStorage2D(texTarget, levels, internalFormat, width, height);
 }
 
 void
-WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
+WebGL2Context::TexStorage3D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat,
                             GLsizei width, GLsizei height, GLsizei depth)
 {
-    if (IsContextLost())
-        return;
-
-    // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
-    if (target != LOCAL_GL_TEXTURE_3D)
-        return ErrorInvalidEnum("texStorage3D: target is not TEXTURE_3D");
-
-    if (!ValidateTexStorage(target, levels, internalformat, width, height, depth, "texStorage3D"))
+    const char funcName[] = "texStorage3D";
+    TexTarget texTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
         return;
 
-    GetAndFlushUnderlyingGLErrors();
-    gl->fTexStorage3D(target, levels, internalformat, width, height, depth);
-    GLenum error = GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        return GenerateWarning("texStorage3D generated error %s", ErrorName(error));
-    }
-
-    WebGLTexture* tex = ActiveBoundTextureForTarget(target);
-    tex->SetImmutable();
-
-    GLsizei w = width;
-    GLsizei h = height;
-    GLsizei d = depth;
-    for (size_t l = 0; l < size_t(levels); l++) {
-        tex->SetImageInfo(TexImageTargetForTargetAndFace(target, 0),
-                          l, w, h, d,
-                          internalformat,
-                          WebGLImageDataStatus::UninitializedImageData);
-        w = std::max(1, w >> 1);
-        h = std::max(1, h >> 1);
-        d = std::max(1, d >> 1);
-    }
+    tex->TexStorage3D(texTarget, levels, internalFormat, width, height, depth);
 }
 
 void
-WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat,
+WebGL2Context::TexImage3D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
                           GLsizei width, GLsizei height, GLsizei depth,
-                          GLint border, GLenum format, GLenum type,
-                          const dom::Nullable<dom::ArrayBufferView> &pixels,
-                          ErrorResult& rv)
+                          GLint border, GLenum unpackFormat, GLenum unpackType,
+                          const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                          ErrorResult& out_rv)
 {
-    if (IsContextLost())
-        return;
-
-    void* data;
-    size_t dataLength;
-    js::Scalar::Type jsArrayType;
-    if (pixels.IsNull()) {
-        data = nullptr;
-        dataLength = 0;
-        jsArrayType = js::Scalar::MaxTypedArrayViewType;
-    } else {
-        const dom::ArrayBufferView& view = pixels.Value();
-        view.ComputeLengthAndData();
-
-        data = view.Data();
-        dataLength = view.Length();
-        jsArrayType = view.Type();
-    }
-
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
-
-    if (!ValidateTexImageTarget(target, func, dims))
-        return;
-
-    TexImageTarget texImageTarget = target;
-
-    if (!ValidateTexImage(texImageTarget, level, internalformat,
-                          0, 0, 0,
-                          width, height, depth,
-                          border, format, type, func, dims))
+    const char funcName[] = "texImage3D";
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex))
     {
         return;
     }
 
-    if (!ValidateTexInputData(type, jsArrayType, func, dims))
-        return;
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
-
-    if (effectiveInternalFormat == LOCAL_GL_NONE) {
-        return ErrorInvalidOperation("texImage3D: bad combination of internalformat and type");
-    }
-
-    // we need to find the exact sized format of the source data. Slightly abusing
-    // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
-    // is the same thing as an unsized internalformat.
-    TexInternalFormat effectiveSourceFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(format, type);
-    MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
-    const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
-    MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
-    size_t srcTexelSize = srcbitsPerTexel / 8;
-
-    CheckedUint32 checked_neededByteLength =
-        GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (dataLength && dataLength < bytesNeeded)
-        return ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)",
-                                 bytesNeeded, dataLength);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-
-    if (!tex)
-        return ErrorInvalidOperation("texImage3D: no texture is bound to this target");
-
-    if (tex->IsImmutable()) {
-        return ErrorInvalidOperation(
-            "texImage3D: disallowed because the texture "
-            "bound to this target has already been made immutable by texStorage3D");
-    }
-
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             effectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    MakeContextCurrent();
-    GetAndFlushUnderlyingGLErrors();
-    gl->fTexImage3D(texImageTarget.get(), level,
-                    driverInternalFormat,
-                    width, height, depth,
-                    0, driverFormat, driverType,
-                    data);
-    GLenum error = GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        return GenerateWarning("texImage3D generated error %s", ErrorName(error));
-    }
-
-    tex->SetImageInfo(texImageTarget, level,
-                      width, height, depth,
-                      effectiveInternalFormat,
-                      data ? WebGLImageDataStatus::InitializedImageData
-                           : WebGLImageDataStatus::UninitializedImageData);
+    tex->TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border,
+                    unpackFormat, unpackType, maybeView, &out_rv);
 }
 
 void
-WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level,
-                             GLint xoffset, GLint yoffset, GLint zoffset,
+WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level,
+                             GLint xOffset, GLint yOffset, GLint zOffset,
                              GLsizei width, GLsizei height, GLsizei depth,
-                             GLenum format, GLenum type,
-                             const dom::Nullable<dom::ArrayBufferView>& pixels,
-                             ErrorResult& rv)
+                             GLenum unpackFormat, GLenum unpackType,
+                             const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                             ErrorResult& out_rv)
 {
-    if (IsContextLost())
-        return;
-
-    if (pixels.IsNull())
-        return ErrorInvalidValue("texSubImage3D: pixels must not be null!");
-
-    const dom::ArrayBufferView& view = pixels.Value();
-    view.ComputeLengthAndData();
-
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
-
-    if (!ValidateTexImageTarget(rawTarget, func, dims))
-        return;
-
-    TexImageTarget texImageTarget(rawTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    if (!tex) {
-        return ErrorInvalidOperation("texSubImage3D: no texture bound on active texture unit");
-    }
-
-    if (!tex->HasImageInfoAt(texImageTarget, level)) {
-        return ErrorInvalidOperation("texSubImage3D: no previously defined texture image");
-    }
-
-    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
-    TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE;
-    TexType existingType = LOCAL_GL_NONE;
-    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat,
-                                                            &existingUnsizedInternalFormat,
-                                                            &existingType);
-
-    if (!ValidateTexImage(texImageTarget, level, existingEffectiveInternalFormat.get(),
-                          xoffset, yoffset, zoffset,
-                          width, height, depth,
-                          0, format, type, func, dims))
+    const char funcName[] = "texSubImage3D";
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex))
     {
         return;
     }
 
-    if (type != existingType) {
-        return ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image");
-    }
-
-    js::Scalar::Type jsArrayType = view.Type();
-    void* data = view.Data();
-    size_t dataLength = view.Length();
-
-    if (!ValidateTexInputData(type, jsArrayType, func, dims))
-        return;
-
-    const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
-    MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-    size_t srcTexelSize = bitsPerTexel / 8;
-
-    if (width == 0 || height == 0 || depth == 0)
-        return; // no effect, we better return right now
-
-    CheckedUint32 checked_neededByteLength =
-        GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (dataLength < bytesNeeded)
-        return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength);
-
-    if (imageInfo.HasUninitializedImageData()) {
-        bool coversWholeImage = xoffset == 0 &&
-                                yoffset == 0 &&
-                                zoffset == 0 &&
-                                width == imageInfo.Width() &&
-                                height == imageInfo.Height() &&
-                                depth == imageInfo.Depth();
-        if (coversWholeImage) {
-            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
-        } else {
-            if (!tex->EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             existingEffectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    MakeContextCurrent();
-    gl->fTexSubImage3D(texImageTarget.get(), level,
-                       xoffset, yoffset, zoffset,
-                       width, height, depth,
-                       driverFormat, driverType, data);
+    tex->TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height,
+                       depth, unpackFormat, unpackType, maybeView, &out_rv);
 
 }
 
 void
 WebGL2Context::TexSubImage3D(GLenum target, GLint level,
-                             GLint xoffset, GLint yoffset, GLint zoffset,
-                             GLenum format, GLenum type, dom::ImageData* data,
-                             ErrorResult& rv)
+                             GLint xOffset, GLint yOffset, GLint zOffset,
+                             GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                             ErrorResult& out_rv)
 {
     GenerateWarning("texSubImage3D: Not implemented.");
 }
 
 void
 WebGL2Context::CopyTexSubImage3D(GLenum target, GLint level,
-                                 GLint xoffset, GLint yoffset, GLint zoffset,
+                                 GLint xOffset, GLint yOffset, GLint zOffset,
                                  GLint x, GLint y, GLsizei width, GLsizei height)
 {
     GenerateWarning("copyTexSubImage3D: Not implemented.");
 }
 
 void
-WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
+WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                                     GLsizei width, GLsizei height, GLsizei depth,
-                                    GLint border, GLsizei imageSize, const dom::ArrayBufferView& data)
+                                    GLint border, GLsizei imageSize, const dom::ArrayBufferView& view)
 {
     GenerateWarning("compressedTexImage3D: Not implemented.");
 }
 
 void
-WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
                                        GLsizei width, GLsizei height, GLsizei depth,
-                                       GLenum format, GLsizei imageSize, const dom::ArrayBufferView& data)
+                                       GLenum unpackFormat, GLsizei imageSize, const dom::ArrayBufferView& view)
 {
     GenerateWarning("compressedTexSubImage3D: Not implemented.");
 }
 
-JS::Value
-WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
+bool
+WebGL2Context::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
-        case LOCAL_GL_TEXTURE_BASE_LEVEL:
-        case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
-        case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
-        case LOCAL_GL_TEXTURE_MAX_LEVEL:
-        case LOCAL_GL_TEXTURE_SWIZZLE_A:
-        case LOCAL_GL_TEXTURE_SWIZZLE_B:
-        case LOCAL_GL_TEXTURE_SWIZZLE_G:
-        case LOCAL_GL_TEXTURE_SWIZZLE_R:
-        case LOCAL_GL_TEXTURE_WRAP_R:
-        {
-            GLint i = 0;
-            gl->fGetTexParameteriv(target.get(), pname, &i);
-            return JS::NumberValue(uint32_t(i));
-        }
+    case LOCAL_GL_TEXTURE_BASE_LEVEL:
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+    case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
+    case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
+    case LOCAL_GL_TEXTURE_MAX_LEVEL:
+    case LOCAL_GL_TEXTURE_SWIZZLE_A:
+    case LOCAL_GL_TEXTURE_SWIZZLE_B:
+    case LOCAL_GL_TEXTURE_SWIZZLE_G:
+    case LOCAL_GL_TEXTURE_SWIZZLE_R:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+        return true;
 
-        case LOCAL_GL_TEXTURE_MAX_LOD:
-        case LOCAL_GL_TEXTURE_MIN_LOD:
-        {
-            GLfloat f = 0.0f;
-            gl->fGetTexParameterfv(target.get(), pname, &f);
-            return JS::NumberValue(float(f));
-        }
+    default:
+        return WebGLContext::IsTexParamValid(pname);
     }
-
-    return WebGLContext::GetTexParameterInternal(target, pname);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1816,175 +1816,29 @@ WebGLContext::GetSurfaceSnapshot(bool* o
 void
 WebGLContext::DidRefresh()
 {
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
-bool
-WebGLContext::TexImageFromVideoElement(const TexImageTarget texImageTarget,
-                                       GLint level, GLenum internalFormat,
-                                       GLenum format, GLenum type,
-                                       mozilla::dom::Element& elt)
-{
-    if (type == LOCAL_GL_HALF_FLOAT_OES &&
-        !gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
-    {
-        type = LOCAL_GL_HALF_FLOAT;
-    }
-
-    if (!ValidateTexImageFormatAndType(format, type,
-                                       WebGLTexImageFunc::TexImage,
-                                       WebGLTexDimensions::Tex2D))
-    {
-        return false;
-    }
-
-    HTMLVideoElement* video = HTMLVideoElement::FromContentOrNull(&elt);
-    if (!video)
-        return false;
-
-    uint16_t readyState;
-    if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
-        readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)
-    {
-        //No frame inside, just return
-        return false;
-    }
-
-    // If it doesn't have a principal, just bail
-    nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
-    if (!principal)
-        return false;
-
-    mozilla::layers::ImageContainer* container = video->GetImageContainer();
-    if (!container)
-        return false;
-
-    if (video->GetCORSMode() == CORS_NONE) {
-        bool subsumes;
-        nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(principal, &subsumes);
-        if (NS_FAILED(rv) || !subsumes) {
-            GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
-                                "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
-            return false;
-        }
-    }
-
-    AutoLockImage lockedImage(container);
-    Image* srcImage = lockedImage.GetImage();
-    if (!srcImage) {
-      return false;
-    }
-
-    gl->MakeCurrent();
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-
-    const WebGLTexture::ImageInfo& info = tex->ImageInfoAt(texImageTarget, 0);
-    bool dimensionsMatch = info.Width() == srcImage->GetSize().width &&
-                           info.Height() == srcImage->GetSize().height;
-    if (!dimensionsMatch) {
-        // we need to allocation
-        gl->fTexImage2D(texImageTarget.get(), level, internalFormat,
-                        srcImage->GetSize().width, srcImage->GetSize().height,
-                        0, format, type, nullptr);
-    }
-
-    const gl::OriginPos destOrigin = mPixelStoreFlipY ? gl::OriginPos::BottomLeft
-                                                      : gl::OriginPos::TopLeft;
-    bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage,
-                                                   srcImage->GetSize(),
-                                                   tex->mGLName,
-                                                   texImageTarget.get(),
-                                                   destOrigin);
-    if (ok) {
-        TexInternalFormat effectiveInternalFormat =
-            EffectiveInternalFormatFromInternalFormatAndType(internalFormat,
-                                                             type);
-        MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
-        tex->SetImageInfo(texImageTarget, level, srcImage->GetSize().width,
-                          srcImage->GetSize().height, 1,
-                          effectiveInternalFormat,
-                          WebGLImageDataStatus::InitializedImageData);
-        tex->Bind(TexImageTargetToTexTarget(texImageTarget));
-    }
-    return ok;
-}
-
-void
-WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
-                            GLint yoffset, GLenum format, GLenum type,
-                            dom::Element* elt, ErrorResult* const out_rv)
-{
-    // TODO: Consolidate all the parameter validation
-    // checks. Instead of spreading out the cheks in multple
-    // places, consolidate into one spot.
-
-    if (IsContextLost())
-        return;
-
-    if (!ValidateTexImageTarget(rawTexImageTarget,
-                                WebGLTexImageFunc::TexSubImage,
-                                WebGLTexDimensions::Tex2D))
-    {
-        ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
-        return;
-    }
-
-    const TexImageTarget texImageTarget(rawTexImageTarget);
-
-    if (level < 0)
-        return ErrorInvalidValue("texSubImage2D: level is negative");
-
-    const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
-    if (level > maxLevel) {
-        ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d",
-                          level, maxLevel);
-        return;
-    }
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    if (!tex)
-        return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
-
-    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-    const TexInternalFormat internalFormat = imageInfo.EffectiveInternalFormat();
-
-    // Trying to handle the video by GPU directly first
-    if (TexImageFromVideoElement(texImageTarget, level,
-                                 internalFormat.get(), format, type, *elt))
-    {
-        return;
-    }
-
-    RefPtr<gfx::DataSourceSurface> data;
-    WebGLTexelFormat srcFormat;
-    nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(*elt);
-    *out_rv = SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
-    if (out_rv->Failed() || !data)
-        return;
-
-    gfx::IntSize size = data->GetSize();
-    uint32_t byteLength = data->Stride() * size.height;
-    TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset, size.width,
-                       size.height, data->Stride(), format, type, data->GetData(),
-                       byteLength, js::Scalar::MaxTypedArrayViewType, srcFormat,
-                       res.mIsPremultiplied);
-}
-
 size_t
 RoundUpToMultipleOf(size_t value, size_t multiple)
 {
     size_t overshoot = value + multiple - 1;
     return overshoot - (overshoot % multiple);
 }
 
+CheckedUint32
+RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y)
+{
+    return ((x + y - 1) / y) * y;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
     : mWebGL(webgl)
     , mFakeNoAlpha(ShouldFakeNoAlpha(webgl))
     , mFakeNoStencil(ShouldFakeNoStencil(webgl))
 {
     if (mFakeNoAlpha) {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -277,19 +277,16 @@ public:
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
      */
     // Returns nullptr if glenum is unknown.
     static const char* EnumName(GLenum glenum);
     // Returns hex formatted version of glenum if glenum is unknown.
     static void EnumName(GLenum glenum, nsACString* out_name);
 
-    bool IsCompressedTextureFormat(GLenum format);
-    bool IsTextureFormatCompressed(TexInternalFormat format);
-
     void DummyFramebufferOperation(const char* funcName);
 
     WebGLTexture* ActiveBoundTextureForTarget(const TexTarget texTarget) const {
         switch (texTarget.get()) {
         case LOCAL_GL_TEXTURE_2D:
             return mBound2DTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_CUBE_MAP:
             return mBoundCubeMapTextures[mActiveTexture];
@@ -369,66 +366,48 @@ public:
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(JSContext* cx,
                                 dom::Nullable< nsTArray<nsString> >& retval);
     void GetExtension(JSContext* cx, const nsAString& name,
                       JS::MutableHandle<JSObject*> retval, ErrorResult& rv);
-    void ActiveTexture(GLenum texture);
     void AttachShader(WebGLProgram* prog, WebGLShader* shader);
     void BindAttribLocation(WebGLProgram* prog, GLuint location,
                             const nsAString& name);
     void BindFramebuffer(GLenum target, WebGLFramebuffer* fb);
     void BindRenderbuffer(GLenum target, WebGLRenderbuffer* fb);
-    void BindTexture(GLenum target, WebGLTexture* tex);
     void BindVertexArray(WebGLVertexArray* vao);
     void BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
     void BlendEquation(GLenum mode);
     void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
     void BlendFunc(GLenum sfactor, GLenum dfactor);
     void BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
                            GLenum srcAlpha, GLenum dstAlpha);
     GLenum CheckFramebufferStatus(GLenum target);
     void Clear(GLbitfield mask);
     void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
     void ClearDepth(GLclampf v);
     void ClearStencil(GLint v);
     void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a);
     void CompileShader(WebGLShader* shader);
     void CompileShaderANGLE(WebGLShader* shader);
     void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource);
-    void CompressedTexImage2D(GLenum target, GLint level,
-                              GLenum internalformat, GLsizei width,
-                              GLsizei height, GLint border,
-                              const dom::ArrayBufferView& view);
-    void CompressedTexSubImage2D(GLenum texImageTarget, GLint level,
-                                 GLint xoffset, GLint yoffset, GLsizei width,
-                                 GLsizei height, GLenum format,
-                                 const dom::ArrayBufferView& view);
-    void CopyTexImage2D(GLenum texImageTarget, GLint level,
-                        GLenum internalformat, GLint x, GLint y, GLsizei width,
-                        GLsizei height, GLint border);
-    void CopyTexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset,
-                           GLint yoffset, GLint x, GLint y, GLsizei width,
-                           GLsizei height);
     already_AddRefed<WebGLFramebuffer> CreateFramebuffer();
     already_AddRefed<WebGLProgram> CreateProgram();
     already_AddRefed<WebGLRenderbuffer> CreateRenderbuffer();
-    already_AddRefed<WebGLTexture> CreateTexture();
     already_AddRefed<WebGLShader> CreateShader(GLenum type);
     already_AddRefed<WebGLVertexArray> CreateVertexArray();
     void CullFace(GLenum face);
     void DeleteFramebuffer(WebGLFramebuffer* fb);
     void DeleteProgram(WebGLProgram* prog);
     void DeleteRenderbuffer(WebGLRenderbuffer* rb);
     void DeleteShader(WebGLShader* shader);
     void DeleteVertexArray(WebGLVertexArray* vao);
-    void DeleteTexture(WebGLTexture* tex);
     void DepthFunc(GLenum func);
     void DepthMask(WebGLboolean b);
     void DepthRange(GLclampf zNear, GLclampf zFar);
     void DetachShader(WebGLProgram* prog, WebGLShader* shader);
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     void Flush();
     void Finish();
     void FramebufferRenderbuffer(GLenum target, GLenum attachment,
@@ -437,17 +416,16 @@ public:
                               GLenum texImageTarget, WebGLTexture* tex,
                               GLint level);
 
     // Framebuffer validation
     bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb,
                                        GLenum attachment, const char* funcName);
 
     void FrontFace(GLenum mode);
-    void GenerateMipmap(GLenum target);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(WebGLProgram* prog,
                                                       GLuint index);
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(WebGLProgram* prog,
                                                        GLuint index);
 
     void
     GetAttachedShaders(WebGLProgram* prog,
                        dom::Nullable<nsTArray<nsRefPtr<WebGLShader>>>& retval);
@@ -503,23 +481,16 @@ public:
 
     already_AddRefed<WebGLShaderPrecisionFormat>
     GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);
 
     void GetShaderInfoLog(WebGLShader* shader, nsACString& retval);
     void GetShaderInfoLog(WebGLShader* shader, nsAString& retval);
     void GetShaderSource(WebGLShader* shader, nsAString& retval);
     void GetShaderTranslatedSource(WebGLShader* shader, nsAString& retval);
-    JS::Value GetTexParameter(GLenum target, GLenum pname);
-
-    void GetTexParameter(JSContext*, GLenum target, GLenum pname,
-                         JS::MutableHandle<JS::Value> retval)
-    {
-        retval.set(GetTexParameter(target, pname));
-    }
 
     JS::Value GetUniform(JSContext* cx, WebGLProgram* prog,
                          WebGLUniformLocation* loc);
 
     void GetUniform(JSContext* cx, WebGLProgram* prog,
                     WebGLUniformLocation* loc,
                     JS::MutableHandle<JS::Value> retval)
     {
@@ -529,17 +500,16 @@ public:
     already_AddRefed<WebGLUniformLocation>
     GetUniformLocation(WebGLProgram* prog, const nsAString& name);
 
     void Hint(GLenum target, GLenum mode);
     bool IsFramebuffer(WebGLFramebuffer* fb);
     bool IsProgram(WebGLProgram* prog);
     bool IsRenderbuffer(WebGLRenderbuffer* rb);
     bool IsShader(WebGLShader* shader);
-    bool IsTexture(WebGLTexture* tex);
     bool IsVertexArray(WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram* prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type,
                     const dom::Nullable<dom::ArrayBufferView>& pixels,
@@ -556,115 +526,16 @@ public:
     void ShaderSource(WebGLShader* shader, const nsAString& source);
     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 TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
-                    GLsizei width, GLsizei height, GLint border, GLenum format,
-                    GLenum type, const dom::Nullable<dom::ArrayBufferView>& pixels,
-                    ErrorResult& rv);
-    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
-                    GLenum format, GLenum type, dom::ImageData* pixels,
-                    ErrorResult& rv);
-    // Allow whatever element types the bindings are willing to pass
-    // us in TexImage2D
-    bool TexImageFromVideoElement(TexImageTarget texImageTarget, GLint level,
-                                  GLenum internalFormat, GLenum format,
-                                  GLenum type, mozilla::dom::Element& image);
-
-    template<class ElementType>
-    void TexImage2D(GLenum rawTexImageTarget, GLint level,
-                    GLenum internalFormat, GLenum format, GLenum type,
-                    ElementType& elt, ErrorResult& rv)
-    {
-        if (IsContextLost())
-            return;
-
-        if (!ValidateTexImageTarget(rawTexImageTarget,
-                                    WebGLTexImageFunc::TexImage,
-                                    WebGLTexDimensions::Tex2D))
-        {
-            ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
-            return;
-        }
-
-        const TexImageTarget texImageTarget(rawTexImageTarget);
-
-        if (level < 0)
-            return ErrorInvalidValue("texImage2D: level is negative");
-
-        const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
-        if (level > maxLevel) {
-            ErrorInvalidValue("texImage2D: level %d is too large, max is %d",
-                              level, maxLevel);
-            return;
-        }
-
-        WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-
-        if (!tex)
-            return ErrorInvalidOperation("no texture is bound to this target");
-
-        // Trying to handle the video by GPU directly first
-        if (TexImageFromVideoElement(texImageTarget, level, internalFormat,
-                                     format, type, elt))
-        {
-            return;
-        }
-
-        RefPtr<gfx::DataSourceSurface> data;
-        WebGLTexelFormat srcFormat;
-        nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
-        rv = SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
-        if (rv.Failed() || !data)
-            return;
-
-        gfx::IntSize size = data->GetSize();
-        uint32_t byteLength = data->Stride() * size.height;
-        return TexImage2D_base(texImageTarget, level, internalFormat,
-                               size.width, size.height, data->Stride(), 0,
-                               format, type, data->GetData(), byteLength,
-                               js::Scalar::MaxTypedArrayViewType, srcFormat,
-                               res.mIsPremultiplied);
-    }
-
-    void TexParameterf(GLenum target, GLenum pname, GLfloat param) {
-        TexParameter_base(target, pname, nullptr, &param);
-    }
-    void TexParameteri(GLenum target, GLenum pname, GLint param) {
-        TexParameter_base(target, pname, &param, nullptr);
-    }
-
-    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset,
-                       GLint yoffset, GLsizei width, GLsizei height,
-                       GLenum format, GLenum type,
-                       const dom::Nullable<dom::ArrayBufferView>& pixels,
-                       ErrorResult& rv);
-    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset,
-                       GLint yoffset, GLenum format, GLenum type,
-                       dom::ImageData* pixels, ErrorResult& rv);
-
-    void TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
-                       GLint yoffset, GLenum format, GLenum type,
-                       dom::Element* elt, ErrorResult* const out_rv);
-
-    // Allow whatever element types the bindings are willing to pass
-    // us in TexSubImage2D
-    template<class ElementType>
-    void TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
-                       GLint yoffset, GLenum format, GLenum type,
-                       ElementType& elt, ErrorResult& out_rv)
-    {
-        TexSubImage2D(rawTexImageTarget, level, xoffset, yoffset, format, type, &elt,
-                      &out_rv);
-    }
 
     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 Uniform1f(WebGLUniformLocation* loc, GLfloat x);
@@ -936,16 +807,107 @@ private:
     realGLboolean mRasterizerDiscardEnabled;
     realGLboolean mScissorTestEnabled;
     realGLboolean mStencilTestEnabled;
 
     bool ValidateCapabilityEnum(GLenum cap, const char* info);
     realGLboolean* GetStateTrackingSlot(GLenum cap);
 
 // -----------------------------------------------------------------------------
+// Texture funcions (WebGLContextTextures.cpp)
+public:
+    void ActiveTexture(GLenum texUnit);
+    void BindTexture(GLenum texTarget, WebGLTexture* tex);
+    already_AddRefed<WebGLTexture> CreateTexture();
+    void DeleteTexture(WebGLTexture* tex);
+    void GenerateMipmap(GLenum texTarget);
+
+    void GetTexParameter(JSContext*, GLenum texTarget, GLenum pname,
+                         JS::MutableHandle<JS::Value> retval)
+    {
+        retval.set(GetTexParameter(texTarget, pname));
+    }
+
+    bool IsTexture(WebGLTexture* tex);
+
+    void TexParameterf(GLenum texTarget, GLenum pname, GLfloat param) {
+        TexParameter_base(texTarget, pname, nullptr, &param);
+    }
+
+    void TexParameteri(GLenum texTarget, GLenum pname, GLint param) {
+        TexParameter_base(texTarget, pname, &param, nullptr);
+    }
+
+protected:
+    JS::Value GetTexParameter(GLenum texTarget, GLenum pname);
+    void TexParameter_base(GLenum texTarget, GLenum pname, GLint* maybeIntParam,
+                           GLfloat* maybeFloatParam);
+
+    virtual bool IsTexParamValid(GLenum pname) const;
+
+    // Upload funcs
+public:
+    void CompressedTexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                              GLsizei width, GLsizei height, GLint border,
+                              const dom::ArrayBufferView& view);
+    void CompressedTexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset,
+                                 GLint yOffset, GLsizei width, GLsizei height,
+                                 GLenum unpackFormat, const dom::ArrayBufferView& view);
+
+    void CopyTexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                        GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+    void CopyTexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset,
+                           GLint yOffset, GLint x, GLint y, GLsizei width,
+                           GLsizei height);
+
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
+                    GLenum unpackType,
+                    const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                    ErrorResult& out_rv);
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                    ErrorResult& out_rv);
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                    ErrorResult* const out_rv);
+
+
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLsizei width, GLsizei height, GLenum unpackFormat,
+                       GLenum unpackType,
+                       const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                       ErrorResult& out_rv);
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                       ErrorResult& out_rv);
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                       ErrorResult* const out_rv);
+
+    // Allow whatever element unpackTypes the bindings are willing to pass
+    // us in Tex(Sub)Image2D
+    template<typename ElementT>
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, ElementT& elem,
+                    ErrorResult& out_rv)
+    {
+        TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, &elem,
+                   &out_rv);
+    }
+    template<typename ElementT>
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLenum unpackFormat, GLenum unpackType, ElementT& elem,
+                       ErrorResult& out_rv)
+    {
+        TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
+                      &elem, &out_rv);
+    }
+
+// -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                              GLsizei primcount);
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
                       WebGLintptr byteOffset);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
@@ -1052,24 +1014,16 @@ protected:
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need();
     bool DoFakeVertexAttrib0(GLuint vertexCount);
     void UndoFakeVertexAttrib0();
 
     static CheckedUint32 GetImageSize(GLsizei height, GLsizei width,
                                       GLsizei depth, uint32_t pixelSize,
                                       uint32_t alignment);
 
-    virtual JS::Value GetTexParameterInternal(const TexTarget& target,
-                                              GLenum pname);
-
-    // Returns x rounded to the next highest multiple of y.
-    static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) {
-        return ((x + y - 1) / y) * y;
-    }
-
     inline void InvalidateBufferFetching()
     {
         mBufferFetchingIsVerified = false;
         mBufferFetchingHasPerVertex = false;
         mMaxFetchedVertices = 0;
         mMaxFetchedInstances = 0;
     }
 
@@ -1222,18 +1176,16 @@ protected:
     bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info);
 
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
                           GLint xoffset, GLint yoffset, GLint zoffset,
                           GLint width, GLint height, GLint depth,
                           GLint border, GLenum format, GLenum type,
                           WebGLTexImageFunc func, WebGLTexDimensions dims);
-    bool ValidateTexImageTarget(GLenum texImageTarget, WebGLTexImageFunc func,
-                                WebGLTexDimensions dims);
     bool ValidateTexImageFormat(GLenum internalFormat, WebGLTexImageFunc func,
                                 WebGLTexDimensions dims);
     bool ValidateTexImageType(GLenum type, WebGLTexImageFunc func,
                               WebGLTexDimensions dims);
     bool ValidateTexImageFormatAndType(GLenum format, GLenum type,
                                        WebGLTexImageFunc func,
                                        WebGLTexDimensions dims);
     bool ValidateCompTexImageInternalFormat(GLenum format,
@@ -1267,34 +1219,16 @@ protected:
 
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
 
-    // If jsArrayType is MaxTypedArrayViewType, it means no array.
-    void TexImage2D_base(TexImageTarget texImageTarget, GLint level,
-                         GLenum internalFormat, GLsizei width,
-                         GLsizei height, GLsizei srcStrideOrZero, GLint border,
-                         GLenum format, GLenum type, void* data,
-                         uint32_t byteLength, js::Scalar::Type jsArrayType,
-                         WebGLTexelFormat srcFormat, bool srcPremultiplied);
-    void TexSubImage2D_base(GLenum texImageTarget, GLint level,
-                            GLint xoffset, GLint yoffset, GLsizei width,
-                            GLsizei height, GLsizei srcStrideOrZero,
-                            GLenum format, GLenum type, void* pixels,
-                            uint32_t byteLength, js::Scalar::Type jsArrayType,
-                            WebGLTexelFormat srcFormat, bool srcPremultiplied);
-
-    void TexParameter_base(GLenum target, GLenum pname,
-                           GLint* const out_intParam,
-                           GLfloat* const out_floatParam);
-
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     template<class ElementType>
     nsLayoutUtils::SurfaceFromElementResult
@@ -1316,21 +1250,16 @@ protected:
        return SurfaceFromElement(&element);
     }
 
     nsresult
     SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
                                            RefPtr<gfx::DataSourceSurface>& imageOut,
                                            WebGLTexelFormat* format);
 
-    void CopyTexSubImage2D_base(TexImageTarget texImageTarget,
-                                GLint level, TexInternalFormat internalFormat,
-                                GLint xoffset, GLint yoffset, GLint x, GLint y,
-                                GLsizei width, GLsizei height, bool isSub);
-
     // Returns false if `object` is null or not valid.
     template<class ObjectType>
     bool ValidateObject(const char* info, ObjectType* object);
 
     // Returns false if `object` is not valid.  Considers null to be valid.
     template<class ObjectType>
     bool ValidateObjectAllowNull(const char* info, ObjectType* object);
 
@@ -1377,24 +1306,16 @@ protected:
     }
 
     /** Like glBufferData, but if the call may change the buffer size, checks
      *  any GL error generated by this glBufferData call and returns it.
      */
     GLenum CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
                              GLenum usage);
 
-    /** Like glTexImage2D, but if the call may change the texture size, checks
-     * any GL error generated by this glTexImage2D call and returns it.
-     */
-    GLenum CheckedTexImage2D(TexImageTarget texImageTarget, GLint level,
-                             TexInternalFormat internalFormat, GLsizei width,
-                             GLsizei height, GLint border, TexFormat format,
-                             TexType type, const GLvoid* data);
-
     void ForceLoseContext(bool simulateLoss = false);
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
@@ -1686,11 +1607,22 @@ public:
 private:
     ~WebGLObserver();
 
     WebGLContext* mWebGL;
 };
 
 size_t RoundUpToMultipleOf(size_t value, size_t multiple);
 
+bool
+ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+                  TexTarget* const out_texTarget, WebGLTexture** const out_tex);
+bool
+ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+                       const char* funcName, TexImageTarget* const out_texImageTarget,
+                       WebGLTexture** const out_tex);
+
+// Returns x rounded to the next highest multiple of y.
+CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y);
+
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -20,19 +20,18 @@
 namespace mozilla {
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
 bool
 WebGLContext::DrawInstanced_check(const char* info)
 {
-    // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
-    if (!IsWebGL2() &&
-        IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays) &&
+    if ((IsWebGL2() ||
+         IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) &&
         !mBufferFetchingHasPerVertex)
     {
         /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
          *  If all of the enabled vertex attribute arrays that are bound to active
          *  generic attributes in the program have a non-zero divisor, the draw
          *  call should return INVALID_OPERATION.
          *
          * NB: This also appears to apply to NV_instanced_arrays, though the
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -214,67 +214,16 @@ WebGLContext::BindRenderbuffer(GLenum ta
 #endif
     } else {
         gl->fBindRenderbuffer(target, 0);
     }
 
     mBoundRenderbuffer = wrb;
 }
 
-void
-WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
-{
-    if (IsContextLost())
-        return;
-
-     if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
-        return;
-
-    // Need to check rawTarget first before comparing against newTex->Target() as
-    // newTex->Target() returns a TexTarget, which will assert on invalid value.
-    WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
-    switch (rawTarget) {
-        case LOCAL_GL_TEXTURE_2D:
-            currentTexPtr = &mBound2DTextures[mActiveTexture];
-            break;
-       case LOCAL_GL_TEXTURE_CUBE_MAP:
-            currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
-            break;
-       case LOCAL_GL_TEXTURE_3D:
-            if (!IsWebGL2()) {
-                return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer");
-            }
-            currentTexPtr = &mBound3DTextures[mActiveTexture];
-            break;
-       default:
-            return ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
-    }
-    const TexTarget target(rawTarget);
-
-    if (newTex) {
-        // silently ignore a deleted texture
-        if (newTex->IsDeleted())
-            return;
-
-        if (newTex->HasEverBeenBound() && newTex->Target() != rawTarget)
-            return ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
-    }
-
-    *currentTexPtr = newTex;
-
-    MakeContextCurrent();
-
-    if (newTex) {
-        SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
-        newTex->Bind(target);
-    } else {
-        gl->fBindTexture(target.get(), 0);
-    }
-}
-
 void WebGLContext::BlendEquation(GLenum mode)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
         return;
 
@@ -358,279 +307,16 @@ WebGLContext::CheckFramebufferStatus(GLe
     }
 
     if (!fb)
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 
     return fb->CheckFramebufferStatus().get();
 }
 
-void
-WebGLContext::CopyTexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
-                                     TexInternalFormat internalformat,
-                                     GLint xoffset, GLint yoffset, GLint x,
-                                     GLint y, GLsizei width, GLsizei height,
-                                     bool sub)
-{
-    const WebGLRectangleObject* framebufferRect = CurValidReadFBRectObject();
-    GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
-    GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
-
-    WebGLTexImageFunc func = sub
-                             ? WebGLTexImageFunc::CopyTexSubImage
-                             : WebGLTexImageFunc::CopyTexImage;
-    WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-    const char* info = InfoFrom(func, dims);
-
-    // TODO: This changes with color_buffer_float. Reassess when the
-    // patch lands.
-    if (!ValidateTexImage(texImageTarget, level, internalformat.get(),
-                          xoffset, yoffset, 0,
-                          width, height, 0,
-                          0,
-                          LOCAL_GL_NONE, LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    if (!ValidateCopyTexImage(internalformat.get(), func, dims))
-        return;
-
-    if (!mBoundReadFramebuffer)
-        ClearBackbufferIfNeeded();
-
-    MakeContextCurrent();
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-
-    if (!tex)
-        return ErrorInvalidOperation("%s: no texture is bound to this target");
-
-    if (tex->IsImmutable()) {
-        if (!sub) {
-            return ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
-        }
-    }
-
-    TexType framebuffertype = LOCAL_GL_NONE;
-    if (mBoundReadFramebuffer) {
-        TexInternalFormat framebuffereffectiveformat = mBoundReadFramebuffer->ColorAttachment(0).EffectiveInternalFormat();
-        framebuffertype = TypeFromInternalFormat(framebuffereffectiveformat);
-    } else {
-        // FIXME - here we're assuming that the default framebuffer is backed by UNSIGNED_BYTE
-        // that might not always be true, say if we had a 16bpp default framebuffer.
-        framebuffertype = LOCAL_GL_UNSIGNED_BYTE;
-    }
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, framebuffertype);
-
-    // this should never fail, validation happened earlier.
-    MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
-
-    const bool widthOrHeightIsZero = (width == 0 || height == 0);
-    if (gl->WorkAroundDriverBugs() &&
-        sub && widthOrHeightIsZero)
-    {
-        // NV driver on Linux complains that CopyTexSubImage2D(level=0,
-        // xoffset=0, yoffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB
-        // to a 0x2 texture. This a useless thing to do, but technically legal.
-        // NV331.38 generates INVALID_VALUE.
-        return DummyFramebufferOperation(info);
-    }
-
-    // check if the memory size of this texture may change with this call
-    bool sizeMayChange = !sub;
-    if (!sub && tex->HasImageInfoAt(texImageTarget, level)) {
-        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-        sizeMayChange = width != imageInfo.Width() ||
-                        height != imageInfo.Height() ||
-                        effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
-    }
-
-    if (sizeMayChange)
-        GetAndFlushUnderlyingGLErrors();
-
-    if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
-        if (sub)
-            gl->fCopyTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, x, y, width, height);
-        else
-            gl->fCopyTexImage2D(texImageTarget.get(), level, internalformat.get(), x, y, width, height, 0);
-    } else {
-
-        // the rect doesn't fit in the framebuffer
-
-        // first, we initialize the texture as black
-        if (!sub) {
-            tex->SetImageInfo(texImageTarget, level, width, height, 1,
-                      effectiveInternalFormat,
-                      WebGLImageDataStatus::UninitializedImageData);
-            if (!tex->EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-
-        // if we are completely outside of the framebuffer, we can exit now with our black texture
-        if (   x >= framebufferWidth
-            || x+width <= 0
-            || y >= framebufferHeight
-            || y+height <= 0)
-        {
-            // we are completely outside of range, can exit now with buffer filled with zeros
-            return DummyFramebufferOperation(info);
-        }
-
-        GLint   actual_x             = clamped(x, 0, framebufferWidth);
-        GLint   actual_x_plus_width  = clamped(x + width, 0, framebufferWidth);
-        GLsizei actual_width   = actual_x_plus_width  - actual_x;
-        GLint   actual_xoffset = xoffset + actual_x - x;
-
-        GLint   actual_y             = clamped(y, 0, framebufferHeight);
-        GLint   actual_y_plus_height = clamped(y + height, 0, framebufferHeight);
-        GLsizei actual_height  = actual_y_plus_height - actual_y;
-        GLint   actual_yoffset = yoffset + actual_y - y;
-
-        gl->fCopyTexSubImage2D(texImageTarget.get(), level, actual_xoffset, actual_yoffset, actual_x, actual_y, actual_width, actual_height);
-    }
-
-    if (sizeMayChange) {
-        GLenum error = GetAndFlushUnderlyingGLErrors();
-        if (error) {
-            GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
-            return;
-        }
-    }
-
-    if (!sub) {
-        tex->SetImageInfo(texImageTarget, level, width, height, 1,
-                          effectiveInternalFormat,
-                          WebGLImageDataStatus::InitializedImageData);
-    }
-}
-
-void
-WebGLContext::CopyTexImage2D(GLenum rawTexImgTarget,
-                             GLint level,
-                             GLenum internalformat,
-                             GLint x,
-                             GLint y,
-                             GLsizei width,
-                             GLsizei height,
-                             GLint border)
-{
-    if (IsContextLost())
-        return;
-
-    // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
-    const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (!ValidateTexImageTarget(rawTexImgTarget, func, dims))
-        return;
-
-    if (!ValidateTexImage(rawTexImgTarget, level, internalformat,
-                          0, 0, 0,
-                          width, height, 0,
-                          border, LOCAL_GL_NONE, LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    if (!ValidateCopyTexImage(internalformat, func, dims))
-        return;
-
-    if (!mBoundReadFramebuffer)
-        ClearBackbufferIfNeeded();
-
-    CopyTexSubImage2D_base(rawTexImgTarget, level, internalformat, 0, 0, x, y, width, height, false);
-}
-
-void
-WebGLContext::CopyTexSubImage2D(GLenum rawTexImgTarget,
-                                GLint level,
-                                GLint xoffset,
-                                GLint yoffset,
-                                GLint x,
-                                GLint y,
-                                GLsizei width,
-                                GLsizei height)
-{
-    if (IsContextLost())
-        return;
-
-    switch (rawTexImgTarget) {
-        case LOCAL_GL_TEXTURE_2D:
-        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:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-            break;
-        default:
-            return ErrorInvalidEnumInfo("copyTexSubImage2D: target", rawTexImgTarget);
-    }
-
-    const TexImageTarget texImageTarget(rawTexImgTarget);
-
-    if (level < 0)
-        return ErrorInvalidValue("copyTexSubImage2D: level may not be negative");
-
-    GLsizei maxTextureSize = MaxTextureSizeForTarget(TexImageTargetToTexTarget(texImageTarget));
-    if (!(maxTextureSize >> level))
-        return ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size");
-
-    if (width < 0 || height < 0)
-        return ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative");
-
-    if (xoffset < 0 || yoffset < 0)
-        return ErrorInvalidValue("copyTexSubImage2D: xoffset and yoffset may not be negative");
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    if (!tex)
-        return ErrorInvalidOperation("copyTexSubImage2D: no texture bound to this target");
-
-    if (!tex->HasImageInfoAt(texImageTarget, level))
-        return ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face");
-
-    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-    GLsizei texWidth = imageInfo.Width();
-    GLsizei texHeight = imageInfo.Height();
-
-    if (xoffset + width > texWidth || xoffset + width < 0)
-      return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large");
-
-    if (yoffset + height > texHeight || yoffset + height < 0)
-      return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
-
-    if (!mBoundReadFramebuffer)
-        ClearBackbufferIfNeeded();
-
-    if (imageInfo.HasUninitializedImageData()) {
-        bool coversWholeImage = xoffset == 0 &&
-                                yoffset == 0 &&
-                                width == texWidth &&
-                                height == texHeight;
-        if (coversWholeImage) {
-            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
-        } else {
-            if (!tex->EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-
-    TexInternalFormat internalformat;
-    TexType type;
-    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(),
-                                             &internalformat, &type);
-    return CopyTexSubImage2D_base(texImageTarget, level, internalformat, xoffset, yoffset, x, y, width, height, true);
-}
-
-
 already_AddRefed<WebGLProgram>
 WebGLContext::CreateProgram()
 {
     if (IsContextLost())
         return nullptr;
     nsRefPtr<WebGLProgram> globj = new WebGLProgram(this);
     return globj.forget();
 }
@@ -933,80 +619,16 @@ WebGLContext::FrontFace(GLenum mode)
         default:
             return ErrorInvalidEnumInfo("frontFace: mode", mode);
     }
 
     MakeContextCurrent();
     gl->fFrontFace(mode);
 }
 
-void
-WebGLContext::GenerateMipmap(GLenum rawTarget)
-{
-    if (IsContextLost())
-        return;
-
-    if (!ValidateTextureTargetEnum(rawTarget, "generateMipmap"))
-        return;
-
-    const TexTarget target(rawTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTarget(target);
-
-    if (!tex)
-        return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
-
-    const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D)
-                                                  ? LOCAL_GL_TEXTURE_2D
-                                                  : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
-    if (!tex->IsMipmapRangeValid())
-    {
-        return ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range.");
-    }
-    if (!tex->HasImageInfoAt(imageTarget, tex->EffectiveBaseMipmapLevel()))
-    {
-        return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
-    }
-
-    if (!IsWebGL2() && !tex->IsFirstImagePowerOfTwo())
-        return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
-
-    TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
-    if (IsTextureFormatCompressed(internalformat))
-        return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
-
-    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
-        (IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat)))
-    {
-        return ErrorInvalidOperation("generateMipmap: "
-                                     "A texture that has a base internal format of "
-                                     "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
-    }
-
-    if (!tex->AreAllLevel0ImageInfosEqual())
-        return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
-
-    tex->SetGeneratedMipmap();
-
-    MakeContextCurrent();
-
-    if (gl->WorkAroundDriverBugs()) {
-        // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
-        // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
-        // overhead so we do it unconditionally.
-        //
-        // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
-        gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST);
-        gl->fGenerateMipmap(target.get());
-        gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter().get());
-    } else {
-        gl->fGenerateMipmap(target.get());
-    }
-}
-
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveAttrib(WebGLProgram* prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getActiveAttrib: program", prog))
         return nullptr;
@@ -1440,220 +1062,16 @@ WebGLContext::GetProgramInfoLog(WebGLPro
     if (!ValidateObject("getProgramInfoLog: program", prog))
         return;
 
     prog->GetProgramInfoLog(&retval);
 
     retval.SetIsVoid(false);
 }
 
-// here we have to support all pnames with both int and float params.
-// See this discussion:
-//  https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
-void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname,
-                                     GLint* intParamPtr,
-                                     GLfloat* floatParamPtr)
-{
-    MOZ_ASSERT(intParamPtr || floatParamPtr);
-
-    if (IsContextLost())
-        return;
-
-    GLint intParam = intParamPtr ? *intParamPtr : GLint(*floatParamPtr);
-    GLfloat floatParam = floatParamPtr ? *floatParamPtr : GLfloat(*intParamPtr);
-
-    if (!ValidateTextureTargetEnum(rawTarget, "texParameter: target"))
-        return;
-
-    const TexTarget texTarget = TexTarget(rawTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTarget(texTarget);
-    if (!tex)
-        return ErrorInvalidOperation("texParameter: no texture is bound to this target");
-
-    bool pnameAndParamAreIncompatible = false;
-    bool paramValueInvalid = false;
-
-    switch (pname) {
-        case LOCAL_GL_TEXTURE_BASE_LEVEL:
-        case LOCAL_GL_TEXTURE_MAX_LEVEL:
-            if (!IsWebGL2())
-                return ErrorInvalidEnumInfo("texParameter: pname", pname);
-            if (intParam < 0) {
-                paramValueInvalid = true;
-                break;
-            }
-            if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL)
-                tex->SetBaseMipmapLevel(intParam);
-            else
-                tex->SetMaxMipmapLevel(intParam);
-            break;
-
-        case LOCAL_GL_TEXTURE_COMPARE_MODE:
-            if (!IsWebGL2())
-                return ErrorInvalidEnumInfo("texParameter: pname", pname);
-
-            paramValueInvalid = (intParam != LOCAL_GL_NONE &&
-                                 intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
-            break;
-
-        case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-            if (!IsWebGL2())
-                return ErrorInvalidEnumInfo("texParameter: pname", pname);
-
-            switch (intParam) {
-            case LOCAL_GL_LEQUAL:
-            case LOCAL_GL_GEQUAL:
-            case LOCAL_GL_LESS:
-            case LOCAL_GL_GREATER:
-            case LOCAL_GL_EQUAL:
-            case LOCAL_GL_NOTEQUAL:
-            case LOCAL_GL_ALWAYS:
-            case LOCAL_GL_NEVER:
-                break;
-
-            default:
-                pnameAndParamAreIncompatible = true;
-            }
-            break;
-
-        case LOCAL_GL_TEXTURE_MIN_FILTER:
-            switch (intParam) {
-                case LOCAL_GL_NEAREST:
-                case LOCAL_GL_LINEAR:
-                case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
-                case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
-                case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
-                case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
-                    tex->SetMinFilter(intParam);
-                    break;
-                default:
-                    pnameAndParamAreIncompatible = true;
-            }
-            break;
-        case LOCAL_GL_TEXTURE_MAG_FILTER:
-            switch (intParam) {
-                case LOCAL_GL_NEAREST:
-                case LOCAL_GL_LINEAR:
-                    tex->SetMagFilter(intParam);
-                    break;
-                default:
-                    pnameAndParamAreIncompatible = true;
-            }
-            break;
-        case LOCAL_GL_TEXTURE_WRAP_S:
-            switch (intParam) {
-                case LOCAL_GL_CLAMP_TO_EDGE:
-                case LOCAL_GL_MIRRORED_REPEAT:
-                case LOCAL_GL_REPEAT:
-                    tex->SetWrapS(intParam);
-                    break;
-                default:
-                    pnameAndParamAreIncompatible = true;
-            }
-            break;
-        case LOCAL_GL_TEXTURE_WRAP_T:
-            switch (intParam) {
-                case LOCAL_GL_CLAMP_TO_EDGE:
-                case LOCAL_GL_MIRRORED_REPEAT:
-                case LOCAL_GL_REPEAT:
-                    tex->SetWrapT(intParam);
-                    break;
-                default:
-                    pnameAndParamAreIncompatible = true;
-            }
-            break;
-        case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
-            if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
-                if (floatParamPtr && floatParam < 1.f)
-                    paramValueInvalid = true;
-                else if (intParamPtr && intParam < 1)
-                    paramValueInvalid = true;
-            }
-            else
-                pnameAndParamAreIncompatible = true;
-            break;
-        default:
-            return ErrorInvalidEnumInfo("texParameter: pname", pname);
-    }
-
-    if (pnameAndParamAreIncompatible) {
-        if (intParamPtr)
-            return ErrorInvalidEnum("texParameteri: pname %x and param %x (decimal %d) are mutually incompatible",
-                                    pname, intParam, intParam);
-        else
-            return ErrorInvalidEnum("texParameterf: pname %x and param %g are mutually incompatible",
-                                    pname, floatParam);
-    } else if (paramValueInvalid) {
-        if (intParamPtr)
-            return ErrorInvalidValue("texParameteri: pname %x and param %x (decimal %d) is invalid",
-                                    pname, intParam, intParam);
-        else
-            return ErrorInvalidValue("texParameterf: pname %x and param %g is invalid",
-                                    pname, floatParam);
-    }
-
-    MakeContextCurrent();
-    if (intParamPtr)
-        gl->fTexParameteri(texTarget.get(), pname, intParam);
-    else
-        gl->fTexParameterf(texTarget.get(), pname, floatParam);
-}
-
-JS::Value
-WebGLContext::GetTexParameter(GLenum rawTarget, GLenum pname)
-{
-    if (IsContextLost())
-        return JS::NullValue();
-
-    MakeContextCurrent();
-
-    if (!ValidateTextureTargetEnum(rawTarget, "getTexParameter: target"))
-        return JS::NullValue();
-
-    const TexTarget target(rawTarget);
-
-    if (!ActiveBoundTextureForTarget(target)) {
-        ErrorInvalidOperation("getTexParameter: no texture bound");
-        return JS::NullValue();
-    }
-
-    return GetTexParameterInternal(target, pname);
-}
-
-JS::Value
-WebGLContext::GetTexParameterInternal(const TexTarget& target, GLenum pname)
-{
-    switch (pname) {
-        case LOCAL_GL_TEXTURE_MIN_FILTER:
-        case LOCAL_GL_TEXTURE_MAG_FILTER:
-        case LOCAL_GL_TEXTURE_WRAP_S:
-        case LOCAL_GL_TEXTURE_WRAP_T:
-        {
-            GLint i = 0;
-            gl->fGetTexParameteriv(target.get(), pname, &i);
-            return JS::NumberValue(uint32_t(i));
-        }
-        case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
-            if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
-                GLfloat f = 0.f;
-                gl->fGetTexParameterfv(target.get(), pname, &f);
-                return JS::DoubleValue(f);
-            }
-
-            ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
-            break;
-
-        default:
-            ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
-    }
-
-    return JS::NullValue();
-}
-
 JS::Value
 WebGLContext::GetUniform(JSContext* js, WebGLProgram* prog,
                          WebGLUniformLocation* loc)
 {
     if (IsContextLost())
         return JS::NullValue();
 
     if (!ValidateObject("getUniform: `program`", prog))
@@ -1767,27 +1185,16 @@ WebGLContext::IsShader(WebGLShader* shad
 {
     if (IsContextLost())
         return false;
 
     return ValidateObjectAllowDeleted("isShader", shader) &&
         !shader->IsDeleted();
 }
 
-bool
-WebGLContext::IsTexture(WebGLTexture* tex)
-{
-    if (IsContextLost())
-        return false;
-
-    return ValidateObjectAllowDeleted("isTexture", tex) &&
-        !tex->IsDeleted() &&
-        tex->HasEverBeenBound();
-}
-
 void
 WebGLContext::LinkProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("linkProgram", prog))
         return;
@@ -1972,17 +1379,17 @@ IsFormatAndTypeUnpackable(GLenum format,
     default:
         return false;
     }
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
                          GLsizei height, GLenum format,
-                         GLenum type, const dom::Nullable<ArrayBufferView>& pixels,
+                         GLenum type, const dom::Nullable<dom::ArrayBufferView>& pixels,
                          ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
         GenerateWarning("readPixels: Not allowed");
         return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
@@ -2045,17 +1452,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
         bytesPerPixel = 2*channels;
         requiredDataType = js::Scalar::Uint16;
         break;
 
     default:
         MOZ_CRASH("bad `type`");
     }
 
-    const ArrayBufferView& pixbuf = pixels.Value();
+    const dom::ArrayBufferView& pixbuf = pixels.Value();
     int dataType = pixbuf.Type();
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
     // Check the pixels param size
     CheckedUint32 checked_neededByteLength =
@@ -2923,137 +2330,16 @@ WebGLContext::CompileShader(WebGLShader*
         return;
 
     if (!ValidateObject("compileShader", shader))
         return;
 
     shader->CompileShader();
 }
 
-void
-WebGLContext::CompressedTexImage2D(GLenum rawTexImgTarget,
-                                   GLint level,
-                                   GLenum internalformat,
-                                   GLsizei width, GLsizei height, GLint border,
-                                   const ArrayBufferView& view)
-{
-    if (IsContextLost())
-        return;
-
-    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (!ValidateTexImageTarget(rawTexImgTarget, func, dims))
-        return;
-
-    if (!ValidateTexImage(rawTexImgTarget, level, internalformat,
-                          0, 0, 0, width, height, 0,
-                          border, LOCAL_GL_NONE,
-                          LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    view.ComputeLengthAndData();
-
-    uint32_t byteLength = view.Length();
-    if (!ValidateCompTexImageDataSize(level, internalformat, width, height, byteLength, func, dims)) {
-        return;
-    }
-
-    if (!ValidateCompTexImageSize(level, internalformat, 0, 0, width, height, width, height, func, dims))
-    {
-        return;
-    }
-
-    const TexImageTarget texImageTarget(rawTexImgTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    MOZ_ASSERT(tex);
-    if (tex->IsImmutable()) {
-        return ErrorInvalidOperation(
-            "compressedTexImage2D: disallowed because the texture bound to "
-            "this target has already been made immutable by texStorage2D");
-    }
-
-    MakeContextCurrent();
-    gl->fCompressedTexImage2D(texImageTarget.get(), level, internalformat, width, height, border, byteLength, view.Data());
-
-    tex->SetImageInfo(texImageTarget, level, width, height, 1, internalformat,
-                      WebGLImageDataStatus::InitializedImageData);
-}
-
-void
-WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint xoffset,
-                                      GLint yoffset, GLsizei width, GLsizei height,
-                                      GLenum internalformat,
-                                      const ArrayBufferView& view)
-{
-    if (IsContextLost())
-        return;
-
-    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (!ValidateTexImageTarget(rawTexImgTarget, func, dims))
-        return;
-
-    if (!ValidateTexImage(rawTexImgTarget,
-                          level, internalformat,
-                          xoffset, yoffset, 0,
-                          width, height, 0,
-                          0, LOCAL_GL_NONE, LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    const TexImageTarget texImageTarget(rawTexImgTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    MOZ_ASSERT(tex);
-    WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(texImageTarget, level);
-
-    if (internalformat != levelInfo.EffectiveInternalFormat()) {
-        return ErrorInvalidOperation("compressedTexImage2D: internalformat does not match the existing image");
-    }
-
-    view.ComputeLengthAndData();
-
-    uint32_t byteLength = view.Length();
-    if (!ValidateCompTexImageDataSize(level, internalformat, width, height, byteLength, func, dims))
-        return;
-
-    if (!ValidateCompTexImageSize(level, internalformat,
-                                  xoffset, yoffset,
-                                  width, height,
-                                  levelInfo.Width(), levelInfo.Height(),
-                                  func, dims))
-    {
-        return;
-    }
-
-    if (levelInfo.HasUninitializedImageData()) {
-        bool coversWholeImage = xoffset == 0 &&
-                                yoffset == 0 &&
-                                width == levelInfo.Width() &&
-                                height == levelInfo.Height();
-        if (coversWholeImage) {
-            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
-        } else {
-            if (!tex->EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-
-    MakeContextCurrent();
-    gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, internalformat, byteLength, view.Data());
-}
-
 JS::Value
 WebGLContext::GetShaderParameter(WebGLShader* shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
     if (!ValidateObject("getShaderParameter: shader", shader))
         return JS::NullValue();
@@ -3160,471 +2446,16 @@ WebGLContext::GetShaderTranslatedSource(
         return;
 
     if (!ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
     shader->GetShaderTranslatedSource(&retval);
 }
 
-GLenum WebGLContext::CheckedTexImage2D(TexImageTarget texImageTarget,
-                                       GLint level,
-                                       TexInternalFormat internalformat,
-                                       GLsizei width,
-                                       GLsizei height,
-                                       GLint border,
-                                       TexFormat format,
-                                       TexType type,
-                                       const GLvoid* data)
-{
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    MOZ_ASSERT(tex, "no texture bound");
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
-    bool sizeMayChange = true;
-
-    if (tex->HasImageInfoAt(texImageTarget, level)) {
-        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-        sizeMayChange = width != imageInfo.Width() ||
-                        height != imageInfo.Height() ||
-                        effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
-    }
-
-    // Convert to format and type required by OpenGL 'driver'.
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             effectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    if (sizeMayChange) {
-        GetAndFlushUnderlyingGLErrors();
-    }
-
-    gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
-
-    if (effectiveInternalFormat != driverInternalFormat)
-        SetLegacyTextureSwizzle(gl, texImageTarget.get(), internalformat.get());
-
-    GLenum error = LOCAL_GL_NO_ERROR;
-    if (sizeMayChange) {
-        error = GetAndFlushUnderlyingGLErrors();
-    }
-
-    return error;
-}
-
-void
-WebGLContext::TexImage2D_base(TexImageTarget texImageTarget, GLint level,
-                              GLenum internalformat,
-                              GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
-                              GLint border,
-                              GLenum format,
-                              GLenum type,
-                              void* data, uint32_t byteLength,
-                              js::Scalar::Type jsArrayType,
-                              WebGLTexelFormat srcFormat, bool srcPremultiplied)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (type == LOCAL_GL_HALF_FLOAT_OES) {
-        type = LOCAL_GL_HALF_FLOAT;
-    }
-
-    if (!ValidateTexImage(texImageTarget, level, internalformat,
-                          0, 0, 0,
-                          width, height, 0,
-                          border, format, type, func, dims))
-    {
-        return;
-    }
-
-    const bool isDepthTexture = format == LOCAL_GL_DEPTH_COMPONENT ||
-                                format == LOCAL_GL_DEPTH_STENCIL;
-
-    if (isDepthTexture && !IsWebGL2()) {
-        if (data != nullptr || level != 0)
-            return ErrorInvalidOperation("texImage2D: "
-                                         "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
-                                         "data must be nullptr, "
-                                         "level must be zero");
-    }
-
-    if (!ValidateTexInputData(type, jsArrayType, func, dims))
-        return;
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
-
-    if (effectiveInternalFormat == LOCAL_GL_NONE) {
-        return ErrorInvalidOperation("texImage2D: bad combination of internalformat and type");
-    }
-
-    size_t srcTexelSize = size_t(-1);
-    if (srcFormat == WebGLTexelFormat::Auto) {
-        // we need to find the exact sized format of the source data. Slightly abusing
-        // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
-        // is the same thing as an unsized internalformat.
-        TexInternalFormat effectiveSourceFormat =
-            EffectiveInternalFormatFromInternalFormatAndType(format, type);
-        MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
-        const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
-        MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
-        srcTexelSize = srcbitsPerTexel / 8;
-    } else {
-        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
-    }
-
-    CheckedUint32 checked_neededByteLength =
-        GetImageSize(height, width, 1, srcTexelSize, mPixelStoreUnpackAlignment);
-
-    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-    CheckedUint32 checked_alignedRowSize =
-        RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (byteLength && byteLength < bytesNeeded)
-        return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
-                                 bytesNeeded, byteLength);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-
-    if (!tex)
-        return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
-
-    if (tex->IsImmutable()) {
-        return ErrorInvalidOperation(
-            "texImage2D: disallowed because the texture "
-            "bound to this target has already been made immutable by texStorage2D");
-    }
-    MakeContextCurrent();
-
-    nsAutoArrayPtr<uint8_t> convertedData;
-    void* pixels = nullptr;
-    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
-
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(effectiveInternalFormat);
-    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
-
-    if (byteLength) {
-        size_t   bitsPerTexel = GetBitsPerTexel(effectiveInternalFormat);
-        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-        size_t   dstTexelSize = bitsPerTexel / 8;
-        size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-        size_t   dstPlainRowSize = dstTexelSize * width;
-        size_t   unpackAlignment = mPixelStoreUnpackAlignment;
-        size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
-
-        if (actualSrcFormat == dstFormat &&
-            srcPremultiplied == mPixelStorePremultiplyAlpha &&
-            srcStride == dstStride &&
-            !mPixelStoreFlipY)
-        {
-            // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-            pixels = data;
-        }
-        else
-        {
-            size_t convertedDataSize = height * dstStride;
-            convertedData = new (fallible) uint8_t[convertedDataSize];
-            if (!convertedData) {
-                ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
-                                 " a buffer for doing format conversion.");
-                return;
-            }
-            if (!ConvertImage(width, height, srcStride, dstStride,
-                              static_cast<uint8_t*>(data), convertedData,
-                              actualSrcFormat, srcPremultiplied,
-                              dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize))
-            {
-                return ErrorInvalidOperation("texImage2D: Unsupported texture format conversion");
-            }
-            pixels = reinterpret_cast<void*>(convertedData.get());
-        }
-        imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
-    }
-
-    GLenum error = CheckedTexImage2D(texImageTarget, level, internalformat, width,
-                                     height, border, format, type, pixels);
-
-    if (error) {
-        GenerateWarning("texImage2D generated error %s", ErrorName(error));
-        return;
-    }
-
-    // in all of the code paths above, we should have either initialized data,
-    // or allocated data and left it uninitialized, but in any case we shouldn't
-    // have NoImageData at this point.
-    MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
-
-    tex->SetImageInfo(texImageTarget, level, width, height, 1,
-                      effectiveInternalFormat, imageInfoStatusIfSuccess);
-}
-
-void
-WebGLContext::TexImage2D(GLenum rawTarget, GLint level,
-                         GLenum internalformat, GLsizei width,
-                         GLsizei height, GLint border, GLenum format,
-                         GLenum type, const dom::Nullable<ArrayBufferView>& pixels,
-                         ErrorResult& rv)
-{
-    if (IsContextLost())
-        return;
-
-    void* data;
-    uint32_t length;
-    js::Scalar::Type jsArrayType;
-    if (pixels.IsNull()) {
-        data = nullptr;
-        length = 0;
-        jsArrayType = js::Scalar::MaxTypedArrayViewType;
-    } else {
-        const ArrayBufferView& view = pixels.Value();
-        view.ComputeLengthAndData();
-
-        data = view.Data();
-        length = view.Length();
-        jsArrayType = view.Type();
-    }
-
-    if (!ValidateTexImageTarget(rawTarget, WebGLTexImageFunc::TexImage, WebGLTexDimensions::Tex2D))
-        return;
-
-    return TexImage2D_base(rawTarget, level, internalformat, width, height, 0, border, format, type,
-                           data, length, jsArrayType,
-                           WebGLTexelFormat::Auto, false);
-}
-
-void
-WebGLContext::TexImage2D(GLenum rawTarget, GLint level,
-                         GLenum internalformat, GLenum format,
-                         GLenum type, ImageData* pixels, ErrorResult& rv)
-{
-    if (IsContextLost())
-        return;
-
-    if (!pixels) {
-        // Spec says to generate an INVALID_VALUE error
-        return ErrorInvalidValue("texImage2D: null ImageData");
-    }
-
-    Uint8ClampedArray arr;
-    DebugOnly<bool> inited = arr.Init(pixels->GetDataObject());
-    MOZ_ASSERT(inited);
-    arr.ComputeLengthAndData();
-
-    void* pixelData = arr.Data();
-    const uint32_t pixelDataLength = arr.Length();
-
-    if (!ValidateTexImageTarget(rawTarget, WebGLTexImageFunc::TexImage, WebGLTexDimensions::Tex2D))
-        return;
-
-    return TexImage2D_base(rawTarget, level, internalformat, pixels->Width(),
-                           pixels->Height(), 4*pixels->Width(), 0,
-                           format, type, pixelData, pixelDataLength, js::Scalar::MaxTypedArrayViewType,
-                           WebGLTexelFormat::RGBA8, false);
-}
-
-
-void
-WebGLContext::TexSubImage2D_base(GLenum rawImageTarget, GLint level,
-                                 GLint xoffset, GLint yoffset,
-                                 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
-                                 GLenum format, GLenum type,
-                                 void* data, uint32_t byteLength,
-                                 js::Scalar::Type jsArrayType,
-                                 WebGLTexelFormat srcFormat, bool srcPremultiplied)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (type == LOCAL_GL_HALF_FLOAT_OES)
-        type = LOCAL_GL_HALF_FLOAT;
-
-    if (!ValidateTexImageTarget(rawImageTarget, func, dims))
-        return;
-
-    TexImageTarget texImageTarget(rawImageTarget);
-
-    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-    if (!tex)
-        return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
-
-    if (!tex->HasImageInfoAt(texImageTarget, level))
-        return ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
-
-    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
-
-    if (!ValidateTexImage(texImageTarget, level,
-                          existingEffectiveInternalFormat.get(),
-                          xoffset, yoffset, 0,
-                          width, height, 0,
-                          0, format, type, func, dims))
-    {
-        return;
-    }
-
-    if (!ValidateTexInputData(type, jsArrayType, func, dims))
-        return;
-
-    if (type != TypeFromInternalFormat(existingEffectiveInternalFormat)) {
-        return ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image");
-    }
-
-    size_t srcTexelSize = size_t(-1);
-    if (srcFormat == WebGLTexelFormat::Auto) {
-        const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
-        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-        srcTexelSize = bitsPerTexel / 8;
-    } else {
-        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
-    }
-
-    if (width == 0 || height == 0)
-        return; // ES 2.0 says it has no effect, we better return right now
-
-    CheckedUint32 checked_neededByteLength =
-        GetImageSize(height, width, 1, srcTexelSize, mPixelStoreUnpackAlignment);
-
-    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-
-    CheckedUint32 checked_alignedRowSize =
-        RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (byteLength < bytesNeeded)
-        return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
-
-    if (imageInfo.HasUninitializedImageData()) {
-        bool coversWholeImage = xoffset == 0 &&
-                                yoffset == 0 &&
-                                width == imageInfo.Width() &&
-                                height == imageInfo.Height();
-        if (coversWholeImage) {
-            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
-        } else {
-            if (!tex->EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-    MakeContextCurrent();
-
-    size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-    uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
-    size_t   dstPlainRowSize = dstTexelSize * width;
-    // There are checks above to ensure that this won't overflow.
-    size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
-
-    void* pixels = data;
-    nsAutoArrayPtr<uint8_t> convertedData;
-
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(existingEffectiveInternalFormat);
-    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
-
-    // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-    bool noConversion = (actualSrcFormat == dstFormat &&
-                         srcPremultiplied == mPixelStorePremultiplyAlpha &&
-                         srcStride == dstStride &&
-                         !mPixelStoreFlipY);
-
-    if (!noConversion) {
-        size_t convertedDataSize = height * dstStride;
-        convertedData = new (fallible) uint8_t[convertedDataSize];
-        if (!convertedData) {
-            ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
-                             " a buffer for doing format conversion.");
-            return;
-        }
-        if (!ConvertImage(width, height, srcStride, dstStride,
-                          static_cast<const uint8_t*>(data), convertedData,
-                          actualSrcFormat, srcPremultiplied,
-                          dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize))
-        {
-            return ErrorInvalidOperation("texSubImage2D: Unsupported texture format conversion");
-        }
-        pixels = reinterpret_cast<void*>(convertedData.get());
-    }
-
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             existingEffectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    gl->fTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
-}
-
-void
-WebGLContext::TexSubImage2D(GLenum rawTarget, GLint level,
-                            GLint xoffset, GLint yoffset,
-                            GLsizei width, GLsizei height,
-                            GLenum format, GLenum type,
-                            const dom::Nullable<ArrayBufferView>& pixels,
-                            ErrorResult& rv)
-{
-    if (IsContextLost())
-        return;
-
-    if (pixels.IsNull())
-        return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
-
-    const ArrayBufferView& view = pixels.Value();
-    view.ComputeLengthAndData();
-
-    if (!ValidateTexImageTarget(rawTarget, WebGLTexImageFunc::TexSubImage, WebGLTexDimensions::Tex2D))
-        return;
-
-    return TexSubImage2D_base(rawTarget, level, xoffset, yoffset,
-                              width, height, 0, format, type,
-                              view.Data(), view.Length(), view.Type(),
-                              WebGLTexelFormat::Auto, false);
-}
-
-void
-WebGLContext::TexSubImage2D(GLenum target, GLint level,
-                            GLint xoffset, GLint yoffset,
-                            GLenum format, GLenum type, ImageData* pixels,
-                            ErrorResult& rv)
-{
-    if (IsContextLost())
-        return;
-
-    if (!pixels)
-        return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
-
-    Uint8ClampedArray arr;
-    DebugOnly<bool> inited = arr.Init(pixels->GetDataObject());
-    MOZ_ASSERT(inited);
-    arr.ComputeLengthAndData();
-
-    return TexSubImage2D_base(target, level, xoffset, yoffset,
-                              pixels->Width(), pixels->Height(),
-                              4*pixels->Width(), format, type,
-                              arr.Data(), arr.Length(),
-                              js::Scalar::MaxTypedArrayViewType,
-                              WebGLTexelFormat::RGBA8, false);
-}
-
 void
 WebGLContext::LoseContext()
 {
     if (IsContextLost())
         return ErrorInvalidOperation("loseContext: Context is already lost.");
 
     ForceLoseContext(true);
 }
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -0,0 +1,469 @@
+/* -*- 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 "WebGLContextUtils.h"
+#include "WebGLBuffer.h"
+#include "WebGLVertexAttribData.h"
+#include "WebGLShader.h"
+#include "WebGLProgram.h"
+#include "WebGLUniformLocation.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLRenderbuffer.h"
+#include "WebGLShaderPrecisionFormat.h"
+#include "WebGLTexture.h"
+#include "WebGLExtensions.h"
+#include "WebGLVertexArray.h"
+
+#include "nsString.h"
+#include "nsDebug.h"
+#include "nsReadableUtils.h"
+
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "GLContext.h"
+
+#include "nsContentUtils.h"
+#include "nsError.h"
+#include "nsLayoutUtils.h"
+
+#include "CanvasUtils.h"
+#include "gfxUtils.h"
+
+#include "jsfriendapi.h"
+
+#include "WebGLTexelConversions.h"
+#include "WebGLValidateStrings.h"
+#include <algorithm>
+
+// needed to check if current OS is lower than 10.7
+#if defined(MOZ_WIDGET_COCOA)
+#include "nsCocoaFeatures.h"
+#endif
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/Endian.h"
+
+namespace mozilla {
+
+static bool
+IsValidTexTarget(WebGLContext* webgl, GLenum rawTexTarget, TexTarget* const out)
+{
+    switch (rawTexTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+    case LOCAL_GL_TEXTURE_CUBE_MAP:
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+        if (!webgl->IsWebGL2())
+            return false;
+
+        break;
+
+    default:
+        return false;
+    }
+
+    *out = rawTexTarget;
+    return true;
+}
+
+static bool
+IsValidTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+                      TexImageTarget* const out)
+{
+    switch (rawTexImageTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+    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:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+        if (!webgl->IsWebGL2())
+            return false;
+
+        break;
+
+    default:
+        return false;
+    }
+
+    *out = rawTexImageTarget;
+    return true;
+}
+
+bool
+ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+                  TexTarget* const out_texTarget, WebGLTexture** const out_tex)
+{
+    if (webgl->IsContextLost())
+        return false;
+
+    TexTarget texTarget;
+    if (!IsValidTexTarget(webgl, rawTexTarget, &texTarget)) {
+        webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName);
+        return false;
+    }
+
+    WebGLTexture* tex = webgl->ActiveBoundTextureForTarget(texTarget);
+    if (!tex) {
+        webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
+        return false;
+    }
+
+    *out_texTarget = texTarget;
+    *out_tex = tex;
+    return true;
+}
+
+bool
+ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+                       const char* funcName, TexImageTarget* const out_texImageTarget,
+                       WebGLTexture** const out_tex)
+{
+    if (webgl->IsContextLost())
+        return false;
+
+    TexImageTarget texImageTarget;
+    if (!IsValidTexImageTarget(webgl, rawTexImageTarget, &texImageTarget)) {
+        webgl->ErrorInvalidEnum("%s: Invalid texImageTarget.", funcName);
+        return false;
+    }
+
+    WebGLTexture* tex = webgl->ActiveBoundTextureForTexImageTarget(texImageTarget);
+    if (!tex) {
+        webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
+        return false;
+    }
+
+    *out_texImageTarget = texImageTarget;
+    *out_tex = tex;
+    return true;
+}
+
+bool
+WebGLContext::IsTexParamValid(GLenum pname) const
+{
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+    case LOCAL_GL_TEXTURE_WRAP_S:
+    case LOCAL_GL_TEXTURE_WRAP_T:
+        return true;
+
+    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
+        return IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic);
+
+    default:
+        return false;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// GL calls
+
+void
+WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
+{
+    if (IsContextLost())
+        return;
+
+     if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
+        return;
+
+    // Need to check rawTarget first before comparing against newTex->Target() as
+    // newTex->Target() returns a TexTarget, which will assert on invalid value.
+    WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
+    switch (rawTarget) {
+        case LOCAL_GL_TEXTURE_2D:
+            currentTexPtr = &mBound2DTextures[mActiveTexture];
+            break;
+
+       case LOCAL_GL_TEXTURE_CUBE_MAP:
+            currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
+            break;
+
+       case LOCAL_GL_TEXTURE_3D:
+            if (!IsWebGL2())
+                return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer");
+
+            currentTexPtr = &mBound3DTextures[mActiveTexture];
+            break;
+
+       default:
+            return ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
+    }
+    const TexTarget texTarget(rawTarget);
+
+    MakeContextCurrent();
+
+    if (newTex && !newTex->BindTexture(texTarget))
+        return;
+
+    if (!newTex) {
+        gl->fBindTexture(texTarget.get(), 0);
+    }
+
+    *currentTexPtr = newTex;
+}
+
+void
+WebGLContext::GenerateMipmap(GLenum rawTexTarget)
+{
+    TexTarget texTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+        return;
+
+    tex->GenerateMipmap(texTarget);
+}
+
+JS::Value
+WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname)
+{
+    TexTarget texTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+        return JS::NullValue();
+
+    if (!IsTexParamValid(pname)) {
+        ErrorInvalidEnumInfo("getTexParameter: pname", pname);
+        return JS::NullValue();
+    }
+
+    return tex->GetTexParameter(texTarget, pname);
+}
+
+bool
+WebGLContext::IsTexture(WebGLTexture* tex)
+{
+    if (IsContextLost())
+        return false;
+
+    if (!ValidateObjectAllowDeleted("isTexture", tex))
+        return false;
+
+    return tex->IsTexture();
+}
+
+void
+WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
+                                GLfloat* maybeFloatParam)
+{
+    MOZ_ASSERT(maybeIntParam || maybeFloatParam);
+
+    TexTarget texTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+        return;
+
+    tex->TexParameter(texTarget, pname, maybeIntParam, maybeFloatParam);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+// Uploads
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// TexImage
+
+void
+WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                         GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                         ErrorResult* const out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, elem,
+                    out_rv);
+}
+
+void
+WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                         GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
+                         GLenum unpackType,
+                         const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                         ErrorResult& out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexImage2D(texImageTarget, level, internalFormat, width, height, border,
+                    unpackFormat, unpackType, maybeView, &out_rv);
+}
+
+void
+WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                         GLenum unpackFormat, GLenum unpackType,
+                         dom::ImageData* imageData, ErrorResult& out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType,
+                    imageData, &out_rv);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// TexSubImage
+
+
+void
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                            dom::Element* elem, ErrorResult* const out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
+                       elem, out_rv);
+}
+
+void
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLsizei width, GLsizei height,
+                            GLenum unpackFormat, GLenum unpackType,
+                            const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                            ErrorResult& out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
+                       unpackFormat, unpackType, maybeView, &out_rv);
+}
+
+void
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                            GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                            ErrorResult& out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
+                       imageData, &out_rv);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CopyTex(Sub)Image
+
+
+void
+WebGLContext::CopyTexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                             GLint x, GLint y, GLsizei width, GLsizei height,
+                             GLint border)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
+
+    tex->CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height,
+                        border);
+}
+
+void
+WebGLContext::CopyTexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                                GLint yOffset, GLint x, GLint y, GLsizei width,
+                                GLsizei height)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexSubImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
+
+    tex->CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CompressedTex(Sub)Image
+
+
+void
+WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level,
+                                   GLenum internalFormat, GLsizei width, GLsizei height,
+                                   GLint border, const dom::ArrayBufferView& view)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
+
+    tex->CompressedTexImage2D(texImageTarget, level, internalFormat, width, height,
+                              border, view);
+}
+
+void
+WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level,
+                                      GLint xOffset, GLint yOffset, GLsizei width,
+                                      GLsizei height, GLenum unpackFormat,
+                                      const dom::ArrayBufferView& view)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexSubImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
+
+    tex->CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
+                                 unpackFormat, view);
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -996,17 +996,17 @@ WebGLContext::EnumName(GLenum glenum, ns
         *out_name = nsDependentCString(name);
     } else {
         nsPrintfCString enumAsHex("<enum 0x%04x>", glenum);
         *out_name = enumAsHex;
     }
 }
 
 bool
-WebGLContext::IsCompressedTextureFormat(GLenum format)
+IsCompressedTextureFormat(GLenum format)
 {
     switch (format) {
     case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
     case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
     case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
     case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
     case LOCAL_GL_ATC_RGB:
     case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
@@ -1029,17 +1029,17 @@ WebGLContext::IsCompressedTextureFormat(
         return true;
     default:
         return false;
     }
 }
 
 
 bool
-WebGLContext::IsTextureFormatCompressed(TexInternalFormat format)
+IsTextureFormatCompressed(TexInternalFormat format)
 {
     return IsCompressedTextureFormat(format.get());
 }
 
 GLenum
 WebGLContext::GetAndFlushUnderlyingGLErrors()
 {
     // Get and clear GL error in ALL cases.
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -56,16 +56,19 @@ size_t GetBitsPerTexel(TexInternalFormat
 // the currently bound texture is appropriate for this texImageTarget.
 //
 // Returns GL_NONE if passed an invalid texture image target
 TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
 
 // Helper function to create a JS::Value from a C string
 JS::Value StringValue(JSContext* cx, const char* str, ErrorResult& rv);
 
+bool IsCompressedTextureFormat(GLenum format);
+bool IsTextureFormatCompressed(TexInternalFormat format);
+
 struct GLComponents
 {
     unsigned char mComponents;
 
     enum Components {
         Red     = (1 << 0),
         Green   = (1 << 1),
         Blue    = (1 << 2),
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -603,50 +603,16 @@ WebGLContext::ValidateTexImageFormat(GLe
         return false;
     }
 
     ErrorInvalidEnumWithName(this, "invalid format", format, func, dims);
     return false;
 }
 
 /**
- * Check if the given texture target is valid for TexImage.
- */
-bool
-WebGLContext::ValidateTexImageTarget(GLenum target, WebGLTexImageFunc func,
-                                     WebGLTexDimensions dims)
-{
-    switch (dims) {
-    case WebGLTexDimensions::Tex2D:
-        if (target == LOCAL_GL_TEXTURE_2D ||
-            IsTexImageCubemapTarget(target))
-        {
-            return true;
-        }
-
-        ErrorInvalidEnumWithName(this, "invalid target", target, func, dims);
-        return false;
-
-    case WebGLTexDimensions::Tex3D:
-        if (target == LOCAL_GL_TEXTURE_3D)
-        {
-            return true;
-        }
-
-        ErrorInvalidEnumWithName(this, "invalid target", target, func, dims);
-        return false;
-
-    default:
-        MOZ_ASSERT(false, "ValidateTexImageTarget: Invalid dims");
-    }
-
-    return false;
-}
-
-/**
  * Return true if type is a valid texture image type for source,
  * taking into account enabled WebGL extensions.
  */
 bool
 WebGLContext::ValidateTexImageType(GLenum type, WebGLTexImageFunc func,
                                    WebGLTexDimensions dims)
 {
     /* Core WebGL texture types */
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -3,23 +3,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLTexture.h"
 
 #include <algorithm>
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/gfx/Logging.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexelConversions.h"
-#include "mozilla/gfx/Logging.h"
 
 namespace mozilla {
 
 JSObject*
 WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
     return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
 }
 
@@ -725,14 +725,297 @@ WebGLTexture::EnsureInitializedImageData
 
 void
 WebGLTexture::SetFakeBlackStatus(WebGLTextureFakeBlackStatus x)
 {
     mFakeBlackStatus = x;
     mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+// GL calls
+
+bool
+WebGLTexture::BindTexture(TexTarget texTarget)
+{
+    // silently ignore a deleted texture
+    if (IsDeleted())
+        return false;
+
+    if (HasEverBeenBound() && mTarget != texTarget) {
+        mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
+        return false;
+    }
+
+    mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
+    Bind(texTarget);
+    return true;
+}
+
+void
+WebGLTexture::GenerateMipmap(TexTarget texTarget)
+{
+    const TexImageTarget imageTarget = (texTarget == LOCAL_GL_TEXTURE_2D)
+                                                  ? LOCAL_GL_TEXTURE_2D
+                                                  : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+    if (!IsMipmapRangeValid())
+    {
+        return mContext->ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range.");
+    }
+    if (!HasImageInfoAt(imageTarget, EffectiveBaseMipmapLevel()))
+    {
+        return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
+    }
+
+    if (!mContext->IsWebGL2() && !IsFirstImagePowerOfTwo())
+        return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
+
+    TexInternalFormat internalformat = ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
+    if (IsTextureFormatCompressed(internalformat))
+        return mContext->ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
+
+    if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
+        (IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat)))
+    {
+        return mContext->ErrorInvalidOperation("generateMipmap: "
+                                     "A texture that has a base internal format of "
+                                     "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
+    }
+
+    if (!AreAllLevel0ImageInfosEqual())
+        return mContext->ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
+
+    SetGeneratedMipmap();
+
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+
+    if (gl->WorkAroundDriverBugs()) {
+        // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
+        // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
+        // overhead so we do it unconditionally.
+        //
+        // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
+        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST);
+        gl->fGenerateMipmap(texTarget.get());
+        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, MinFilter().get());
+    } else {
+        gl->fGenerateMipmap(texTarget.get());
+    }
+}
+
+JS::Value
+WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
+{
+    mContext->MakeContextCurrent();
+
+    GLint i = 0;
+    GLfloat f = 0.0f;
+
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+    case LOCAL_GL_TEXTURE_WRAP_S:
+    case LOCAL_GL_TEXTURE_WRAP_T:
+    case LOCAL_GL_TEXTURE_BASE_LEVEL:
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+    case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
+    case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
+    case LOCAL_GL_TEXTURE_MAX_LEVEL:
+    case LOCAL_GL_TEXTURE_SWIZZLE_A:
+    case LOCAL_GL_TEXTURE_SWIZZLE_B:
+    case LOCAL_GL_TEXTURE_SWIZZLE_G:
+    case LOCAL_GL_TEXTURE_SWIZZLE_R:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i);
+        return JS::NumberValue(uint32_t(i));
+
+    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+        mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f);
+        return JS::NumberValue(float(f));
+
+    default:
+        MOZ_CRASH("Unhandled pname.");
+    }
+}
+
+bool
+WebGLTexture::IsTexture() const
+{
+    return HasEverBeenBound() && !IsDeleted();
+}
+
+// Here we have to support all pnames with both int and float params.
+// See this discussion:
+//   https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
+void
+WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
+                           GLfloat* maybeFloatParam)
+{
+    MOZ_ASSERT(maybeIntParam || maybeFloatParam);
+
+    GLint   intParam   = maybeIntParam   ? *maybeIntParam   : GLint(*maybeFloatParam);
+    GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam);
+
+    bool paramBadEnum = false;
+    bool paramBadValue = false;
+
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_BASE_LEVEL:
+    case LOCAL_GL_TEXTURE_MAX_LEVEL:
+        if (!mContext->IsWebGL2())
+            return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
+
+        if (intParam < 0) {
+            paramBadValue = true;
+            break;
+        }
+
+        SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
+
+        if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL)
+            mBaseMipmapLevel = intParam;
+        else
+            mMaxMipmapLevel = intParam;
+
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+        if (!mContext->IsWebGL2())
+            return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
+
+        paramBadValue = (intParam != LOCAL_GL_NONE &&
+                         intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        if (!mContext->IsWebGL2())
+            return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
+
+        switch (intParam) {
+        case LOCAL_GL_LEQUAL:
+        case LOCAL_GL_GEQUAL:
+        case LOCAL_GL_LESS:
+        case LOCAL_GL_GREATER:
+        case LOCAL_GL_EQUAL:
+        case LOCAL_GL_NOTEQUAL:
+        case LOCAL_GL_ALWAYS:
+        case LOCAL_GL_NEVER:
+            break;
+
+        default:
+            paramBadValue = true;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+        switch (intParam) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+        case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
+        case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
+        case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
+        case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
+            SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
+            mMinFilter = intParam;
+            break;
+
+        default:
+            paramBadEnum = true;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+        switch (intParam) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+            SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
+            mMagFilter = intParam;
+            break;
+
+        default:
+            paramBadEnum = true;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_S:
+        switch (intParam) {
+        case LOCAL_GL_CLAMP_TO_EDGE:
+        case LOCAL_GL_MIRRORED_REPEAT:
+        case LOCAL_GL_REPEAT:
+            SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
+            mWrapS = intParam;
+            break;
+
+        default:
+            paramBadEnum = true;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_T:
+        switch (intParam) {
+        case LOCAL_GL_CLAMP_TO_EDGE:
+        case LOCAL_GL_MIRRORED_REPEAT:
+        case LOCAL_GL_REPEAT:
+            SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
+            mWrapT = intParam;
+            break;
+
+        default:
+            paramBadEnum = true;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
+        if (!mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic))
+            return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
+
+        if (maybeFloatParam && floatParam < 1.0f)
+            paramBadValue = true;
+        else if (maybeIntParam && intParam < 1)
+            paramBadValue = true;
+
+        break;
+
+    default:
+        return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
+    }
+
+    if (paramBadEnum) {
+        if (maybeIntParam) {
+            mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param"
+                                       " 0x%04x.",
+                                       pname, intParam);
+        } else {
+            mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.",
+                                       pname, floatParam);
+        }
+        return;
+    }
+
+    if (paramBadValue) {
+        if (maybeIntParam) {
+            mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i"
+                                        " (0x%x).",
+                                        pname, intParam, intParam);
+        } else {
+            mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.",
+                                        pname, floatParam);
+        }
+        return;
+    }
+
+    mContext->MakeContextCurrent();
+    if (maybeIntParam)
+        mContext->gl->fTexParameteri(texTarget.get(), pname, intParam);
+    else
+        mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam);
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -5,77 +5,245 @@
 
 #ifndef WEBGL_TEXTURE_H_
 #define WEBGL_TEXTURE_H_
 
 #include <algorithm>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/dom/TypedArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+class Element;
+class ImageData;
+} // namespace dom
 
 // Zero is not an integer power of two.
 inline bool
 IsPOTAssumingNonnegative(GLsizei x)
 {
     MOZ_ASSERT(x >= 0);
     return x && (x & (x-1)) == 0;
 }
 
+bool
+DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t dims,
+                          const char* funcName);
+
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
+    friend class WebGLContext;
+    friend class WebGLFramebuffer;
+
 public:
+    class ImageInfo;
+
+    const GLuint mGLName;
+
+protected:
+    GLenum mTarget;
+    TexMinFilter mMinFilter;
+    TexMagFilter mMagFilter;
+    TexWrap mWrapS, mWrapT;
+
+    size_t mFacesCount, mMaxLevelWithCustomImages;
+    nsTArray<ImageInfo> mImageInfos;
+
+    bool mHaveGeneratedMipmap; // Set by generateMipmap
+    bool mImmutable; // Set by texStorage*
+
+    size_t mBaseMipmapLevel; // Set by texParameter (defaults to 0)
+    size_t mMaxMipmapLevel;  // Set by texParameter (defaults to 1000)
+
+    WebGLTextureFakeBlackStatus mFakeBlackStatus;
+
+public:
+    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
+
     explicit WebGLTexture(WebGLContext* webgl, GLuint tex);
 
     void Delete();
 
     bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
     GLenum Target() const { return mTarget; }
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
-    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
-
 protected:
     ~WebGLTexture() {
         DeleteOnce();
     }
+public:
+    ////////////////////////////////////
+    // GL calls
+    bool BindTexture(TexTarget texTarget);
+    void GenerateMipmap(TexTarget texTarget);
+    JS::Value GetTexParameter(TexTarget texTarget, GLenum pname);
+    bool IsTexture() const;
+    void TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
+                      GLfloat* maybeFloatParam);
 
-    friend class WebGLContext;
-    friend class WebGLFramebuffer;
+    ////////////////////////////////////
+    // WebGLTextureUpload.cpp
+
+    void CompressedTexImage2D(TexImageTarget texImageTarget, GLint level,
+                              GLenum internalFormat, GLsizei width, GLsizei height,
+                              GLint border, const dom::ArrayBufferView& view);
+
+    void CompressedTexImage3D(TexImageTarget texImageTarget, GLint level,
+                              GLenum internalFormat, GLsizei width, GLsizei height,
+                              GLsizei depth, GLint border, GLsizei imageSize,
+                              const dom::ArrayBufferView& view);
+
+
+    void CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level,
+                                 GLint xOffset, GLint yOffset, GLsizei width,
+                                 GLsizei height, GLenum unpackFormat,
+                                 const dom::ArrayBufferView& view);
+
+    void CompressedTexSubImage3D(TexImageTarget texImageTarget, GLint level,
+                                 GLint xOffset, GLint yOffset, GLint zOffset,
+                                 GLsizei width, GLsizei height, GLsizei depth,
+                                 GLenum unpackFormat, GLsizei imageSize,
+                                 const dom::ArrayBufferView& view);
+
+
+    void CopyTexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
+                        GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+
+
+    void CopyTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                           GLint yOffset, GLint x, GLint y, GLsizei width,
+                           GLsizei height);
+
+    void CopyTexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                           GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width,
+                           GLsizei height);
+
+
+    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
+                    GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
+                    GLenum unpackType,
+                    const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                    ErrorResult* const out_rv);
+    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                    ErrorResult* const out_rv);
+    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                    ErrorResult* const out_rv);
+
+    void TexImage3D(TexImageTarget target, GLint level, GLenum internalFormat,
+                    GLsizei width, GLsizei height, GLsizei depth, GLint border,
+                    GLenum unpackFormat, GLenum unpackType,
+                    const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                    ErrorResult* const out_rv);
+
+
+    void TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
+                      GLsizei width, GLsizei height);
+    void TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
+                      GLsizei width, GLsizei height, GLsizei depth);
+
 
+    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat,
+                       GLenum unpackType,
+                       const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                       ErrorResult* const out_rv);
+    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                       dom::ImageData* imageData, ErrorResult* const out_rv);
+    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                       dom::Element* elem, ErrorResult* const out_rv);
+
+    void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLint zOffset, GLsizei width, GLsizei height,
+                       GLsizei depth, GLenum unpackFormat, GLenum unpackType,
+                       const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                       ErrorResult* const out_rv);
+    void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                       GLenum unpackType, dom::ImageData* imageData,
+                       ErrorResult* const out_rv);
+    void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                       GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                       GLenum unpackType, dom::Element* elem, ErrorResult* const out_rv);
+
+protected:
+
+    /** Like glTexImage2D, but if the call may change the texture size, checks
+     * any GL error generated by this glTexImage2D call and returns it.
+     */
+    GLenum CheckedTexImage2D(TexImageTarget texImageTarget, GLint level,
+                             TexInternalFormat internalFormat, GLsizei width,
+                             GLsizei height, GLint border, TexFormat format,
+                             TexType type, const GLvoid* data);
+
+    bool ValidateTexStorage(TexImageTarget texImageTarget, GLsizei levels, GLenum internalFormat,
+                            GLsizei width, GLsizei height, GLsizei depth,
+                            const char* funcName);
+    void SpecifyTexStorage(GLsizei levels, TexInternalFormat internalFormat,
+                           GLsizei width, GLsizei height, GLsizei depth);
+
+    void CopyTexSubImage2D_base(TexImageTarget texImageTarget,
+                                GLint level, TexInternalFormat internalFormat,
+                                GLint xoffset, GLint yoffset, GLint x, GLint y,
+                                GLsizei width, GLsizei height, bool isSub);
+
+    bool TexImageFromVideoElement(TexImageTarget texImageTarget, GLint level,
+                                  GLenum internalFormat, GLenum unpackFormat,
+                                  GLenum unpackType, dom::Element* elem);
+
+    // If jsArrayType is MaxTypedArrayViewType, it means no array.
+    void TexImage2D_base(TexImageTarget texImageTarget, GLint level,
+                         GLenum internalFormat, GLsizei width, GLsizei height,
+                         GLsizei srcStrideOrZero, GLint border, GLenum unpackFormat,
+                         GLenum unpackType, void* data, uint32_t byteLength,
+                         js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat,
+                         bool srcPremultiplied);
+    void TexSubImage2D_base(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLsizei width, GLsizei height,
+                            GLsizei srcStrideOrZero, GLenum unpackFormat,
+                            GLenum unpackType, void* pixels, uint32_t byteLength,
+                            js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat,
+                            bool srcPremultiplied);
+
+    bool ValidateTexStorage(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
+                                      GLsizei width, GLsizei height, GLsizei depth,
+                                      const char* info);
+    bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
+
+
+public:
     // We store information about the various images that are part of this
     // texture. (cubemap faces, mipmap levels)
-
-public:
-    const GLuint mGLName;
-
-protected:
-    GLenum mTarget;
-
-public:
     class ImageInfo
         : public WebGLRectangleObject
     {
     public:
         ImageInfo()
             : mEffectiveInternalFormat(LOCAL_GL_NONE)
             , mDepth(0)
             , mImageDataStatus(WebGLImageDataStatus::NoImageData)
@@ -211,30 +379,16 @@ public:
             SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
 
         imageInfo.mImageDataStatus = newStatus;
     }
 
     bool EnsureInitializedImageData(TexImageTarget imageTarget, GLint level);
 
 protected:
-    TexMinFilter mMinFilter;
-    TexMagFilter mMagFilter;
-    TexWrap mWrapS, mWrapT;
-
-    size_t mFacesCount, mMaxLevelWithCustomImages;
-    nsTArray<ImageInfo> mImageInfos;
-
-    bool mHaveGeneratedMipmap; // Set by generateMipmap
-    bool mImmutable; // Set by texStorage*
-
-    size_t mBaseMipmapLevel; // Set by texParameter (defaults to 0)
-    size_t mMaxMipmapLevel;  // Set by texParameter (defaults to 1000)
-
-    WebGLTextureFakeBlackStatus mFakeBlackStatus;
 
     void EnsureMaxLevelWithCustomImagesAtLeast(size_t maxLevelWithCustomImages) {
         mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages,
                                              maxLevelWithCustomImages);
         mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
     }
 
     bool CheckFloatTextureFilterParams() const {
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -0,0 +1,1419 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLTexture.h"
+
+#include <algorithm>
+#include "CanvasUtils.h"
+#include "GLBlitHelper.h"
+#include "GLContext.h"
+#include "mozilla/dom/HTMLVideoElement.h"
+#include "mozilla/dom/ImageData.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Scoped.h"
+#include "ScopedGLHelpers.h"
+#include "WebGLContext.h"
+#include "WebGLContextUtils.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLTexelConversions.h"
+
+namespace mozilla {
+
+bool
+DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t funcDims,
+                          const char* funcName)
+{
+    uint8_t targetDims;
+    switch (target.get()) {
+    case LOCAL_GL_TEXTURE_2D:
+    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:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        targetDims = 2;
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+        targetDims = 3;
+        break;
+
+    default:
+        MOZ_CRASH("Unhandled texImageTarget.");
+    }
+
+    if (targetDims != funcDims) {
+        webgl->ErrorInvalidEnum("%s: `target` must match function dimensions.", funcName);
+        return false;
+    }
+
+    return true;
+}
+
+void
+WebGLTexture::CompressedTexImage2D(TexImageTarget texImageTarget,
+                                   GLint level,
+                                   GLenum internalFormat,
+                                   GLsizei width, GLsizei height, GLint border,
+                                   const dom::ArrayBufferView& view)
+{
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+
+    const char funcName[] = "compressedTexImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (!mContext->ValidateTexImage(texImageTarget.get(), level, internalFormat,
+                          0, 0, 0, width, height, 0,
+                          border, LOCAL_GL_NONE,
+                          LOCAL_GL_NONE,
+                          func, dims))
+    {
+        return;
+    }
+
+    view.ComputeLengthAndData();
+
+    uint32_t byteLength = view.Length();
+    if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims)) {
+        return;
+    }
+
+    if (!mContext->ValidateCompTexImageSize(level, internalFormat, 0, 0, width, height, width, height, func, dims))
+    {
+        return;
+    }
+
+    if (mImmutable) {
+        return mContext->ErrorInvalidOperation(
+            "compressedTexImage2D: disallowed because the texture bound to "
+            "this target has already been made immutable by texStorage2D");
+    }
+
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+    gl->fCompressedTexImage2D(texImageTarget.get(), level, internalFormat, width, height, border, byteLength, view.Data());
+
+    SetImageInfo(texImageTarget, level, width, height, 1, internalFormat,
+                      WebGLImageDataStatus::InitializedImageData);
+}
+
+void
+WebGLTexture::CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                                      GLint yOffset, GLsizei width, GLsizei height,
+                                      GLenum internalFormat,
+                                      const dom::ArrayBufferView& view)
+{
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+
+    const char funcName[] = "compressedTexSubImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (!mContext->ValidateTexImage(texImageTarget.get(),
+                          level, internalFormat,
+                          xOffset, yOffset, 0,
+                          width, height, 0,
+                          0, LOCAL_GL_NONE, LOCAL_GL_NONE,
+                          func, dims))
+    {
+        return;
+    }
+
+    WebGLTexture::ImageInfo& levelInfo = ImageInfoAt(texImageTarget, level);
+
+    if (internalFormat != levelInfo.EffectiveInternalFormat()) {
+        return mContext->ErrorInvalidOperation("compressedTexImage2D: internalFormat does not match the existing image");
+    }
+
+    view.ComputeLengthAndData();
+
+    uint32_t byteLength = view.Length();
+    if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims))
+        return;
+
+    if (!mContext->ValidateCompTexImageSize(level, internalFormat,
+                                  xOffset, yOffset,
+                                  width, height,
+                                  levelInfo.Width(), levelInfo.Height(),
+                                  func, dims))
+    {
+        return;
+    }
+
+    if (levelInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xOffset == 0 &&
+                                yOffset == 0 &&
+                                width == levelInfo.Width() &&
+                                height == levelInfo.Height();
+        if (coversWholeImage) {
+            SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            if (!EnsureInitializedImageData(texImageTarget, level))
+                return;
+        }
+    }
+
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+    gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, internalFormat, byteLength, view.Data());
+}
+
+void
+WebGLTexture::CopyTexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
+                                     TexInternalFormat internalFormat,
+                                     GLint xOffset, GLint yOffset, GLint x,
+                                     GLint y, GLsizei width, GLsizei height,
+                                     bool sub)
+{
+    const WebGLRectangleObject* framebufferRect = mContext->CurValidReadFBRectObject();
+    GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
+    GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
+
+    WebGLTexImageFunc func = sub
+                             ? WebGLTexImageFunc::CopyTexSubImage
+                             : WebGLTexImageFunc::CopyTexImage;
+    WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+    const char* info = InfoFrom(func, dims);
+
+    // TODO: This changes with color_buffer_float. Reassess when the
+    // patch lands.
+    if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat.get(),
+                          xOffset, yOffset, 0,
+                          width, height, 0,
+                          0,
+                          LOCAL_GL_NONE, LOCAL_GL_NONE,
+                          func, dims))
+    {
+        return;
+    }
+
+    if (!mContext->ValidateCopyTexImage(internalFormat.get(), func, dims))
+        return;
+
+    if (!mContext->mBoundReadFramebuffer)
+        mContext->ClearBackbufferIfNeeded();
+
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+
+    if (mImmutable) {
+        if (!sub) {
+            return mContext->ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
+        }
+    }
+
+    TexType framebuffertype = LOCAL_GL_NONE;
+    if (mContext->mBoundReadFramebuffer) {
+        TexInternalFormat framebuffereffectiveformat = mContext->mBoundReadFramebuffer->ColorAttachment(0).EffectiveInternalFormat();
+        framebuffertype = TypeFromInternalFormat(framebuffereffectiveformat);
+    } else {
+        // FIXME - here we're assuming that the default framebuffer is backed by UNSIGNED_BYTE
+        // that might not always be true, say if we had a 16bpp default framebuffer.
+        framebuffertype = LOCAL_GL_UNSIGNED_BYTE;
+    }
+
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalFormat, framebuffertype);
+
+    // this should never fail, validation happened earlier.
+    MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
+
+    const bool widthOrHeightIsZero = (width == 0 || height == 0);
+    if (gl->WorkAroundDriverBugs() &&
+        sub && widthOrHeightIsZero)
+    {
+        // NV driver on Linux complains that CopyTexSubImage2D(level=0,
+        // xOffset=0, yOffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB
+        // to a 0x2 texture. This a useless thing to do, but technically legal.
+        // NV331.38 generates INVALID_VALUE.
+        return mContext->DummyFramebufferOperation(info);
+    }
+
+    // check if the memory size of this texture may change with this call
+    bool sizeMayChange = !sub;
+    if (!sub && HasImageInfoAt(texImageTarget, level)) {
+        const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+        sizeMayChange = width != imageInfo.Width() ||
+                        height != imageInfo.Height() ||
+                        effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
+    }
+
+    if (sizeMayChange)
+        mContext->GetAndFlushUnderlyingGLErrors();
+
+    if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
+        if (sub)
+            gl->fCopyTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, x, y, width, height);
+        else
+            gl->fCopyTexImage2D(texImageTarget.get(), level, internalFormat.get(), x, y, width, height, 0);
+    } else {
+
+        // the rect doesn't fit in the framebuffer
+
+        // first, we initialize the texture as black
+        if (!sub) {
+            SetImageInfo(texImageTarget, level, width, height, 1,
+                      effectiveInternalFormat,
+                      WebGLImageDataStatus::UninitializedImageData);
+            if (!EnsureInitializedImageData(texImageTarget, level))
+                return;
+        }
+
+        // if we are completely outside of the framebuffer, we can exit now with our black texture
+        if (   x >= framebufferWidth
+            || x+width <= 0
+            || y >= framebufferHeight
+            || y+height <= 0)
+        {
+            // we are completely outside of range, can exit now with buffer filled with zeros
+            return mContext->DummyFramebufferOperation(info);
+        }
+
+        GLint   actual_x             = clamped(x, 0, framebufferWidth);
+        GLint   actual_x_plus_width  = clamped(x + width, 0, framebufferWidth);
+        GLsizei actual_width   = actual_x_plus_width  - actual_x;
+        GLint   actual_xOffset = xOffset + actual_x - x;
+
+        GLint   actual_y             = clamped(y, 0, framebufferHeight);
+        GLint   actual_y_plus_height = clamped(y + height, 0, framebufferHeight);
+        GLsizei actual_height  = actual_y_plus_height - actual_y;
+        GLint   actual_yOffset = yOffset + actual_y - y;
+
+        gl->fCopyTexSubImage2D(texImageTarget.get(), level, actual_xOffset, actual_yOffset, actual_x, actual_y, actual_width, actual_height);
+    }
+
+    if (sizeMayChange) {
+        GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
+        if (error) {
+            mContext->GenerateWarning("copyTexImage2D generated error %s", mContext->ErrorName(error));
+            return;
+        }
+    }
+
+    if (!sub) {
+        SetImageInfo(texImageTarget, level, width, height, 1,
+                          effectiveInternalFormat,
+                          WebGLImageDataStatus::InitializedImageData);
+    }
+}
+
+void
+WebGLTexture::CopyTexImage2D(TexImageTarget texImageTarget,
+                             GLint level,
+                             GLenum internalFormat,
+                             GLint x,
+                             GLint y,
+                             GLsizei width,
+                             GLsizei height,
+                             GLint border)
+{
+    // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+
+    const char funcName[] = "copyTexImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (!mContext->ValidateTexImage(texImageTarget.get(), level, internalFormat,
+                          0, 0, 0,
+                          width, height, 0,
+                          border, LOCAL_GL_NONE, LOCAL_GL_NONE,
+                          func, dims))
+    {
+        return;
+    }
+
+    if (!mContext->ValidateCopyTexImage(internalFormat, func, dims))
+        return;
+
+    if (!mContext->mBoundReadFramebuffer)
+        mContext->ClearBackbufferIfNeeded();
+
+    CopyTexSubImage2D_base(texImageTarget, level, internalFormat, 0, 0, x, y, width, height, false);
+}
+
+void
+WebGLTexture::CopyTexSubImage2D(TexImageTarget texImageTarget,
+                                GLint level,
+                                GLint xOffset,
+                                GLint yOffset,
+                                GLint x,
+                                GLint y,
+                                GLsizei width,
+                                GLsizei height)
+{
+    switch (texImageTarget.get()) {
+    case LOCAL_GL_TEXTURE_2D:
+    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:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        break;
+    default:
+        return mContext->ErrorInvalidEnumInfo("copyTexSubImage2D: target", texImageTarget.get());
+    }
+
+    if (level < 0)
+        return mContext->ErrorInvalidValue("copyTexSubImage2D: level may not be negative");
+
+    GLsizei maxTextureSize = mContext->MaxTextureSizeForTarget(TexImageTargetToTexTarget(texImageTarget));
+    if (!(maxTextureSize >> level))
+        return mContext->ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size");
+
+    if (width < 0 || height < 0)
+        return mContext->ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative");
+
+    if (xOffset < 0 || yOffset < 0)
+        return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset and yOffset may not be negative");
+
+    if (!HasImageInfoAt(texImageTarget, level))
+        return mContext->ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face");
+
+    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+    GLsizei texWidth = imageInfo.Width();
+    GLsizei texHeight = imageInfo.Height();
+
+    if (xOffset + width > texWidth || xOffset + width < 0)
+      return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset+width is too large");
+
+    if (yOffset + height > texHeight || yOffset + height < 0)
+      return mContext->ErrorInvalidValue("copyTexSubImage2D: yOffset+height is too large");
+
+    if (!mContext->mBoundReadFramebuffer)
+        mContext->ClearBackbufferIfNeeded();
+
+    if (imageInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xOffset == 0 &&
+                                yOffset == 0 &&
+                                width == texWidth &&
+                                height == texHeight;
+        if (coversWholeImage) {
+            SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            if (!EnsureInitializedImageData(texImageTarget, level))
+                return;
+        }
+    }
+
+    TexInternalFormat internalFormat;
+    TexType type;
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(),
+                                             &internalFormat, &type);
+    return CopyTexSubImage2D_base(texImageTarget, level, internalFormat, xOffset, yOffset, x, y, width, height, true);
+}
+
+
+GLenum
+WebGLTexture::CheckedTexImage2D(TexImageTarget texImageTarget,
+                                       GLint level,
+                                       TexInternalFormat internalFormat,
+                                       GLsizei width,
+                                       GLsizei height,
+                                       GLint border,
+                                       TexFormat unpackFormat,
+                                       TexType unpackType,
+                                       const GLvoid* data)
+{
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
+    bool sizeMayChange = true;
+
+    if (HasImageInfoAt(texImageTarget, level)) {
+        const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+        sizeMayChange = width != imageInfo.Width() ||
+                        height != imageInfo.Height() ||
+                        effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
+    }
+
+    gl::GLContext* gl = mContext->gl;
+
+    // Convert to format and type required by OpenGL 'driver'.
+    GLenum driverType = LOCAL_GL_NONE;
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             effectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverFormat,
+                                             &driverType);
+
+    if (sizeMayChange) {
+        mContext->GetAndFlushUnderlyingGLErrors();
+    }
+
+    gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
+
+    if (effectiveInternalFormat != driverInternalFormat)
+        SetLegacyTextureSwizzle(gl, texImageTarget.get(), internalFormat.get());
+
+    GLenum error = LOCAL_GL_NO_ERROR;
+    if (sizeMayChange) {
+        error = mContext->GetAndFlushUnderlyingGLErrors();
+    }
+
+    return error;
+}
+
+void
+WebGLTexture::TexImage2D_base(TexImageTarget texImageTarget, GLint level,
+                              GLenum internalFormat,
+                              GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
+                              GLint border,
+                              GLenum unpackFormat,
+                              GLenum unpackType,
+                              void* data, uint32_t byteLength,
+                              js::Scalar::Type jsArrayType,
+                              WebGLTexelFormat srcFormat, bool srcPremultiplied)
+{
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+
+    if (unpackType == LOCAL_GL_HALF_FLOAT_OES) {
+        unpackType = LOCAL_GL_HALF_FLOAT;
+    }
+
+    if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat,
+                          0, 0, 0,
+                          width, height, 0,
+                          border, unpackFormat, unpackType, func, dims))
+    {
+        return;
+    }
+
+    const bool isDepthTexture = unpackFormat == LOCAL_GL_DEPTH_COMPONENT ||
+                                unpackFormat == LOCAL_GL_DEPTH_STENCIL;
+
+    if (isDepthTexture && !mContext->IsWebGL2()) {
+        if (data != nullptr || level != 0)
+            return mContext->ErrorInvalidOperation("texImage2D: "
+                                         "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
+                                         "data must be nullptr, "
+                                         "level must be zero");
+    }
+
+    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
+        return;
+
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
+
+    if (effectiveInternalFormat == LOCAL_GL_NONE) {
+        return mContext->ErrorInvalidOperation("texImage2D: bad combination of internalFormat and type");
+    }
+
+    size_t srcTexelSize = size_t(-1);
+    if (srcFormat == WebGLTexelFormat::Auto) {
+        // we need to find the exact sized format of the source data. Slightly abusing
+        // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
+        // is the same thing as an unsized internalFormat.
+        TexInternalFormat effectiveSourceFormat =
+            EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType);
+        MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
+        const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
+        MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
+        srcTexelSize = srcbitsPerTexel / 8;
+    } else {
+        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
+    }
+
+    CheckedUint32 checked_neededByteLength =
+        mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
+
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
+    CheckedUint32 checked_alignedRowSize =
+        RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return mContext->ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
+
+    uint32_t bytesNeeded = checked_neededByteLength.value();
+
+    if (byteLength && byteLength < bytesNeeded)
+        return mContext->ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
+                                 bytesNeeded, byteLength);
+
+    if (mImmutable) {
+        return mContext->ErrorInvalidOperation(
+            "texImage2D: disallowed because the texture "
+            "bound to this target has already been made immutable by texStorage2D");
+    }
+    mContext->MakeContextCurrent();
+
+    nsAutoArrayPtr<uint8_t> convertedData;
+    void* pixels = nullptr;
+    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
+
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(effectiveInternalFormat);
+    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
+
+    if (byteLength) {
+        size_t   bitsPerTexel = GetBitsPerTexel(effectiveInternalFormat);
+        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
+        size_t   dstTexelSize = bitsPerTexel / 8;
+        size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
+        size_t   dstPlainRowSize = dstTexelSize * width;
+        size_t   unpackAlignment = mContext->mPixelStoreUnpackAlignment;
+        size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
+
+        if (actualSrcFormat == dstFormat &&
+            srcPremultiplied == mContext->mPixelStorePremultiplyAlpha &&
+            srcStride == dstStride &&
+            !mContext->mPixelStoreFlipY)
+        {
+            // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
+            pixels = data;
+        }
+        else
+        {
+            size_t convertedDataSize = height * dstStride;
+            convertedData = new (fallible) uint8_t[convertedDataSize];
+            if (!convertedData) {
+                mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
+                                 " a buffer for doing format conversion.");
+                return;
+            }
+            if (!mContext->ConvertImage(width, height, srcStride, dstStride,
+                              static_cast<uint8_t*>(data), convertedData,
+                              actualSrcFormat, srcPremultiplied,
+                              dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize))
+            {
+                return mContext->ErrorInvalidOperation("texImage2D: Unsupported texture format conversion");
+            }
+            pixels = reinterpret_cast<void*>(convertedData.get());
+        }
+        imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
+    }
+
+    GLenum error = CheckedTexImage2D(texImageTarget, level, internalFormat, width,
+                                     height, border, unpackFormat, unpackType, pixels);
+
+    if (error) {
+        mContext->GenerateWarning("texImage2D generated error %s", mContext->ErrorName(error));
+        return;
+    }
+
+    // in all of the code paths above, we should have either initialized data,
+    // or allocated data and left it uninitialized, but in any case we shouldn't
+    // have NoImageData at this point.
+    MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
+
+    SetImageInfo(texImageTarget, level, width, height, 1,
+                      effectiveInternalFormat, imageInfoStatusIfSuccess);
+}
+
+void
+WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
+                         GLenum internalFormat, GLsizei width,
+                         GLsizei height, GLint border, GLenum unpackFormat,
+                         GLenum unpackType, const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                         ErrorResult* const out_rv)
+{
+    void* data;
+    uint32_t length;
+    js::Scalar::Type jsArrayType;
+    if (maybeView.IsNull()) {
+        data = nullptr;
+        length = 0;
+        jsArrayType = js::Scalar::MaxTypedArrayViewType;
+    } else {
+        const dom::ArrayBufferView& view = maybeView.Value();
+        view.ComputeLengthAndData();
+
+        data = view.Data();
+        length = view.Length();
+        jsArrayType = view.Type();
+    }
+
+    const char funcName[] = "texImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    return TexImage2D_base(texImageTarget, level, internalFormat, width, height, 0, border, unpackFormat, unpackType,
+                           data, length, jsArrayType,
+                           WebGLTexelFormat::Auto, false);
+}
+
+void
+WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
+                         GLenum internalFormat, GLenum unpackFormat,
+                         GLenum unpackType, dom::ImageData* imageData, ErrorResult* const out_rv)
+{
+    if (!imageData) {
+        // Spec says to generate an INVALID_VALUE error
+        return mContext->ErrorInvalidValue("texImage2D: null ImageData");
+    }
+
+    dom::Uint8ClampedArray arr;
+    DebugOnly<bool> inited = arr.Init(imageData->GetDataObject());
+    MOZ_ASSERT(inited);
+    arr.ComputeLengthAndData();
+
+    void* pixelData = arr.Data();
+    const uint32_t pixelDataLength = arr.Length();
+
+    const char funcName[] = "texImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    return TexImage2D_base(texImageTarget, level, internalFormat, imageData->Width(),
+                           imageData->Height(), 4*imageData->Width(), 0,
+                           unpackFormat, unpackType, pixelData, pixelDataLength, js::Scalar::MaxTypedArrayViewType,
+                           WebGLTexelFormat::RGBA8, false);
+}
+
+void
+WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
+                GLenum internalFormat, GLenum unpackFormat, GLenum unpackType,
+                dom::Element* elem, ErrorResult* out_rv)
+{
+    const char funcName[] = "texImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (level < 0)
+        return mContext->ErrorInvalidValue("texImage2D: level is negative");
+
+    const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget);
+    if (level > maxLevel) {
+        mContext->ErrorInvalidValue("texImage2D: level %d is too large, max is %d",
+                          level, maxLevel);
+        return;
+    }
+
+    // Trying to handle the video by GPU directly first
+    if (TexImageFromVideoElement(texImageTarget, level, internalFormat,
+                                 unpackFormat, unpackType, elem))
+    {
+        return;
+    }
+
+    RefPtr<gfx::DataSourceSurface> data;
+    WebGLTexelFormat srcFormat;
+    nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem);
+    *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
+    if (out_rv->Failed() || !data)
+        return;
+
+    gfx::IntSize size = data->GetSize();
+    uint32_t byteLength = data->Stride() * size.height;
+    return TexImage2D_base(texImageTarget, level, internalFormat,
+                           size.width, size.height, data->Stride(), 0,
+                           unpackFormat, unpackType, data->GetData(), byteLength,
+                           js::Scalar::MaxTypedArrayViewType, srcFormat,
+                           res.mIsPremultiplied);
+}
+
+
+void
+WebGLTexture::TexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
+                                 GLint xOffset, GLint yOffset,
+                                 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
+                                 GLenum unpackFormat, GLenum unpackType,
+                                 void* data, uint32_t byteLength,
+                                 js::Scalar::Type jsArrayType,
+                                 WebGLTexelFormat srcFormat, bool srcPremultiplied)
+{
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+
+    if (unpackType == LOCAL_GL_HALF_FLOAT_OES)
+        unpackType = LOCAL_GL_HALF_FLOAT;
+
+    const char funcName[] = "texSubImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (!HasImageInfoAt(texImageTarget, level))
+        return mContext->ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
+
+    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
+
+    if (!mContext->ValidateTexImage(texImageTarget, level,
+                          existingEffectiveInternalFormat.get(),
+                          xOffset, yOffset, 0,
+                          width, height, 0,
+                          0, unpackFormat, unpackType, func, dims))
+    {
+        return;
+    }
+
+    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
+        return;
+
+    if (unpackType != TypeFromInternalFormat(existingEffectiveInternalFormat)) {
+        return mContext->ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image");
+    }
+
+    size_t srcTexelSize = size_t(-1);
+    if (srcFormat == WebGLTexelFormat::Auto) {
+        const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
+        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
+        srcTexelSize = bitsPerTexel / 8;
+    } else {
+        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
+    }
+
+    if (width == 0 || height == 0)
+        return; // ES 2.0 says it has no effect, we better return right now
+
+    CheckedUint32 checked_neededByteLength =
+        mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
+
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
+
+    CheckedUint32 checked_alignedRowSize =
+        RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
+
+    uint32_t bytesNeeded = checked_neededByteLength.value();
+
+    if (byteLength < bytesNeeded)
+        return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
+
+    if (imageInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xOffset == 0 &&
+                                yOffset == 0 &&
+                                width == imageInfo.Width() &&
+                                height == imageInfo.Height();
+        if (coversWholeImage) {
+            SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            if (!EnsureInitializedImageData(texImageTarget, level))
+                return;
+        }
+    }
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+
+    size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
+    uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
+    size_t   dstPlainRowSize = dstTexelSize * width;
+    // There are checks above to ensure that this won't overflow.
+    size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mContext->mPixelStoreUnpackAlignment).value();
+
+    void* pixels = data;
+    nsAutoArrayPtr<uint8_t> convertedData;
+
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(existingEffectiveInternalFormat);
+    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
+
+    // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
+    bool noConversion = (actualSrcFormat == dstFormat &&
+                         srcPremultiplied == mContext->mPixelStorePremultiplyAlpha &&
+                         srcStride == dstStride &&
+                         !mContext->mPixelStoreFlipY);
+
+    if (!noConversion) {
+        size_t convertedDataSize = height * dstStride;
+        convertedData = new (fallible) uint8_t[convertedDataSize];
+        if (!convertedData) {
+            mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
+                             " a buffer for doing format conversion.");
+            return;
+        }
+        if (!mContext->ConvertImage(width, height, srcStride, dstStride,
+                          static_cast<const uint8_t*>(data), convertedData,
+                          actualSrcFormat, srcPremultiplied,
+                          dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize))
+        {
+            return mContext->ErrorInvalidOperation("texSubImage2D: Unsupported texture format conversion");
+        }
+        pixels = reinterpret_cast<void*>(convertedData.get());
+    }
+
+    GLenum driverType = LOCAL_GL_NONE;
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             existingEffectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverFormat,
+                                             &driverType);
+
+    gl->fTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, driverFormat, driverType, pixels);
+}
+
+void
+WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level,
+                            GLint xOffset, GLint yOffset,
+                            GLsizei width, GLsizei height,
+                            GLenum unpackFormat, GLenum unpackType,
+                            const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                            ErrorResult* const out_rv)
+{
+    if (maybeView.IsNull())
+        return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!");
+
+    const dom::ArrayBufferView& view = maybeView.Value();
+    view.ComputeLengthAndData();
+
+    const char funcName[] = "texSubImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset,
+                              width, height, 0, unpackFormat, unpackType,
+                              view.Data(), view.Length(), view.Type(),
+                              WebGLTexelFormat::Auto, false);
+}
+
+void
+WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level,
+                            GLint xOffset, GLint yOffset,
+                            GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                            ErrorResult* const out_rv)
+{
+    if (!imageData)
+        return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!");
+
+    dom::Uint8ClampedArray arr;
+    DebugOnly<bool> inited = arr.Init(imageData->GetDataObject());
+    MOZ_ASSERT(inited);
+    arr.ComputeLengthAndData();
+
+    return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset,
+                              imageData->Width(), imageData->Height(),
+                              4*imageData->Width(), unpackFormat, unpackType,
+                              arr.Data(), arr.Length(),
+                              js::Scalar::MaxTypedArrayViewType,
+                              WebGLTexelFormat::RGBA8, false);
+}
+
+
+
+bool
+WebGLTexture::TexImageFromVideoElement(TexImageTarget texImageTarget,
+                                       GLint level, GLenum internalFormat,
+                                       GLenum unpackFormat, GLenum unpackType,
+                                       mozilla::dom::Element* elem)
+{
+    if (unpackType == LOCAL_GL_HALF_FLOAT_OES &&
+        !mContext->gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
+    {
+        unpackType = LOCAL_GL_HALF_FLOAT;
+    }
+
+    if (!mContext->ValidateTexImageFormatAndType(unpackFormat, unpackType,
+                                       WebGLTexImageFunc::TexImage,
+                                       WebGLTexDimensions::Tex2D))
+    {
+        return false;
+    }
+
+    dom::HTMLVideoElement* video = dom::HTMLVideoElement::FromContentOrNull(elem);
+    if (!video)
+        return false;
+
+    uint16_t readyState;
+    if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
+        readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)
+    {
+        //No frame inside, just return
+        return false;
+    }
+
+    // If it doesn't have a principal, just bail
+    nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
+    if (!principal)
+        return false;
+
+    mozilla::layers::ImageContainer* container = video->GetImageContainer();
+    if (!container)
+        return false;
+
+    if (video->GetCORSMode() == CORS_NONE) {
+        bool subsumes;
+        nsresult rv = mContext->mCanvasElement->NodePrincipal()->Subsumes(principal, &subsumes);
+        if (NS_FAILED(rv) || !subsumes) {
+            mContext->GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
+                                "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
+            return false;
+        }
+    }
+
+    layers::AutoLockImage lockedImage(container);
+    layers::Image* srcImage = lockedImage.GetImage();
+    if (!srcImage) {
+      return false;
+    }
+
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
+
+    const WebGLTexture::ImageInfo& info = ImageInfoAt(texImageTarget, 0);
+    bool dimensionsMatch = info.Width() == srcImage->GetSize().width &&
+                           info.Height() == srcImage->GetSize().height;
+    if (!dimensionsMatch) {
+        // we need to allocation
+        gl->fTexImage2D(texImageTarget.get(), level, internalFormat,
+                        srcImage->GetSize().width, srcImage->GetSize().height,
+                        0, unpackFormat, unpackType, nullptr);
+    }
+
+    const gl::OriginPos destOrigin = mContext->mPixelStoreFlipY ? gl::OriginPos::BottomLeft
+                                                      : gl::OriginPos::TopLeft;
+    bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage,
+                                                   srcImage->GetSize(),
+                                                   mGLName,
+                                                   texImageTarget.get(),
+                                                   destOrigin);
+    if (ok) {
+        TexInternalFormat effectiveInternalFormat =
+            EffectiveInternalFormatFromInternalFormatAndType(internalFormat,
+                                                             unpackType);
+        MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
+        SetImageInfo(texImageTarget, level, srcImage->GetSize().width,
+                          srcImage->GetSize().height, 1,
+                          effectiveInternalFormat,
+                          WebGLImageDataStatus::InitializedImageData);
+        Bind(TexImageTargetToTexTarget(texImageTarget));
+    }
+    return ok;
+}
+
+void
+WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                            dom::Element* elem, ErrorResult* const out_rv)
+{
+    const char funcName[] = "texSubImage2D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+        return;
+
+    if (level < 0)
+        return mContext->ErrorInvalidValue("texSubImage2D: level is negative");
+
+    const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget);
+    if (level > maxLevel) {
+        mContext->ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d",
+                          level, maxLevel);
+        return;
+    }
+
+    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+    const TexInternalFormat internalFormat = imageInfo.EffectiveInternalFormat();
+
+    // Trying to handle the video by GPU directly first
+    if (TexImageFromVideoElement(texImageTarget, level, internalFormat.get(), unpackFormat, unpackType, elem))
+    {
+        return;
+    }
+
+    RefPtr<gfx::DataSourceSurface> data;
+    WebGLTexelFormat srcFormat;
+    nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem);
+    *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
+    if (out_rv->Failed() || !data)
+        return;
+
+    gfx::IntSize size = data->GetSize();
+    uint32_t byteLength = data->Stride() * size.height;
+    TexSubImage2D_base(texImageTarget, level, xOffset, yOffset, size.width,
+                       size.height, data->Stride(), unpackFormat, unpackType, data->GetData(),
+                       byteLength, js::Scalar::MaxTypedArrayViewType, srcFormat,
+                       res.mIsPremultiplied);
+}
+
+bool
+WebGLTexture::ValidateSizedInternalFormat(GLenum internalFormat, const char* info)
+{
+    switch (internalFormat) {
+        // Sized Internal Formats
+        // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
+    case LOCAL_GL_R8:
+    case LOCAL_GL_R8_SNORM:
+    case LOCAL_GL_R16F:
+    case LOCAL_GL_R32F:
+    case LOCAL_GL_R8UI:
+    case LOCAL_GL_R8I:
+    case LOCAL_GL_R16UI:
+    case LOCAL_GL_R16I:
+    case LOCAL_GL_R32UI:
+    case LOCAL_GL_R32I:
+    case LOCAL_GL_RG8:
+    case LOCAL_GL_RG8_SNORM:
+    case LOCAL_GL_RG16F:
+    case LOCAL_GL_RG32F:
+    case LOCAL_GL_RG8UI:
+    case LOCAL_GL_RG8I:
+    case LOCAL_GL_RG16UI:
+    case LOCAL_GL_RG16I:
+    case LOCAL_GL_RG32UI:
+    case LOCAL_GL_RG32I:
+    case LOCAL_GL_RGB8:
+    case LOCAL_GL_SRGB8:
+    case LOCAL_GL_RGB565:
+    case LOCAL_GL_RGB8_SNORM:
+    case LOCAL_GL_R11F_G11F_B10F:
+    case LOCAL_GL_RGB9_E5:
+    case LOCAL_GL_RGB16F:
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_RGB8UI:
+    case LOCAL_GL_RGB8I:
+    case LOCAL_GL_RGB16UI:
+    case LOCAL_GL_RGB16I:
+    case LOCAL_GL_RGB32UI:
+    case LOCAL_GL_RGB32I:
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_SRGB8_ALPHA8:
+    case LOCAL_GL_RGBA8_SNORM:
+    case LOCAL_GL_RGB5_A1:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB10_A2:
+    case LOCAL_GL_RGBA16F:
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_RGBA8UI:
+    case LOCAL_GL_RGBA8I:
+    case LOCAL_GL_RGB10_A2UI:
+    case LOCAL_GL_RGBA16UI:
+    case LOCAL_GL_RGBA16I:
+    case LOCAL_GL_RGBA32I:
+    case LOCAL_GL_RGBA32UI:
+    case LOCAL_GL_DEPTH_COMPONENT16:
+    case LOCAL_GL_DEPTH_COMPONENT24:
+    case LOCAL_GL_DEPTH_COMPONENT32F:
+    case LOCAL_GL_DEPTH24_STENCIL8:
+    case LOCAL_GL_DEPTH32F_STENCIL8:
+        return true;
+    }
+
+    if (IsCompressedTextureFormat(internalFormat))
+        return true;
+
+    nsCString name;
+    mContext->EnumName(internalFormat, &name);
+    mContext->ErrorInvalidEnum("%s: invalid internal format %s", info, name.get());
+
+    return false;
+}
+
+
+/** Validates parameters to texStorage{2D,3D} */
+bool
+WebGLTexture::ValidateTexStorage(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
+                                      GLsizei width, GLsizei height, GLsizei depth,
+                                      const char* info)
+{
+    // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has
+    // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE.
+    if (mImmutable) {
+        mContext->ErrorInvalidOperation("%s: texture bound to target %s is already immutable",
+                                        info, mContext->EnumName(texTarget.get()));
+        return false;
+    }
+
+    // GL_INVALID_ENUM is generated if internalFormat is not a valid sized internal format.
+    if (!ValidateSizedInternalFormat(internalFormat, info))
+        return false;
+
+    // GL_INVALID_VALUE is generated if width, height or levels are less than 1.
+    if (width < 1)  { mContext->ErrorInvalidValue("%s: width is < 1", info);  return false; }
+    if (height < 1) { mContext->ErrorInvalidValue("%s: height is < 1", info); return false; }
+    if (depth < 1)  { mContext->ErrorInvalidValue("%s: depth is < 1", info);  return false; }
+    if (levels < 1) { mContext->ErrorInvalidValue("%s: levels is < 1", info); return false; }
+
+    // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1.
+    if (FloorLog2(std::max(std::max(width, height), depth)) + 1 < levels) {
+        mContext->ErrorInvalidOperation("%s: too many levels for given texture dimensions", info);
+        return false;
+    }
+
+    return true;
+}
+
+void
+WebGLTexture::TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height)
+{
+    // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
+    if (texTarget != LOCAL_GL_TEXTURE_2D && texTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
+        return mContext->ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP");
+
+    if (!ValidateTexStorage(texTarget, levels, internalFormat, width, height, 1, "texStorage2D"))
+        return;
+
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
+
+    mContext->GetAndFlushUnderlyingGLErrors();
+    gl->fTexStorage2D(texTarget.get(), levels, internalFormat, width, height);
+    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return mContext->GenerateWarning("texStorage2D generated error %s", mContext->ErrorName(error));
+    }
+
+    SetImmutable();
+
+    const size_t facesCount = (texTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
+    GLsizei w = width;
+    GLsizei h = height;
+    for (size_t l = 0; l < size_t(levels); l++) {
+        for (size_t f = 0; f < facesCount; f++) {
+            SetImageInfo(TexImageTargetForTargetAndFace(texTarget, f),
+                              l, w, h, 1,
+                              internalFormat,
+                              WebGLImageDataStatus::UninitializedImageData);
+        }
+        w = std::max(1, w / 2);
+        h = std::max(1, h / 2);
+    }
+}
+
+void
+WebGLTexture::TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
+                            GLsizei width, GLsizei height, GLsizei depth)
+{
+    // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
+    if (texTarget != LOCAL_GL_TEXTURE_3D)
+        return mContext->ErrorInvalidEnum("texStorage3D: target is not TEXTURE_3D");
+
+    if (!ValidateTexStorage(texTarget, levels, internalFormat, width, height, depth, "texStorage3D"))
+        return;
+
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
+
+    mContext->GetAndFlushUnderlyingGLErrors();
+    gl->fTexStorage3D(texTarget.get(), levels, internalFormat, width, height, depth);
+    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return mContext->GenerateWarning("texStorage3D generated error %s", mContext->ErrorName(error));
+    }
+
+    SetImmutable();
+
+    GLsizei w = width;
+    GLsizei h = height;
+    GLsizei d = depth;
+    for (size_t l = 0; l < size_t(levels); l++) {
+        SetImageInfo(TexImageTargetForTargetAndFace(texTarget, 0),
+                          l, w, h, d,
+                          internalFormat,
+                          WebGLImageDataStatus::UninitializedImageData);
+        w = std::max(1, w >> 1);
+        h = std::max(1, h >> 1);
+        d = std::max(1, d >> 1);
+    }
+}
+
+void
+WebGLTexture::TexImage3D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
+                          GLsizei width, GLsizei height, GLsizei depth,
+                          GLint border, GLenum unpackFormat, GLenum unpackType,
+                          const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                          ErrorResult* const out_rv)
+{
+    void* data;
+    size_t dataLength;
+    js::Scalar::Type jsArrayType;
+    if (maybeView.IsNull()) {
+        data = nullptr;
+        dataLength = 0;
+        jsArrayType = js::Scalar::MaxTypedArrayViewType;
+    } else {
+        const dom::ArrayBufferView& view = maybeView.Value();
+        view.ComputeLengthAndData();
+
+        data = view.Data();
+        dataLength = view.Length();
+        jsArrayType = view.Type();
+    }
+
+    const char funcName[] = "texImage3D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName))
+        return;
+
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
+
+    if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat,
+                          0, 0, 0,
+                          width, height, depth,
+                          border, unpackFormat, unpackType, func, dims))
+    {
+        return;
+    }
+
+    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
+        return;
+
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
+
+    if (effectiveInternalFormat == LOCAL_GL_NONE) {
+        return mContext->ErrorInvalidOperation("texImage3D: bad combination of internalFormat and unpackType");
+    }
+
+    // we need to find the exact sized format of the source data. Slightly abusing
+    // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
+    // is the same thing as an unsized internalFormat.
+    TexInternalFormat effectiveSourceFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType);
+    MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated unpack format/type combo earlier
+    const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
+    MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
+    size_t srcTexelSize = srcbitsPerTexel / 8;
+
+    CheckedUint32 checked_neededByteLength =
+        mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
+
+    uint32_t bytesNeeded = checked_neededByteLength.value();
+
+    if (dataLength && dataLength < bytesNeeded)
+        return mContext->ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)",
+                                 bytesNeeded, dataLength);
+
+    if (IsImmutable()) {
+        return mContext->ErrorInvalidOperation(
+            "texImage3D: disallowed because the texture "
+            "bound to this target has already been made immutable by texStorage3D");
+    }
+
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
+
+    GLenum driverUnpackType = LOCAL_GL_NONE;
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverUnpackFormat = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             effectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverUnpackFormat,
+                                             &driverUnpackType);
+
+    mContext->GetAndFlushUnderlyingGLErrors();
+    gl->fTexImage3D(texImageTarget.get(), level,
+                    driverInternalFormat,
+                    width, height, depth,
+                    0, driverUnpackFormat, driverUnpackType,
+                    data);
+    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return mContext->GenerateWarning("texImage3D generated error %s", mContext->ErrorName(error));
+    }
+
+    SetImageInfo(texImageTarget, level,
+                      width, height, depth,
+                      effectiveInternalFormat,
+                      data ? WebGLImageDataStatus::InitializedImageData
+                           : WebGLImageDataStatus::UninitializedImageData);
+}
+
+void
+WebGLTexture::TexSubImage3D(TexImageTarget texImageTarget, GLint level,
+                             GLint xOffset, GLint yOffset, GLint zOffset,
+                             GLsizei width, GLsizei height, GLsizei depth,
+                             GLenum unpackFormat, GLenum unpackType,
+                             const dom::Nullable<dom::ArrayBufferView>& maybeView,
+                             ErrorResult* const out_rv)
+{
+    if (maybeView.IsNull())
+        return mContext->ErrorInvalidValue("texSubImage3D: pixels must not be null!");
+
+    const dom::ArrayBufferView& view = maybeView.Value();
+    view.ComputeLengthAndData();
+
+    const char funcName[] = "texSubImage3D";
+    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName))
+        return;
+
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
+
+    if (!HasImageInfoAt(texImageTarget, level)) {
+        return mContext->ErrorInvalidOperation("texSubImage3D: no previously defined texture image");
+    }
+
+    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
+    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat();
+    TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE;
+    TexType existingType = LOCAL_GL_NONE;
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat,
+                                                            &existingUnsizedInternalFormat,
+                                                            &existingType);
+
+    if (!mContext->ValidateTexImage(texImageTarget, level, existingEffectiveInternalFormat.get(),
+                          xOffset, yOffset, zOffset,
+                          width, height, depth,
+                          0, unpackFormat, unpackType, func, dims))
+    {
+        return;
+    }
+
+    if (unpackType != existingType) {
+        return mContext->ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image");
+    }
+
+    js::Scalar::Type jsArrayType = view.Type();
+    void* data = view.Data();
+    size_t dataLength = view.Length();
+
+    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
+        return;
+
+    const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
+    MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
+    size_t srcTexelSize = bitsPerTexel / 8;
+
+    if (width == 0 || height == 0 || depth == 0)
+        return; // no effect, we better return right now
+
+    CheckedUint32 checked_neededByteLength =
+        mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
+
+    uint32_t bytesNeeded = checked_neededByteLength.value();
+
+    if (dataLength < bytesNeeded)
+        return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength);
+
+    if (imageInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xOffset == 0 &&
+                                yOffset == 0 &&
+                                zOffset == 0 &&
+                                width == imageInfo.Width() &&
+                                height == imageInfo.Height() &&
+                                depth == imageInfo.Depth();
+        if (coversWholeImage) {
+            SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            if (!EnsureInitializedImageData(texImageTarget, level))
+                return;
+        }
+    }
+
+    GLenum driverUnpackType = LOCAL_GL_NONE;
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverUnpackFormat = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(mContext->gl,
+                                             existingEffectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverUnpackFormat,
+                                             &driverUnpackType);
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fTexSubImage3D(texImageTarget.get(), level,
+                       xOffset, yOffset, zOffset,
+                       width, height, depth,
+                       driverUnpackFormat, driverUnpackType, data);
+
+}
+
+
+} // namespace mozilla
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -73,16 +73,17 @@ UNIFIED_SOURCES += [
     'WebGLContext.cpp',
     'WebGLContextBuffers.cpp',
     'WebGLContextDraw.cpp',
     'WebGLContextExtensions.cpp',
     'WebGLContextFramebufferOperations.cpp',
     'WebGLContextGL.cpp',
     'WebGLContextLossHandler.cpp',
     'WebGLContextState.cpp',
+    'WebGLContextTextures.cpp',
     'WebGLContextUnchecked.cpp',
     'WebGLContextUtils.cpp',
     'WebGLContextValidate.cpp',
     'WebGLContextVertexArray.cpp',
     'WebGLContextVertices.cpp',
     'WebGLElementArrayCache.cpp',
     'WebGLExtensionBase.cpp',
     'WebGLExtensionBlendMinMax.cpp',
@@ -120,16 +121,17 @@ UNIFIED_SOURCES += [
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
     'WebGLShaderValidator.cpp',
     'WebGLSync.cpp',
     'WebGLTexelConversions.cpp',
     'WebGLTexture.cpp',
+    'WebGLTextureUpload.cpp',
     'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
     'WebGLVertexArrayObject.cpp',
--- a/dom/canvas/test/captureStream_common.js
+++ b/dom/canvas/test/captureStream_common.js
@@ -30,17 +30,17 @@ CaptureStreamTestHelper.prototype = {
 
   /* Default element size for createAndAppendElement() */
   elemWidth: 100,
   elemHeight: 100,
 
   /* Request a frame from the stream played by |video|. */
   requestFrame: function (video) {
     info("Requesting frame from " + video.id);
-    video.mozSrcObject.requestFrame();
+    video.srcObject.requestFrame();
   },
 
   /* Tests the top left pixel of |video| against |refData|. Format [R,G,B,A]. */
   testPixel: function (video, refData, threshold) {
     var ctxout = this.cout.getContext('2d');
     ctxout.drawImage(video, 0, 0);
     var pixel = ctxout.getImageData(0, 0, 1, 1).data;
     return pixel.every((val, i) => Math.abs(val - refData[i]) <= threshold);
--- a/dom/canvas/test/reftest/capturestream.html
+++ b/dom/canvas/test/reftest/capturestream.html
@@ -14,17 +14,17 @@ function finished() {
 
 function runTest() {
   var canvas = document.getElementById('canvas');
   var context = canvas.getContext('2d');
   context.fillStyle = "rgba(0, 255, 0, 1)";
   context.fillRect(0, 0, canvas.width, canvas.height);
 
   var video = document.getElementById('video');
-  video.mozSrcObject = canvas.captureStream(0);
+  video.srcObject = canvas.captureStream(0);
   video.play();
   video.onloadeddata = finished;
   video.onerror = finished;
 }
   </script>
 </head>
 
 <body onload='runTest();'>
--- a/dom/canvas/test/reftest/webgl-capturestream-test.html
+++ b/dom/canvas/test/reftest/webgl-capturestream-test.html
@@ -28,17 +28,17 @@ function runTest() {
     setStatus('WebGL context creation failed.');
     return;
   }
 
   gl.clearColor(0.0, 1.0, 0.0, 1.0);
   gl.clear(gl.COLOR_BUFFER_BIT);
 
   var video = document.getElementById('video');
-  video.mozSrcObject = canvas.captureStream(0);
+  video.srcObject = canvas.captureStream(0);
   video.play();
   video.onloadeddata = finished;
   video.onerror = finished;
 }
   </script>
 </head>
 
 <body onload='runTest();'>
--- a/dom/canvas/test/test_capture.html
+++ b/dom/canvas/test/test_capture.html
@@ -14,19 +14,19 @@ var vauto;   // Video element with captu
 var vmanual; // Video element with captureStream stream in manual (fps 0) mode.
 var vrate;   // Video element with captureStream stream with fixed frame rate.
 
 function checkDrawColorInitialRed() {
   info("Checking that all video elements become red after first drawColor(red).");
 
   h.drawColor(c, h.red);
 
-  vauto.mozSrcObject = c.captureStream();
-  vmanual.mozSrcObject = c.captureStream(0);
-  vrate.mozSrcObject = c.captureStream(10);
+  vauto.srcObject = c.captureStream();
+  vmanual.srcObject = c.captureStream(0);
+  vrate.srcObject = c.captureStream(10);
 
   ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "vauto hould not be drawn to before stable state");
   ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "vrate Should not be drawn to before stable state");
   ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "vmanual Should not be drawn to before stable state");
 
   return Promise.resolve()
     .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically"))
     .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically"))
--- a/dom/canvas/test/webgl-mochitest/test_capture.html
+++ b/dom/canvas/test/webgl-mochitest/test_capture.html
@@ -45,19 +45,19 @@ function checkGLError(info) {
   is("0x" + error.toString(16), "0x0", "WebGL error [" + info + "]");
 }
 
 function checkClearColorInitialRed() {
   info("Checking that clearing to red works for first frame.");
 
   h.clearColor(c, h.red);
 
-  vauto.mozSrcObject = c.captureStream();
-  vmanual.mozSrcObject = c.captureStream(0);
-  vrate.mozSrcObject = c.captureStream(10);
+  vauto.srcObject = c.captureStream();
+  vmanual.srcObject = c.captureStream(0);
+  vrate.srcObject = c.captureStream(10);
 
   ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
   ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
   ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
 
   return Promise.resolve()
     .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically"))
     .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically"))
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -739,29 +739,31 @@ GetContentBR(dom::Element* aElement)
   return content->IsHTMLElement(nsGkAtoms::br) ? content : nullptr;
 }
 
 void
 IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
                                         dom::Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsIAtom* aAttribute,
-                                        int32_t aModType)
+                                        int32_t aModType,
+                                        const nsAttrValue* aNewValue)
 {
   nsIContent *content = GetContentBR(aElement);
   mPreAttrChangeLength = content ?
     ContentEventHandler::GetNativeTextLength(content) : 0;
 }
 
 void
 IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
                                      dom::Element* aElement,
                                      int32_t aNameSpaceID,
                                      nsIAtom* aAttribute,
-                                     int32_t aModType)
+                                     int32_t aModType,
+                                     const nsAttrValue* aOldValue)
 {
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
 
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (!mTextChangeData.IsValid() && causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
--- a/dom/html/HTMLButtonElement.cpp
+++ b/dom/html/HTMLButtonElement.cpp
@@ -488,17 +488,17 @@ HTMLButtonElement::DoneCreatingElement()
     if (NS_SUCCEEDED(rv)) {
       RestoreFormControlState();
     }
   }
 }
 
 nsresult
 HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                 const nsAttrValueOrString* aValue,
+                                 nsAttrValueOrString* aValue,
                                  bool aNotify)
 {
   if (aNotify && aName == nsGkAtoms::disabled &&
       aNameSpaceID == kNameSpaceID_None) {
     mDisabledChanged = true;
   }
 
   return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
--- a/dom/html/HTMLButtonElement.h
+++ b/dom/html/HTMLButtonElement.h
@@ -75,17 +75,17 @@ public:
 
   void UpdateBarredFromConstraintValidation();
   // Element
   EventStates IntrinsicState() const override;
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                 const nsAttrValueOrString* aValue,
+                                 nsAttrValueOrString* aValue,
                                  bool aNotify) override;
   /**
    * Called when an attribute has just been changed
    */
   nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                         const nsAttrValue* aValue, bool aNotify) override;
   virtual bool ParseAttribute(int32_t aNamespaceID,
                               nsIAtom* aAttribute,
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -370,17 +370,17 @@ nsMapRuleToAttributesFunc
 HTMLImageElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 
 nsresult
 HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                const nsAttrValueOrString* aValue,
+                                nsAttrValueOrString* aValue,
                                 bool aNotify)
 {
 
   if (aNameSpaceID == kNameSpaceID_None && mForm &&
       (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
     // remove the image from the hashtable as needed
     nsAutoString tmp;
     GetAttr(kNameSpaceID_None, aName, tmp);
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -329,17 +329,17 @@ protected:
 
   CSSIntPoint GetXY();
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
   void UpdateFormOwner();
 
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                 const nsAttrValueOrString* aValue,
+                                 nsAttrValueOrString* aValue,
                                  bool aNotify) override;
 
   virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify) override;
 
   // This is a weak reference that this element and the HTMLFormElement
   // cooperate in maintaining.
   HTMLFormElement* mForm;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1015,17 +1015,17 @@ HTMLInputElement::Clone(mozilla::dom::No
   }
 
   it.forget(aResult);
   return NS_OK;
 }
 
 nsresult
 HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                const nsAttrValueOrString* aValue,
+                                nsAttrValueOrString* aValue,
                                 bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     //
     // When name or type changes, radio should be removed from radio group.
     // (type changes are handled in the form itself currently)
     // If the parser is not done creating the radio, we also should not do it.
     //
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -860,17 +860,17 @@ protected:
                                 bool aShouldInvalidate);
 
   nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
 
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                 const nsAttrValueOrString* aValue,
+                                 nsAttrValueOrString* aValue,
                                  bool aNotify) override;
   /**
    * Called when an attribute has just been changed
    */
   virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify) override;
 
   /**
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -523,16 +523,40 @@ HTMLMediaElement::IsVideo()
 already_AddRefed<MediaSource>
 HTMLMediaElement::GetMozMediaSourceObject() const
 {
   nsRefPtr<MediaSource> source = mMediaSource;
   return source.forget();
 }
 
 already_AddRefed<DOMMediaStream>
+HTMLMediaElement::GetSrcObject() const
+{
+  NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
+               "MediaStream should have been set up properly");
+  nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
+  return stream.forget();
+}
+
+void
+HTMLMediaElement::SetSrcObject(DOMMediaStream& aValue)
+{
+  SetMozSrcObject(&aValue);
+}
+
+void
+HTMLMediaElement::SetSrcObject(DOMMediaStream* aValue)
+{
+  mSrcAttrStream = aValue;
+  DoLoad();
+}
+
+// TODO: Remove prefixed versions soon (1183495)
+
+already_AddRefed<DOMMediaStream>
 HTMLMediaElement::GetMozSrcObject() const
 {
   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
                "MediaStream should have been set up properly");
   nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
   return stream.forget();
 }
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -542,18 +542,22 @@ public:
   }
 
   void SetMozIsCasting(bool aShow)
   {
     mIsCasting = aShow;
   }
 
   already_AddRefed<MediaSource> GetMozMediaSourceObject() const;
+  already_AddRefed<DOMMediaStream> GetSrcObject() const;
+  void SetSrcObject(DOMMediaStream& aValue);
+  void SetSrcObject(DOMMediaStream* aValue);
+
+  // TODO: remove prefixed versions soon (1183495).
   already_AddRefed<DOMMediaStream> GetMozSrcObject() const;
-
   void SetMozSrcObject(DOMMediaStream& aValue);
   void SetMozSrcObject(DOMMediaStream* aValue);
 
   bool MozPreservesPitch() const
   {