Merge mozilla-central to inbound. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 04 Jul 2018 13:02:19 +0300
changeset 424978 796893f4d2f5555c8e29658bae05b98cffad0fe1
parent 424944 a355ad34b92b9b75bce1667db40a62063ead215f (current diff)
parent 424977 cc3401e78e8bbae22e6dbc854e525ceae4923bcf (diff)
child 424979 17718af0d6a867b484c30ac979fbfda1b8a722c7
push id104946
push userrgurzau@mozilla.com
push dateWed, 04 Jul 2018 10:03:16 +0000
treeherdermozilla-inbound@796893f4d2f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/.eslintignore
+++ b/.eslintignore
@@ -258,16 +258,17 @@ dom/tests/js/**
 dom/tests/mochitest/**
 dom/tests/unit/**
 dom/time/**
 dom/u2f/**
 dom/url/**
 dom/vr/**
 dom/webauthn/**
 dom/webbrowserpersist/**
+dom/webgpu/**
 dom/webidl/**
 dom/websocket/**
 dom/workers/**
 dom/worklet/**
 dom/xbl/**
 dom/xhr/**
 dom/xml/**
 dom/xslt/**
--- a/browser/base/content/hiddenWindow.xul
+++ b/browser/base/content/hiddenWindow.xul
@@ -1,16 +1,15 @@
 <?xml version="1.0"?>
 # -*- Mode: HTML -*-
 #
 # 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/.
 
-#ifdef XP_MACOSX
 <?xml-stylesheet href="chrome://browser/skin/webRTC-indicator.css" type="text/css"?>
 
 <!DOCTYPE window [
 #include browser-doctype.inc
 ]>
 
 <window id="main-window"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -26,10 +25,8 @@
     <menuitem label="&newNavigatorCmd.label;" oncommand="OpenBrowserWindowFromDockMenu();"
               id="macDockMenuNewWindow" />
     <menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});"
               id="macDockMenuNewPrivateWindow" />
   </menupopup>
 </popupset>
 
 </window>
-
-#endif
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -95,18 +95,20 @@ browser.jar:
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.js                 (content/tabbrowser.js)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
 *       content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
         content/browser/utilityOverlay.js             (content/utilityOverlay.js)
         content/browser/webext-panels.js              (content/webext-panels.js)
 *       content/browser/webext-panels.xul             (content/webext-panels.xul)
         content/browser/nsContextMenu.js              (content/nsContextMenu.js)
+#ifdef XP_MACOSX
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
+#endif
 #ifndef XP_MACOSX
 *       content/browser/webrtcIndicator.xul           (content/webrtcIndicator.xul)
         content/browser/webrtcIndicator.js            (content/webrtcIndicator.js)
 #endif
 # the following files are browser-specific overrides
 *       content/browser/license.html                  (/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://browser/content/license.html
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/node.configure
@@ -0,0 +1,73 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+option('--enable-nodejs',
+       help='Require Node.js to build')
+
+
+# "nodejs" is first in the tuple on the assumption that it's only likely to
+# exist on systems (probably linux distros) where there is a program in the path
+# called "node" that does something else.
+nodejs = check_prog('NODEJS', ('nodejs', 'node',),
+                    allow_missing=True)
+
+
+@depends_if(nodejs)
+@checking('for node.js version')
+@imports('os')
+@imports('re')
+@imports('subprocess')
+def nodejs_version(node):
+    env = dict(os.environ)
+    env[b'NODE_DISABLE_COLORS'] = b'1'
+
+    try:
+        out = subprocess.check_output([node, '--version'], env=env,
+                                      stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as e:
+        return None
+
+    match = re.search(r'v(.+)$', out)
+
+    if not match:
+        return None
+
+    return Version(match.group(1))
+
+
+@depends('--enable-nodejs', nodejs, nodejs_version)
+def nodejs_suitability(require, node, version):
+    MIN_NODE_VERSION = Version('8.11')
+
+    if not node:
+        msg = ('could not find Node.js executable; ensure `node` or `nodejs` '
+               'is in PATH or set NODEJS in environment to point to an '
+               'executable')
+
+        if require:
+            raise FatalCheckError(msg)
+        else:
+            log.warning(msg)
+            log.warning('(This will become a fatal error in the near future.)')
+            return
+
+    if not version:
+        msg = 'could not resolve Node.js version'
+        if require:
+            raise FatalCheckError(msg)
+        else:
+            log.warning(msg)
+            log.warning('(This will become a fatal error in the near future.)')
+            return
+
+    if version < MIN_NODE_VERSION:
+        msg = 'NODEJS must point to node %s or newer; %s found' (
+            MIN_NODE_VERSION, node_version)
+
+        if require:
+            raise FatalCheckError(msg)
+        else:
+            log.warning(msg)
--- a/caps/OriginAttributes.cpp
+++ b/caps/OriginAttributes.cpp
@@ -123,18 +123,20 @@ OriginAttributes::CreateSuffix(nsACStrin
 
   if (mPrivateBrowsingId) {
     value.Truncate();
     value.AppendInt(mPrivateBrowsingId);
     params.Set(NS_LITERAL_STRING("privateBrowsingId"), value);
   }
 
   if (!mFirstPartyDomain.IsEmpty()) {
-    MOZ_RELEASE_ASSERT(mFirstPartyDomain.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
-    params.Set(NS_LITERAL_STRING("firstPartyDomain"), mFirstPartyDomain);
+    nsAutoString sanitizedFirstPartyDomain(mFirstPartyDomain);
+    sanitizedFirstPartyDomain.ReplaceChar(dom::quota::QuotaManager::kReplaceChars, '+');
+
+    params.Set(NS_LITERAL_STRING("firstPartyDomain"), sanitizedFirstPartyDomain);
   }
 
   aStr.Truncate();
 
   params.Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
--- a/devtools/client/framework/components/ToolboxTabs.js
+++ b/devtools/client/framework/components/ToolboxTabs.js
@@ -70,16 +70,18 @@ class ToolboxTabs extends Component {
   componentDidUpdate(prevProps, prevState) {
     if (this.shouldUpdateToolboxTabs(prevProps, this.props)) {
       this.updateCachedToolTabsWidthMap();
       this.updateOverflowedTabs();
     }
   }
 
   componentWillUnmount() {
+    window.removeEventListener("resize", this.resizeHandler);
+    window.cancelIdleCallback(this._resizeTimerId);
     this._tabsOrderManager.destroy();
   }
 
   /**
    * Check if two array of ids are the same or not.
    */
   equalToolIdArray(prevPanels, nextPanels) {
     if (prevPanels.length !== nextPanels.length) {
--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -512,17 +512,18 @@ class AnimationInspector {
    * If no node is provided, hide the box model highlighter.
    *
    * @param {NodeFront} nodeFront
    */
   async setHighlightedNode(nodeFront) {
     await this.inspector.highlighters.hideBoxModelHighlighter();
 
     if (nodeFront) {
-      await this.inspector.highlighters.showBoxModelHighlighter(nodeFront);
+      await this.inspector.highlighters.showBoxModelHighlighter(
+        nodeFront, { hideInfoBar: true, hideGuides: true });
     }
 
     this.inspector.store.dispatch(updateHighlightedNode(nodeFront));
   }
 
   /**
    * Returns simulatable animation by given parameters.
    * The returned animation is implementing Animation interface of Web Animation API.
--- a/devtools/client/inspector/animation/components/AnimationTarget.js
+++ b/devtools/client/inspector/animation/components/AnimationTarget.js
@@ -83,17 +83,18 @@ class AnimationTarget extends Component 
       await this.updateNodeFront(this.props.animation);
     }
   }
 
   async highlight() {
     await this.ensureNodeFront();
 
     if (this.state.nodeFront) {
-      this.props.onShowBoxModelHighlighterForNode(this.state.nodeFront);
+      this.props.onShowBoxModelHighlighterForNode(
+        this.state.nodeFront, { hideInfoBar: true, hideGuides: true });
     }
   }
 
   async select() {
     await this.ensureNodeFront();
 
     if (this.state.nodeFront) {
       this.props.setSelectedNode(this.state.nodeFront);
--- a/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
+++ b/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
@@ -28,17 +28,17 @@ class ColorPath extends ComputedStylePat
     return "color";
   }
 
   getPropertyValue(keyframe) {
     return keyframe.value;
   }
 
   propToState({ keyframes }) {
-    const maxObject = { distance: 0 };
+    const maxObject = { distance: -Number.MAX_VALUE };
 
     for (let i = 0; i < keyframes.length - 1; i++) {
       const value1 = getRGBA(keyframes[i].value);
       for (let j = i + 1; j < keyframes.length; j++) {
         const value2 = getRGBA(keyframes[j].value);
         const distance = getRGBADistance(value1, value2);
 
         if (maxObject.distance >= distance) {
--- a/devtools/client/inspector/animation/test/doc_multi_keyframes.html
+++ b/devtools/client/inspector/animation/test/doc_multi_keyframes.html
@@ -195,11 +195,35 @@
           offset: 0.5,
         },
         {
           opacity: 1,
           offset: 1,
         },
       ]
     );
+
+    createAnimation(
+      "same-color",
+      [
+        {
+          backgroundColor: "lime",
+        },
+        {
+          backgroundColor: "lime",
+        },
+      ]
+    );
+
+    createAnimation(
+      "currentcolor",
+      [
+        {
+          backgroundColor: "currentColor",
+        },
+        {
+          backgroundColor: "lime",
+        },
+      ]
+    );
     </script>
   </body>
 </html>
--- a/devtools/client/inspector/animation/test/keyframes-graph_keyframe-marker_head.js
+++ b/devtools/client/inspector/animation/test/keyframes-graph_keyframe-marker_head.js
@@ -127,17 +127,53 @@ const KEYFRAMES_TEST_DATA = [
           },
           {
             title: "1",
             marginInlineStart: "100%",
           },
         ],
       },
     ],
-  }
+  },
+  {
+    targetClass: "same-color",
+    properties: [
+      {
+        name: "background-color",
+        expectedValues: [
+          {
+            title: "rgb(0, 255, 0)",
+            marginInlineStart: "0%",
+          },
+          {
+            title: "rgb(0, 255, 0)",
+            marginInlineStart: "100%",
+          },
+        ],
+      },
+    ],
+  },
+  {
+    targetClass: "currentcolor",
+    properties: [
+      {
+        name: "background-color",
+        expectedValues: [
+          {
+            title: "currentcolor",
+            marginInlineStart: "0%",
+          },
+          {
+            title: "rgb(0, 255, 0)",
+            marginInlineStart: "100%",
+          },
+        ],
+      },
+    ],
+  },
 ];
 
 /**
  * Do test for keyframes-graph_keyframe-marker-ltf/rtl.
  *
  * @param {Array} testData
  */
 // eslint-disable-next-line no-unused-vars
@@ -174,9 +210,8 @@ async function testKeyframesGraphKeyfram
         info(`Checking marginInlineStart style in ${ hintTarget }`);
         is(markerEl.style.marginInlineStart, expectedValue.marginInlineStart,
           `marginInlineStart in ${ hintTarget } should be ` +
           `${ expectedValue.marginInlineStart }`);
       }
     }
   }
 }
-
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1316,16 +1316,53 @@ Element::GetAttribute(const nsAString& a
       // See bug 232598
       // aReturn is already empty
     } else {
       aReturn.SetNull();
     }
   }
 }
 
+bool
+Element::ToggleAttribute(const nsAString& aName,
+                         const Optional<bool>& aForce,
+                         nsIPrincipal* aTriggeringPrincipal,
+                         ErrorResult& aError)
+{
+  aError = nsContentUtils::CheckQName(aName, false);
+  if (aError.Failed()) {
+    return false;
+  }
+
+  nsAutoString nameToUse;
+  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
+  if (!name) {
+    if (aForce.WasPassed() && !aForce.Value()) {
+      return false;
+    }
+    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
+    if (!nameAtom) {
+      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return false;
+    }
+    aError = SetAttr(kNameSpaceID_None, nameAtom, EmptyString(), aTriggeringPrincipal, true);
+    return true;
+  }
+  if (aForce.WasPassed() && aForce.Value()) {
+    return true;
+  }
+  // Hold a strong reference here so that the atom or nodeinfo doesn't go
+  // away during UnsetAttr. If it did UnsetAttr would be left with a
+  // dangling pointer as argument without knowing it.
+  nsAttrName tmp(*name);
+
+  aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
+  return false;
+}
+
 void
 Element::SetAttribute(const nsAString& aName,
                       const nsAString& aValue,
                       nsIPrincipal* aTriggeringPrincipal,
                       ErrorResult& aError)
 {
   aError = nsContentUtils::CheckQName(aName, false);
   if (aError.Failed()) {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1096,16 +1096,18 @@ public:
     GetAttribute(aName, str);
     str.ToString(aReturn);
   }
 
   void GetAttribute(const nsAString& aName, DOMString& aReturn);
   void GetAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aLocalName,
                       nsAString& aReturn);
+  bool ToggleAttribute(const nsAString& aName, const Optional<bool>& aForce,
+                       nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError);
   void SetAttribute(const nsAString& aName, const nsAString& aValue,
                     nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError);
   void SetAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aLocalName,
                       const nsAString& aValue,
                       nsIPrincipal* aTriggeringPrincipal,
                       ErrorResult& aError);
   void SetAttribute(const nsAString& aName, const nsAString& aValue,
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -881,16 +881,17 @@ public:
 };
 
 //*****************************************************************************
 //***    nsGlobalWindowInner: Object Management
 //*****************************************************************************
 
 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter *aOuterWindow)
   : nsPIDOMWindowInner(aOuterWindow->AsOuter()),
+    mozilla::webgpu::InstanceProvider(this),
     mIdleFuzzFactor(0),
     mIdleCallbackIndex(-1),
     mCurrentlyIdle(false),
     mAddActiveEventFuzzTime(true),
     mWasOffline(false),
     mHasHadSlowScript(false),
     mNotifyIdleObserversIdleOnThaw(false),
     mNotifyIdleObserversActiveOnThaw(false),
@@ -1474,16 +1475,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPromises)
 
   for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
   }
 
+  static_cast<mozilla::webgpu::InstanceProvider*>(tmp)->CcTraverse(cb);
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
   tmp->CleanupCachedXBLHandlers();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
@@ -1577,16 +1580,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPromises)
   for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
   }
   tmp->mDocumentFlushedResolvers.Clear();
 
+  static_cast<mozilla::webgpu::InstanceProvider*>(tmp)->CcUnlink();
+
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindowInner::RiskyUnlink()
 {
   NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -40,16 +40,17 @@
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/webgpu/InstanceProvider.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "Units.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSize.h"
@@ -212,16 +213,17 @@ class nsGlobalWindowInner final
   // implemented on chrome windows.
   , private nsIDOMChromeWindow
   , public nsIScriptGlobalObject
   , public nsIScriptObjectPrincipal
   , public nsSupportsWeakReference
   , public nsIInterfaceRequestor
   , public PRCListStr
   , public nsAPostRefreshObserver
+  , public mozilla::webgpu::InstanceProvider
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
   typedef nsDataHashtable<nsUint64HashKey, nsGlobalWindowInner*> InnerWindowByIdTable;
 
   static void
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1292,16 +1292,149 @@ DOMInterfaces = {
     'headerFile': 'WebGLUniformLocation.h'
 },
 
 'WebGLVertexArrayObject': {
     'nativeType': 'mozilla::WebGLVertexArray',
     'headerFile': 'WebGLVertexArray.h'
 },
 
+# WebGPU
+
+'WebGPU': {
+    'nativeType': 'mozilla::webgpu::Instance',
+},
+'WebGPUAdapter': {
+    'nativeType': 'mozilla::webgpu::Adapter',
+},
+'WebGPUAttachmentState': {
+    'nativeType': 'mozilla::webgpu::AttachmentState',
+},
+'WebGPUBindGroup': {
+    'nativeType': 'mozilla::webgpu::BindGroup',
+},
+'WebGPUBindGroupLayout': {
+    'nativeType': 'mozilla::webgpu::BindGroupLayout',
+},
+'WebGPUBlendState': {
+    'nativeType': 'mozilla::webgpu::BlendState',
+},
+'WebGPUBuffer': {
+    'nativeType': 'mozilla::webgpu::Buffer',
+},
+'WebGPUCommandBuffer': {
+    'nativeType': 'mozilla::webgpu::CommandBuffer',
+},
+'WebGPUCommandEncoder': {
+    'nativeType': 'mozilla::webgpu::CommandEncoder',
+},
+'WebGPUComputePipeline': {
+    'nativeType': 'mozilla::webgpu::ComputePipeline',
+},
+'WebGPUDepthStencilState': {
+    'nativeType': 'mozilla::webgpu::DepthStencilState',
+},
+'WebGPUDevice': {
+    'nativeType': 'mozilla::webgpu::Device',
+},
+'WebGPUFence': {
+    'nativeType': 'mozilla::webgpu::Fence',
+},
+'WebGPUInputState': {
+    'nativeType': 'mozilla::webgpu::InputState',
+},
+'WebGPULogEntry': {
+    'nativeType': 'mozilla::webgpu::LogEntry',
+},
+'WebGPUPipelineLayout': {
+    'nativeType': 'mozilla::webgpu::PipelineLayout',
+},
+'WebGPUQueue': {
+    'nativeType': 'mozilla::webgpu::Queue',
+},
+'WebGPURenderPipeline': {
+    'nativeType': 'mozilla::webgpu::RenderPipeline',
+},
+'WebGPUSampler': {
+    'nativeType': 'mozilla::webgpu::Sampler',
+},
+'WebGPUShaderModule': {
+    'nativeType': 'mozilla::webgpu::ShaderModule',
+},
+'WebGPUSwapChain': {
+    'nativeType': 'mozilla::webgpu::SwapChain',
+},
+'WebGPUTexture': {
+    'nativeType': 'mozilla::webgpu::Texture',
+},
+'WebGPUTextureView': {
+    'nativeType': 'mozilla::webgpu::TextureView',
+},
+
+
+'WebGPUBindingType': {
+    'concrete': False,
+},
+'WebGPUBlendFactor': {
+    'concrete': False,
+},
+'WebGPUBlendOperation': {
+    'concrete': False,
+},
+'WebGPUBufferUsage': {
+    'concrete': False,
+},
+'WebGPUColorWriteBits': {
+    'concrete': False,
+},
+'WebGPUCompareFunction': {
+    'concrete': False,
+},
+'WebGPUFilterMode': {
+    'concrete': False,
+},
+'WebGPUIndexFormat': {
+    'concrete': False,
+},
+'WebGPUInputStepMode': {
+    'concrete': False,
+},
+'WebGPULoadOp': {
+    'concrete': False,
+},
+'WebGPUPrimitiveTopology': {
+    'concrete': False,
+},
+'WebGPUShaderStage': {
+    'concrete': False,
+},
+'WebGPUShaderStageBit': {
+    'concrete': False,
+},
+'WebGPUStencilOperation': {
+    'concrete': False,
+},
+'WebGPUStoreOp': {
+    'concrete': False,
+},
+'WebGPUTextureDimension': {
+    'concrete': False,
+},
+'WebGPUTextureFormat': {
+    'concrete': False,
+},
+'WebGPUTextureUsage': {
+    'concrete': False,
+},
+'WebGPUVertexFormat': {
+    'concrete': False,
+},
+
+# WebRTC
+
 'WebrtcGlobalInformation': {
     'nativeType': 'mozilla::dom::WebrtcGlobalInformation',
     'headerFile': 'WebrtcGlobalInformation.h',
     'wrapperCache': False,
     'concrete': False,
 },
 
 'Window': {
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -2,16 +2,19 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
+if CONFIG['CC_TYPE'] == 'msvc':
+    CXXFLAGS += ['-bigobj']
+
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'nsIScriptError.idl'
 ]
 
 XPIDL_MODULE = 'dom_bindings'
 
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -34,16 +34,17 @@ DIRS += [
     'abort',
     'animation',
     'base',
     'bindings',
     'battery',
     'browser-element',
     'cache',
     'canvas',
+    'webgpu',
     'chrome-webidl',
     'clients',
     'commandhandler',
     'credentialmanagement',
     'crypto',
     'encoding',
     'events',
     'fetch',
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Adapter.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Adapter.h"
+
+#include "Instance.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Adapter::~Adapter() = default;
+
+void
+Adapter::Extensions(dom::WebGPUExtensions& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+Adapter::Features(dom::WebGPUFeatures& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Device>
+Adapter::CreateDevice(const dom::WebGPUDeviceDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(Adapter)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Adapter.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_Adapter_H_
+#define WEBGPU_Adapter_H_
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "nsString.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+struct WebGPUDeviceDescriptor;
+struct WebGPUExtensions;
+struct WebGPUFeatures;
+} // namespace dom
+
+namespace webgpu {
+class Device;
+class Instance;
+
+class Adapter final
+    : public ChildOf<Instance>
+{
+public:
+    WEBGPU_DECL_GOOP(Adapter)
+
+    const nsString mName;
+
+private:
+    Adapter() = delete;
+    virtual ~Adapter();
+
+public:
+    void GetName(nsString& out) const {
+        out = mName;
+    }
+
+    void Extensions(dom::WebGPUExtensions& out) const;
+    void Features(dom::WebGPUFeatures& out) const;
+    already_AddRefed<Device> CreateDevice(const dom::WebGPUDeviceDescriptor& desc) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_Adapter_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/AttachmentState.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AttachmentState.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+AttachmentState::~AttachmentState() = default;
+
+WEBGPU_IMPL_GOOP_0(AttachmentState)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/AttachmentState.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_AttachmentState_H_
+#define WEBGPU_AttachmentState_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class AttachmentState final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(AttachmentState)
+
+private:
+    AttachmentState() = delete;
+    virtual ~AttachmentState();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_AttachmentState_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BindGroup.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BindGroup.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+BindGroup::~BindGroup() = default;
+
+WEBGPU_IMPL_GOOP_0(BindGroup)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BindGroup.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_BindGroup_H_
+#define WEBGPU_BindGroup_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class BindGroup final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(BindGroup)
+
+private:
+    BindGroup() = delete;
+    virtual ~BindGroup();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_BindGroup_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BindGroupLayout.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BindGroupLayout.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+BindGroupLayout::~BindGroupLayout() = default;
+
+WEBGPU_IMPL_GOOP_0(BindGroupLayout)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BindGroupLayout.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_BindGroupLayout_H_
+#define WEBGPU_BindGroupLayout_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class BindGroupLayout final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(BindGroupLayout)
+
+private:
+    BindGroupLayout() = delete;
+    virtual ~BindGroupLayout();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_BindGroupLayout_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BlendState.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BlendState.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+BlendState::~BlendState() = default;
+
+WEBGPU_IMPL_GOOP_0(BlendState)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/BlendState.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_BlendState_H_
+#define WEBGPU_BlendState_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class BlendState final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(BlendState)
+
+private:
+    BlendState() = delete;
+    virtual ~BlendState();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_BlendState_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Buffer.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 "Buffer.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Buffer::Buffer(Device* const parent)
+    : ChildOf(parent)
+{
+    mozilla::HoldJSObjects(this); // Mimed from PushSubscriptionOptions
+}
+
+Buffer::~Buffer()
+{
+    mMapping = nullptr;
+    mozilla::DropJSObjects(this);
+}
+
+void
+Buffer::GetMapping(JSContext*, JS::MutableHandle<JSObject*> out) const
+{
+    out.set(mMapping);
+}
+
+void
+Buffer::Unmap() const
+{
+    MOZ_CRASH("todo");
+}
+
+JSObject*
+webgpu::Buffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
+{
+    return dom::WebGPUBuffer_Binding::Wrap(cx, this, givenProto);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Buffer)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Buffer)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->mMapping = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Buffer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Buffer)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMapping)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Buffer, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Buffer, Release)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Buffer.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_BUFFER_H_
+#define WEBGPU_BUFFER_H_
+
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/TypedArray.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class Buffer final
+    : public ChildOf<Device>
+{
+public:
+    JS::Heap<JSObject*> mMapping;
+
+    WEBGPU_DECL_GOOP(Buffer)
+
+private:
+    explicit Buffer(Device* parent);
+    virtual ~Buffer();
+
+public:
+    void GetMapping(JSContext* cx, JS::MutableHandle<JSObject*> out) const;
+    void Unmap() const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_BUFFER_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/CommandBuffer.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CommandBuffer.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+CommandBuffer::~CommandBuffer() = default;
+
+WEBGPU_IMPL_GOOP_0(CommandBuffer)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/CommandBuffer.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_CommandBuffer_H_
+#define WEBGPU_CommandBuffer_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class CommandBuffer final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(CommandBuffer)
+
+private:
+    CommandBuffer() = delete;
+    virtual ~CommandBuffer();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_CommandBuffer_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/CommandEncoder.cpp
@@ -0,0 +1,147 @@
+/* -*- 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 "CommandEncoder.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+CommandEncoder::~CommandEncoder() = default;
+
+already_AddRefed<CommandBuffer>
+CommandEncoder::FinishEncoding() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::CopyBufferToBuffer(const Buffer& src, const uint32_t srcOffset,
+                                   const Buffer& dst, const uint32_t dstOffset,
+                                   const uint32_t size) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::CopyBufferToTexture() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::CopyTextureToBuffer() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::CopyTextureToTexture() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::Blit() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::TransitionBuffer(const Buffer& b, const uint32_t flags) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetPushConstants(const uint32_t stageFlags, const uint32_t offset,
+                                 const uint32_t count, const dom::ArrayBuffer& data) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetBindGroup(const uint32_t index, const BindGroup& bindGroup) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetPipeline(const dom::WebGPUComputePipelineOrWebGPURenderPipeline& pipeline) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::BeginComputePass() const
+{
+    MOZ_CRASH("todo");
+}
+void
+CommandEncoder::EndComputePass() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::Dispatch(const uint32_t x, const uint32_t y, const uint32_t z) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::BeginRenderPass(const dom::WebGPURenderPassDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::EndRenderPass() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetBlendColor(const float r, const float g, const float b,
+                              const float a) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetIndexBuffer(const Buffer& buffer, const uint32_t offset) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::SetVertexBuffers(const uint32_t startSlot,
+                                 const dom::Sequence<OwningNonNull<Buffer>>& buffers,
+                                 const dom::Sequence<uint32_t>& offsets) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::Draw(const uint32_t vertexCount, const uint32_t instanceCount,
+                     const uint32_t firstVertex, const uint32_t firstInstance) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+CommandEncoder::DrawIndexed(const uint32_t indexCount, const uint32_t instanceCount,
+                            const uint32_t firstIndex, const uint32_t firstInstance,
+                            const uint32_t firstVertex) const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(CommandEncoder)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/CommandEncoder.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_CommandEncoder_H_
+#define WEBGPU_CommandEncoder_H_
+
+#include "mozilla/dom/TypedArray.h"
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+template<typename T> class Sequence;
+class WebGPUComputePipelineOrWebGPURenderPipeline;
+struct WebGPURenderPassDescriptor;
+} // namespace dom
+namespace webgpu {
+
+class BindGroup;
+class Buffer;
+class CommandBuffer;
+class Device;
+
+class CommandEncoder final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(CommandEncoder)
+
+private:
+    CommandEncoder() = delete;
+    virtual ~CommandEncoder();
+
+public:
+    already_AddRefed<CommandBuffer> FinishEncoding() const;
+
+    // Commands allowed outside of "passes"
+    void CopyBufferToBuffer(const Buffer& src, uint32_t srcOffset, const Buffer& dst,
+                            uint32_t dstOffset, uint32_t size) const;
+    // TODO figure out all the arguments required for these
+    void CopyBufferToTexture() const;
+    void CopyTextureToBuffer() const;
+    void CopyTextureToTexture() const;
+    void Blit() const;
+
+    void TransitionBuffer(const Buffer& b, uint32_t flags) const;
+
+    // Allowed in both compute and render passes
+    void SetPushConstants(uint32_t stageFlags,
+                          uint32_t offset,
+                          uint32_t count,
+                          const dom::ArrayBuffer& data) const;
+    void SetBindGroup(uint32_t index, const BindGroup& bindGroup) const;
+    void SetPipeline(const dom::WebGPUComputePipelineOrWebGPURenderPipeline& pipeline) const;
+
+    // Compute pass commands
+    void BeginComputePass() const;
+    void EndComputePass() const;
+
+    void Dispatch(uint32_t x, uint32_t y, uint32_t z) const;
+
+    // Render pass commands
+    void BeginRenderPass(const dom::WebGPURenderPassDescriptor& desc) const;
+    void EndRenderPass() const;
+
+    void SetBlendColor(float r, float g, float b, float a) const;
+    void SetIndexBuffer(const Buffer& buffer, uint32_t offset) const;
+    void SetVertexBuffers(uint32_t startSlot,
+                          const dom::Sequence<OwningNonNull<Buffer>>& buffers,
+                          const dom::Sequence<uint32_t>& offsets) const;
+
+    void Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex,
+              uint32_t firstInstance) const;
+    void DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex,
+                     uint32_t firstInstance, uint32_t firstVertex) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_CommandEncoder_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ComputePipeline.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ComputePipeline.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+ComputePipeline::~ComputePipeline() = default;
+
+WEBGPU_IMPL_GOOP_0(ComputePipeline)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ComputePipeline.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_ComputePipeline_H_
+#define WEBGPU_ComputePipeline_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class ComputePipeline final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(ComputePipeline)
+
+private:
+    ComputePipeline() = delete;
+    virtual ~ComputePipeline();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_ComputePipeline_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/DepthStencilState.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DepthStencilState.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+DepthStencilState::~DepthStencilState() = default;
+
+WEBGPU_IMPL_GOOP_0(DepthStencilState)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/DepthStencilState.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_DepthStencilState_H_
+#define WEBGPU_DepthStencilState_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class DepthStencilState final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(DepthStencilState)
+
+private:
+    DepthStencilState() = delete;
+    virtual ~DepthStencilState();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_DepthStencilState_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Device.cpp
@@ -0,0 +1,153 @@
+/* -*- 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 "Device.h"
+
+#include "Adapter.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Device::~Device() = default;
+
+already_AddRefed<webgpu::Adapter>
+Device::Adapter() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+Device::Extensions(dom::WebGPUExtensions& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+Device::Features(dom::WebGPUFeatures& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+Device::Limits(dom::WebGPULimits& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Buffer>
+Device::CreateBuffer(const dom::WebGPUBufferDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Texture>
+Device::CreateTexture(const dom::WebGPUTextureDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Sampler>
+Device::CreateSampler(const dom::WebGPUSamplerDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+
+already_AddRefed<BindGroupLayout>
+Device::CreateBindGroupLayout(const dom::WebGPUBindGroupLayoutDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<PipelineLayout>
+Device::CreatePipelineLayout(const dom::WebGPUPipelineLayoutDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<BindGroup>
+Device::CreateBindGroup(const dom::WebGPUBindGroupDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+
+already_AddRefed<BlendState>
+Device::CreateBlendState(const dom::WebGPUBlendStateDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<DepthStencilState>
+Device::CreateDepthStencilState(const dom::WebGPUDepthStencilStateDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<InputState>
+Device::CreateInputState(const dom::WebGPUInputStateDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<ShaderModule>
+Device::CreateShaderModule(const dom::WebGPUShaderModuleDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<AttachmentState>
+Device::CreateAttachmentState(const dom::WebGPUAttachmentStateDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<ComputePipeline>
+Device::CreateComputePipeline(const dom::WebGPUComputePipelineDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<RenderPipeline>
+Device::CreateRenderPipeline(const dom::WebGPURenderPipelineDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<CommandEncoder>
+Device::CreateCommandEncoder(const dom::WebGPUCommandEncoderDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Queue>
+Device::GetQueue() const
+{
+    MOZ_CRASH("todo");
+}
+
+RefPtr<dom::WebGPULogCallback>
+Device::OnLog() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+Device::SetOnLog(const dom::WebGPULogCallback& callback) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<dom::Promise>
+Device::GetObjectStatus(const dom::WebGPUBufferOrWebGPUTexture& obj) const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(Device)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Device.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_DEVICE_H_
+#define WEBGPU_DEVICE_H_
+
+#include "mozilla/RefPtr.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+struct WebGPUExtensions;
+struct WebGPUFeatures;
+struct WebGPULimits;
+
+struct WebGPUBufferDescriptor;
+struct WebGPUTextureDescriptor;
+struct WebGPUSamplerDescriptor;
+struct WebGPUBindGroupLayoutDescriptor;
+struct WebGPUPipelineLayoutDescriptor;
+struct WebGPUBindGroupDescriptor;
+struct WebGPUBlendStateDescriptor;
+struct WebGPUDepthStencilStateDescriptor;
+struct WebGPUInputStateDescriptor;
+struct WebGPUShaderModuleDescriptor;
+struct WebGPUAttachmentStateDescriptor;
+struct WebGPUComputePipelineDescriptor;
+struct WebGPURenderPipelineDescriptor;
+struct WebGPUCommandEncoderDescriptor;
+
+class Promise;
+class WebGPUBufferOrWebGPUTexture;
+class WebGPULogCallback;
+} // namespace dom
+
+namespace webgpu {
+class Adapter;
+class AttachmentState;
+class BindGroup;
+class BindGroupLayout;
+class BlendState;
+class Buffer;
+class CommandEncoder;
+class ComputePipeline;
+class DepthStencilState;
+class Fence;
+class InputState;
+class PipelineLayout;
+class Queue;
+class RenderPipeline;
+class Sampler;
+class ShaderModule;
+class Texture;
+
+class Device final
+    : public ChildOf<Adapter>
+{
+public:
+    WEBGPU_DECL_GOOP(Device)
+
+private:
+    Device() = delete;
+    virtual ~Device();
+
+public:
+    already_AddRefed<webgpu::Adapter> Adapter() const;
+
+    void Extensions(dom::WebGPUExtensions& out) const;
+    void Features(dom::WebGPUFeatures& out) const;
+    void Limits(dom::WebGPULimits& out) const;
+
+    already_AddRefed<Buffer> CreateBuffer(const dom::WebGPUBufferDescriptor& desc) const;
+    already_AddRefed<Texture> CreateTexture(const dom::WebGPUTextureDescriptor& desc) const;
+    already_AddRefed<Sampler> CreateSampler(const dom::WebGPUSamplerDescriptor& desc) const;
+
+    already_AddRefed<BindGroupLayout> CreateBindGroupLayout(const dom::WebGPUBindGroupLayoutDescriptor& desc) const;
+    already_AddRefed<PipelineLayout> CreatePipelineLayout(const dom::WebGPUPipelineLayoutDescriptor& desc) const;
+    already_AddRefed<BindGroup> CreateBindGroup(const dom::WebGPUBindGroupDescriptor& desc) const;
+
+    already_AddRefed<BlendState> CreateBlendState(const dom::WebGPUBlendStateDescriptor& desc) const;
+    already_AddRefed<DepthStencilState> CreateDepthStencilState(const dom::WebGPUDepthStencilStateDescriptor& desc) const;
+    already_AddRefed<InputState> CreateInputState(const dom::WebGPUInputStateDescriptor& desc) const;
+    already_AddRefed<ShaderModule> CreateShaderModule(const dom::WebGPUShaderModuleDescriptor& desc) const;
+    already_AddRefed<AttachmentState> CreateAttachmentState(const dom::WebGPUAttachmentStateDescriptor& desc) const;
+    already_AddRefed<ComputePipeline> CreateComputePipeline(const dom::WebGPUComputePipelineDescriptor& desc) const;
+    already_AddRefed<RenderPipeline> CreateRenderPipeline(const dom::WebGPURenderPipelineDescriptor& desc) const;
+
+    already_AddRefed<CommandEncoder> CreateCommandEncoder(const dom::WebGPUCommandEncoderDescriptor& desc) const;
+
+    already_AddRefed<Queue> GetQueue() const;
+
+    RefPtr<dom::WebGPULogCallback> OnLog() const;
+    void SetOnLog(const dom::WebGPULogCallback& callback) const;
+
+    already_AddRefed<dom::Promise> GetObjectStatus(const dom::WebGPUBufferOrWebGPUTexture& obj) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_DEVICE_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Fence.cpp
@@ -0,0 +1,31 @@
+/* -*- 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 "Fence.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Fence::~Fence() = default;
+
+bool
+Fence::Wait(const double milliseconds) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<dom::Promise>
+Fence::Promise() const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(Fence)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Fence.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_Fence_H_
+#define WEBGPU_Fence_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+class Promise;
+} // namespace dom
+namespace webgpu {
+
+class Device;
+
+class Fence final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(Fence)
+
+private:
+    Fence() = delete;
+    virtual ~Fence();
+
+public:
+    bool Wait(double milliseconds) const;
+    already_AddRefed<dom::Promise> Promise() const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_Fence_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/InputState.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputState.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+InputState::~InputState() = default;
+
+WEBGPU_IMPL_GOOP_0(InputState)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/InputState.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_InputState_H_
+#define WEBGPU_InputState_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class InputState final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(InputState)
+
+private:
+    InputState() = delete;
+    virtual ~InputState();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_InputState_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Instance.cpp
@@ -0,0 +1,63 @@
+/* -*- 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 "Instance.h"
+
+#include "Adapter.h"
+#include "InstanceProvider.h"
+#include "mozilla/dom/WebGPUBinding.h"
+#include "nsIGlobalObject.h"
+
+namespace mozilla {
+namespace webgpu {
+
+/*static*/ RefPtr<Instance>
+Instance::Create(nsIGlobalObject* parent)
+{
+    return new Instance(parent);
+}
+
+Instance::Instance(nsIGlobalObject* parent)
+    : mParent(parent)
+{
+}
+
+Instance::~Instance() = default;
+
+already_AddRefed<Adapter>
+Instance::GetAdapter(const dom::WebGPUAdapterDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+template<typename T>
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+                            const nsCOMPtr<T>& field,
+                            const char* name, uint32_t flags)
+{
+    CycleCollectionNoteChild(callback, field.get(), name, flags);
+}
+
+template<typename T>
+void
+ImplCycleCollectionUnlink(const nsCOMPtr<T>& field)
+{
+    const auto mut = const_cast<nsCOMPtr<T>*>(&field);
+    *mut = nullptr;
+}
+
+JSObject*
+Instance::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
+{
+    return dom::WebGPU_Binding::Wrap(cx, this, givenProto);
+}
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Instance, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Instance, Release)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Instance, mParent)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Instance.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_INSTANCE_H_
+#define WEBGPU_INSTANCE_H_
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+struct WebGPUAdapterDescriptor;
+} // namespace dom
+
+namespace webgpu {
+class Adapter;
+class InstanceProvider;
+
+class Instance final
+    : public nsWrapperCache
+{
+public:
+    WEBGPU_DECL_GOOP(Instance)
+
+    const nsCOMPtr<nsIGlobalObject> mParent;
+
+    static RefPtr<Instance> Create(nsIGlobalObject* parent);
+
+private:
+    explicit Instance(nsIGlobalObject* parent);
+    virtual ~Instance();
+
+public:
+    nsIGlobalObject* GetParentObject() const { return mParent.get(); }
+
+    already_AddRefed<Adapter> GetAdapter(const dom::WebGPUAdapterDescriptor& desc) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_INSTANCE_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/InstanceProvider.cpp
@@ -0,0 +1,47 @@
+/* -*- 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 "InstanceProvider.h"
+
+#include "Instance.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+InstanceProvider::InstanceProvider(nsIGlobalObject* const global)
+    : mGlobal(global)
+{ }
+
+InstanceProvider::~InstanceProvider() = default;
+
+already_AddRefed<Instance>
+InstanceProvider::Webgpu() const
+{
+    if (!mInstance) {
+        const auto inst = Instance::Create(mGlobal);
+        mInstance = Some(inst);
+    }
+    auto ret = mInstance.value();
+    return ret.forget();
+}
+
+void
+InstanceProvider::CcTraverse(nsCycleCollectionTraversalCallback& callback) const
+{
+    if (mInstance) {
+        CycleCollectionNoteChild(callback, mInstance.ref().get(),
+                                 "webgpu::InstanceProvider::mInstance", 0);
+    }
+}
+
+void
+InstanceProvider::CcUnlink()
+{
+    mInstance = Some(nullptr);
+}
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/InstanceProvider.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_INSTANCE_PROVIDER_H_
+#define WEBGPU_INSTANCE_PROVIDER_H_
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+
+class nsCycleCollectionTraversalCallback;
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace webgpu {
+class Instance;
+
+class InstanceProvider
+{
+private:
+    nsIGlobalObject* const mGlobal;
+    mutable Maybe<RefPtr<Instance>> mInstance;
+
+protected:
+    explicit InstanceProvider(nsIGlobalObject* global);
+    virtual ~InstanceProvider();
+
+public:
+    already_AddRefed<Instance> Webgpu() const;
+
+    nsIGlobalObject* GetParentObject() const { return mGlobal; }
+
+    void CcTraverse(nsCycleCollectionTraversalCallback&) const;
+    void CcUnlink();
+};
+
+template<typename T>
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+                            const Maybe<T>& field,
+                            const char* name, uint32_t flags)
+{
+    if (field) {
+        CycleCollectionNoteChild(callback, field.value(), name, flags);
+    }
+}
+
+template<typename T>
+void
+ImplCycleCollectionUnlink(Maybe<T>& field)
+{
+    field = Nothing();
+}
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_INSTANCE_PROVIDER_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/LogEntry.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LogEntry.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+LogEntry::~LogEntry() = default;
+
+dom::WebGPULogEntryType
+LogEntry::Type() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+LogEntry::GetObj(JSContext* cx, JS::MutableHandleValue out) const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+LogEntry::GetReason(nsString& out) const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(LogEntry)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/LogEntry.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_LogEntry_H_
+#define WEBGPU_LogEntry_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+enum class WebGPULogEntryType : uint8_t;
+} // namespace dom
+namespace webgpu {
+
+class Device;
+
+class LogEntry final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(LogEntry)
+
+private:
+    LogEntry() = delete;
+    virtual ~LogEntry();
+
+public:
+    dom::WebGPULogEntryType Type() const;
+    void GetObj(JSContext* cx, JS::MutableHandleValue out) const;
+    void GetReason(nsString& out) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_LogEntry_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ObjectModel.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "ObjectModel.h"
+
+#include "Adapter.h"
+#include "Device.h"
+#include "Instance.h"
+
+namespace mozilla {
+namespace webgpu {
+
+template<typename T>
+ChildOf<T>::ChildOf(T* const parent)
+    : mParent(parent)
+{ }
+
+template<typename T>
+ChildOf<T>::~ChildOf() = default;
+
+template<typename T>
+nsIGlobalObject*
+ChildOf<T>::GetParentObject() const
+{
+    return mParent->GetParentObject();
+}
+
+template class ChildOf<Adapter>;
+template class ChildOf<Device>;
+template class ChildOf<Instance>;
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ObjectModel.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_OBJECT_MODEL_H_
+#define WEBGPU_OBJECT_MODEL_H_
+
+#include "nsWrapperCache.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace webgpu {
+
+template<typename T>
+class ChildOf
+    : public nsWrapperCache
+{
+public:
+    const RefPtr<T> mParent;
+
+    explicit ChildOf(T* parent = nullptr); // TODO: This can't be nullptr eventually.
+protected:
+    virtual ~ChildOf();
+
+public:
+    nsIGlobalObject* GetParentObject() const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#define WEBGPU_DECL_GOOP(T) \
+    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(T) \
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(T) \
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
+
+#define WEBGPU_IMPL_GOOP_INTERNAL(T) \
+    JSObject* \
+    T::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) \
+    { \
+        return dom::WebGPU ## T ## _Binding::Wrap(cx, this, givenProto); \
+    } \
+    NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(T, AddRef) \
+    NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(T, Release)
+
+#define WEBGPU_IMPL_GOOP(T,...) \
+    WEBGPU_IMPL_GOOP_INTERNAL(T) \
+    NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(T, mParent, __VA_ARGS__)
+
+#define WEBGPU_IMPL_GOOP_0(T) \
+    WEBGPU_IMPL_GOOP_INTERNAL(T) \
+    NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(T, mParent)
+
+template<typename T>
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+                            const RefPtr<T>& field,
+                            const char* name, uint32_t flags)
+{
+    CycleCollectionNoteChild(callback, field.get(), name, flags);
+}
+
+template<typename T>
+void
+ImplCycleCollectionUnlink(const RefPtr<T>& field)
+{
+    const auto mutPtr = const_cast< RefPtr<T>* >(&field);
+    ImplCycleCollectionUnlink(*mutPtr);
+}
+
+#endif // WEBGPU_OBJECT_MODEL_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/PipelineLayout.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PipelineLayout.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+PipelineLayout::~PipelineLayout() = default;
+
+WEBGPU_IMPL_GOOP_0(PipelineLayout)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/PipelineLayout.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_PipelineLayout_H_
+#define WEBGPU_PipelineLayout_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class PipelineLayout final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(PipelineLayout)
+
+private:
+    PipelineLayout() = delete;
+    virtual ~PipelineLayout();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_PipelineLayout_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Queue.cpp
@@ -0,0 +1,31 @@
+/* -*- 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 "Queue.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Queue::~Queue() = default;
+
+void
+Queue::Submit(const dom::Sequence<OwningNonNull<CommandBuffer>>& buffers) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Fence>
+Queue::InsertFence() const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(Queue)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Queue.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_Queue_H_
+#define WEBGPU_Queue_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+template<typename T> class Sequence;
+} // namespace dom
+
+namespace webgpu {
+
+class CommandBuffer;
+class Device;
+class Fence;
+
+class Queue final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(Queue)
+
+private:
+    Queue() = delete;
+    virtual ~Queue();
+
+public:
+    void Submit(const dom::Sequence<OwningNonNull<CommandBuffer>>& buffers) const;
+    already_AddRefed<Fence> InsertFence() const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_Queue_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/RenderPipeline.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderPipeline.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+RenderPipeline::~RenderPipeline() = default;
+
+WEBGPU_IMPL_GOOP_0(RenderPipeline)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/RenderPipeline.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_RenderPipeline_H_
+#define WEBGPU_RenderPipeline_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class RenderPipeline final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(RenderPipeline)
+
+private:
+    RenderPipeline() = delete;
+    virtual ~RenderPipeline();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_RenderPipeline_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Sampler.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Sampler.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Sampler::~Sampler() = default;
+
+WEBGPU_IMPL_GOOP_0(Sampler)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Sampler.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_SAMPLER_H_
+#define WEBGPU_SAMPLER_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class Sampler final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(Sampler)
+
+private:
+    Sampler() = delete;
+    virtual ~Sampler();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_SAMPLER_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ShaderModule.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShaderModule.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+ShaderModule::~ShaderModule() = default;
+
+WEBGPU_IMPL_GOOP_0(ShaderModule)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/ShaderModule.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_ShaderModule_H_
+#define WEBGPU_ShaderModule_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class ShaderModule final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(ShaderModule)
+
+private:
+    ShaderModule() = delete;
+    virtual ~ShaderModule();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_ShaderModule_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/SwapChain.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SwapChain.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+SwapChain::~SwapChain() = default;
+
+void
+SwapChain::Configure(const dom::WebGPUSwapChainDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+already_AddRefed<Texture>
+SwapChain::GetNextTexture() const
+{
+    MOZ_CRASH("todo");
+}
+
+void
+SwapChain::Present() const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(SwapChain)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/SwapChain.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_SwapChain_H_
+#define WEBGPU_SwapChain_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+struct WebGPUSwapChainDescriptor;
+} // namespace dom
+
+namespace webgpu {
+
+class Device;
+class Texture;
+
+class SwapChain final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(SwapChain)
+
+private:
+    SwapChain() = delete;
+    virtual ~SwapChain();
+
+public:
+    void Configure(const dom::WebGPUSwapChainDescriptor& desc) const;
+    already_AddRefed<Texture> GetNextTexture() const;
+    void Present() const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_SwapChain_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Texture.cpp
@@ -0,0 +1,26 @@
+/* -*- 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 "Texture.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+#include "TextureView.h"
+
+namespace mozilla {
+namespace webgpu {
+
+Texture::~Texture() = default;
+
+already_AddRefed<TextureView>
+Texture::CreateTextureView(const dom::WebGPUTextureViewDescriptor& desc) const
+{
+    MOZ_CRASH("todo");
+}
+
+WEBGPU_IMPL_GOOP_0(Texture)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/Texture.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_Texture_H_
+#define WEBGPU_Texture_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace dom {
+struct WebGPUTextureViewDescriptor;
+} // namespace dom
+
+namespace webgpu {
+
+class Device;
+class TextureView;
+
+class Texture final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(Texture)
+
+private:
+    Texture() = delete;
+    virtual ~Texture();
+
+public:
+    already_AddRefed<TextureView> CreateTextureView(const dom::WebGPUTextureViewDescriptor&) const;
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_Texture_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/TextureView.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureView.h"
+
+#include "Device.h"
+#include "mozilla/dom/WebGPUBinding.h"
+
+namespace mozilla {
+namespace webgpu {
+
+TextureView::~TextureView() = default;
+
+WEBGPU_IMPL_GOOP_0(TextureView)
+
+} // namespace webgpu
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/TextureView.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGPU_TEXTURE_VIEW_H_
+#define WEBGPU_TEXTURE_VIEW_H_
+
+#include "nsWrapperCache.h"
+#include "ObjectModel.h"
+
+namespace mozilla {
+namespace webgpu {
+
+class Device;
+
+class TextureView final
+    : public ChildOf<Device>
+{
+public:
+    WEBGPU_DECL_GOOP(TextureView)
+
+private:
+    TextureView() = delete;
+    virtual ~TextureView();
+};
+
+} // namespace webgpu
+} // namespace mozilla
+
+#endif // WEBGPU_TEXTURE_VIEW_H_
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/mochitest/mochitest-no-pref.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+subsuite = webgl1-core
+
+[test_disabled.html]
+
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/mochitest/mochitest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+subsuite = webgl1-core
+prefs = dom.webgpu.enable=true
+
+[test_enabled.html]
+
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/mochitest/test_disabled.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset='utf-8'>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+
+ok(!SpecialPowers.getBoolPref('dom.webgpu.enable'), 'Pref should be disabled.');
+ok(window.WebGPU === undefined, 'window.WebGPU === undefined');
+ok(window.webgpu === undefined, 'window.webgpu === undefined');
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/mochitest/test_enabled.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset='utf-8'>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+
+ok(SpecialPowers.getBoolPref('dom.webgpu.enable'), 'Pref should be enabled.');
+ok(window.WebGPU !== undefined, 'window.WebGPU !== undefined');
+ok(window.webgpu !== undefined, 'window.webgpu !== undefined');
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/webgpu/moz.build
@@ -0,0 +1,49 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
+MOCHITEST_MANIFESTS += [
+    'mochitest/mochitest-no-pref.ini',
+    'mochitest/mochitest.ini',
+]
+
+h_and_cpp = [
+    'Adapter',
+    'AttachmentState',
+    'BindGroup',
+    'BindGroupLayout',
+    'BlendState',
+    'Buffer',
+    'CommandBuffer',
+    'CommandEncoder',
+    'ComputePipeline',
+    'DepthStencilState',
+    'Device',
+    'Fence',
+    'InputState',
+    'Instance',
+    'InstanceProvider',
+    'LogEntry',
+    'ObjectModel',
+    'PipelineLayout',
+    'Queue',
+    'RenderPipeline',
+    'Sampler',
+    'ShaderModule',
+    'SwapChain',
+    'Texture',
+    'TextureView',
+]
+EXPORTS.mozilla.webgpu += [x + '.h' for x in h_and_cpp]
+UNIFIED_SOURCES += [x + '.cpp' for x in h_and_cpp]
+
+EXPORTS.mozilla.webgpu += [
+#    'ObjectModel.h',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -36,16 +36,18 @@ interface Element : Node {
   readonly attribute NamedNodeMap attributes;
   [Pure]
   sequence<DOMString> getAttributeNames();
   [Pure]
   DOMString? getAttribute(DOMString name);
   [Pure]
   DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
   [CEReactions, NeedsSubjectPrincipal=NonSystem, Throws]
+  boolean toggleAttribute(DOMString name, optional boolean force);
+  [CEReactions, NeedsSubjectPrincipal=NonSystem, Throws]
   void setAttribute(DOMString name, DOMString value);
   [CEReactions, NeedsSubjectPrincipal=NonSystem, Throws]
   void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);
   [CEReactions, Throws]
   void removeAttribute(DOMString name);
   [CEReactions, Throws]
   void removeAttributeNS(DOMString? namespace, DOMString localName);
   [Pure]
--- a/dom/webidl/ScrollBoxObject.webidl
+++ b/dom/webidl/ScrollBoxObject.webidl
@@ -18,53 +18,28 @@ interface ScrollBoxObject : BoxObject {
 
   /**
    * Scroll the given amount of device pixels to the right and down.
    * Values will be clamped to make the resuling position legal.
    */
   [Throws]
   void scrollBy(long dx, long dy);
   [Throws]
-  void scrollByLine(long dlines);
-  [Throws]
   void scrollByIndex(long dindexes);
   [Throws]
-  void scrollToLine(long line);
-  [Throws]
   void scrollToElement(Element child);
-  [Throws]
-  void scrollToIndex(long index);
 
   /**
    * Get the current scroll position in css pixels.
    * @see scrollTo for the definition of x and y.
    */
   [Pure, Throws]
   readonly attribute long positionX;
   [Pure, Throws]
   readonly attribute long positionY;
   [Pure, Throws]
   readonly attribute long scrolledWidth;
   [Pure, Throws]
   readonly attribute long scrolledHeight;
 
-  /**
-   * DEPRECATED: Please use positionX and positionY
-   *
-   * Get the current scroll position in css pixels.
-   * @see scrollTo for the definition of x and y.
-   */
-  [Throws]
-  void getPosition(object x, object y);
-
-  /**
-   * DEPRECATED: Please use scrolledWidth and scrolledHeight
-   */
-  [Throws]
-  void getScrolledSize(object width, object height);
-
   [Throws]
   void ensureElementIsVisible(Element child);
-  [Throws]
-  void ensureIndexIsVisible(long index);
-  [Throws]
-  void ensureLineIsVisible(long line);
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WebGPU.webidl
@@ -0,0 +1,639 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
+ */
+
+typedef unsigned long u32;
+typedef unsigned long long u64;
+
+// ****************************************************************************
+// ERROR HANDLING
+// ****************************************************************************
+
+enum WebGPULogEntryType {
+    "device-lost",
+    "validation-error",
+    "recoverable-out-of-memory",
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPULogEntry {
+    readonly attribute WebGPULogEntryType type;
+    readonly attribute any obj;
+    readonly attribute DOMString? reason;
+};
+
+enum WebGPUObjectStatus {
+    "valid",
+    "out-of-memory",
+    "invalid",
+};
+
+typedef (WebGPUBuffer or WebGPUTexture) WebGPUStatusable;
+
+callback WebGPULogCallback = void (WebGPULogEntry error);
+
+// ****************************************************************************
+// SHADER RESOURCES (buffer, textures, texture views, samples)
+// ****************************************************************************
+
+// Buffer
+typedef u32 WebGPUBufferUsageFlags;
+[Pref="dom.webgpu.enable"]
+interface WebGPUBufferUsage {
+    const u32 NONE = 0;
+    const u32 MAP_READ = 1;
+    const u32 MAP_WRITE = 2;
+    const u32 TRANSFER_SRC = 4;
+    const u32 TRANSFER_DST = 8;
+    const u32 INDEX = 16;
+    const u32 VERTEX = 32;
+    const u32 UNIFORM = 64;
+    const u32 STORAGE = 128;
+};
+
+dictionary WebGPUBufferDescriptor {
+    u32 size;
+    WebGPUBufferUsageFlags usage;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUBuffer {
+    readonly attribute ArrayBuffer? mapping;
+    void unmap();
+};
+
+// Texture view
+dictionary WebGPUTextureViewDescriptor {
+    // TODO Investigate what goes in there.
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUTextureView {
+};
+
+// Texture
+typedef u32 WebGPUTextureDimensionEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUTextureDimension {
+    const u32 e1D = 0;
+    const u32 e2D = 1;
+    const u32 e3D = 2;
+    // TODO other dimensions (cube, arrays)
+};
+
+typedef u32 WebGPUTextureFormatEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUTextureFormat {
+    const u32 R8_G8_B8_A8_UNORM = 0;
+    const u32 R8_G8_B8_A8_UINT = 1;
+    const u32 B8_G8_R8_A8_UNORM = 2;
+    const u32 D32_FLOAT_S8_UINT = 3;
+    // TODO other formats
+};
+
+typedef u32 WebGPUTextureUsageFlags;
+[Pref="dom.webgpu.enable"]
+interface WebGPUTextureUsage {
+    const u32 NONE = 0;
+    const u32 TRANSFER_SRC = 1;
+    const u32 TRANSFER_DST = 2;
+    const u32 SAMPLED = 4;
+    const u32 STORAGE = 8;
+    const u32 OUTPUT_ATTACHMENT = 16;
+    const u32 PRESENT = 32;
+};
+
+dictionary WebGPUTextureDescriptor {
+    u32 width;
+    u32 height;
+    u32 depth;
+    u32 arraySize;
+    WebGPUTextureDimensionEnum dimension;
+    WebGPUTextureFormatEnum format;
+    WebGPUTextureUsageFlags usage;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUTexture {
+    WebGPUTextureView createTextureView(optional WebGPUTextureViewDescriptor desc);
+};
+
+// Sampler
+typedef u32 WebGPUFilterModeEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUFilterMode {
+    const u32 NEAREST = 0;
+    const u32 LINEAR = 1;
+};
+
+dictionary WebGPUSamplerDescriptor {
+    WebGPUFilterModeEnum magFilter;
+    WebGPUFilterModeEnum minFilter;
+    WebGPUFilterModeEnum mipmapFilter;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUSampler {
+};
+
+// ****************************************************************************
+// BINDING MODEL (bindgroup layout, bindgroup)
+// ****************************************************************************
+
+// BindGroupLayout
+typedef u32 WebGPUShaderStageFlags;
+[Pref="dom.webgpu.enable"]
+interface WebGPUShaderStageBit {
+    const u32 NONE = 0;
+    const u32 VERTEX = 1;
+    const u32 FRAGMENT = 2;
+    const u32 COMPUTE = 4;
+};
+
+typedef u32 WebGPUBindingTypeEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUBindingType {
+    const u32 UNIFORM_BUFFER = 0;
+    const u32 SAMPLER = 1;
+    const u32 SAMPLED_TEXTURE = 2;
+    const u32 STORAGE_BUFFER = 3;
+    // TODO other binding types (storage images, ...)
+};
+
+dictionary WebGPUBindGroupBinding {
+    WebGPUShaderStageFlags visibility;
+    WebGPUBindingTypeEnum type;
+    u32 start;
+    u32 count;
+};
+
+dictionary WebGPUBindGroupLayoutDescriptor {
+    sequence<WebGPUBindGroupBinding> bindingTypes;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUBindGroupLayout {
+};
+
+// PipelineLayout
+dictionary WebGPUPipelineLayoutDescriptor {
+    sequence<WebGPUBindGroupLayout> bindGroupLayouts;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUPipelineLayout {
+};
+
+// BindGroup
+/* Moved to WebGPUExtras.webidl for now.
+dictionary WebGPUBufferBinding {
+    WebGPUBuffer buffer;
+    u32 offset;
+    u32 size;
+};
+*/
+
+typedef (WebGPUSampler or WebGPUTextureView or WebGPUBufferBinding) WebGPUBindingResource;
+
+dictionary WebGPUBinding {
+    sequence<WebGPUBindingResource> resources;
+    u32 start;
+    u32 count;
+};
+
+dictionary WebGPUBindGroupDescriptor {
+    WebGPUBindGroupLayout layout;
+    sequence<WebGPUBinding> bindings;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUBindGroup {
+};
+
+// ****************************************************************************
+// PIPELINE CREATION (blend state, DS state, ..., pipelines)
+// ****************************************************************************
+
+// BlendState
+typedef u32 WebGPUBlendFactorEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUBlendFactor {
+    const u32 ZERO = 0;
+    const u32 ONE = 1;
+    const u32 SRC_COLOR = 2;
+    const u32 ONE_MINUS_SRC_COLOR = 3;
+    const u32 SRC_ALPHA = 4;
+    const u32 ONE_MINUS_SRC_ALPHA = 5;
+    const u32 DST_COLOR = 6;
+    const u32 ONE_MINUS_DST_COLOR = 7;
+    const u32 DST_ALPHA = 8;
+    const u32 ONE_MINUS_DST_ALPHA = 9;
+    const u32 SRC_ALPHA_SATURATED = 10;
+    const u32 BLEND_COLOR = 11;
+    const u32 ONE_MINUS_BLEND_COLOR = 12;
+};
+
+typedef u32 WebGPUBlendOperationEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUBlendOperation {
+    const u32 ADD = 0;
+    const u32 SUBTRACT = 1;
+    const u32 REVERSE_SUBTRACT = 2;
+    const u32 MIN = 3;
+    const u32 MAX = 4;
+};
+
+typedef u32 WebGPUColorWriteFlags;
+[Pref="dom.webgpu.enable"]
+interface WebGPUColorWriteBits {
+    const u32 NONE = 0;
+    const u32 RED = 1;
+    const u32 GREEN = 2;
+    const u32 BLUE = 4;
+    const u32 ALPHA = 8;
+    const u32 ALL = 15;
+};
+
+dictionary WebGPUBlendDescriptor {
+    WebGPUBlendFactorEnum srcFactor;
+    WebGPUBlendFactorEnum dstFactor;
+    WebGPUBlendOperationEnum operation;
+};
+
+dictionary WebGPUBlendStateDescriptor {
+    boolean blendEnabled;
+    WebGPUBlendDescriptor alpha;
+    WebGPUBlendDescriptor color;
+    WebGPUColorWriteFlags writeMask;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUBlendState {
+};
+
+// DepthStencilState
+typedef u32 WebGPUCompareFunctionEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUCompareFunction {
+    const u32 NEVER = 0;
+    const u32 LESS = 1;
+    const u32 LESS_EQUAL = 2;
+    const u32 GREATER = 3;
+    const u32 GREATER_EQUAL = 4;
+    const u32 EQUAL = 5;
+    const u32 NOT_EQUAL = 6;
+    const u32 ALWAYS = 7;
+};
+
+typedef u32 WebGPUStencilOperationEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUStencilOperation {
+    const u32 KEEP = 0;
+    const u32 ZERO = 1;
+    const u32 REPLACE = 2;
+    const u32 INVERT = 3;
+    const u32 INCREMENT_CLAMP = 4;
+    const u32 DECREMENT_CLAMP = 5;
+    const u32 INCREMENT_WRAP = 6;
+    const u32 DECREMENT_WRAP = 7;
+};
+
+dictionary WebGPUStencilStateFaceDescriptor {
+    WebGPUCompareFunctionEnum compare;
+    WebGPUStencilOperationEnum stencilFailOp;
+    WebGPUStencilOperationEnum depthFailOp;
+    WebGPUStencilOperationEnum passOp;
+};
+
+dictionary WebGPUDepthStencilStateDescriptor {
+    boolean depthWriteEnabled;
+    WebGPUCompareFunctionEnum depthCompare;
+
+    WebGPUStencilStateFaceDescriptor front;
+    WebGPUStencilStateFaceDescriptor back;
+
+    u32 stencilReadMask;
+    u32 stencilWriteMask;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUDepthStencilState {
+};
+
+// InputState
+typedef u32 WebGPUIndexFormatEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUIndexFormat {
+    const u32 UINT16 = 0;
+    const u32 UINT32 = 1;
+};
+
+typedef u32 WebGPUVertexFormatEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUVertexFormat {
+    const u32 FLOAT_R32_G32_B32_A32 = 0;
+    const u32 FLOAT_R32_G32_B32 = 1;
+    const u32 FLOAT_R32_G32 = 2;
+    const u32 FLOAT_R32 = 3;
+    // TODO other vertex formats
+};
+
+typedef u32 WebGPUInputStepModeEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUInputStepMode {
+    const u32 VERTEX = 0;
+    const u32 INSTANCE = 1;
+};
+
+dictionary WebGPUVertexAttributeDescriptor {
+    u32 shaderLocation;
+    u32 inputSlot;
+    u32 offset;
+    WebGPUVertexFormatEnum format;
+};
+
+dictionary WebGPUVertexInputDescriptor {
+    u32 inputSlot;
+    u32 stride;
+    WebGPUInputStepModeEnum stepMode;
+};
+
+dictionary WebGPUInputStateDescriptor {
+    WebGPUIndexFormatEnum indexFormat;
+
+    sequence<WebGPUVertexAttributeDescriptor> attributes;
+    sequence<WebGPUVertexInputDescriptor> inputs;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUInputState {
+};
+
+// ShaderModule
+dictionary WebGPUShaderModuleDescriptor {
+    required ArrayBuffer code;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUShaderModule {
+};
+
+// AttachmentState
+dictionary WebGPUAttachmentStateDescriptor {
+    sequence<WebGPUTextureFormatEnum> formats;
+    // TODO other stuff like sample count etc.
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUAttachmentState {
+};
+
+// Common stuff for ComputePipeline and RenderPipeline
+typedef u32 WebGPUShaderStageEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUShaderStage {
+    const u32 VERTEX = 0;
+    const u32 FRAGMENT = 1;
+    const u32 COMPUTE = 2;
+};
+
+dictionary WebGPUPipelineStageDescriptor {
+    required WebGPUShaderModule shaderModule;
+    required WebGPUShaderStageEnum stage;
+    required DOMString entryPoint;
+    // TODO other stuff like specialization constants?
+};
+
+dictionary WebGPUPipelineDescriptorBase {
+    required WebGPUPipelineLayout layout;
+    sequence<WebGPUPipelineStageDescriptor> stages;
+};
+
+// ComputePipeline
+dictionary WebGPUComputePipelineDescriptor : WebGPUPipelineDescriptorBase {
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUComputePipeline {
+};
+
+// WebGPURenderPipeline
+typedef u32 WebGPUPrimitiveTopologyEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUPrimitiveTopology {
+    const u32 POINT_LIST = 0;
+    const u32 LINE_LIST = 1;
+    const u32 LINE_STRIP = 2;
+    const u32 TRIANGLE_LIST = 3;
+    const u32 TRIANGLE_STRIP = 4;
+};
+
+dictionary WebGPURenderPipelineDescriptor : WebGPUPipelineDescriptorBase {
+    WebGPUPrimitiveTopologyEnum primitiveTopology;
+    sequence<WebGPUBlendState> blendState;
+    WebGPUDepthStencilState depthStencilState;
+    WebGPUInputState inputState;
+    WebGPUAttachmentState attachmentState;
+    // TODO other properties
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPURenderPipeline {
+};
+// ****************************************************************************
+// COMMAND RECORDING (Command buffer and all relevant structures)
+// ****************************************************************************
+
+typedef u32 WebGPULoadOpEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPULoadOp {
+    const u32 CLEAR = 0;
+    const u32 LOAD = 1;
+};
+
+typedef u32 WebGPUStoreOpEnum;
+[Pref="dom.webgpu.enable"]
+interface WebGPUStoreOp {
+    const u32 STORE = 0;
+};
+
+dictionary WebGPURenderPassAttachmentDescriptor {
+    WebGPUTextureView attachment;
+    WebGPULoadOpEnum loadOp;
+    WebGPUStoreOpEnum storeOp;
+};
+
+dictionary WebGPURenderPassDescriptor {
+    sequence<WebGPURenderPassAttachmentDescriptor> colorAttachments;
+    WebGPURenderPassAttachmentDescriptor depthStencilAttachment;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUCommandBuffer {
+};
+
+dictionary WebGPUCommandEncoderDescriptor {
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUCommandEncoder {
+    WebGPUCommandBuffer finishEncoding();
+
+    // Commands allowed outside of "passes"
+    void copyBufferToBuffer(WebGPUBuffer src,
+                            u32 srcOffset,
+                            WebGPUBuffer dst,
+                            u32 dstOffset,
+                            u32 size);
+    // TODO figure out all the arguments required for these
+    void copyBufferToTexture();
+    void copyTextureToBuffer();
+    void copyTextureToTexture();
+    void blit();
+
+    void transitionBuffer(WebGPUBuffer b, WebGPUBufferUsageFlags f);
+
+    // Allowed in both compute and render passes
+    void setPushConstants(WebGPUShaderStageFlags stage,
+                          u32 offset,
+                          u32 count,
+                          ArrayBuffer data);
+    void setBindGroup(u32 index, WebGPUBindGroup bindGroup);
+    void setPipeline((WebGPUComputePipeline or WebGPURenderPipeline) pipeline);
+
+    // Compute pass commands
+    void beginComputePass();
+    void endComputePass();
+
+    void dispatch(u32 x, u32 y, u32 z);
+
+    // Render pass commands
+    void beginRenderPass(optional WebGPURenderPassDescriptor descriptor);
+    void endRenderPass();
+
+    void setBlendColor(float r, float g, float b, float a);
+    void setIndexBuffer(WebGPUBuffer buffer, u32 offset);
+    void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<u32> offsets);
+
+    void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
+    void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, u32 firstInstance, u32 firstVertex);
+
+    // TODO add missing commands
+};
+
+// ****************************************************************************
+// OTHER (Fence, Queue SwapChain, Device)
+// ****************************************************************************
+
+// Fence
+[Pref="dom.webgpu.enable"]
+interface WebGPUFence {
+    boolean wait(double milliseconds);
+    readonly attribute Promise<void> promise;
+};
+
+// Queue
+[Pref="dom.webgpu.enable"]
+interface WebGPUQueue {
+    void submit(sequence<WebGPUCommandBuffer> buffers);
+    WebGPUFence insertFence();
+};
+
+// SwapChain / RenderingContext
+dictionary WebGPUSwapChainDescriptor {
+    WebGPUTextureUsageFlags usage;
+    WebGPUTextureFormatEnum format;
+    u32 width;
+    u32 height;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUSwapChain {
+    void configure(optional WebGPUSwapChainDescriptor descriptor);
+    WebGPUTexture getNextTexture();
+    void present();
+};
+
+//[Pref="dom.webgpu.enable"]
+//interface WebGPURenderingContext : WebGPUSwapChain {
+//};
+
+// WebGPU "namespace" used for device creation
+dictionary WebGPUExtensions {
+    boolean anisotropicFiltering;
+    boolean logicOp; // Previously a "Feature".
+};
+
+dictionary WebGPULimits {
+    u32 maxBindGroups;
+};
+
+// Device
+[Pref="dom.webgpu.enable"]
+interface WebGPUDevice {
+    readonly attribute WebGPUAdapter adapter;
+    WebGPUExtensions extensions();
+    WebGPULimits limits();
+
+    WebGPUBuffer createBuffer(optional WebGPUBufferDescriptor descriptor);
+    WebGPUTexture createTexture(optional WebGPUTextureDescriptor descriptor);
+    WebGPUSampler createSampler(optional WebGPUSamplerDescriptor descriptor);
+
+    WebGPUBindGroupLayout createBindGroupLayout(optional WebGPUBindGroupLayoutDescriptor descriptor);
+    WebGPUPipelineLayout createPipelineLayout(optional WebGPUPipelineLayoutDescriptor descriptor);
+    WebGPUBindGroup createBindGroup(optional WebGPUBindGroupDescriptor descriptor);
+
+    WebGPUBlendState createBlendState(optional WebGPUBlendStateDescriptor descriptor);
+    WebGPUDepthStencilState createDepthStencilState(optional WebGPUDepthStencilStateDescriptor descriptor);
+    WebGPUInputState createInputState(optional WebGPUInputStateDescriptor descriptor);
+    WebGPUShaderModule createShaderModule(WebGPUShaderModuleDescriptor descriptor);
+    WebGPUAttachmentState createAttachmentState(optional WebGPUAttachmentStateDescriptor descriptor);
+    WebGPUComputePipeline createComputePipeline(WebGPUComputePipelineDescriptor descriptor);
+    WebGPURenderPipeline createRenderPipeline(WebGPURenderPipelineDescriptor descriptor);
+
+    WebGPUCommandEncoder createCommandEncoder(optional WebGPUCommandEncoderDescriptor descriptor);
+
+    WebGPUQueue getQueue();
+
+    attribute WebGPULogCallback onLog;
+    Promise<WebGPUObjectStatus> getObjectStatus(WebGPUStatusable obj);
+};
+
+dictionary WebGPUDeviceDescriptor {
+    WebGPUExtensions extensions;
+    //WebGPULimits limits; Don't expose higher limits for now.
+
+    // TODO are other things configurable like queues?
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPUAdapter {
+    readonly attribute DOMString name;
+    WebGPUExtensions extensions();
+    //WebGPULimits limits(); Don't expose higher limits for now.
+
+    WebGPUDevice createDevice(optional WebGPUDeviceDescriptor descriptor);
+};
+
+enum WebGPUPowerPreference { "default", "low-power", "high-performance" };
+
+dictionary WebGPUAdapterDescriptor {
+    WebGPUPowerPreference powerPreference;
+};
+
+[Pref="dom.webgpu.enable"]
+interface WebGPU {
+    WebGPUAdapter getAdapter(optional WebGPUAdapterDescriptor desc);
+};
+
+// Add a "webgpu" member to Window that contains the global instance of a "WebGPU"
+[NoInterfaceObject]
+interface WebGPUProvider {
+    [SameObject, Replaceable, Pref="dom.webgpu.enable"] readonly attribute WebGPU webgpu;
+};
+//Window includes WebGPUProvider;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WebGPUExtras.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * Some parts of WebGPU.webidl need to be pulled into a different file due to a codegen
+ * bug/missing support.
+ */
+
+dictionary WebGPUBufferBinding {
+    WebGPUBuffer buffer;
+    u32 offset;
+    u32 size;
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -558,8 +558,10 @@ partial interface Window {
 
   /**
    * Getter funcion for IntlUtils, which provides helper functions for
    * localization.
    */
   [Throws, Func="IsChromeOrXBL"]
   readonly attribute IntlUtils intlUtils;
 };
+
+Window implements WebGPUProvider;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -323,16 +323,19 @@ with Files("WaveShaperNode.webidl"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("WebAuthentication.webidl"):
     BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
 
 with Files("WebGL*"):
     BUG_COMPONENT = ("Core", "Canvas: WebGL")
 
+with Files("WebGPU*"):
+    BUG_COMPONENT = ("Core", "Canvas: WebGL")
+
 with Files("WebKitCSSMatrix.webidl"):
     BUG_COMPONENT = ("Core", "DOM: CSS Object Model")
 
 with Files("Webrtc*"):
     BUG_COMPONENT = ("Core", "WebRTC")
 
 with Files("WheelEvent.webidl"):
     BUG_COMPONENT = ("Core", "DOM: Events")
@@ -917,16 +920,18 @@ WEBIDL_FILES = [
     'VRServiceTest.webidl',
     'VTTCue.webidl',
     'VTTRegion.webidl',
     'WaveShaperNode.webidl',
     'WebAuthentication.webidl',
     'WebComponents.webidl',
     'WebGL2RenderingContext.webidl',
     'WebGLRenderingContext.webidl',
+    'WebGPU.webidl',
+    'WebGPUExtras.webidl',
     'WebKitCSSMatrix.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
     'WidevineCDMManifest.webidl',
     'WindowOrWorkerGlobalScope.webidl',
     'WindowRoot.webidl',
     'Worker.webidl',
     'WorkerDebuggerGlobalScope.webidl',
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4348,19 +4348,22 @@ GetIteratorIRGenerator::tryAttachStub()
 
     ValOperandId valId(writer.setInputOperandId(0));
     if (!val_.isObject())
         return false;
 
     RootedObject obj(cx_, &val_.toObject());
 
     ObjOperandId objId = writer.guardIsObject(valId);
-    if (tryAttachNativeIterator(objId, obj))
-        return true;
-
+    if (tryAttachNativeIterator(objId, obj)) {
+      trackAttached("GetIterator");
+      return true;
+    }
+
+    trackAttached(IRGenerator::NotAttached);
     return false;
 }
 
 bool
 GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject obj)
 {
     MOZ_ASSERT(JSOp(*pc_) == JSOP_ITER);
 
@@ -4386,16 +4389,26 @@ GetIteratorIRGenerator::tryAttachNativeI
     ObjOperandId iterId =
         writer.guardAndGetIterator(objId, iterobj, &ObjectRealm::get(obj).enumerators);
     writer.loadObjectResult(iterId);
     writer.returnFromIC();
 
     return true;
 }
 
+void
+GetIteratorIRGenerator::trackAttached(const char* name)
+{
+#ifdef JS_CACHEIR_SPEW
+    if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
+        sp.valueProperty("val", val_);
+    }
+#endif
+}
+
 CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op,
                                  ICState::Mode mode, uint32_t argc,
                                  HandleValue callee, HandleValue thisval, HandleValueArray args)
   : IRGenerator(cx, script, pc, CacheKind::Call, mode),
     op_(op),
     argc_(argc),
     callee_(callee),
     thisval_(thisval),
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -1663,16 +1663,18 @@ class MOZ_RAII GetIteratorIRGenerator : 
 
     bool tryAttachNativeIterator(ObjOperandId objId, HandleObject obj);
 
   public:
     GetIteratorIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
                            HandleValue value);
 
     bool tryAttachStub();
+
+    void trackAttached(const char *name);
 };
 
 class MOZ_RAII CallIRGenerator : public IRGenerator
 {
   private:
     JSOp op_;
     uint32_t argc_;
     HandleValue callee_;
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -574,17 +574,18 @@ public:
    */
   void Accumulate(ContainerState* aState,
                   nsDisplayItem* aItem,
                   const nsIntRect& aVisibleRect,
                   const nsRect& aContentRect,
                   const DisplayItemClip& aClip,
                   LayerState aLayerState,
                   nsDisplayList *aList,
-                  DisplayItemEntryType aType);
+                  DisplayItemEntryType aType,
+                  nsTArray<size_t>& aOpacityIndices);
   AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
 
   /**
    * A region including the horizontal pan, vertical pan, and no action regions.
    */
   nsRegion CombinedTouchActionRegion();
 
   /**
@@ -763,21 +764,16 @@ public:
    * This is a conservative approximation: it contains the true region.
    */
   nsIntRegion mVisibleAboveRegion;
   /**
    * All the display items that have been assigned to this painted layer.
    * These items get added by Accumulate().
    */
   nsTArray<AssignedDisplayItem> mAssignedDisplayItems;
-  /**
-   * Tracks the active opacity markers by holding the indices to PUSH_OPACITY
-   * items in |mAssignedDisplayItems|.
-   */
-  nsTArray<size_t> mOpacityIndices;
 };
 
 struct NewLayerEntry {
   NewLayerEntry()
     : mAnimatedGeometryRoot(nullptr)
     , mASR(nullptr)
     , mClipChain(nullptr)
     , mScrollMetadataASR(nullptr)
@@ -3346,18 +3342,16 @@ SetBackfaceHiddenForLayer(bool aBackface
   }
 }
 
 template<typename FindOpaqueBackgroundColorCallbackType>
 void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor)
 {
   PaintedLayerData* data = &aData;
 
-  MOZ_ASSERT(data->mOpacityIndices.IsEmpty());
-
   if (!data->mLayer) {
     // No layer was recycled, so we create a new one.
     RefPtr<PaintedLayer> paintedLayer = CreatePaintedLayer(data);
     data->mLayer = paintedLayer;
 
     NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, paintedLayer) < 0,
                  "Layer already in list???");
     mNewChildLayers[data->mNewChildLayersIndex].mLayer = paintedLayer.forget();
@@ -3634,28 +3628,29 @@ IsItemAreaInWindowOpaqueRegion(nsDisplay
 void
 PaintedLayerData::Accumulate(ContainerState* aState,
                              nsDisplayItem* aItem,
                              const nsIntRect& aVisibleRect,
                              const nsRect& aContentRect,
                              const DisplayItemClip& aClip,
                              LayerState aLayerState,
                              nsDisplayList* aList,
-                             DisplayItemEntryType aType)
+                             DisplayItemEntryType aType,
+                             nsTArray<size_t>& aOpacityIndices)
 {
   FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
 
-  const bool hasOpacity = mOpacityIndices.Length() > 0;
+  const bool hasOpacity = aOpacityIndices.Length() > 0;
 
   const DisplayItemClip* oldClip = mItemClip;
   mItemClip = &aClip;
 
   if (aType == DisplayItemEntryType::POP_OPACITY) {
-    MOZ_ASSERT(!mOpacityIndices.IsEmpty());
-    mOpacityIndices.RemoveLastElement();
+    MOZ_ASSERT(!aOpacityIndices.IsEmpty());
+    aOpacityIndices.RemoveLastElement();
 
     AssignedDisplayItem item(aItem, aLayerState,
                              nullptr, aContentRect, aType, hasOpacity);
     mAssignedDisplayItems.AppendElement(std::move(item));
     return;
   }
 
   if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
@@ -3668,17 +3663,17 @@ PaintedLayerData::Accumulate(ContainerSt
     // Note that the transform (if any) on the PaintedLayer is always an integer
     // translation so we don't have to factor that in here.
     aItem->DisableComponentAlpha();
   } else {
     componentAlphaBounds = aItem->GetComponentAlphaBounds(aState->mBuilder);
 
     if (!componentAlphaBounds.IsEmpty()) {
       // This display item needs background copy when pushing opacity group.
-      for (size_t i : mOpacityIndices) {
+      for (size_t i : aOpacityIndices) {
         AssignedDisplayItem& item = mAssignedDisplayItems[i];
         MOZ_ASSERT(item.mType == DisplayItemEntryType::PUSH_OPACITY ||
                    item.mType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG);
         item.mType = DisplayItemEntryType::PUSH_OPACITY_WITH_BG;
       }
     }
   }
 
@@ -3691,17 +3686,17 @@ PaintedLayerData::Accumulate(ContainerSt
     aState->mLayerBuilder->GetOldLayerForFrame(aItem->Frame(),
                                                aItem->GetPerFrameKey(),
                                                currentData);
   AssignedDisplayItem item(aItem, aLayerState,
                            oldData, aContentRect, aType, hasOpacity);
   mAssignedDisplayItems.AppendElement(std::move(item));
 
   if (aType == DisplayItemEntryType::PUSH_OPACITY) {
-    mOpacityIndices.AppendElement(mAssignedDisplayItems.Length() - 1);
+    aOpacityIndices.AppendElement(mAssignedDisplayItems.Length() - 1);
   }
 
   if (aItem->MustPaintOnContentSide()) {
      mShouldPaintOnContentSide = true;
   }
 
   if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aVisibleRect) &&
       mVisibleRegion.Contains(aVisibleRect) && !mImage) {
@@ -4283,16 +4278,17 @@ ContainerState::ProcessDisplayItems(nsDi
   }
 
   AnimatedGeometryRoot* lastAnimatedGeometryRoot = nullptr;
   nsPoint lastTopLeft;
 
   // Tracks the PaintedLayerData that the item will be accumulated in, if it is
   // non-null. Currently only used with PUSH_OPACITY and POP_OPACITY markers.
   PaintedLayerData* selectedPLD = nullptr;
+  AutoTArray<size_t, 2> opacityIndices;
 
   FLBDisplayItemIterator iter(mBuilder, aList, this);
   while (iter.HasNext()) {
     DisplayItemEntry e = iter.GetNextEntry();
     nsDisplayItem* i = e.mItem;
     DisplayItemEntryType marker = e.mType;
 
 
@@ -4836,17 +4832,17 @@ ContainerState::ProcessDisplayItems(nsDi
       MOZ_ASSERT(paintedLayerData);
 
       if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
         nsDisplayCompositorHitTestInfo* hitTestInfo =
           static_cast<nsDisplayCompositorHitTestInfo*>(item);
         paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo);
       } else {
         paintedLayerData->Accumulate(this, item, itemVisibleRect, itemContent, itemClip,
-                                     layerState, aList, marker);
+                                     layerState, aList, marker, opacityIndices);
 
         if (!paintedLayerData->mLayer) {
           // Try to recycle the old layer of this display item.
           RefPtr<PaintedLayer> layer =
             AttemptToRecyclePaintedLayer(animatedGeometryRoot, item,
                                          topLeft, referenceFrame);
           if (layer) {
             paintedLayerData->mLayer = layer;
@@ -4863,17 +4859,17 @@ ContainerState::ProcessDisplayItems(nsDi
 
       if (marker == DisplayItemEntryType::PUSH_OPACITY) {
         selectedPLD = paintedLayerData;
       }
 
       if (marker == DisplayItemEntryType::POP_OPACITY ) {
         MOZ_ASSERT(selectedPLD);
 
-        if (selectedPLD->mOpacityIndices.IsEmpty()) {
+        if (opacityIndices.IsEmpty()) {
           selectedPLD = nullptr;
         }
       }
     }
 
     nsDisplayList* childItems = item->GetSameCoordinateSystemChildren();
     if (childItems && childItems->NeedsTransparentSurface()) {
       aList->SetNeedsTransparentSurface();
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -928,17 +928,17 @@ class RefTest(object):
 
 
 def run_test_harness(parser, options):
     reftest = RefTest(options.suite)
     parser.validate(options, reftest)
 
     # We have to validate options.app here for the case when the mach
     # command is able to find it after argument parsing. This can happen
-    # when running from a tests.zip.
+    # when running from a tests archive.
     if not options.app:
         parser.error("could not find the application path, --appname must be specified")
 
     options.app = reftest.getFullPath(options.app)
     if not os.path.exists(options.app):
         parser.error("Error: Path %(app)s doesn't exist. Are you executing "
                      "$objdir/_tests/reftest/runreftest.py?" % {"app": options.app})
 
--- a/layout/xul/ScrollBoxObject.cpp
+++ b/layout/xul/ScrollBoxObject.cpp
@@ -54,28 +54,16 @@ void ScrollBoxObject::ScrollBy(int32_t d
 
   if (aRv.Failed()) {
     return;
   }
 
   ScrollTo(pt.x + dx, pt.y + dy, aRv);
 }
 
-void ScrollBoxObject::ScrollByLine(int32_t dlines, ErrorResult& aRv)
-{
-  nsIScrollableFrame* sf = GetScrollFrame();
-  if (!sf) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  sf->ScrollBy(nsIntPoint(0, dlines), nsIScrollableFrame::LINES,
-               nsIScrollableFrame::SMOOTH);
-}
-
 // XUL <scrollbox> elements have a single box child element.
 // Get a pointer to that box.
 // Note that now that the <scrollbox> is just a regular box
 // with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame,
 // the <scrollbox>'s box frame is the scrollframe's "scrolled frame", and
 // the <scrollbox>'s child box is a child of that.
 static nsIFrame* GetScrolledBox(BoxObject* aScrollBox) {
   nsIFrame* frame = aScrollBox->GetFrame(false);
@@ -207,30 +195,16 @@ void ScrollBoxObject::ScrollByIndex(int3
        sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range);
    } else {
        // Use a destination range that ensures the top edge will be visible.
        nsRect range(cp.x, rect.y - csspixel, 0, csspixel);
        sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range);
    }
 }
 
-void ScrollBoxObject::ScrollToLine(int32_t line, ErrorResult& aRv)
-{
-  nsIScrollableFrame* sf = GetScrollFrame();
-  if (!sf) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nscoord y = sf->GetLineScrollAmount().height * line;
-  nsRect range(0, y - nsPresContext::CSSPixelsToAppUnits(1),
-               0, nsPresContext::CSSPixelsToAppUnits(1));
-  sf->ScrollTo(nsPoint(0, y), nsIScrollableFrame::INSTANT, &range);
-}
-
 void ScrollBoxObject::ScrollToElement(Element& child, ErrorResult& aRv)
 {
   nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
   if (!shell) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
@@ -240,21 +214,16 @@ void ScrollBoxObject::ScrollToElement(El
                                  nsIPresShell::SCROLL_ALWAYS),
                                nsIPresShell::ScrollAxis(
                                  nsIPresShell::SCROLL_LEFT,
                                  nsIPresShell::SCROLL_ALWAYS),
                                nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
                                nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 }
 
-void ScrollBoxObject::ScrollToIndex(int32_t index, ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-}
-
 int32_t ScrollBoxObject::GetPositionX(ErrorResult& aRv)
 {
   CSSIntPoint pt;
   GetPosition(pt, aRv);
   return pt.x;
 }
 
 int32_t ScrollBoxObject::GetPositionY(ErrorResult& aRv)
@@ -299,86 +268,33 @@ void ScrollBoxObject::GetScrolledSize(ns
       return;
     }
 
     aRect = scrolledBox->GetRect();
     aRect.width  = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
     aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
 }
 
-void ScrollBoxObject::GetPosition(JSContext* cx,
-                                  JS::Handle<JSObject*> x,
-                                  JS::Handle<JSObject*> y,
-                                  ErrorResult& aRv)
-{
-  CSSIntPoint pt;
-  GetPosition(pt, aRv);
-  JS::Rooted<JS::Value> v(cx);
-  if (!ToJSValue(cx, pt.x, &v) ||
-      !JS_SetProperty(cx, x, "value", v)) {
-    aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
-    return;
-  }
-  if (!ToJSValue(cx, pt.y, &v) ||
-      !JS_SetProperty(cx, y, "value", v)) {
-    aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
-    return;
-  }
-}
-
-void ScrollBoxObject::GetScrolledSize(JSContext* cx,
-                                      JS::Handle<JSObject*> width,
-                                      JS::Handle<JSObject*> height,
-                                      ErrorResult& aRv)
-{
-  nsRect rect;
-  GetScrolledSize(rect, aRv);
-  JS::Rooted<JS::Value> v(cx);
-  if (!ToJSValue(cx, rect.width, &v) ||
-      !JS_SetProperty(cx, width, "value", v)) {
-    aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
-    return;
-  }
-  if (!ToJSValue(cx, rect.height, &v) ||
-      !JS_SetProperty(cx, height, "value", v)) {
-    aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL);
-    return;
-  }
-}
-
 void ScrollBoxObject::EnsureElementIsVisible(Element& child, ErrorResult& aRv)
 {
     nsCOMPtr<nsIPresShell> shell = GetPresShell(false);
     if (!shell) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
     shell->ScrollContentIntoView(&child,
                                  nsIPresShell::ScrollAxis(),
                                  nsIPresShell::ScrollAxis(),
                                  nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY |
                                  nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 }
-
-void ScrollBoxObject::EnsureIndexIsVisible(int32_t index, ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-}
-
-void ScrollBoxObject::EnsureLineIsVisible(int32_t line, ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-}
-
 } // namespace dom
 } // namespace mozilla
 
-// Creation Routine ///////////////////////////////////////////////////////////////////////
-
 using namespace mozilla::dom;
 
 nsresult
 NS_NewScrollBoxObject(nsIBoxObject** aResult)
 {
-  NS_ADDREF(*aResult = new ScrollBoxObject());
-  return NS_OK;
+    NS_ADDREF(*aResult = new ScrollBoxObject());
+    return NS_OK;
 }
--- a/layout/xul/ScrollBoxObject.h
+++ b/layout/xul/ScrollBoxObject.h
@@ -22,39 +22,24 @@ public:
   ScrollBoxObject();
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual nsIScrollableFrame* GetScrollFrame();
 
   void ScrollTo(int32_t x, int32_t y, ErrorResult& aRv);
   void ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv);
-  void ScrollByLine(int32_t dlines, ErrorResult& aRv);
   void ScrollByIndex(int32_t dindexes, ErrorResult& aRv);
-  void ScrollToLine(int32_t line, ErrorResult& aRv);
   void ScrollToElement(Element& child, ErrorResult& aRv);
-  void ScrollToIndex(int32_t index, ErrorResult& aRv);
   int32_t GetPositionX(ErrorResult& aRv);
   int32_t GetPositionY(ErrorResult& aRv);
   int32_t GetScrolledWidth(ErrorResult& aRv);
   int32_t GetScrolledHeight(ErrorResult& aRv);
   void EnsureElementIsVisible(Element& child, ErrorResult& aRv);
-  void EnsureIndexIsVisible(int32_t index, ErrorResult& aRv);
-  void EnsureLineIsVisible(int32_t line, ErrorResult& aRv);
 
-  // Deprecated APIs from old IDL
-  void GetPosition(JSContext* cx,
-                   JS::Handle<JSObject*> x,
-                   JS::Handle<JSObject*> y,
-                   ErrorResult& aRv);
-
-  void GetScrolledSize(JSContext* cx,
-                       JS::Handle<JSObject*> width,
-                       JS::Handle<JSObject*> height,
-                       ErrorResult& aRv);
 
 protected:
   void GetScrolledSize(nsRect& aRect, ErrorResult& aRv);
   void GetPosition(CSSIntPoint& aPos, ErrorResult& aRv);
 
 private:
   ~ScrollBoxObject();
 };
--- a/media/webrtc/signaling/gtest/sdp_unittests.cpp
+++ b/media/webrtc/signaling/gtest/sdp_unittests.cpp
@@ -3732,17 +3732,16 @@ TEST_P(NewSdpTest, CheckMalformedImageat
     return;
   }
 
   ParseSdp(kMalformedImageattr, false);
   ASSERT_NE("", GetParseErrors());
 }
 
 TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchSendRid) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432931
   ParseSdp("v=0" CRLF
            "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF
            "s=SIP Call" CRLF
            "c=IN IP4 198.51.100.7" CRLF
            "b=CT:5000" CRLF
            "t=0 0" CRLF
            "m=video 56436 RTP/SAVPF 120" CRLF
            "a=rtpmap:120 VP8/90000" CRLF
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs
@@ -694,16 +694,22 @@ fn sanity_check_sdp_session(session: &Sd
                                                          }
                                                   }).collect();
         let recv_rids:Vec<&str> = rids.iter().filter_map(|rid| {
           match rid.direction {
               SdpSingleDirection::Recv => Some(rid.id.as_str()),
               _ => None,
           }
         }).collect();
+        let send_rids:Vec<&str> = rids.iter().filter_map(|rid| {
+          match rid.direction {
+              SdpSingleDirection::Send => Some(rid.id.as_str()),
+              _ => None,
+          }
+        }).collect();
 
 
         for rid_format in rids.iter().flat_map(|rid| &rid.formats) {
             match msection.get_formats() {
                 &SdpFormatList::Integers(ref int_fmt) => {
                     if !int_fmt.contains(&(*rid_format as u32))  {
                         return Err(make_error("Rid pts must be declared in the media section"));
                     }
@@ -713,29 +719,29 @@ fn sanity_check_sdp_session(session: &Sd
                         return Err(make_error("Rid pts must be declared in the media section"));
                     }
                 }
             }
         }
 
         if let Some(&SdpAttribute::Simulcast(ref simulcast)) =
                                             msection.get_attribute(SdpAttributeType::Simulcast) {
-            // This is already a closure as the next Bug 1432931 will require the same procedure
             let check_defined_rids = |simulcast_version_list: &Vec<SdpAttributeSimulcastVersion>,
                                       rid_ids: &[&str]| -> Result<(),SdpParserError> {
                 for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) {
                     if !rid_ids.contains(&simulcast_rid.id.as_str()) {
                         return Err(make_error(
                                        "Simulcast RIDs must be defined in any rid attribute"));
                     }
                 }
                 Ok(())
             };
 
-            check_defined_rids(&simulcast.receive, &recv_rids)?
+            check_defined_rids(&simulcast.receive, &recv_rids)?;
+            check_defined_rids(&simulcast.send, &send_rids)?;
         }
     }
 
     Ok(())
 }
 
 #[cfg(test)]
 fn create_dummy_sdp_session() -> SdpSession {
--- a/mobile/android/app/ua-update.json.in
+++ b/mobile/android/app/ua-update.json.in
@@ -10,13 +10,11 @@
   // bug 1177298, lohaco.jp
   "lohaco.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36",
   // bug 1177298, www.nhk.or.jp
   "nhk.or.jp": "\\)\\s# AppleWebKit ",
   // bug 1177298, uniqlo.com
   "uniqlo.com": "\\)\\s#) Mobile Safari ",
   // bug 1338260, directv.com
   "directv.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
-  // bug 1310951, m.canadiantire.ca
-  "m.canadiantire.ca": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
   // bug 1385206, rakuten.co.jp
   "rakuten.co.jp": "Firefox.+$#"
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4711,16 +4711,18 @@ pref("webgl.vendor-string-override", "")
 #ifdef XP_WIN
 pref("webgl.angle.try-d3d11", true);
 pref("webgl.angle.force-d3d11", false);
 pref("webgl.angle.force-warp", false);
 pref("webgl.dxgl.enabled", true);
 pref("webgl.dxgl.needs-finish", false);
 #endif
 
+pref("dom.webgpu.enable", false);
+
 pref("gfx.offscreencanvas.enabled", false);
 
 // sendbuffer of 0 means use OS default, sendbuffer unset means use
 // gecko default which varies depending on windows version and is OS
 // default on non windows
 // pref("network.tcp.sendbuffer", 0);
 
 // TCP Keepalive
--- a/python/mozbuild/mozbuild/action/test_archive.py
+++ b/python/mozbuild/mozbuild/action/test_archive.py
@@ -568,17 +568,17 @@ def find_generated_harness_files():
 
 
 def find_files(archive):
     extra_entries = []
     generated_harness_files = find_generated_harness_files()
 
     if archive == 'common':
         # Construct entries ensuring all our generated harness files are
-        # packaged in the common tests zip.
+        # packaged in the common tests archive.
         packaged_paths = set()
         for entry in OBJDIR_TEST_FILES.values():
             pat = mozpath.join(entry['base'], entry['pattern'])
             del entry['pattern']
             patterns = []
             for path in generated_harness_files:
                 if mozpath.match(path, pat):
                     patterns.append(path[len(entry['base']) + 1:])
--- a/servo/components/style/gecko/global_style_data.rs
+++ b/servo/components/style/gecko/global_style_data.rs
@@ -60,17 +60,22 @@ fn thread_shutdown(_: usize) {
 
 lazy_static! {
     /// Global thread pool
     pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
         let stylo_threads = env::var("STYLO_THREADS")
             .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
         let mut num_threads = match stylo_threads {
             Ok(num) => num,
-            _ => cmp::max(num_cpus::get() * 3 / 4, 1),
+            // The default heuristic is num_virtual_cores * .75. This gives us
+            // three threads on a hyper-threaded dual core, and six threads on
+            // a hyper-threaded quad core. The performance benefit of additional
+            // threads seems to level off at around six, so we cap it there on
+            // many-core machines (see bug 1431285 comment 14).
+            _ => cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6),
         };
 
         // If num_threads is one, there's no point in creating a thread pool, so
         // force it to zero.
         //
         // We allow developers to force a one-thread pool for testing via a
         // special environmental variable.
         if num_threads == 1 {
--- a/taskcluster/ci/source-test/python.yml
+++ b/taskcluster/ci/source-test/python.yml
@@ -62,25 +62,27 @@ mochitest-harness:
             linux64.*:
                 docker-image: {in-tree: "desktop1604-test"}
                 max-run-time: 3600
     run:
         using: run-task
         use-artifacts:
             build:
                 - target.tar.bz2
-                - target.common.tests.zip
-                - target.mochitest.tests.zip
+                - target.common.tests.tar.gz
+                - target.mochitest.tests.tar.gz
         command: >
             source /builds/worker/scripts/xvfb.sh &&
             start_xvfb '1600x1200x24' 0 &&
             cd $USE_ARTIFACT_PATH/build &&
             tar -xf target.tar.bz2 &&
-            unzip -q -d tests target.common.tests.zip &&
-            unzip -q -d tests target.mochitest.tests.zip &&
+            mkdir -p tests &&
+            (cd tests &&
+             tar xf ../target.common.tests.tar.gz &&
+             tar xf ../target.mochitest.tests.tar.gz) &&
             export GECKO_BINARY_PATH=$USE_ARTIFACT_PATH/build/firefox/firefox &&
             export TEST_HARNESS_ROOT=$USE_ARTIFACT_PATH/build/tests &&
             cd /builds/worker/checkouts/gecko &&
             ./mach python-test --python 2 --subsuite mochitest
     when:
         files-changed:
             - 'testing/mochitest/**'
             - 'testing/mozbase/moztest/moztest/selftest/**'
@@ -179,25 +181,27 @@ reftest-harness:
             linux64.*:
                 docker-image: {in-tree: "desktop1604-test"}
                 max-run-time: 3600
     run:
         using: run-task
         use-artifacts:
             build:
                 - target.tar.bz2
-                - target.common.tests.zip
-                - target.reftest.tests.zip
+                - target.common.tests.tar.gz
+                - target.reftest.tests.tar.gz
         command: >
             source /builds/worker/scripts/xvfb.sh &&
             start_xvfb '1600x1200x24' 0 &&
             cd $USE_ARTIFACT_PATH/build &&
             tar -xf target.tar.bz2 &&
-            unzip -q -d tests target.common.tests.zip &&
-            unzip -q -d tests target.reftest.tests.zip &&
+            mkdir -p tests &&
+            (cd tests &&
+             tar xf ../target.common.tests.tar.gz &&
+             tar xf ../target.reftest.tests.tar.gz) &&
             export GECKO_BINARY_PATH=$USE_ARTIFACT_PATH/build/firefox/firefox &&
             export TEST_HARNESS_ROOT=$USE_ARTIFACT_PATH/build/tests &&
             cd /builds/worker/checkouts/gecko &&
             ./mach python-test --python 2 --subsuite reftest
     when:
         files-changed:
             - 'layout/tools/reftest/**'
             - 'testing/mozbase/moztest/moztest/selftest/**'
--- a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
+++ b/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
@@ -493,25 +493,25 @@ MAJOR_VERSION="${VERSION%%.*}"
 echo "INFO: parsed version is ${VERSION}"
 if [ "${USE_MC}" == "true" ]; then
   MCVERSION=$(get_version "${MCREPO}")
   echo "INFO: parsed mozilla-central version is ${MCVERSION}"
   MAJOR_VERSION="${MCVERSION%%.*}"
 fi
 
 BROWSER_ARCHIVE="${PRODUCT}-${VERSION}.en-US.${PLATFORM}.${PLATFORM_EXT}"
-TESTS_ARCHIVE="${PRODUCT}-${VERSION}.en-US.${PLATFORM}.common.tests.zip"
+TESTS_ARCHIVE="${PRODUCT}-${VERSION}.en-US.${PLATFORM}.common.tests.tar.gz"
 if [ "${USE_MC}" == "true" ]; then
   BROWSER_ARCHIVE="${PRODUCT}-${MCVERSION}.en-US.${PLATFORM}.${PLATFORM_EXT}"
-  TESTS_ARCHIVE="${PRODUCT}-${MCVERSION}.en-US.${PLATFORM}.common.tests.zip"
+  TESTS_ARCHIVE="${PRODUCT}-${MCVERSION}.en-US.${PLATFORM}.common.tests.tar.gz"
 fi
 # Simple name builds on >=53.0.0
 if [ "${MAJOR_VERSION}" -ge 53 ] ; then
   BROWSER_ARCHIVE="target.${PLATFORM_EXT}"
-  TESTS_ARCHIVE="target.common.tests.zip"
+  TESTS_ARCHIVE="target.common.tests.tar.gz"
 fi
 # End 'remove once 52esr is off support'
 
 preflight_cleanup
 if [ "${DO_HSTS}" == "true" ] || [ "${DO_HPKP}" == "true" ] || [ "${DO_PRELOAD_PINSET}" == "true" ]
 then
   if [ "${USE_TC}" == "true" ]; then
     download_shared_artifacts_from_tc
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/actions/cancel.py
@@ -0,0 +1,28 @@
+# -*- coding: 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.util.taskcluster import cancel_task
+from .registry import register_callback_action
+
+
+@register_callback_action(
+    title='Cancel Task',
+    name='cancel',
+    symbol='cx',
+    kind='hook',
+    generic=True,
+    description=(
+        'Cancel the given task'
+    ),
+    order=100,
+    context=[{}]
+)
+def cancel_action(parameters, graph_config, input, task_group_id, task_id, task):
+    # Note that this is limited by the scopes afforded to generic actions to
+    # only cancel tasks with the level-specific schedulerId.
+    cancel_task(task_id, use_proxy=True)
--- a/taskcluster/taskgraph/transforms/beetmover.py
+++ b/taskcluster/taskgraph/transforms/beetmover.py
@@ -20,29 +20,29 @@ from voluptuous import Any, Required, Op
 
 
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
 # See example in bug 1348286
 _DESKTOP_UPSTREAM_ARTIFACTS_UNSIGNED_EN_US = [
     'buildhub.json',
-    "target.common.tests.zip",
-    "target.cppunittest.tests.zip",
+    "target.common.tests.tar.gz",
+    "target.cppunittest.tests.tar.gz",
     "target.crashreporter-symbols.zip",
     "target.json",
-    "target.mochitest.tests.zip",
+    "target.mochitest.tests.tar.gz",
     "target.mozinfo.json",
-    "target.reftest.tests.zip",
-    "target.talos.tests.zip",
-    "target.awsy.tests.zip",
+    "target.reftest.tests.tar.gz",
+    "target.talos.tests.tar.gz",
+    "target.awsy.tests.tar.gz",
     "target.test_packages.json",
     "target.txt",
     "target.web-platform.tests.tar.gz",
-    "target.xpcshell.tests.zip",
+    "target.xpcshell.tests.tar.gz",
     "target_info.txt",
     "target.jsshell.zip",
     "mozharness.zip",
     "target.langpack.xpi",
 ]
 
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
@@ -66,29 +66,29 @@ from voluptuous import Any, Required, Op
     "target.complete.mar",
 ]
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
 # See example in bug 1348286
 _MOBILE_UPSTREAM_ARTIFACTS_UNSIGNED_EN_US = [
     "en-US/buildhub.json",
-    "en-US/target.common.tests.zip",
-    "en-US/target.cppunittest.tests.zip",
+    "en-US/target.common.tests.tar.gz",
+    "en-US/target.cppunittest.tests.tar.gz",
     "en-US/target.crashreporter-symbols.zip",
     "en-US/target.json",
-    "en-US/target.mochitest.tests.zip",
+    "en-US/target.mochitest.tests.tar.gz",
     "en-US/target.mozinfo.json",
-    "en-US/target.reftest.tests.zip",
-    "en-US/target.talos.tests.zip",
-    "en-US/target.awsy.tests.zip",
+    "en-US/target.reftest.tests.tar.gz",
+    "en-US/target.talos.tests.tar.gz",
+    "en-US/target.awsy.tests.tar.gz",
     "en-US/target.test_packages.json",
     "en-US/target.txt",
     "en-US/target.web-platform.tests.tar.gz",
-    "en-US/target.xpcshell.tests.zip",
+    "en-US/target.xpcshell.tests.tar.gz",
     "en-US/target_info.txt",
     "en-US/mozharness.zip",
     "en-US/robocop.apk",
     "en-US/target.jsshell.zip",
 ]
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
--- a/taskcluster/taskgraph/transforms/beetmover_repackage.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage.py
@@ -36,29 +36,29 @@ logger = logging.getLogger(__name__)
 ]
 
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
 # See example in bug 1348286
 _DESKTOP_UPSTREAM_ARTIFACTS_UNSIGNED_EN_US = [
     "buildhub.json",
-    "target.common.tests.zip",
-    "target.cppunittest.tests.zip",
+    "target.common.tests.tar.gz",
+    "target.cppunittest.tests.tar.gz",
     "target.crashreporter-symbols.zip",
     "target.json",
-    "target.mochitest.tests.zip",
+    "target.mochitest.tests.tar.gz",
     "target.mozinfo.json",
-    "target.reftest.tests.zip",
-    "target.talos.tests.zip",
-    "target.awsy.tests.zip",
+    "target.reftest.tests.tar.gz",
+    "target.talos.tests.tar.gz",
+    "target.awsy.tests.tar.gz",
     "target.test_packages.json",
     "target.txt",
     "target.web-platform.tests.tar.gz",
-    "target.xpcshell.tests.zip",
+    "target.xpcshell.tests.tar.gz",
     "target_info.txt",
     "target.jsshell.zip",
     "mozharness.zip",
 ]
 
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
--- a/testing/marionette/doc/Testing.md
+++ b/testing/marionette/doc/Testing.md
@@ -181,17 +181,17 @@ Out-of-tree testing
 -------------------
 
 All the above examples show tests running _in-tree_, with a local
 checkout of _central_ and a local build of Firefox.  It is also
 possibly to run the Marionette tests _without_ a local build and
 with a downloaded test archive from <Taskcluster.html>.
 
 If you want to run tests from a downloaded test archive, you will
-need to download the `target.common.tests.zip` artifact attached to
+need to download the `target.common.tests.tar.gz` artifact attached to
 Treeherder [build jobs] `B` for your system.  Extract the archive
 and set up the Python Marionette client and harness by executing
 the following command in a virtual environment:
 
 	% pip install -r config/marionette_requirements.txt
 
 The tests can then be found under
 _marionette/tests/testing/marionette/harness/marionette_harness/tests_
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -748,17 +748,17 @@ class MochitestArguments(ArgumentContain
             if build_obj:
                 possible.insert(0, os.path.join(build_obj.topobjdir, '_tests', 'modules'))
 
             for p in possible:
                 if os.path.isdir(p):
                     options.testingModulesDir = p
                     break
 
-        # Paths to specialpowers and mochijar from the tests zip.
+        # Paths to specialpowers and mochijar from the tests archive.
         options.stagedAddons = [
             os.path.join(here, 'extensions', 'specialpowers'),
             os.path.join(here, 'mochijar'),
         ]
         if build_obj:
             objdir_xpi_stage = os.path.join(build_obj.distdir, 'xpi-stage')
             if os.path.isdir(objdir_xpi_stage):
                 options.stagedAddons = [
--- a/testing/mozbase/moztest/moztest/selftest/fixtures.py
+++ b/testing/mozbase/moztest/moztest/selftest/fixtures.py
@@ -1,12 +1,12 @@
 # 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/.
-"""Pytest fixtures to help set up Firefox and a tests.zip
+"""Pytest fixtures to help set up Firefox and a tests archive
 in test harness selftests.
 """
 
 from __future__ import absolute_import
 
 import os
 import shutil
 import sys
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -84,17 +84,17 @@ TBPL_UPLOAD_ERRORS = [
 class MakeUploadOutputParser(OutputParser):
     tbpl_error_list = TBPL_UPLOAD_ERRORS
     # let's create a switch case using name-spaces/dict
     # rather than a long if/else with duplicate code
     property_conditions = [
         # key: property name, value: condition
         ('symbolsUrl', "m.endswith('crashreporter-symbols.zip') or "
                        "m.endswith('crashreporter-symbols-full.zip')"),
-        ('testsUrl', "m.endswith(('tests.tar.bz2', 'tests.zip'))"),
+        ('testsUrl', "m.endswith(('tests.tar.bz2', 'tests.zip', 'tests.tar.gz'))"),
         ('robocopApkUrl', "m.endswith('apk') and 'robocop' in m"),
         ('jsshellUrl', "'jsshell-' in m and m.endswith('.zip')"),
         ('partialMarUrl', "m.endswith('.mar') and '.partial.' in m"),
         ('completeMarUrl', "m.endswith('.mar')"),
         ('codeCoverageUrl', "m.endswith('code-coverage-gcno.zip')"),
     ]
 
     def __init__(self, use_package_as_marfile=False, package_filename=None, **kwargs):
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -354,17 +354,17 @@ You can set this by specifying --test-ur
 
             if "common.tests" in file_name and isinstance(unpack_dirs, list):
                 # Ensure that the following files are always getting extracted
                 required_files = ["mach",
                                   "mozinfo.json",
                                   ]
                 for req_file in required_files:
                     if req_file not in unpack_dirs:
-                        self.info("Adding '{}' for extraction from common.tests zip file"
+                        self.info("Adding '{}' for extraction from common.tests archive"
                                   .format(req_file))
                         unpack_dirs.append(req_file)
 
             if "jsshell-" in file_name or file_name == "target.jsshell.zip":
                 self.info("Special-casing the jsshell zip file")
                 unpack_dirs = None
                 target_dir = dirs['abs_test_bin_dir']
 
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -718,17 +718,17 @@ class AndroidEmulatorTest(TestingMixin, 
         self.info(logcat_cmd)
         os.system(logcat_cmd)
         # Get a post-boot emulator process list for diagnostics
         ps_cmd = ['shell', 'ps']
         self._run_adb_with_timeout(30, ps_cmd)
 
     def download_and_extract(self):
         """
-        Download and extract fennec APK, tests.zip, host utils, and robocop (if required).
+        Download and extract fennec APK, tests, host utils, and robocop (if required).
         """
         super(AndroidEmulatorTest, self).download_and_extract(
             suite_categories=self._query_suite_categories())
         dirs = self.query_abs_dirs()
         if self.test_suite and self.test_suite.startswith('robocop'):
             robocop_url = self.installer_url[:self.installer_url.rfind('/')] + '/robocop.apk'
             self.info("Downloading robocop...")
             self.download_file(robocop_url, 'robocop.apk', dirs['abs_work_dir'], error_level=FATAL)
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -549,17 +549,17 @@ class DesktopUnittest(TestingMixin, Merc
             self.record_status(TBPL_EXCEPTION)
             self.fatal("There are specified suites that are incompatible with "
                        "--artifact try syntax flag: {}".format(', '.join(rejected)),
                        exit_code=self.return_code)
 
     def download_and_extract(self):
         """
         download and extract test zip / download installer
-        optimizes which subfolders to extract from tests zip
+        optimizes which subfolders to extract from tests archive
         """
         c = self.config
 
         extract_dirs = None
 
         if c.get('run_all_suites'):
             target_categories = SUITE_CATEGORIES
         else:
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -108,27 +108,27 @@ ifdef MOZ_WEBRTC
 stage-all: stage-steeplechase
 endif
 
 ifdef COMPILE_ENVIRONMENT
 stage-all: stage-cppunittests
 endif
 
 TEST_PKGS_ZIP := \
+  $(NULL)
+
+TEST_PKGS_TARGZ := \
   common \
   cppunittest \
   mochitest \
   reftest \
   talos \
   raptor \
   awsy \
   xpcshell \
-  $(NULL)
-
-TEST_PKGS_TARGZ := \
   web-platform \
   $(NULL)
 
 ifdef LINK_GTEST_DURING_COMPILE
 stage-all: stage-gtest
 TEST_PKGS_ZIP += gtest
 endif
 
--- a/testing/web-platform/tests/README.md
+++ b/testing/web-platform/tests/README.md
@@ -128,21 +128,21 @@ sudo apt install libnss3-tools
 ```
 
 And on macOS with homebrew using:
 
 ```
 brew install nss
 ```
 
-On other platforms, download the firefox archive and common.tests.zip
+On other platforms, download the firefox archive and common.tests.tar.gz
 archive for your platform from
 [Mozilla CI](https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/).
 
-Then extract `certutil[.exe]` from the tests.zip package and
+Then extract `certutil[.exe]` from the tests.tar.gz package and
 `libnss3[.so|.dll|.dynlib]` and put the former on your path and the latter on
 your library path.
 
 
 Command Line Tools
 ==================
 
 The `wpt` command provides a frontend to a variety of tools for
--- a/testing/web-platform/tests/interfaces/dom.idl
+++ b/testing/web-platform/tests/interfaces/dom.idl
@@ -355,16 +355,17 @@ interface Element : Node {
   [SameObject] readonly attribute NamedNodeMap attributes;
   sequence<DOMString> getAttributeNames();
   DOMString? getAttribute(DOMString qualifiedName);
   DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
   void setAttribute(DOMString qualifiedName, DOMString value);
   void setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value);
   void removeAttribute(DOMString qualifiedName);
   void removeAttributeNS(DOMString? namespace, DOMString localName);
+  boolean toggleAttribute(DOMString qualifiedName, optional boolean force);
   boolean hasAttribute(DOMString qualifiedName);
   boolean hasAttributeNS(DOMString? namespace, DOMString localName);
 
   Attr? getAttributeNode(DOMString qualifiedName);
   Attr? getAttributeNodeNS(DOMString? namespace, DOMString localName);
   Attr? setAttributeNode(Attr attr);
   Attr? setAttributeNodeNS(Attr attr);
   Attr removeAttributeNode(Attr attr);
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -528,31 +528,31 @@ def verifyRemoteOptions(parser, options)
                 options['localLib'] = os.path.join(options['objdir'], path)
                 if os.path.isdir(options['localLib']):
                     break
             else:
                 parser.error("Couldn't find local library dir, specify --local-lib-dir")
         elif options['objdir']:
             options['localLib'] = os.path.join(options['objdir'], 'dist/bin')
         elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')):
-            # assume tests are being run from a tests.zip
+            # assume tests are being run from a tests archive
             options['localLib'] = os.path.abspath(os.path.join(here, '..', 'bin'))
         else:
             parser.error("Couldn't find local library dir, specify --local-lib-dir")
 
     if options['localBin'] is None:
         if options['objdir']:
             for path in ['dist/bin', 'bin']:
                 options['localBin'] = os.path.join(options['objdir'], path)
                 if os.path.isdir(options['localBin']):
                     break
             else:
                 parser.error("Couldn't find local binary dir, specify --local-bin-dir")
         elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')):
-            # assume tests are being run from a tests.zip
+            # assume tests are being run from a tests archive
             options['localBin'] = os.path.abspath(os.path.join(here, '..', 'bin'))
         else:
             parser.error("Couldn't find local binary dir, specify --local-bin-dir")
     return options
 
 
 class PathMapping:
 
--- a/toolkit/components/extensions/test/mochitest/test_ext_cookies_first_party.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_cookies_first_party.html
@@ -5,16 +5,18 @@
 <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 async function background() {
   const url = "http://ext-cookie-first-party.mochi.test/";
   const firstPartyDomain = "ext-cookie-first-party.mochi.test";
+  // A first party domain with invalid characters for the file system, which just happens to be a IPv6 address.
+  const firstPartyDomainInvalidChars = "[2606:4700:4700::1111]";
   const expectedError = "First-Party Isolation is enabled, but the required 'firstPartyDomain' attribute was not set.";
 
   const assertExpectedCookies = (expected, cookies, message) => {
     let matches = (cookie, expected) => {
       if (!cookie || !expected) {
         return cookie === expected; // true if both are null.
       }
       for (let key of Object.keys(expected)) {
@@ -211,16 +213,48 @@ async function background() {
     ], [cookie], "remove: FPI on, w/ firstPartyDomain, FP cookie (set when FPI off)");
 
     // Test if FP cookies set when FPI on can be accessed when FPI off.
     await browser.cookies.set({url, name: "foo4", value: "bar4", firstPartyDomain});
 
     browser.test.sendMessage("test_fpi_enabled");
   };
 
+  // Test FPI with a first party domain with invalid characters for
+  // the file system.
+  const test_fpi_with_invalid_characters = async () => {
+    let cookie;
+
+    // Test setting a cookie with a first party domain with invalid characters
+    // for the file system.
+    cookie = await browser.cookies.set({url, name: "foo5", value: "bar5",
+                                        firstPartyDomain: firstPartyDomainInvalidChars});
+    assertExpectedCookies([
+      {name: "foo5", value: "bar5", firstPartyDomain: firstPartyDomainInvalidChars},
+    ], [cookie], "set: FPI on, w/ firstPartyDomain with invalid characters, FP cookie");
+
+    // Test getting a cookie with a first party domain with invalid characters
+    // for the file system.
+    cookie = await browser.cookies.get({url, name: "foo5",
+                                        firstPartyDomain: firstPartyDomainInvalidChars});
+    assertExpectedCookies([
+      {name: "foo5", value: "bar5", firstPartyDomain: firstPartyDomainInvalidChars},
+    ], [cookie], "get: FPI on, w/ firstPartyDomain with invalid characters, FP cookie");
+
+    // Test removing a cookie with a first party domain with invalid characters
+    // for the file system.
+    cookie = await browser.cookies.remove({url, name: "foo5",
+                                           firstPartyDomain: firstPartyDomainInvalidChars});
+    assertExpectedCookies([
+      {url, name: "foo5", firstPartyDomain: firstPartyDomainInvalidChars},
+    ], [cookie], "remove: FPI on, w/ firstPartyDomain with invalid characters, FP cookie");
+
+    browser.test.sendMessage("test_fpi_with_invalid_characters");
+  };
+
   // Test when FPI is disabled again, accessing FP cookies set when FPI is enabled.
   const test_fpd_cookies_on_fpi_disabled = async () => {
     let cookie, cookies;
     cookie = await browser.cookies.get({url, name: "foo4", firstPartyDomain});
     assertExpectedCookies([
       {name: "foo4", value: "bar4", firstPartyDomain},
     ], [cookie], "get: FPI off, w/ firstPartyDomain, FP cookie (set when FPI on)");
     cookie = await browser.cookies.remove({url, name: "foo4", firstPartyDomain});
@@ -236,16 +270,17 @@ async function background() {
 
     browser.test.sendMessage("test_fpd_cookies_on_fpi_disabled");
   };
 
   browser.test.onMessage.addListener((message) => {
     switch (message) {
       case "test_fpi_disabled": return test_fpi_disabled();
       case "test_fpi_enabled": return test_fpi_enabled();
+      case "test_fpi_with_invalid_characters": return test_fpi_with_invalid_characters();
       case "test_fpd_cookies_on_fpi_disabled": return test_fpd_cookies_on_fpi_disabled();
       default: return browser.test.notifyFail("unknown-message");
     }
   });
 }
 
 function enableFirstPartyIsolation() {
   return SpecialPowers.pushPrefEnv({
@@ -267,14 +302,16 @@ add_task(async () => {
     },
   });
   await extension.startup();
   extension.sendMessage("test_fpi_disabled");
   await extension.awaitMessage("test_fpi_disabled");
   await enableFirstPartyIsolation();
   extension.sendMessage("test_fpi_enabled");
   await extension.awaitMessage("test_fpi_enabled");
+  extension.sendMessage("test_fpi_with_invalid_characters");
+  await extension.awaitMessage("test_fpi_with_invalid_characters");
   await disableFirstPartyIsolation();
   extension.sendMessage("test_fpd_cookies_on_fpi_disabled");
   await extension.awaitMessage("test_fpd_cookies_on_fpi_disabled");
   await extension.unload();
 });
 </script>
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -94,16 +94,19 @@ set_define('MOZ_PULSEAUDIO', depends_if(
 include('../js/moz.configure')
 
 
 # Rust
 # ==============================================================
 include('../build/moz.configure/rust.configure',
         when='--enable-compile-environment')
 
+# NodeJS
+# ==============================================================
+include('../build/moz.configure/node.configure')
 
 # L10N
 # ==============================================================
 option('--with-l10n-base', nargs=1, env='L10NBASEDIR',
        help='Path to l10n repositories')
 
 @depends('--with-l10n-base')
 @imports(_from='os.path', _import='isdir')
--- a/toolkit/mozapps/installer/package-name.mk
+++ b/toolkit/mozapps/installer/package-name.mk
@@ -89,25 +89,25 @@ CODE_COVERAGE_ARCHIVE_BASENAME = $(PKG_B
 # Mozsearch package naming
 MOZSEARCH_ARCHIVE_BASENAME = $(PKG_BASENAME).mozsearch-index
 MOZSEARCH_RUST_ANALYSIS_BASENAME = $(PKG_BASENAME).mozsearch-rust
 
 # Mozharness naming
 MOZHARNESS_PACKAGE = mozharness.zip
 
 # Test package naming
-TEST_PACKAGE = $(PKG_BASENAME).common.tests.zip
-CPP_TEST_PACKAGE = $(PKG_BASENAME).cppunittest.tests.zip
-XPC_TEST_PACKAGE = $(PKG_BASENAME).xpcshell.tests.zip
-MOCHITEST_PACKAGE = $(PKG_BASENAME).mochitest.tests.zip
-REFTEST_PACKAGE = $(PKG_BASENAME).reftest.tests.zip
+TEST_PACKAGE = $(PKG_BASENAME).common.tests.tar.gz
+CPP_TEST_PACKAGE = $(PKG_BASENAME).cppunittest.tests.tar.gz
+XPC_TEST_PACKAGE = $(PKG_BASENAME).xpcshell.tests.tar.gz
+MOCHITEST_PACKAGE = $(PKG_BASENAME).mochitest.tests.tar.gz
+REFTEST_PACKAGE = $(PKG_BASENAME).reftest.tests.tar.gz
 WP_TEST_PACKAGE = $(PKG_BASENAME).web-platform.tests.tar.gz
-TALOS_PACKAGE = $(PKG_BASENAME).talos.tests.zip
-AWSY_PACKAGE = $(PKG_BASENAME).awsy.tests.zip
-GTEST_PACKAGE = $(PKG_BASENAME).gtest.tests.zip
+TALOS_PACKAGE = $(PKG_BASENAME).talos.tests.tar.gz
+AWSY_PACKAGE = $(PKG_BASENAME).awsy.tests.tar.gz
+GTEST_PACKAGE = $(PKG_BASENAME).gtest.tests.tar.gz
 
 ifneq (,$(wildcard $(DIST)/bin/application.ini))
 BUILDID = $(shell $(PYTHON) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID)
 else
 BUILDID = $(shell $(PYTHON) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID)
 endif
 
 MOZ_SOURCESTAMP_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).txt