Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Tue, 29 May 2018 00:53:52 +0300
changeset 474375 35aa0dde259f5f51c0aaf86935a54b8087c2e8c6
parent 474338 85c2c2f860d91f23bc0c36c4bf7d773f04733e47 (current diff)
parent 474374 2ee8bb4a4b7c9c7d4b6b2d7589b302807c39bedc (diff)
child 474379 f53afd7c76808e464c0ddcba48f26368a644e4ee
child 474404 c58d8cbdc302060f3ab7d0395344a54553ce28d0
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
35aa0dde259f / 62.0a1 / 20180528220216 / files
nightly linux64
35aa0dde259f / 62.0a1 / 20180528220216 / files
nightly mac
35aa0dde259f / 62.0a1 / 20180528220216 / files
nightly win32
35aa0dde259f / 62.0a1 / 20180528220216 / files
nightly win64
35aa0dde259f / 62.0a1 / 20180528220216 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/workers/test/extensions/bootstrap/moz.build
dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi
js/src/jit-test/tests/wasm/spec/proposal_mutable_global/directives.txt
js/src/jit-test/tests/wasm/spec/proposal_mutable_global/globals.wast.js
js/src/jit-test/tests/wasm/spec/proposal_mutable_global/linking.wast.js
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -85,17 +85,17 @@ add_task(async function startup() {
       min: 100,
       max: 150,
     },
     "layout.css.dpi": {
       min: 45,
       max: 75,
     },
     "extensions.getAddons.cache.enabled": {
-      min: 10,
+      min: 9,
       max: 55,
     },
   };
 
   let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
   await startupRecorder.done;
 
   ok(startupRecorder.data.prefStats, "startupRecorder has prefStats");
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -93,18 +93,18 @@ class RemoteAutomation(Automation):
             if maxTime:
                 print("TEST-UNEXPECTED-FAIL | %s | application ran for longer than "
                       "allowed maximum time of %s seconds" % (
                           self.lastTestSeen, maxTime))
             else:
                 print("TEST-UNEXPECTED-FAIL | %s | application ran for longer than "
                       "allowed maximum time" % (self.lastTestSeen))
         if status == 2:
-            print("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with"
-                  "no output"
+            print("TEST-UNEXPECTED-FAIL | %s | application timed out after %d "
+                  "seconds with no output"
                   % (self.lastTestSeen, int(timeout)))
 
         return status
 
     def deleteANRs(self):
         # empty ANR traces.txt file; usually need root permissions
         # we make it empty and writable so we can test the ANR reporter later
         traces = "/data/anr/traces.txt"
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -118,16 +118,17 @@ skip-if = os == 'win' # bug 1413442
 [browser_inspector_highlighter-keybinding_04.js]
 [browser_inspector_highlighter-measure_01.js]
 [browser_inspector_highlighter-measure_02.js]
 [browser_inspector_highlighter-options.js]
 [browser_inspector_highlighter-preview.js]
 [browser_inspector_highlighter-rulers_01.js]
 [browser_inspector_highlighter-rulers_02.js]
 [browser_inspector_highlighter-rulers_03.js]
+skip-if = (os == 'win' && !debug) # Bug 1449754
 [browser_inspector_highlighter-selector_01.js]
 [browser_inspector_highlighter-selector_02.js]
 [browser_inspector_highlighter-xbl.js]
 [browser_inspector_highlighter-zoom.js]
 [browser_inspector_iframe-navigation.js]
 [browser_inspector_infobar_01.js]
 [browser_inspector_infobar_02.js]
 [browser_inspector_infobar_03.js]
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4394,30 +4394,35 @@ AssertNoBitsPropagatedFrom(nsINode* aRoo
   auto* element = aRoot->GetFlattenedTreeParentElementForStyle();
   while (element) {
     MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits));
     element = element->GetFlattenedTreeParentElementForStyle();
   }
 #endif
 }
 
-// Sets |aBits| on aElement and all of its flattened-tree ancestors up to and
-// including aStopAt or the root element (whichever is encountered first).
+// Sets `aBits` on `aElement` and all of its flattened-tree ancestors up to and
+// including aStopAt or the root element (whichever is encountered first), and
+// as long as `aBitsToStopAt` isn't found anywhere in the chain.
 static inline Element*
-PropagateBits(Element* aElement, uint32_t aBits, nsINode* aStopAt)
+PropagateBits(Element* aElement, uint32_t aBits, nsINode* aStopAt, uint32_t aBitsToStopAt)
 {
   Element* curr = aElement;
-  while (curr && !curr->HasAllFlags(aBits)) {
+  while (curr && !curr->HasAllFlags(aBitsToStopAt)) {
     curr->SetFlags(aBits);
     if (curr == aStopAt) {
       break;
     }
     curr = curr->GetFlattenedTreeParentElementForStyle();
   }
 
+  if (aBitsToStopAt != aBits && curr) {
+    curr->SetFlags(aBits);
+  }
+
   return curr;
 }
 
 // Notes that a given element is "dirty" with respect to the given descendants
 // bit (which may be one of dirty descendants, dirty animation descendants, or
 // need frame construction for descendants).
 //
 // This function operates on the dirty element itself, despite the fact that the
@@ -4518,40 +4523,43 @@ NoteDirtyElement(Element* aElement, uint
     doc->SetServoRestyleRoot(aElement, aBits);
     return;
   }
 
   // There is an existing restyle root - walk up the tree from our element,
   // propagating bits as we go.
   const bool reachedDocRoot =
     !parent->IsElement() ||
-    !PropagateBits(parent->AsElement(), aBits, existingRoot);
+    !PropagateBits(parent->AsElement(), aBits, existingRoot, aBits);
 
   uint32_t existingBits = doc->GetServoRestyleRootDirtyBits();
   if (!reachedDocRoot || existingRoot == doc) {
       // We're a descendant of the existing root. All that's left to do is to
       // make sure the bit we propagated is also registered on the root.
       doc->SetServoRestyleRoot(existingRoot, existingBits | aBits);
   } else {
     // We reached the root without crossing the pre-existing restyle root. We
     // now need to find the nearest common ancestor, so climb up from the
     // existing root, extending bits along the way.
     Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle();
-    if (Element* commonAncestor = PropagateBits(rootParent, existingBits, aElement)) {
+    // We can stop at the first occurrence of `aBits` in order to find the
+    // common ancestor.
+    if (Element* commonAncestor = PropagateBits(rootParent, existingBits, aElement, aBits)) {
       MOZ_ASSERT(commonAncestor == aElement ||
                  commonAncestor == nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(aElement, rootParent));
 
       // We found a common ancestor. Make that the new style root, and clear the
       // bits between the new style root and the document root.
       doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits);
       Element* curr = commonAncestor;
       while ((curr = curr->GetFlattenedTreeParentElementForStyle())) {
         MOZ_ASSERT(curr->HasAllFlags(aBits));
         curr->UnsetFlags(aBits);
       }
+      AssertNoBitsPropagatedFrom(commonAncestor);
     } else {
       // We didn't find a common ancestor element. That means we're descended
       // from two different document style roots, so the common ancestor is the
       // document.
       doc->SetServoRestyleRoot(doc, existingBits | aBits);
     }
   }
 
@@ -4580,17 +4588,18 @@ Element::NoteDirtySubtreeForServo()
 
   if (existingRoot &&
       existingRoot->IsElement() &&
       existingRoot != this &&
       nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
         existingRoot->AsElement(), this)) {
     PropagateBits(existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(),
                   existingBits,
-                  this);
+                  this,
+                  existingBits);
 
     doc->ClearServoRestyleRoot();
   }
 
   NoteDirtyElement(this, existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
 }
 
 void
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -76,20 +76,16 @@ LOCAL_INCLUDES += [
     '/xpcom/build',
     '/xpcom/threads',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
-TEST_DIRS += [
-    'test/extensions/bootstrap',
-]
-
 MOCHITEST_MANIFESTS += [
     'test/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'test/chrome.ini',
 ]
 
deleted file mode 100644
--- a/dom/workers/test/extensions/bootstrap/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TEST_HARNESS_FILES.testing.mochitest.extensions += [
-    'workerbootstrap-test@mozilla.org.xpi',
-]
deleted file mode 100644
index 5c9bc9e3abe8f85d1813c2b0d838ee343dbf9909..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/gfx/qcms/matrix.c
+++ b/gfx/qcms/matrix.c
@@ -59,20 +59,21 @@ struct matrix matrix_invert(struct matri
 	static int a[3] = { 2, 2, 1 };
 	static int b[3] = { 1, 0, 0 };
 
 	/* inv  (A) = 1/det (A) * adj (A) */
 	float det = matrix_det(mat);
 
 	if (det == 0) {
 		dest_mat.invalid = true;
-	} else {
-		dest_mat.invalid = false;
+		return dest_mat;
 	}
 
+	dest_mat.invalid = false;
+
 	det = 1/det;
 
 	for (j = 0; j < 3; j++) {
 		for (i = 0; i < 3; i++) {
 			double p;
 			int ai = a[i];
 			int aj = a[j];
 			int bi = b[i];
--- a/gfx/qcms/transform.c
+++ b/gfx/qcms/transform.c
@@ -163,16 +163,19 @@ static struct matrix build_RGB_to_XYZ_tr
 	primaries.m[2][2] = 1 - xb - yb;
 	primaries.invalid = false;
 
 	white_point.v[0] = xn/yn;
 	white_point.v[1] = 1.;
 	white_point.v[2] = (1.0-xn-yn)/yn;
 
 	primaries_invert = matrix_invert(primaries);
+	if (primaries_invert.invalid) {
+		return matrix_invalid();
+	}
 
 	coefs = matrix_eval(primaries_invert, white_point);
 
 	result.m[0][0] = coefs.v[0]*xr;
 	result.m[0][1] = coefs.v[1]*xg;
 	result.m[0][2] = coefs.v[2]*xb;
 
 	result.m[1][0] = coefs.v[0]*yr;
@@ -220,16 +223,19 @@ compute_chromatic_adaption(struct CIE_XY
 {
 	struct matrix chad_inv;
 	struct vector cone_source_XYZ, cone_source_rgb;
 	struct vector cone_dest_XYZ, cone_dest_rgb;
 	struct matrix cone, tmp;
 
 	tmp = chad;
 	chad_inv = matrix_invert(tmp);
+	if (chad_inv.invalid) {
+		return matrix_invalid();
+	}
 
 	cone_source_XYZ.v[0] = source_white_point.X;
 	cone_source_XYZ.v[1] = source_white_point.Y;
 	cone_source_XYZ.v[2] = source_white_point.Z;
 
 	cone_dest_XYZ.v[0] = dest_white_point.X;
 	cone_dest_XYZ.v[1] = dest_white_point.Y;
 	cone_dest_XYZ.v[2] = dest_white_point.Z;
@@ -267,22 +273,26 @@ adaption_matrix(struct CIE_XYZ source_il
 }
 
 /* from lcms: cmsAdaptMatrixToD50 */
 static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt)
 {
 	struct CIE_XYZ Dn;
 	struct matrix Bradford;
 
-	if (source_white_pt.y == 0.0)
+	if (source_white_pt.y == 0.0) {
 		return matrix_invalid();
+	}
 
 	Dn = xyY2XYZ(source_white_pt);
 
 	Bradford = adaption_matrix(Dn, D50_XYZ);
+	if (Bradford.invalid) {
+		return matrix_invalid();
+	}
 	return matrix_multiply(Bradford, r);
 }
 
 qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries)
 {
 	struct matrix colorants;
 	colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries);
 	colorants = adapt_matrix_to_D50(colorants, white_point);
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -1754,17 +1754,18 @@ GetSystemFontList(nsTArray<nsString>& aL
     nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
     if (!os) {
         return;
     }
 
     // add the lang to the pattern
     nsAutoCString fcLang;
     gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
-    pfl->GetSampleLangForGroup(aLangGroup, fcLang);
+    pfl->GetSampleLangForGroup(aLangGroup, fcLang,
+                               /*aForFontEnumerationThread*/ true);
     if (!fcLang.IsEmpty()) {
         FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
     }
 
     nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os));
     if (!fs) {
         return;
     }
@@ -2391,17 +2392,18 @@ const MozLangGroupData MozLangGroups[] =
     { nsGkAtoms::x_telu,         "te" },
     { nsGkAtoms::x_tibt,         "bo" },
     { nsGkAtoms::Unicode,        0    }
 };
 
 bool
 gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
                                        nsAtom* aLangGroup,
-                                       nsACString& aFcLang)
+                                       nsACString& aFcLang,
+                                       bool aForFontEnumerationThread)
 {
     // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
     // aOSLang is in the form "language[_territory][.codeset][@modifier]".
     // fontconfig takes languages in the form "language-territory".
     // nsLanguageAtomService takes languages in the form language-subtag,
     // where subtag may be a territory.  fontconfig and nsLanguageAtomService
     // handle case-conversion for us.
     const char *pos, *end;
@@ -2418,24 +2420,36 @@ gfxFcPlatformFontList::TryLangForGroup(c
                 aFcLang.Append('-');
                 break;
             default:
                 aFcLang.Append(*pos);
         }
         ++pos;
     }
 
-    nsAtom *atom = mLangService->LookupLanguage(aFcLang);
-    return atom == aLangGroup;
+    if (!aForFontEnumerationThread) {
+        nsAtom *atom = mLangService->LookupLanguage(aFcLang);
+        return atom == aLangGroup;
+    }
+
+    // If we were called by the font enumeration thread, we can't use
+    // mLangService->LookupLanguage because it is not thread-safe.
+    // Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group
+    // mapping cache hashtable.
+    nsAutoCString lowered(aFcLang);
+    ToLowerCase(lowered);
+    RefPtr<nsAtom> lang = NS_Atomize(lowered);
+    RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang);
+    return group.get() == aLangGroup;
 }
 
 void
 gfxFcPlatformFontList::GetSampleLangForGroup(nsAtom* aLanguage,
                                              nsACString& aLangStr,
-                                             bool aCheckEnvironment)
+                                             bool aForFontEnumerationThread)
 {
     aLangStr.Truncate();
     if (!aLanguage) {
         return;
     }
 
     // set up lang string
     const MozLangGroupData *mozLangGroup = nullptr;
@@ -2454,42 +2468,42 @@ gfxFcPlatformFontList::GetSampleLangForG
         // Not a special mozilla language group.
         // Use aLanguage as a language code.
         aLanguage->ToUTF8String(aLangStr);
         return;
     }
 
     // -- check the environment for the user's preferred language that
     //    corresponds to this mozilla lang group.
-    if (aCheckEnvironment) {
-        const char *languages = getenv("LANGUAGE");
-        if (languages) {
-            const char separator = ':';
+    const char *languages = getenv("LANGUAGE");
+    if (languages) {
+        const char separator = ':';
 
-            for (const char *pos = languages; true; ++pos) {
-                if (*pos == '\0' || *pos == separator) {
-                    if (languages < pos &&
-                        TryLangForGroup(Substring(languages, pos),
-                                        aLanguage, aLangStr)) {
-                        return;
-                    }
+        for (const char *pos = languages; true; ++pos) {
+            if (*pos == '\0' || *pos == separator) {
+                if (languages < pos &&
+                    TryLangForGroup(Substring(languages, pos),
+                                    aLanguage, aLangStr,
+                                    aForFontEnumerationThread)) {
+                    return;
+                }
 
-                    if (*pos == '\0') {
-                        break;
-                    }
+                if (*pos == '\0') {
+                    break;
+                }
 
-                    languages = pos + 1;
-                }
+                languages = pos + 1;
             }
         }
-        const char *ctype = setlocale(LC_CTYPE, nullptr);
-        if (ctype &&
-            TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr)) {
-            return;
-        }
+    }
+    const char *ctype = setlocale(LC_CTYPE, nullptr);
+    if (ctype &&
+        TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr,
+                        aForFontEnumerationThread)) {
+        return;
     }
 
     if (mozLangGroup->defaultLang) {
         aLangStr.Assign(mozLangGroup->defaultLang);
     } else {
         aLangStr.Truncate();
     }
 }
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -323,18 +323,21 @@ public:
     void ClearLangGroupPrefFonts() override;
 
     // clear out cached generic-lang ==> family-list mappings
     void ClearGenericMappings() {
         mGenericMappings.Clear();
     }
 
     // map lang group ==> lang string
+    // When aForFontEnumerationThread is true, this method will avoid using
+    // LanguageService::LookupLanguage, because it is not safe for off-main-
+    // thread use (except by stylo traversal, which does the necessary locking)
     void GetSampleLangForGroup(nsAtom* aLanguage, nsACString& aLangStr,
-                               bool aCheckEnvironment = true);
+                               bool aForFontEnumerationThread = false);
 
     static FT_Library GetFTLibrary();
 
 protected:
     virtual ~gfxFcPlatformFontList();
 
 #ifdef MOZ_CONTENT_SANDBOX
     typedef mozilla::SandboxBroker::Policy SandboxPolicy;
@@ -366,17 +369,17 @@ protected:
 
     virtual gfxFontFamily*
     GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     // helper method for finding an appropriate lang string
     bool TryLangForGroup(const nsACString& aOSLang, nsAtom* aLangGroup,
-                         nsACString& aLang);
+                         nsACString& aLang, bool aForFontEnumerationThread);
 
 #ifdef MOZ_BUNDLED_FONTS
     void ActivateBundledFonts();
     nsCString mBundledFontsPath;
     bool mBundledFontsInitialized;
 #endif
 
     // to avoid enumerating all fonts, maintain a mapping of local font
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -101,39 +101,28 @@ module = wasmEvalText(`(module
     (export "f" $f)
 )`, {
     globals: {
         a: 1
     }
 }).exports;
 assertEq(module.f, module.tbl.get(1));
 
-// Import/export rules.
-if (typeof WebAssembly.Global === "undefined") {
-    wasmFailValidateText(`(module (import "globals" "x" (global (mut i32))))`,
-             /can't import.* mutable globals in the MVP/);
-    wasmFailValidateText(`(module (global (mut i32) (i32.const 42)) (export "" global 0))`,
-             /can't .*export mutable globals in the MVP/);
-}
-
 // Import/export semantics.
 module = wasmEvalText(`(module
  (import $g "globals" "x" (global i32))
  (func $get (result i32) (get_global $g))
  (export "getter" $get)
  (export "value" global 0)
 )`, { globals: {x: 42} }).exports;
 
 assertEq(module.getter(), 42);
-// Adapt to ongoing experiment with WebAssembly.Global.
+
 // assertEq() will not trigger @@toPrimitive, so we must have a cast here.
-if (typeof WebAssembly.Global === "function")
-    assertEq(Number(module.value), 42);
-else
-    assertEq(module.value, 42);
+assertEq(Number(module.value), 42);
 
 // Can only import numbers (no implicit coercions).
 module = new Module(wasmTextToBinary(`(module
     (global (import "globs" "i32") i32)
     (global (import "globs" "f32") f32)
     (global (import "globs" "f64") f32)
 )`));
 
@@ -178,24 +167,18 @@ for (let v of [
 // Imported globals and locally defined globals use the same index space.
 module = wasmEvalText(`(module
  (import "globals" "x" (global i32))
  (global i32 (i32.const 1337))
  (export "imported" global 0)
  (export "defined" global 1)
 )`, { globals: {x: 42} }).exports;
 
-// See comment earlier about WebAssembly.Global
-if (typeof WebAssembly.Global === "function") {
-    assertEq(Number(module.imported), 42);
-    assertEq(Number(module.defined), 1337);
-} else {
-    assertEq(module.imported, 42);
-    assertEq(module.defined, 1337);
-}
+assertEq(Number(module.imported), 42);
+assertEq(Number(module.defined), 1337);
 
 // Initializer expressions can reference an imported immutable global.
 wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
 wasmFailValidateText(`(module (global (mut f32) (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
 wasmFailValidateText(`(module (global (mut i32) (i32.const 0)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
 
 wasmFailValidateText(`(module (import "globals" "a" (global f32)) (global i32 (get_global 0)))`, /type mismatch/);
 
@@ -222,30 +205,22 @@ function testInitExpr(type, initialValue
     )`, {
         globals: {
             a: coercion(initialValue)
         }
     }).exports;
 
     assertFunc(module.get0(), coercion(initialValue));
     assertFunc(module.get1(), coercion(initialValue));
-    // See comment earlier about WebAssembly.Global
-    if (typeof WebAssembly.Global === "function")
-        assertFunc(Number(module.global_imm), coercion(initialValue));
-    else
-        assertFunc(module.global_imm, coercion(initialValue));
+    assertFunc(Number(module.global_imm), coercion(initialValue));
 
     assertEq(module.set1(coercion(nextValue)), undefined);
     assertFunc(module.get1(), coercion(nextValue));
     assertFunc(module.get0(), coercion(initialValue));
-    // See comment earlier about WebAssembly.Global
-    if (typeof WebAssembly.Global === "function")
-        assertFunc(Number(module.global_imm), coercion(initialValue));
-    else
-        assertFunc(module.global_imm, coercion(initialValue));
+    assertFunc(Number(module.global_imm), coercion(initialValue));
 
     assertFunc(module.get_cst(), coercion(initialValue));
 }
 
 testInitExpr('i32', 13, 37, x => x|0);
 testInitExpr('f32', 13.37, 0.1989, Math.fround);
 testInitExpr('f64', 13.37, 0.1989, x => +x);
 
@@ -262,28 +237,17 @@ assertErrorMessage(() => wasmEvalText(`(
 
 // The imported value is a Number, so the int64 guard should stop us
 assertErrorMessage(() => wasmEvalText(`(module
                                         (import "globals" "x" (global i64)))`,
                                       {globals: {x:42}}),
                    LinkError,
                    /cannot pass i64 to or from JS/);
 
-if (typeof WebAssembly.Global === "undefined") {
-
-    // Cannot export int64 at all.
-
-    assertErrorMessage(() => wasmEvalText(`(module
-                                            (global i64 (i64.const 42))
-                                            (export "" global 0))`),
-                       LinkError,
-                       /cannot pass i64 to or from JS/);
-
-} else {
-
+{
     // We can import and export i64 globals as cells.  They cannot be created
     // from JS because there's no way to specify a non-zero initial value; that
     // restriction is tested later.  But we can export one from a module and
     // import it into another.
 
     let i = wasmEvalText(`(module
                            (global (export "g") i64 (i64.const 37))
                            (global (export "h") (mut i64) (i64.const 37)))`);
@@ -338,20 +302,18 @@ wasmAssert(`(module
     dv.setFloat64(0, module.nan64, true);
     assertEq(dv.getUint32(4, true), 0x7ff80000);
     assertEq(dv.getUint32(0, true), 0x00000000);
 
     dv.setFloat32(0, module.nan32, true);
     assertEq(dv.getUint32(0, true), 0x7fc00000);
 }
 
-// WebAssembly.Global experiment
-
-if (typeof WebAssembly.Global === "function") {
-
+// WebAssembly.Global
+{
     const Global = WebAssembly.Global;
 
     // These types should work:
     assertEq(new Global({value: "i32"}) instanceof Global, true);
     assertEq(new Global({value: "f32"}) instanceof Global, true);
     assertEq(new Global({value: "f64"}) instanceof Global, true);
 
     // These types should not work:
--- a/js/src/jit-test/tests/wasm/spec/globals.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/globals.wast.js
@@ -48,54 +48,61 @@ run(() => call(instance("\x00\x61\x73\x6
 run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7d\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x35\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9a\x80\x80\x80\x00\x01\x94\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbc\x43\x00\x00\x00\x41\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-5", []), 8.)
 
 // globals.wast:47
 run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7c\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x36\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9e\x80\x80\x80\x00\x01\x98\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbd\x44\x00\x00\x00\x00\x00\x00\x22\x40\xbd\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-6", []), 9.)
 
 // globals.wast:49
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x00\x00\x0b\x0a\x8c\x80\x80\x80\x00\x01\x86\x80\x80\x80\x00\x00\x41\x01\x24\x00\x0b");
 
-if (typeof WebAssembly.Global === "undefined") {
-    // globals.wast:54
-    assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x88\x80\x80\x80\x00\x01\x01\x6d\x01\x61\x03\x7f\x01");
-
-    // globals.wast:59
-    assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x88\x80\x80\x80\x00\x01\x01\x6d\x01\x61\x03\x7f\x01");
+// globals.wast:55
+let $2 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
 
-    // globals.wast:64
-    assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
+// globals.wast:56
+let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
 
-    // globals.wast:69
-    assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
-}
-
-// globals.wast:74
+// globals.wast:58
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8a\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x00\x00\x8c\x0b");
 
-// globals.wast:79
+// globals.wast:63
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7d\x00\x20\x00\x0b");
 
-// globals.wast:84
+// globals.wast:68
+assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8a\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x80\x3f\x8c\x0b");
+
+// globals.wast:73
+assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x87\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x01\x0b");
+
+// globals.wast:78
+assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x85\x80\x80\x80\x00\x01\x7f\x00\x01\x0b");
+
+// globals.wast:83
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7f\x00\x43\x00\x00\x00\x00\x0b");
 
-// globals.wast:89
+// globals.wast:88
+assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x88\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x41\x00\x0b");
+
+// globals.wast:93
+assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x84\x80\x80\x80\x00\x01\x7f\x00\x0b");
+
+// globals.wast:98
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x23\x00\x0b");
 
-// globals.wast:94
+// globals.wast:103
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8b\x80\x80\x80\x00\x02\x7f\x00\x23\x01\x0b\x7f\x00\x41\x00\x0b");
 
-// globals.wast:99
-let $2 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x06\x67\x6c\x6f\x62\x61\x6c\x03\x7f\x00");
+// globals.wast:108
+let $4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x98\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\x00");
 
-// globals.wast:102
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x06\x67\x6c\x6f\x62\x61\x6c\x03\x7f\x02");
+// globals.wast:111
+assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\x02");
 
-// globals.wast:115
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x06\x67\x6c\x6f\x62\x61\x6c\x03\x7f\xff");
+// globals.wast:124
+assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\xff");
 
-// globals.wast:129
-let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x0b");
+// globals.wast:138
+let $5 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x0b");
 
-// globals.wast:132
+// globals.wast:141
 assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x02\x41\x00\x0b");
 
-// globals.wast:144
+// globals.wast:153
 assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\xff\x41\x00\x0b");
--- a/js/src/jit-test/tests/wasm/spec/harness/index.js
+++ b/js/src/jit-test/tests/wasm/spec/harness/index.js
@@ -219,26 +219,21 @@ function call(instance, name, args) {
 };
 
 function get(instance, name) {
     _assert(instance instanceof Result);
 
     if (instance.isError())
         return instance;
 
-    // Experimental API change.  We try to export WebAssembly.Global instances,
-    // not primitive values.  In that case the Number() cast is necessary here
-    // to convert the Global to a value: the harness examines types carefully
-    // and will not trigger the @@toPrimitive hook on Global, unlike most user
-    // code.
-
-    if (typeof WebAssembly.Global === "function")
-        return ValueResult(Number(instance.value.exports[name]));
-
-    return ValueResult(instance.value.exports[name]);
+    // We export WebAssembly.Global instances, not primitive values.  The
+    // Number() cast is necessary to convert the Global to a value: the
+    // harness examines types carefully and will not trigger the
+    // @@toPrimitive hook on Global, unlike most user code.
+    return ValueResult(Number(instance.value.exports[name]));
 }
 
 function exports(name, instance) {
     _assert(instance instanceof Result);
 
     if (instance.isError())
         return instance;
 
--- a/js/src/jit-test/tests/wasm/spec/linking.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/linking.wast.js
@@ -18,288 +18,335 @@ assert_return(() => call($Nf, "Mf.call",
 
 // linking.wast:19
 assert_return(() => call($Nf, "call", []), 3);
 
 // linking.wast:20
 assert_return(() => call($Nf, "call Mf.call", []), 2);
 
 // linking.wast:22
-let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7f\x00\x02\x92\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x05\x70\x72\x69\x6e\x74\x00\x00\x07\x89\x80\x80\x80\x00\x01\x05\x70\x72\x69\x6e\x74\x00\x00");
+let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7f\x00\x02\x96\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x70\x72\x69\x6e\x74\x5f\x69\x33\x32\x00\x00\x07\x89\x80\x80\x80\x00\x01\x05\x70\x72\x69\x6e\x74\x00\x00");
 
 // linking.wast:26
 register("reexport_f", $3)
 
 // linking.wast:27
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7e\x00\x02\x94\x80\x80\x80\x00\x01\x0a\x72\x65\x65\x78\x70\x6f\x72\x74\x5f\x66\x05\x70\x72\x69\x6e\x74\x00\x00");
 
 // linking.wast:31
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x94\x80\x80\x80\x00\x01\x0a\x72\x65\x65\x78\x70\x6f\x72\x74\x5f\x66\x05\x70\x72\x69\x6e\x74\x00\x00");
 
 // linking.wast:39
-let $4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x2a\x0b\x07\x8e\x80\x80\x80\x00\x02\x04\x67\x6c\x6f\x62\x03\x00\x03\x67\x65\x74\x00\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x23\x00\x0b");
+let $4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x00\x03\x84\x80\x80\x80\x00\x03\x00\x00\x01\x06\x8c\x80\x80\x80\x00\x02\x7f\x00\x41\x2a\x0b\x7f\x01\x41\x8e\x01\x0b\x07\xad\x80\x80\x80\x00\x05\x04\x67\x6c\x6f\x62\x03\x00\x03\x67\x65\x74\x00\x00\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x01\x07\x67\x65\x74\x5f\x6d\x75\x74\x00\x01\x07\x73\x65\x74\x5f\x6d\x75\x74\x00\x02\x0a\x9e\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x23\x00\x0b\x84\x80\x80\x80\x00\x00\x23\x01\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x01\x0b");
 let $Mg = $4;
 
-// linking.wast:43
+// linking.wast:48
 register("Mg", $Mg)
 
-// linking.wast:45
-let $5 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x95\x80\x80\x80\x00\x02\x02\x4d\x67\x04\x67\x6c\x6f\x62\x03\x7f\x00\x02\x4d\x67\x03\x67\x65\x74\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x2b\x0b\x07\xa1\x80\x80\x80\x00\x04\x07\x4d\x67\x2e\x67\x6c\x6f\x62\x03\x00\x06\x4d\x67\x2e\x67\x65\x74\x00\x00\x04\x67\x6c\x6f\x62\x03\x01\x03\x67\x65\x74\x00\x01\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x23\x01\x0b");
+// linking.wast:50
+let $5 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x00\x02\xbe\x80\x80\x80\x00\x05\x02\x4d\x67\x04\x67\x6c\x6f\x62\x03\x7f\x00\x02\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x02\x4d\x67\x03\x67\x65\x74\x00\x00\x02\x4d\x67\x07\x67\x65\x74\x5f\x6d\x75\x74\x00\x00\x02\x4d\x67\x07\x73\x65\x74\x5f\x6d\x75\x74\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x2b\x0b\x07\xc9\x80\x80\x80\x00\x07\x07\x4d\x67\x2e\x67\x6c\x6f\x62\x03\x00\x06\x4d\x67\x2e\x67\x65\x74\x00\x00\x04\x67\x6c\x6f\x62\x03\x02\x03\x67\x65\x74\x00\x03\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x01\x0a\x4d\x67\x2e\x67\x65\x74\x5f\x6d\x75\x74\x00\x01\x0a\x4d\x67\x2e\x73\x65\x74\x5f\x6d\x75\x74\x00\x02\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x23\x02\x0b");
 let $Ng = $5;
 
-// linking.wast:54
+// linking.wast:67
 assert_return(() => get($Mg, "glob"), 42);
 
-// linking.wast:55
+// linking.wast:68
 assert_return(() => get($Ng, "Mg.glob"), 42);
 
-// linking.wast:56
+// linking.wast:69
 assert_return(() => get($Ng, "glob"), 43);
 
-// linking.wast:57
+// linking.wast:70
 assert_return(() => call($Mg, "get", []), 42);
 
-// linking.wast:58
+// linking.wast:71
 assert_return(() => call($Ng, "Mg.get", []), 42);
 
-// linking.wast:59
+// linking.wast:72
 assert_return(() => call($Ng, "get", []), 43);
 
-// linking.wast:64
+// linking.wast:74
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x91\x80\x80\x80\x00\x01\x03\x24\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\x8e\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Mg", $Mg)),  "run", []));  // assert_return(() => get($Mg, "mut_glob"), 142)
+
+// linking.wast:75
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x94\x80\x80\x80\x00\x01\x03\x24\x4e\x67\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\x8e\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Ng", $Ng)),  "run", []));  // assert_return(() => get($Ng, "Mg.mut_glob"), 142)
+
+// linking.wast:76
+assert_return(() => call($Mg, "get_mut", []), 142);
+
+// linking.wast:77
+assert_return(() => call($Ng, "Mg.get_mut", []), 142);
+
+// linking.wast:79
+assert_return(() => call($Mg, "set_mut", [241]));
+
+// linking.wast:80
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x91\x80\x80\x80\x00\x01\x03\x24\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\xf1\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Mg", $Mg)),  "run", []));  // assert_return(() => get($Mg, "mut_glob"), 241)
+
+// linking.wast:81
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x94\x80\x80\x80\x00\x01\x03\x24\x4e\x67\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\xf1\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Ng", $Ng)),  "run", []));  // assert_return(() => get($Ng, "Mg.mut_glob"), 241)
+
+// linking.wast:82
+assert_return(() => call($Mg, "get_mut", []), 241);
+
+// linking.wast:83
+assert_return(() => call($Ng, "Mg.get_mut", []), 241);
+
+// linking.wast:86
+assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x90\x80\x80\x80\x00\x01\x02\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x00");
+
+// linking.wast:90
+assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x67\x04\x67\x6c\x6f\x62\x03\x7f\x01");
+
+// linking.wast:97
 let $6 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8d\x80\x80\x80\x00\x03\x60\x00\x01\x7f\x60\x00\x00\x60\x01\x7f\x01\x7f\x03\x84\x80\x80\x80\x00\x03\x00\x00\x02\x04\x84\x80\x80\x80\x00\x01\x70\x00\x0a\x07\x92\x80\x80\x80\x00\x03\x03\x74\x61\x62\x01\x00\x01\x68\x00\x01\x04\x63\x61\x6c\x6c\x00\x02\x09\x8a\x80\x80\x80\x00\x01\x00\x41\x02\x0b\x04\x00\x00\x00\x00\x0a\x9f\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x41\x04\x0b\x84\x80\x80\x80\x00\x00\x41\x7c\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x00\x00\x0b");
 let $Mt = $6;
 
-// linking.wast:77
+// linking.wast:110
 register("Mt", $Mt)
 
-// linking.wast:79
+// linking.wast:112
 let $7 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8d\x80\x80\x80\x00\x03\x60\x00\x00\x60\x00\x01\x7f\x60\x01\x7f\x01\x7f\x02\x92\x80\x80\x80\x00\x02\x02\x4d\x74\x04\x63\x61\x6c\x6c\x00\x02\x02\x4d\x74\x01\x68\x00\x01\x03\x84\x80\x80\x80\x00\x03\x01\x02\x02\x04\x85\x80\x80\x80\x00\x01\x70\x01\x05\x05\x07\xa1\x80\x80\x80\x00\x03\x07\x4d\x74\x2e\x63\x61\x6c\x6c\x00\x00\x0c\x63\x61\x6c\x6c\x20\x4d\x74\x2e\x63\x61\x6c\x6c\x00\x03\x04\x63\x61\x6c\x6c\x00\x04\x09\x8b\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x05\x02\x02\x02\x01\x00\x0a\xa1\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x41\x05\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x10\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x01\x00\x0b");
 let $Nt = $7;
 
-// linking.wast:98
+// linking.wast:131
 assert_return(() => call($Mt, "call", [2]), 4);
 
-// linking.wast:99
+// linking.wast:132
 assert_return(() => call($Nt, "Mt.call", [2]), 4);
 
-// linking.wast:100
+// linking.wast:133
 assert_return(() => call($Nt, "call", [2]), 5);
 
-// linking.wast:101
+// linking.wast:134
 assert_return(() => call($Nt, "call Mt.call", [2]), 4);
 
-// linking.wast:103
+// linking.wast:136
 assert_trap(() => call($Mt, "call", [1]));
 
-// linking.wast:104
+// linking.wast:137
 assert_trap(() => call($Nt, "Mt.call", [1]));
 
-// linking.wast:105
+// linking.wast:138
 assert_return(() => call($Nt, "call", [1]), 5);
 
-// linking.wast:106
+// linking.wast:139
 assert_trap(() => call($Nt, "call Mt.call", [1]));
 
-// linking.wast:108
+// linking.wast:141
 assert_trap(() => call($Mt, "call", [0]));
 
-// linking.wast:109
+// linking.wast:142
 assert_trap(() => call($Nt, "Mt.call", [0]));
 
-// linking.wast:110
+// linking.wast:143
 assert_return(() => call($Nt, "call", [0]), 5);
 
-// linking.wast:111
+// linking.wast:144
 assert_trap(() => call($Nt, "call Mt.call", [0]));
 
-// linking.wast:113
+// linking.wast:146
 assert_trap(() => call($Mt, "call", [20]));
 
-// linking.wast:114
+// linking.wast:147
 assert_trap(() => call($Nt, "Mt.call", [20]));
 
-// linking.wast:115
+// linking.wast:148
 assert_trap(() => call($Nt, "call", [7]));
 
-// linking.wast:116
+// linking.wast:149
 assert_trap(() => call($Nt, "call Mt.call", [20]));
 
-// linking.wast:118
+// linking.wast:151
 assert_return(() => call($Nt, "call", [3]), -4);
 
-// linking.wast:119
+// linking.wast:152
 assert_trap(() => call($Nt, "call", [4]));
 
-// linking.wast:121
+// linking.wast:154
 let $8 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8a\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x01\x7f\x02\x93\x80\x80\x80\x00\x02\x02\x4d\x74\x01\x68\x00\x00\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x05\x03\x83\x80\x80\x80\x00\x02\x00\x01\x07\x88\x80\x80\x80\x00\x01\x04\x63\x61\x6c\x6c\x00\x02\x09\x88\x80\x80\x80\x00\x01\x00\x41\x01\x0b\x02\x01\x00\x0a\x96\x80\x80\x80\x00\x02\x84\x80\x80\x80\x00\x00\x41\x06\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x00\x00\x0b");
 let $Ot = $8;
 
-// linking.wast:134
+// linking.wast:167
 assert_return(() => call($Mt, "call", [3]), 4);
 
-// linking.wast:135
+// linking.wast:168
 assert_return(() => call($Nt, "Mt.call", [3]), 4);
 
-// linking.wast:136
+// linking.wast:169
 assert_return(() => call($Nt, "call Mt.call", [3]), 4);
 
-// linking.wast:137
+// linking.wast:170
 assert_return(() => call($Ot, "call", [3]), 4);
 
-// linking.wast:139
+// linking.wast:172
 assert_return(() => call($Mt, "call", [2]), -4);
 
-// linking.wast:140
+// linking.wast:173
 assert_return(() => call($Nt, "Mt.call", [2]), -4);
 
-// linking.wast:141
+// linking.wast:174
 assert_return(() => call($Nt, "call", [2]), 5);
 
-// linking.wast:142
+// linking.wast:175
 assert_return(() => call($Nt, "call Mt.call", [2]), -4);
 
-// linking.wast:143
+// linking.wast:176
 assert_return(() => call($Ot, "call", [2]), -4);
 
-// linking.wast:145
+// linking.wast:178
 assert_return(() => call($Mt, "call", [1]), 6);
 
-// linking.wast:146
+// linking.wast:179
 assert_return(() => call($Nt, "Mt.call", [1]), 6);
 
-// linking.wast:147
+// linking.wast:180
 assert_return(() => call($Nt, "call", [1]), 5);
 
-// linking.wast:148
+// linking.wast:181
 assert_return(() => call($Nt, "call Mt.call", [1]), 6);
 
-// linking.wast:149
+// linking.wast:182
 assert_return(() => call($Ot, "call", [1]), 6);
 
-// linking.wast:151
+// linking.wast:184
 assert_trap(() => call($Mt, "call", [0]));
 
-// linking.wast:152
+// linking.wast:185
 assert_trap(() => call($Nt, "Mt.call", [0]));
 
-// linking.wast:153
+// linking.wast:186
 assert_return(() => call($Nt, "call", [0]), 5);
 
-// linking.wast:154
+// linking.wast:187
 assert_trap(() => call($Nt, "call Mt.call", [0]));
 
-// linking.wast:155
+// linking.wast:188
 assert_trap(() => call($Ot, "call", [0]));
 
-// linking.wast:157
+// linking.wast:190
 assert_trap(() => call($Ot, "call", [20]));
 
-// linking.wast:159
+// linking.wast:192
 let $9 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x09\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
 
-// linking.wast:165
+// linking.wast:198
+let $10 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x05\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x67\x03\x00");
+let $G1 = $10;
+
+// linking.wast:199
+register("G1", $G1)
+
+// linking.wast:200
+let $11 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x89\x80\x80\x80\x00\x01\x02\x47\x31\x01\x67\x03\x7f\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x23\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x67\x03\x01");
+let $G2 = $11;
+
+// linking.wast:204
+assert_return(() => get($G2, "g"), 5);
+
+// linking.wast:206
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
 
-// linking.wast:174
+// linking.wast:215
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x96\x80\x80\x80\x00\x02\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x02\x4d\x74\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x09\x8d\x80\x80\x80\x00\x02\x00\x41\x07\x0b\x01\x00\x00\x41\x09\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b");
 
-// linking.wast:184
+// linking.wast:225
 assert_trap(() => call($Mt, "call", [7]));
 
-// linking.wast:186
+// linking.wast:227
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x03\x82\x80\x80\x80\x00\x01\x00\x09\x8d\x80\x80\x80\x00\x02\x00\x41\x07\x0b\x01\x00\x00\x41\x0c\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b");
 
-// linking.wast:195
-assert_trap(() => call($Mt, "call", [7]));
-
-// linking.wast:197
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x09\x87\x80\x80\x80\x00\x01\x00\x41\x07\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x80\x80\x04\x0b\x01\x64");
-
-// linking.wast:207
+// linking.wast:236
 assert_trap(() => call($Mt, "call", [7]));
 
-// linking.wast:212
-let $10 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x05\x84\x80\x80\x80\x00\x01\x01\x01\x05\x07\x8e\x80\x80\x80\x00\x02\x03\x6d\x65\x6d\x02\x00\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x90\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x0a\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09");
-let $Mm = $10;
+// linking.wast:238
+assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x09\x87\x80\x80\x80\x00\x01\x00\x41\x07\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x80\x80\x04\x0b\x01\x64");
+
+// linking.wast:248
+assert_trap(() => call($Mt, "call", [7]));
 
-// linking.wast:220
+// linking.wast:253
+let $12 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x05\x84\x80\x80\x80\x00\x01\x01\x01\x05\x07\x8e\x80\x80\x80\x00\x02\x03\x6d\x65\x6d\x02\x00\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x90\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x0a\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09");
+let $Mm = $12;
+
+// linking.wast:261
 register("Mm", $Mm)
 
-// linking.wast:222
-let $11 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x04\x6c\x6f\x61\x64\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x07\x92\x80\x80\x80\x00\x02\x07\x4d\x6d\x2e\x6c\x6f\x61\x64\x00\x00\x04\x6c\x6f\x61\x64\x00\x01\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8c\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x06\xf0\xf1\xf2\xf3\xf4\xf5");
-let $Nm = $11;
+// linking.wast:263
+let $13 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x04\x6c\x6f\x61\x64\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x07\x92\x80\x80\x80\x00\x02\x07\x4d\x6d\x2e\x6c\x6f\x61\x64\x00\x00\x04\x6c\x6f\x61\x64\x00\x01\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8c\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x06\xf0\xf1\xf2\xf3\xf4\xf5");
+let $Nm = $13;
 
-// linking.wast:234
+// linking.wast:275
 assert_return(() => call($Mm, "load", [12]), 2);
 
-// linking.wast:235
+// linking.wast:276
 assert_return(() => call($Nm, "Mm.load", [12]), 2);
 
-// linking.wast:236
+// linking.wast:277
 assert_return(() => call($Nm, "load", [12]), 242);
 
-// linking.wast:238
-let $12 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8e\x80\x80\x80\x00\x01\x00\x41\x05\x0b\x08\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7");
-let $Om = $12;
+// linking.wast:279
+let $14 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8e\x80\x80\x80\x00\x01\x00\x41\x05\x0b\x08\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7");
+let $Om = $14;
 
-// linking.wast:247
+// linking.wast:288
 assert_return(() => call($Mm, "load", [12]), 167);
 
-// linking.wast:248
+// linking.wast:289
 assert_return(() => call($Nm, "Mm.load", [12]), 167);
 
-// linking.wast:249
+// linking.wast:290
 assert_return(() => call($Nm, "load", [12]), 242);
 
-// linking.wast:250
+// linking.wast:291
 assert_return(() => call($Om, "load", [12]), 167);
 
-// linking.wast:252
-let $13 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\xff\xff\x03\x0b\x01\x61");
+// linking.wast:293
+let $15 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\xff\xff\x03\x0b\x01\x61");
 
-// linking.wast:257
+// linking.wast:298
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x80\x80\x04\x0b\x01\x61");
 
-// linking.wast:265
-let $14 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x01\x01\x08\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x67\x72\x6f\x77\x00\x00\x0a\x8c\x80\x80\x80\x00\x01\x86\x80\x80\x80\x00\x00\x20\x00\x40\x00\x0b");
-let $Pm = $14;
+// linking.wast:306
+let $16 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x01\x01\x08\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x67\x72\x6f\x77\x00\x00\x0a\x8c\x80\x80\x80\x00\x01\x86\x80\x80\x80\x00\x00\x20\x00\x40\x00\x0b");
+let $Pm = $16;
 
-// linking.wast:273
+// linking.wast:314
 assert_return(() => call($Pm, "grow", [0]), 1);
 
-// linking.wast:274
+// linking.wast:315
 assert_return(() => call($Pm, "grow", [2]), 1);
 
-// linking.wast:275
+// linking.wast:316
 assert_return(() => call($Pm, "grow", [0]), 3);
 
-// linking.wast:276
+// linking.wast:317
 assert_return(() => call($Pm, "grow", [1]), 3);
 
-// linking.wast:277
+// linking.wast:318
 assert_return(() => call($Pm, "grow", [1]), 4);
 
-// linking.wast:278
+// linking.wast:319
 assert_return(() => call($Pm, "grow", [0]), 5);
 
-// linking.wast:279
+// linking.wast:320
 assert_return(() => call($Pm, "grow", [1]), -1);
 
-// linking.wast:280
+// linking.wast:321
 assert_return(() => call($Pm, "grow", [0]), 5);
 
-// linking.wast:282
+// linking.wast:323
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\xa7\x80\x80\x80\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x05\x70\x72\x69\x6e\x74\x00\x00\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x02\x4d\x6d\x03\x74\x61\x62\x01\x70\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x03\x61\x62\x63");
 
-// linking.wast:291
+// linking.wast:332
 assert_return(() => call($Mm, "load", [0]), 0);
 
-// linking.wast:293
+// linking.wast:334
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x0b\x91\x80\x80\x80\x00\x02\x00\x41\x00\x0b\x03\x61\x62\x63\x00\x41\x80\x80\x14\x0b\x01\x64");
 
-// linking.wast:301
+// linking.wast:342
 assert_return(() => call($Mm, "load", [0]), 0);
 
-// linking.wast:303
+// linking.wast:344
 assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x04\x84\x80\x80\x80\x00\x01\x70\x00\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x03\x61\x62\x63");
 
-// linking.wast:313
+// linking.wast:354
 assert_return(() => call($Mm, "load", [0]), 0);
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/spec/proposal_mutable_global/directives.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm-testharness.js
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/spec/proposal_mutable_global/globals.wast.js
+++ /dev/null
@@ -1,108 +0,0 @@
-
-// globals.wast:3
-let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa1\x80\x80\x80\x00\x08\x60\x00\x01\x7f\x60\x00\x01\x7e\x60\x01\x7f\x00\x60\x01\x7e\x00\x60\x00\x01\x7d\x60\x00\x01\x7c\x60\x01\x7d\x00\x60\x01\x7c\x00\x03\x8d\x80\x80\x80\x00\x0c\x00\x01\x00\x01\x02\x03\x04\x05\x04\x05\x06\x07\x06\xbd\x80\x80\x80\x00\x08\x7f\x00\x41\x7e\x0b\x7d\x00\x43\x00\x00\x40\xc0\x0b\x7c\x00\x44\x00\x00\x00\x00\x00\x00\x10\xc0\x0b\x7e\x00\x42\x7b\x0b\x7f\x01\x41\x74\x0b\x7d\x01\x43\x00\x00\x50\xc1\x0b\x7c\x01\x44\x00\x00\x00\x00\x00\x00\x2c\xc0\x0b\x7e\x01\x42\x71\x0b\x07\xe1\x80\x80\x80\x00\x0c\x05\x67\x65\x74\x2d\x61\x00\x00\x05\x67\x65\x74\x2d\x62\x00\x01\x05\x67\x65\x74\x2d\x78\x00\x02\x05\x67\x65\x74\x2d\x79\x00\x03\x05\x73\x65\x74\x2d\x78\x00\x04\x05\x73\x65\x74\x2d\x79\x00\x05\x05\x67\x65\x74\x2d\x31\x00\x06\x05\x67\x65\x74\x2d\x32\x00\x07\x05\x67\x65\x74\x2d\x35\x00\x08\x05\x67\x65\x74\x2d\x36\x00\x09\x05\x73\x65\x74\x2d\x35\x00\x0a\x05\x73\x65\x74\x2d\x36\x00\x0b\x0a\xf5\x80\x80\x80\x00\x0c\x84\x80\x80\x80\x00\x00\x23\x00\x0b\x84\x80\x80\x80\x00\x00\x23\x03\x0b\x84\x80\x80\x80\x00\x00\x23\x04\x0b\x84\x80\x80\x80\x00\x00\x23\x07\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x04\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x07\x0b\x84\x80\x80\x80\x00\x00\x23\x01\x0b\x84\x80\x80\x80\x00\x00\x23\x02\x0b\x84\x80\x80\x80\x00\x00\x23\x05\x0b\x84\x80\x80\x80\x00\x00\x23\x06\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x05\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x06\x0b");
-
-// globals.wast:29
-assert_return(() => call($1, "get-a", []), -2);
-
-// globals.wast:30
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7e\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x62\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x97\x80\x80\x80\x00\x01\x91\x80\x80\x80\x00\x00\x02\x40\x10\x00\x01\x42\x7b\x01\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-b", []), int64("-5"))
-
-// globals.wast:31
-assert_return(() => call($1, "get-x", []), -12);
-
-// globals.wast:32
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7e\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x79\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x97\x80\x80\x80\x00\x01\x91\x80\x80\x80\x00\x00\x02\x40\x10\x00\x01\x42\x71\x01\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-y", []), int64("-15"))
-
-// globals.wast:34
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7d\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x31\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9a\x80\x80\x80\x00\x01\x94\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbc\x43\x00\x00\x40\xc0\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-1", []), -3.)
-
-// globals.wast:35
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7c\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x32\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9e\x80\x80\x80\x00\x01\x98\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbd\x44\x00\x00\x00\x00\x00\x00\x10\xc0\xbd\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-2", []), -4.)
-
-// globals.wast:36
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7d\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x35\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9a\x80\x80\x80\x00\x01\x94\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbc\x43\x00\x00\x50\xc1\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-5", []), -13.)
-
-// globals.wast:37
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7c\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x36\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9e\x80\x80\x80\x00\x01\x98\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbd\x44\x00\x00\x00\x00\x00\x00\x2c\xc0\xbd\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-6", []), -14.)
-
-// globals.wast:39
-assert_return(() => call($1, "set-x", [6]));
-
-// globals.wast:40
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x01\x7e\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x73\x65\x74\x2d\x79\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x91\x80\x80\x80\x00\x01\x8b\x80\x80\x80\x00\x00\x02\x40\x42\x07\x10\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "set-y", [int64("7")]))
-
-// globals.wast:41
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x01\x7d\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x73\x65\x74\x2d\x35\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x94\x80\x80\x80\x00\x01\x8e\x80\x80\x80\x00\x00\x02\x40\x43\x00\x00\x00\x41\x10\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "set-5", [8.]))
-
-// globals.wast:42
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x01\x7c\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x73\x65\x74\x2d\x36\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x44\x00\x00\x00\x00\x00\x00\x22\x40\x10\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "set-6", [9.]))
-
-// globals.wast:44
-assert_return(() => call($1, "get-x", []), 6);
-
-// globals.wast:45
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7e\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x79\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x97\x80\x80\x80\x00\x01\x91\x80\x80\x80\x00\x00\x02\x40\x10\x00\x01\x42\x07\x01\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-y", []), int64("7"))
-
-// globals.wast:46
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7d\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x35\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9a\x80\x80\x80\x00\x01\x94\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbc\x43\x00\x00\x00\x41\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-5", []), 8.)
-
-// globals.wast:47
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x02\x60\x00\x00\x60\x00\x01\x7c\x02\x8c\x80\x80\x80\x00\x01\x02\x24\x31\x05\x67\x65\x74\x2d\x36\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9e\x80\x80\x80\x00\x01\x98\x80\x80\x80\x00\x00\x02\x40\x10\x00\xbd\x44\x00\x00\x00\x00\x00\x00\x22\x40\xbd\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "get-6", []), 9.)
-
-// globals.wast:49
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x00\x00\x0b\x0a\x8c\x80\x80\x80\x00\x01\x86\x80\x80\x80\x00\x00\x41\x01\x24\x00\x0b");
-
-// globals.wast:55
-let $2 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
-
-// globals.wast:56
-let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7d\x01\x43\x00\x00\x00\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x61\x03\x00");
-
-// globals.wast:58
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8a\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x00\x00\x8c\x0b");
-
-// globals.wast:63
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7d\x00\x20\x00\x0b");
-
-// globals.wast:68
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8a\x80\x80\x80\x00\x01\x7d\x00\x43\x00\x00\x80\x3f\x8c\x0b");
-
-// globals.wast:73
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x87\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x01\x0b");
-
-// globals.wast:78
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x85\x80\x80\x80\x00\x01\x7f\x00\x01\x0b");
-
-// globals.wast:83
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x89\x80\x80\x80\x00\x01\x7f\x00\x43\x00\x00\x00\x00\x0b");
-
-// globals.wast:88
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x88\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x41\x00\x0b");
-
-// globals.wast:93
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x84\x80\x80\x80\x00\x01\x7f\x00\x0b");
-
-// globals.wast:98
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x23\x00\x0b");
-
-// globals.wast:103
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x8b\x80\x80\x80\x00\x02\x7f\x00\x23\x01\x0b\x7f\x00\x41\x00\x0b");
-
-// globals.wast:108
-let $4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x98\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\x00");
-
-// globals.wast:111
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\x02");
-
-// globals.wast:124
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x94\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x67\x6c\x6f\x62\x61\x6c\x5f\x69\x33\x32\x03\x7f\xff");
-
-// globals.wast:138
-let $5 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x00\x0b");
-
-// globals.wast:141
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x02\x41\x00\x0b");
-
-// globals.wast:153
-assert_malformed("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\xff\x41\x00\x0b");
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/spec/proposal_mutable_global/linking.wast.js
+++ /dev/null
@@ -1,352 +0,0 @@
-
-// linking.wast:3
-let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x03\x83\x80\x80\x80\x00\x02\x00\x00\x07\x88\x80\x80\x80\x00\x01\x04\x63\x61\x6c\x6c\x00\x00\x0a\x93\x80\x80\x80\x00\x02\x84\x80\x80\x80\x00\x00\x10\x01\x0b\x84\x80\x80\x80\x00\x00\x41\x02\x0b");
-let $Mf = $1;
-
-// linking.wast:7
-register("Mf", $Mf)
-
-// linking.wast:9
-let $2 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x66\x04\x63\x61\x6c\x6c\x00\x00\x03\x84\x80\x80\x80\x00\x03\x00\x00\x00\x07\xa1\x80\x80\x80\x00\x03\x07\x4d\x66\x2e\x63\x61\x6c\x6c\x00\x00\x0c\x63\x61\x6c\x6c\x20\x4d\x66\x2e\x63\x61\x6c\x6c\x00\x01\x04\x63\x61\x6c\x6c\x00\x02\x0a\x9c\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x10\x00\x0b\x84\x80\x80\x80\x00\x00\x10\x03\x0b\x84\x80\x80\x80\x00\x00\x41\x03\x0b");
-let $Nf = $2;
-
-// linking.wast:17
-assert_return(() => call($Mf, "call", []), 2);
-
-// linking.wast:18
-assert_return(() => call($Nf, "Mf.call", []), 2);
-
-// linking.wast:19
-assert_return(() => call($Nf, "call", []), 3);
-
-// linking.wast:20
-assert_return(() => call($Nf, "call Mf.call", []), 2);
-
-// linking.wast:22
-let $3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7f\x00\x02\x96\x80\x80\x80\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x70\x72\x69\x6e\x74\x5f\x69\x33\x32\x00\x00\x07\x89\x80\x80\x80\x00\x01\x05\x70\x72\x69\x6e\x74\x00\x00");
-
-// linking.wast:26
-register("reexport_f", $3)
-
-// linking.wast:27
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7e\x00\x02\x94\x80\x80\x80\x00\x01\x0a\x72\x65\x65\x78\x70\x6f\x72\x74\x5f\x66\x05\x70\x72\x69\x6e\x74\x00\x00");
-
-// linking.wast:31
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x94\x80\x80\x80\x00\x01\x0a\x72\x65\x65\x78\x70\x6f\x72\x74\x5f\x66\x05\x70\x72\x69\x6e\x74\x00\x00");
-
-// linking.wast:39
-let $4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x00\x03\x84\x80\x80\x80\x00\x03\x00\x00\x01\x06\x8c\x80\x80\x80\x00\x02\x7f\x00\x41\x2a\x0b\x7f\x01\x41\x8e\x01\x0b\x07\xad\x80\x80\x80\x00\x05\x04\x67\x6c\x6f\x62\x03\x00\x03\x67\x65\x74\x00\x00\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x01\x07\x67\x65\x74\x5f\x6d\x75\x74\x00\x01\x07\x73\x65\x74\x5f\x6d\x75\x74\x00\x02\x0a\x9e\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x23\x00\x0b\x84\x80\x80\x80\x00\x00\x23\x01\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x24\x01\x0b");
-let $Mg = $4;
-
-// linking.wast:48
-register("Mg", $Mg)
-
-// linking.wast:50
-let $5 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x00\x02\xbe\x80\x80\x80\x00\x05\x02\x4d\x67\x04\x67\x6c\x6f\x62\x03\x7f\x00\x02\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x02\x4d\x67\x03\x67\x65\x74\x00\x00\x02\x4d\x67\x07\x67\x65\x74\x5f\x6d\x75\x74\x00\x00\x02\x4d\x67\x07\x73\x65\x74\x5f\x6d\x75\x74\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x2b\x0b\x07\xc9\x80\x80\x80\x00\x07\x07\x4d\x67\x2e\x67\x6c\x6f\x62\x03\x00\x06\x4d\x67\x2e\x67\x65\x74\x00\x00\x04\x67\x6c\x6f\x62\x03\x02\x03\x67\x65\x74\x00\x03\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x01\x0a\x4d\x67\x2e\x67\x65\x74\x5f\x6d\x75\x74\x00\x01\x0a\x4d\x67\x2e\x73\x65\x74\x5f\x6d\x75\x74\x00\x02\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x23\x02\x0b");
-let $Ng = $5;
-
-// linking.wast:67
-assert_return(() => get($Mg, "glob"), 42);
-
-// linking.wast:68
-assert_return(() => get($Ng, "Mg.glob"), 42);
-
-// linking.wast:69
-assert_return(() => get($Ng, "glob"), 43);
-
-// linking.wast:70
-assert_return(() => call($Mg, "get", []), 42);
-
-// linking.wast:71
-assert_return(() => call($Ng, "Mg.get", []), 42);
-
-// linking.wast:72
-assert_return(() => call($Ng, "get", []), 43);
-
-// linking.wast:74
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x91\x80\x80\x80\x00\x01\x03\x24\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\x8e\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Mg", $Mg)),  "run", []));  // assert_return(() => get($Mg, "mut_glob"), 142)
-
-// linking.wast:75
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x94\x80\x80\x80\x00\x01\x03\x24\x4e\x67\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\x8e\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Ng", $Ng)),  "run", []));  // assert_return(() => get($Ng, "Mg.mut_glob"), 142)
-
-// linking.wast:76
-assert_return(() => call($Mg, "get_mut", []), 142);
-
-// linking.wast:77
-assert_return(() => call($Ng, "Mg.get_mut", []), 142);
-
-// linking.wast:79
-assert_return(() => call($Mg, "set_mut", [241]));
-
-// linking.wast:80
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x91\x80\x80\x80\x00\x01\x03\x24\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\xf1\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Mg", $Mg)),  "run", []));  // assert_return(() => get($Mg, "mut_glob"), 241)
-
-// linking.wast:81
-run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x94\x80\x80\x80\x00\x01\x03\x24\x4e\x67\x0b\x4d\x67\x2e\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x00\x0a\x98\x80\x80\x80\x00\x01\x92\x80\x80\x80\x00\x00\x02\x40\x23\x00\x01\x41\xf1\x01\x01\x46\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$Ng", $Ng)),  "run", []));  // assert_return(() => get($Ng, "Mg.mut_glob"), 241)
-
-// linking.wast:82
-assert_return(() => call($Mg, "get_mut", []), 241);
-
-// linking.wast:83
-assert_return(() => call($Ng, "Mg.get_mut", []), 241);
-
-// linking.wast:86
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x90\x80\x80\x80\x00\x01\x02\x4d\x67\x08\x6d\x75\x74\x5f\x67\x6c\x6f\x62\x03\x7f\x00");
-
-// linking.wast:90
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x67\x04\x67\x6c\x6f\x62\x03\x7f\x01");
-
-// linking.wast:97
-let $6 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8d\x80\x80\x80\x00\x03\x60\x00\x01\x7f\x60\x00\x00\x60\x01\x7f\x01\x7f\x03\x84\x80\x80\x80\x00\x03\x00\x00\x02\x04\x84\x80\x80\x80\x00\x01\x70\x00\x0a\x07\x92\x80\x80\x80\x00\x03\x03\x74\x61\x62\x01\x00\x01\x68\x00\x01\x04\x63\x61\x6c\x6c\x00\x02\x09\x8a\x80\x80\x80\x00\x01\x00\x41\x02\x0b\x04\x00\x00\x00\x00\x0a\x9f\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x41\x04\x0b\x84\x80\x80\x80\x00\x00\x41\x7c\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x00\x00\x0b");
-let $Mt = $6;
-
-// linking.wast:110
-register("Mt", $Mt)
-
-// linking.wast:112
-let $7 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8d\x80\x80\x80\x00\x03\x60\x00\x00\x60\x00\x01\x7f\x60\x01\x7f\x01\x7f\x02\x92\x80\x80\x80\x00\x02\x02\x4d\x74\x04\x63\x61\x6c\x6c\x00\x02\x02\x4d\x74\x01\x68\x00\x01\x03\x84\x80\x80\x80\x00\x03\x01\x02\x02\x04\x85\x80\x80\x80\x00\x01\x70\x01\x05\x05\x07\xa1\x80\x80\x80\x00\x03\x07\x4d\x74\x2e\x63\x61\x6c\x6c\x00\x00\x0c\x63\x61\x6c\x6c\x20\x4d\x74\x2e\x63\x61\x6c\x6c\x00\x03\x04\x63\x61\x6c\x6c\x00\x04\x09\x8b\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x05\x02\x02\x02\x01\x00\x0a\xa1\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x41\x05\x0b\x86\x80\x80\x80\x00\x00\x20\x00\x10\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x01\x00\x0b");
-let $Nt = $7;
-
-// linking.wast:131
-assert_return(() => call($Mt, "call", [2]), 4);
-
-// linking.wast:132
-assert_return(() => call($Nt, "Mt.call", [2]), 4);
-
-// linking.wast:133
-assert_return(() => call($Nt, "call", [2]), 5);
-
-// linking.wast:134
-assert_return(() => call($Nt, "call Mt.call", [2]), 4);
-
-// linking.wast:136
-assert_trap(() => call($Mt, "call", [1]));
-
-// linking.wast:137
-assert_trap(() => call($Nt, "Mt.call", [1]));
-
-// linking.wast:138
-assert_return(() => call($Nt, "call", [1]), 5);
-
-// linking.wast:139
-assert_trap(() => call($Nt, "call Mt.call", [1]));
-
-// linking.wast:141
-assert_trap(() => call($Mt, "call", [0]));
-
-// linking.wast:142
-assert_trap(() => call($Nt, "Mt.call", [0]));
-
-// linking.wast:143
-assert_return(() => call($Nt, "call", [0]), 5);
-
-// linking.wast:144
-assert_trap(() => call($Nt, "call Mt.call", [0]));
-
-// linking.wast:146
-assert_trap(() => call($Mt, "call", [20]));
-
-// linking.wast:147
-assert_trap(() => call($Nt, "Mt.call", [20]));
-
-// linking.wast:148
-assert_trap(() => call($Nt, "call", [7]));
-
-// linking.wast:149
-assert_trap(() => call($Nt, "call Mt.call", [20]));
-
-// linking.wast:151
-assert_return(() => call($Nt, "call", [3]), -4);
-
-// linking.wast:152
-assert_trap(() => call($Nt, "call", [4]));
-
-// linking.wast:154
-let $8 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8a\x80\x80\x80\x00\x02\x60\x00\x01\x7f\x60\x01\x7f\x01\x7f\x02\x93\x80\x80\x80\x00\x02\x02\x4d\x74\x01\x68\x00\x00\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x05\x03\x83\x80\x80\x80\x00\x02\x00\x01\x07\x88\x80\x80\x80\x00\x01\x04\x63\x61\x6c\x6c\x00\x02\x09\x88\x80\x80\x80\x00\x01\x00\x41\x01\x0b\x02\x01\x00\x0a\x96\x80\x80\x80\x00\x02\x84\x80\x80\x80\x00\x00\x41\x06\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x11\x00\x00\x0b");
-let $Ot = $8;
-
-// linking.wast:167
-assert_return(() => call($Mt, "call", [3]), 4);
-
-// linking.wast:168
-assert_return(() => call($Nt, "Mt.call", [3]), 4);
-
-// linking.wast:169
-assert_return(() => call($Nt, "call Mt.call", [3]), 4);
-
-// linking.wast:170
-assert_return(() => call($Ot, "call", [3]), 4);
-
-// linking.wast:172
-assert_return(() => call($Mt, "call", [2]), -4);
-
-// linking.wast:173
-assert_return(() => call($Nt, "Mt.call", [2]), -4);
-
-// linking.wast:174
-assert_return(() => call($Nt, "call", [2]), 5);
-
-// linking.wast:175
-assert_return(() => call($Nt, "call Mt.call", [2]), -4);
-
-// linking.wast:176
-assert_return(() => call($Ot, "call", [2]), -4);
-
-// linking.wast:178
-assert_return(() => call($Mt, "call", [1]), 6);
-
-// linking.wast:179
-assert_return(() => call($Nt, "Mt.call", [1]), 6);
-
-// linking.wast:180
-assert_return(() => call($Nt, "call", [1]), 5);
-
-// linking.wast:181
-assert_return(() => call($Nt, "call Mt.call", [1]), 6);
-
-// linking.wast:182
-assert_return(() => call($Ot, "call", [1]), 6);
-
-// linking.wast:184
-assert_trap(() => call($Mt, "call", [0]));
-
-// linking.wast:185
-assert_trap(() => call($Nt, "Mt.call", [0]));
-
-// linking.wast:186
-assert_return(() => call($Nt, "call", [0]), 5);
-
-// linking.wast:187
-assert_trap(() => call($Nt, "call Mt.call", [0]));
-
-// linking.wast:188
-assert_trap(() => call($Ot, "call", [0]));
-
-// linking.wast:190
-assert_trap(() => call($Ot, "call", [20]));
-
-// linking.wast:192
-let $9 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x09\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
-
-// linking.wast:198
-let $10 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x41\x05\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x67\x03\x00");
-let $G1 = $10;
-
-// linking.wast:199
-register("G1", $G1)
-
-// linking.wast:200
-let $11 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x89\x80\x80\x80\x00\x01\x02\x47\x31\x01\x67\x03\x7f\x00\x06\x86\x80\x80\x80\x00\x01\x7f\x00\x23\x00\x0b\x07\x85\x80\x80\x80\x00\x01\x01\x67\x03\x01");
-let $G2 = $11;
-
-// linking.wast:204
-assert_return(() => get($G2, "g"), 5);
-
-// linking.wast:206
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
-
-// linking.wast:215
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x96\x80\x80\x80\x00\x02\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x02\x4d\x74\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x09\x8d\x80\x80\x80\x00\x02\x00\x41\x07\x0b\x01\x00\x00\x41\x09\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b");
-
-// linking.wast:225
-assert_trap(() => call($Mt, "call", [7]));
-
-// linking.wast:227
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x03\x82\x80\x80\x80\x00\x01\x00\x09\x8d\x80\x80\x80\x00\x02\x00\x41\x07\x0b\x01\x00\x00\x41\x0c\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b");
-
-// linking.wast:236
-assert_trap(() => call($Mt, "call", [7]));
-
-// linking.wast:238
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x74\x03\x74\x61\x62\x01\x70\x00\x0a\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x09\x87\x80\x80\x80\x00\x01\x00\x41\x07\x0b\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x41\x00\x0b\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x80\x80\x04\x0b\x01\x64");
-
-// linking.wast:248
-assert_trap(() => call($Mt, "call", [7]));
-
-// linking.wast:253
-let $12 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x05\x84\x80\x80\x80\x00\x01\x01\x01\x05\x07\x8e\x80\x80\x80\x00\x02\x03\x6d\x65\x6d\x02\x00\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x90\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x0a\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09");
-let $Mm = $12;
-
-// linking.wast:261
-register("Mm", $Mm)
-
-// linking.wast:263
-let $13 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x04\x6c\x6f\x61\x64\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00\x05\x83\x80\x80\x80\x00\x01\x00\x01\x07\x92\x80\x80\x80\x00\x02\x07\x4d\x6d\x2e\x6c\x6f\x61\x64\x00\x00\x04\x6c\x6f\x61\x64\x00\x01\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8c\x80\x80\x80\x00\x01\x00\x41\x0a\x0b\x06\xf0\xf1\xf2\xf3\xf4\xf5");
-let $Nm = $13;
-
-// linking.wast:275
-assert_return(() => call($Mm, "load", [12]), 2);
-
-// linking.wast:276
-assert_return(() => call($Nm, "Mm.load", [12]), 2);
-
-// linking.wast:277
-assert_return(() => call($Nm, "load", [12]), 242);
-
-// linking.wast:279
-let $14 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x6c\x6f\x61\x64\x00\x00\x0a\x8d\x80\x80\x80\x00\x01\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x0b\x8e\x80\x80\x80\x00\x01\x00\x41\x05\x0b\x08\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7");
-let $Om = $14;
-
-// linking.wast:288
-assert_return(() => call($Mm, "load", [12]), 167);
-
-// linking.wast:289
-assert_return(() => call($Nm, "Mm.load", [12]), 167);
-
-// linking.wast:290
-assert_return(() => call($Nm, "load", [12]), 242);
-
-// linking.wast:291
-assert_return(() => call($Om, "load", [12]), 167);
-
-// linking.wast:293
-let $15 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\xff\xff\x03\x0b\x01\x61");
-
-// linking.wast:298
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x80\x80\x04\x0b\x01\x61");
-
-// linking.wast:306
-let $16 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x02\x8c\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x01\x01\x08\x03\x82\x80\x80\x80\x00\x01\x00\x07\x88\x80\x80\x80\x00\x01\x04\x67\x72\x6f\x77\x00\x00\x0a\x8c\x80\x80\x80\x00\x01\x86\x80\x80\x80\x00\x00\x20\x00\x40\x00\x0b");
-let $Pm = $16;
-
-// linking.wast:314
-assert_return(() => call($Pm, "grow", [0]), 1);
-
-// linking.wast:315
-assert_return(() => call($Pm, "grow", [2]), 1);
-
-// linking.wast:316
-assert_return(() => call($Pm, "grow", [0]), 3);
-
-// linking.wast:317
-assert_return(() => call($Pm, "grow", [1]), 3);
-
-// linking.wast:318
-assert_return(() => call($Pm, "grow", [1]), 4);
-
-// linking.wast:319
-assert_return(() => call($Pm, "grow", [0]), 5);
-
-// linking.wast:320
-assert_return(() => call($Pm, "grow", [1]), -1);
-
-// linking.wast:321
-assert_return(() => call($Pm, "grow", [0]), 5);
-
-// linking.wast:323
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\xa7\x80\x80\x80\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x05\x70\x72\x69\x6e\x74\x00\x00\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x02\x4d\x6d\x03\x74\x61\x62\x01\x70\x00\x00\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x03\x61\x62\x63");
-
-// linking.wast:332
-assert_return(() => call($Mm, "load", [0]), 0);
-
-// linking.wast:334
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x0b\x91\x80\x80\x80\x00\x02\x00\x41\x00\x0b\x03\x61\x62\x63\x00\x41\x80\x80\x14\x0b\x01\x64");
-
-// linking.wast:342
-assert_return(() => call($Mm, "load", [0]), 0);
-
-// linking.wast:344
-assert_unlinkable("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01\x60\x00\x00\x02\x8b\x80\x80\x80\x00\x01\x02\x4d\x6d\x03\x6d\x65\x6d\x02\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x04\x84\x80\x80\x80\x00\x01\x70\x00\x00\x09\x87\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b\x0b\x89\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x03\x61\x62\x63");
-
-// linking.wast:354
-assert_return(() => call($Mm, "load", [0]), 0);
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -1450,23 +1450,53 @@ CodeGenerator::visitRoundF(LRoundF* lir)
     bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot());
 
     masm.bind(&end);
 }
 
 void
 CodeGenerator::visitTrunc(LTrunc* lir)
 {
-    MOZ_CRASH("visitTrunc");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label notZero;
+    masm.as_truncwd(ScratchFloat32Reg, input);
+    masm.as_cfc1(ScratchRegister, Assembler::FCSR);
+    masm.moveFromFloat32(ScratchFloat32Reg, output);
+    masm.ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
+
+    masm.ma_b(output, Imm32(0), &notZero, Assembler::NotEqual, ShortJump);
+    masm.moveFromDoubleHi(input, ScratchRegister);
+    // Check if input is in ]-1; -0] range by checking the sign bit.
+    masm.as_slt(ScratchRegister, ScratchRegister, zero);
+    masm.bind(&notZero);
+
+    bailoutCmp32(Assembler::NotEqual, ScratchRegister, Imm32(0), lir->snapshot());
 }
 
 void
 CodeGenerator::visitTruncF(LTruncF* lir)
 {
-    MOZ_CRASH("visitTruncF");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label notZero;
+    masm.as_truncws(ScratchFloat32Reg, input);
+    masm.as_cfc1(ScratchRegister, Assembler::FCSR);
+    masm.moveFromFloat32(ScratchFloat32Reg, output);
+    masm.ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
+
+    masm.ma_b(output, Imm32(0), &notZero, Assembler::NotEqual, ShortJump);
+    masm.moveFromFloat32(input, ScratchRegister);
+    // Check if input is in ]-1; -0] range by checking the sign bit.
+    masm.as_slt(ScratchRegister, ScratchRegister, zero);
+    masm.bind(&notZero);
+
+    bailoutCmp32(Assembler::NotEqual, ScratchRegister, Imm32(0), lir->snapshot());
 }
 
 void
 CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
     emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()),
                        ins->mir());
 }
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -151,17 +151,16 @@ if CONFIG['JS_BUILD_BINAST'] and CONFIG[
     # we can't have data files in js/src tests.
     UNIFIED_SOURCES += [
         'testBinASTReader.cpp',
         'testBinTokenReaderTester.cpp'
     ]
 
 
 DEFINES['EXPORT_JS_API'] = True
-DEFINES['ENABLE_WASM_GLOBAL'] = True
 
 LOCAL_INCLUDES += [
     '!..',
     '..',
 ]
 
 USE_LIBS += [
     'static:js',
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -697,23 +697,16 @@ if CONFIG['NIGHTLY_BUILD']:
     DEFINES['ENABLE_BINARYDATA'] = True
     DEFINES['ENABLE_SIMD'] = True
     DEFINES['ENABLE_WASM_BULKMEM_OPS'] = True
     DEFINES['ENABLE_WASM_SATURATING_TRUNC_OPS'] = True
     DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
     DEFINES['ENABLE_WASM_THREAD_OPS'] = True
     DEFINES['ENABLE_WASM_GC'] = True
 
-# An experiment we want to run on Nightly and early Beta: Can we change the JS
-# representation of an exported global from the global's value to an instance
-# of WebAssembly.Global without breaking existing wasm content?
-#
-# Additionally guarded by EARLY_BETA_OR_EARLIER in the code.
-DEFINES['ENABLE_WASM_GLOBAL'] = True
-
 if CONFIG['JS_BUILD_BINAST']:
     # Using SOURCES, as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.
     # These parts of BinAST are designed only to test evolutions of the
     # specification.
     SOURCES += ['frontend/BinTokenReaderTester.cpp']
     # These parts of BinAST should eventually move to release.
     SOURCES += [
         'frontend/BinSource-auto.cpp',
--- a/js/src/shell/moz.build
+++ b/js/src/shell/moz.build
@@ -13,17 +13,16 @@ if CONFIG['JS_SHELL_NAME']:
 UNIFIED_SOURCES += [
     'js.cpp',
     'jsoptparse.cpp',
     'jsshell.cpp',
     'OSObject.cpp'
 ]
 
 DEFINES['EXPORT_JS_API'] = True
-DEFINES['ENABLE_WASM_GLOBAL'] = True
 
 if CONFIG['NIGHTLY_BUILD']:
     DEFINES['ENABLE_WASM_GC'] = True
 
 # Also set in ../moz.build
 DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
 
 if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -241,17 +241,16 @@ GetImports(JSContext* cx,
             break;
 
           case DefinitionKind::Global:
             Val val;
             const uint32_t index = globalIndex++;
             const GlobalDesc& global = globals[index];
             MOZ_ASSERT(global.importIndex() == index);
 
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
             if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
                 RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
 
                 if (obj->isMutable() != global.isMutable()) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
                     return false;
                 }
                 if (obj->type() != global.type()) {
@@ -261,29 +260,26 @@ GetImports(JSContext* cx,
 
                 if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 globalObjs[index] = obj;
                 val = obj->val();
             } else
-#endif
             if (v.isNumber()) {
                 if (global.type() == ValType::I64) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
                     return false;
                 }
 
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
                 if (global.isMutable()) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
                     return false;
                 }
-#endif
 
                 if (!ToWebAssemblyValue(cx, global.type(), v, &val))
                     return false;
             } else {
                 return ThrowBadImportType(cx, import.field.get(), "Number");
             }
 
             if (!globalImportValues->append(val))
@@ -1061,39 +1057,35 @@ WasmInstanceObject::create(JSContext* cx
     UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone());
     if (!scopes || !scopes->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     uint32_t indirectGlobals = 0;
 
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     for (uint32_t i = 0; i < globalObjs.length(); i++) {
         if (globalObjs[i] && globals[i].isIndirect())
             indirectGlobals++;
     }
-#endif
 
     Rooted<UniquePtr<WasmGlobalObjectVector>> indirectGlobalObjs(cx,
         js::MakeUnique<WasmGlobalObjectVector>());
     if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     {
         uint32_t next = 0;
         for (uint32_t i = 0; i < globalObjs.length(); i++) {
             if (globalObjs[i] && globals[i].isIndirect())
                 (*indirectGlobalObjs)[next++] = globalObjs[i];
         }
     }
-#endif
 
     AutoSetNewObjectMetadata metadata(cx);
     RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
 
@@ -3197,31 +3189,27 @@ js::InitWebAssemblyClass(JSContext* cx, 
     RootedObject wasm(cx, NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject));
     if (!wasm)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, wasm, WebAssembly_static_methods))
         return nullptr;
 
     RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     RootedObject globalProto(cx);
-#endif
     if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
         return nullptr;
     if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance", &instanceProto))
         return nullptr;
     if (!InitConstructor<WasmMemoryObject>(cx, wasm, "Memory", &memoryProto))
         return nullptr;
     if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
         return nullptr;
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     if (!InitConstructor<WasmGlobalObject>(cx, wasm, "Global", &globalProto))
         return nullptr;
-#endif
     if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR))
         return nullptr;
     if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR))
         return nullptr;
     if (!InitErrorClass(cx, wasm, "RuntimeError", JSEXN_WASMRUNTIMEERROR))
         return nullptr;
 
     // Perform the final fallible write of the WebAssembly object to a global
@@ -3231,16 +3219,14 @@ js::InitWebAssemblyClass(JSContext* cx, 
 
     if (!JS_DefineProperty(cx, global, js_WebAssembly_str, wasm, JSPROP_RESOLVING))
         return nullptr;
 
     global->setPrototype(JSProto_WasmModule, ObjectValue(*moduleProto));
     global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
     global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
     global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     global->setPrototype(JSProto_WasmGlobal, ObjectValue(*globalProto));
-#endif
     global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
 
     MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));
     return wasm;
 }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1020,17 +1020,16 @@ ExtractGlobalValue(const ValVector& glob
       }
       case GlobalKind::Constant: {
         return global.constantValue();
       }
     }
     MOZ_CRASH("Not a global value");
 }
 
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
 static bool
 EnsureGlobalObject(JSContext* cx, const ValVector& globalImportValues, size_t globalIndex,
                    const GlobalDesc& global, WasmGlobalObjectVector& globalObjs)
 {
     if (globalIndex < globalObjs.length() && globalObjs[globalIndex])
         return true;
 
     Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
@@ -1041,23 +1040,21 @@ EnsureGlobalObject(JSContext* cx, const 
     if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex + 1)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     globalObjs[globalIndex] = go;
     return true;
 }
-#endif
 
 bool
 Module::instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
                            WasmGlobalObjectVector& globalObjs) const
 {
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     // If there are exported globals that aren't in globalObjs because they
     // originate in this module or because they were immutable imports that came
     // in as primitive values then we must create cells in the globalObjs for
     // them here, as WasmInstanceObject::create() and CreateExportObject() will
     // need the cells to exist.
 
     const GlobalDescVector& globals = metadata().globals;
 
@@ -1069,30 +1066,29 @@ Module::instantiateGlobals(JSContext* cx
         if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
             return false;
     }
 
     // Imported globals that are not re-exported may also have received only a
     // primitive value; these globals are always immutable.  Assert that we do
     // not need to create any additional Global objects for such imports.
 
-# ifdef DEBUG
+#ifdef DEBUG
     size_t numGlobalImports = 0;
     for (const Import& import : imports_) {
         if (import.kind != DefinitionKind::Global)
             continue;
         size_t globalIndex = numGlobalImports++;
         const GlobalDesc& global = globals[globalIndex];
         MOZ_ASSERT(global.importIndex() == globalIndex);
         MOZ_ASSERT_IF(global.isIndirect(),
                       globalIndex < globalObjs.length() || globalObjs[globalIndex]);
     }
     MOZ_ASSERT_IF(!metadata().isAsmJS(),
                   numGlobalImports == globals.length() || !globals[numGlobalImports].isImport());
-# endif
 #endif
     return true;
 }
 
 static bool
 GetFunctionExport(JSContext* cx,
                   HandleWasmInstanceObject instanceObj,
                   Handle<FunctionVector> funcImports,
@@ -1117,32 +1113,17 @@ GetFunctionExport(JSContext* cx,
 static bool
 GetGlobalExport(JSContext* cx,
                 const GlobalDescVector& globals,
                 uint32_t globalIndex,
                 const ValVector& globalImportValues,
                 const WasmGlobalObjectVector& globalObjs,
                 MutableHandleValue jsval)
 {
-#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     jsval.setObject(*globalObjs[globalIndex]);
-#else
-    const GlobalDesc& global = globals[globalIndex];
-
-    MOZ_ASSERT(!global.isMutable(), "Mutable variables can't be exported.");
-
-    Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
-    if (val.type() == ValType::I64) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
-        return false;
-    }
-
-    jsval.set(ToJSValue(val));
-#endif
-
     return true;
 }
 
 static bool
 CreateExportObject(JSContext* cx,
                    HandleWasmInstanceObject instanceObj,
                    Handle<FunctionVector> funcImports,
                    HandleWasmTableObject tableObj,
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1245,21 +1245,16 @@ GlobalIsJSCompatible(Decoder& d, ValType
       case ValType::F32:
       case ValType::F64:
       case ValType::I64:
         break;
       default:
         return d.fail("unexpected variable type in global import/export");
     }
 
-#if !(defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER))
-    if (isMutable)
-        return d.fail("can't import/export mutable globals in the MVP");
-#endif
-
     return true;
 }
 
 static bool
 DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
 {
     // No gc types in globals at the moment.
     if (!DecodeValType(d, ModuleKind::Wasm, HasGcTypes::False, type))
--- a/layout/style/ComputedStyle.cpp
+++ b/layout/style/ComputedStyle.cpp
@@ -38,75 +38,41 @@
 // Ensure the binding function declarations in ComputedStyle.h matches
 // those in ServoBindings.h.
 #include "mozilla/ServoBindings.h"
 
 namespace mozilla {
 
 //----------------------------------------------------------------------
 
-#ifdef DEBUG
-
-// Check that the style struct IDs are in the same order as they are
-// in nsStyleStructList.h, since when we set up the IDs, we include
-// the inherited and reset structs spearately from nsStyleStructList.h
-enum DebugStyleStruct {
-#define STYLE_STRUCT(name) eDebugStyleStruct_##name,
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-};
-
-#define STYLE_STRUCT(name)                                    \
-  static_assert(static_cast<int>(eDebugStyleStruct_##name) == \
-                  static_cast<int>(eStyleStruct_##name),      \
-                "Style struct IDs are not declared in order?");
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-
-#endif
-
-
 ComputedStyle::ComputedStyle(nsPresContext* aPresContext,
                              nsAtom* aPseudoTag,
                              CSSPseudoElementType aPseudoType,
                              ServoComputedDataForgotten aComputedValues)
   : mPresContext(aPresContext)
   , mSource(aComputedValues)
   , mPseudoTag(aPseudoTag)
-  , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
+  , mBits(static_cast<Bit>(Servo_ComputedValues_GetStyleBits(this)))
+  , mPseudoType(aPseudoType)
 {
-  AddStyleBit(Servo_ComputedValues_GetStyleBits(this));
   MOZ_ASSERT(ComputedData());
-
-  // This check has to be done "backward", because if it were written the
-  // more natural way it wouldn't fail even when it needed to.
-  static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
-                 static_cast<CSSPseudoElementTypeBase>(
-                   CSSPseudoElementType::MAX),
-                "pseudo element bits no longer fit in a uint64_t");
-
-#define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
-  static_assert(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
-                "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
-#undef eStyleStruct_LastItem
 }
 
 nsChangeHint
 ComputedStyle::CalcStyleDifference(ComputedStyle* aNewContext,
                                    uint32_t* aEqualStructs)
 {
+  MOZ_ASSERT(aNewContext);
   AUTO_PROFILER_LABEL("ComputedStyle::CalcStyleDifference", CSS);
-
-  static_assert(nsStyleStructID_Length <= 32,
+  static_assert(StyleStructConstants::kStyleStructCount <= 32,
                 "aEqualStructs is not big enough");
 
   *aEqualStructs = 0;
 
   nsChangeHint hint = nsChangeHint(0);
-  NS_ENSURE_TRUE(aNewContext, hint);
   // We must always ensure that we populate the structs on the new style
   // context that are filled in on the old context, so that if we get
   // two style changes in succession, the second of which causes a real
   // style change, the PeekStyleData doesn't return null (implying that
   // nobody ever looked at that struct's data).  In other words, we
   // can't skip later structs if we get a big change up front, because
   // we could later get a small change in one of those structs that we
   // don't want to miss.
@@ -119,42 +85,44 @@ ComputedStyle::CalcStyleDifference(Compu
   // that children need to be recascade for relies on comparing all of the
   // structs, not just those that are returned from PeekStyleData, although
   // if PeekStyleData does return null we could avoid to accumulate any change
   // hints for those structs.
   //
   // FIXME(emilio): Reintroduce that optimization either for all kind of structs
   // after bug 1368290 with a weak parent pointer from text, or just for reset
   // structs.
+#define STYLE_STRUCT_BIT(name_) \
+  StyleStructConstants::BitFor(StyleStructID::name_)
 #define PEEK(struct_) \
    ComputedData()->GetStyle##struct_()
 
 #define EXPAND(...) __VA_ARGS__
 #define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)                  \
   PR_BEGIN_MACRO                                                              \
     const nsStyle##struct_* this##struct_ = PEEK(struct_);                    \
     if (this##struct_) {                                                      \
-      structsFound |= NS_STYLE_INHERIT_BIT(struct_);                          \
+      structsFound |= STYLE_STRUCT_BIT(struct_);                              \
                                                                               \
       const nsStyle##struct_* other##struct_ =                                \
         aNewContext->ThreadsafeStyle##struct_();                              \
       if (this##struct_ == other##struct_) {                                  \
         /* The very same struct, so we know that there will be no */          \
         /* differences.                                           */          \
-        *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_);                      \
+        *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                          \
       } else {                                                                \
         nsChangeHint difference =                                             \
           this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_);  \
         hint |= difference;                                                   \
         if (!difference) {                                                    \
-          *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_);                    \
+          *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                        \
         }                                                                     \
       }                                                                       \
     } else {                                                                  \
-      *aEqualStructs |= NS_STYLE_INHERIT_BIT(struct_);                        \
+      *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                            \
     }                                                                         \
     styleStructCount++;                                                       \
   PR_END_MACRO
 #define DO_STRUCT_DIFFERENCE(struct_) \
   DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())
 
   // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
   // significant.  With a small amount of effort, we could replace them with a
@@ -182,22 +150,22 @@ ComputedStyle::CalcStyleDifference(Compu
   DO_STRUCT_DIFFERENCE(Effects);
   DO_STRUCT_DIFFERENCE(Background);
   DO_STRUCT_DIFFERENCE(Color);
 
 #undef DO_STRUCT_DIFFERENCE
 #undef DO_STRUCT_DIFFERENCE_WITH_ARGS
 #undef EXPAND
 
-  MOZ_ASSERT(styleStructCount == nsStyleStructID_Length,
+  MOZ_ASSERT(styleStructCount == StyleStructConstants::kStyleStructCount,
              "missing a call to DO_STRUCT_DIFFERENCE");
 
 #ifdef DEBUG
   #define STYLE_STRUCT(name_)                                             \
-    MOZ_ASSERT(!!(structsFound & NS_STYLE_INHERIT_BIT(name_)) ==          \
+    MOZ_ASSERT(!!(structsFound & STYLE_STRUCT_BIT(name_)) ==              \
                (PEEK(name_) != nullptr),                                  \
                "PeekStyleData results must not change in the middle of "  \
                "difference calculation.");
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 #endif
 
   // Note that we do not check whether this->RelevantLinkVisited() !=
@@ -217,17 +185,17 @@ ComputedStyle::CalcStyleDifference(Compu
   // things that can depend on :visited) for the properties on which we
   // call GetVisitedDependentColor.
   ComputedStyle* thisVis = GetStyleIfVisited();
   ComputedStyle* otherVis = aNewContext->GetStyleIfVisited();
   if (!thisVis != !otherVis) {
     // One style has a style-if-visited and the other doesn't.
     // Presume a difference.
 #define STYLE_STRUCT(name_, fields_) \
-    *aEqualStructs &= ~NS_STYLE_INHERIT_BIT(name_);
+    *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
 #include "nsCSSVisitedDependentPropList.h"
 #undef STYLE_STRUCT
     hint |= nsChangeHint_RepaintFrame;
   } else if (thisVis) {
     // Both styles have a style-if-visited.
     bool change = false;
 
     // NB: Calling Peek on |this|, not |thisVis|, since callers may look
@@ -238,23 +206,24 @@ ComputedStyle::CalcStyleDifference(Compu
 #define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_
 #define STYLE_STRUCT(name_, fields_)                                    \
     if (PEEK(name_)) {                                                  \
       const nsStyle##name_* thisVisStruct =                             \
         thisVis->ThreadsafeStyle##name_();                              \
       const nsStyle##name_* otherVisStruct =                            \
         otherVis->ThreadsafeStyle##name_();                             \
       if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) {     \
-        *aEqualStructs &= ~NS_STYLE_INHERIT_BIT(name_);                 \
+        *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);                     \
         change = true;                                                  \
       }                                                                 \
     }
 #include "nsCSSVisitedDependentPropList.h"
 #undef STYLE_STRUCT
 #undef STYLE_FIELD
+#undef STYLE_STRUCT_BIT
 
     if (change) {
       hint |= nsChangeHint_RepaintFrame;
     }
   }
 
   if (hint & nsChangeHint_UpdateContainingBlock) {
     // If a struct returned nsChangeHint_UpdateContainingBlock, that
@@ -265,22 +234,22 @@ ComputedStyle::CalcStyleDifference(Compu
 
     // This depends on data in nsStyleDisplay, nsStyleEffects and
     // nsStyleSVGReset, so we do it here.
 
     // Note that it's perhaps good for this test to be last because it
     // doesn't use Peek* functions to get the structs on the old
     // context.  But this isn't a big concern because these struct
     // getters should be called during frame construction anyway.
-    if (ThreadsafeStyleDisplay()->IsAbsPosContainingBlockForAppropriateFrame(this) ==
+    if (ThreadsafeStyleDisplay()->IsAbsPosContainingBlockForAppropriateFrame(*this) ==
         aNewContext->ThreadsafeStyleDisplay()->
-          IsAbsPosContainingBlockForAppropriateFrame(aNewContext) &&
-        ThreadsafeStyleDisplay()->IsFixedPosContainingBlockForAppropriateFrame(this) ==
+          IsAbsPosContainingBlockForAppropriateFrame(*aNewContext) &&
+        ThreadsafeStyleDisplay()->IsFixedPosContainingBlockForAppropriateFrame(*this) ==
         aNewContext->ThreadsafeStyleDisplay()->
-          IsFixedPosContainingBlockForAppropriateFrame(aNewContext)) {
+          IsFixedPosContainingBlockForAppropriateFrame(*aNewContext)) {
       // While some styles that cause the frame to be a containing block
       // has changed, the overall result hasn't.
       hint &= ~nsChangeHint_UpdateContainingBlock;
     }
   }
 
   MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
              "Added a new hint without bumping AllHints?");
@@ -394,42 +363,38 @@ ComputedStyle::CombineVisitedColors(nsco
   nscolor colorColor = aColors[set.colorIndex];
   nscolor alphaColor = aColors[set.alphaIndex];
   return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
                  NS_GET_B(colorColor), NS_GET_A(alphaColor));
 }
 
 #ifdef DEBUG
 /* static */ const char*
-ComputedStyle::StructName(nsStyleStructID aSID)
+ComputedStyle::StructName(StyleStructID aSID)
 {
   switch (aSID) {
 #define STYLE_STRUCT(name_)     \
-    case eStyleStruct_##name_:  \
+    case StyleStructID::name_:  \
       return #name_;
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
     default:
       return "Unknown";
   }
 }
 
-/* static */ bool
-ComputedStyle::LookupStruct(const nsACString& aName, nsStyleStructID& aResult)
+/* static */ Maybe<StyleStructID>
+ComputedStyle::LookupStruct(const nsACString& aName)
 {
-  if (false)
-    ;
 #define STYLE_STRUCT(name_)             \
-  else if (aName.EqualsLiteral(#name_)) \
-    aResult = eStyleStruct_##name_;
+  if (aName.EqualsLiteral(#name_)) \
+    return Some(StyleStructID::name_);
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
-  else
-    return false;
-  return true;
+  return Nothing();
 }
 #endif // DEBUG
 
 ComputedStyle*
 ComputedStyle::GetCachedLazyPseudoStyle(CSSPseudoElementType aPseudo) const
 {
   MOZ_ASSERT(aPseudo != CSSPseudoElementType::NotPseudo &&
              aPseudo != CSSPseudoElementType::InheritingAnonBox &&
--- a/layout/style/ComputedStyle.h
+++ b/layout/style/ComputedStyle.h
@@ -16,63 +16,18 @@
 #include "mozilla/ServoComputedData.h"
 #include "mozilla/ServoTypes.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/StyleComplexColor.h"
 #include "mozilla/CachedInheritingStyles.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 
-// Includes nsStyleStructID.
 #include "nsStyleStructFwd.h"
 
-// Bits for each struct.
-// NS_STYLE_INHERIT_BIT defined in nsStyleStructFwd.h
-#define NS_STYLE_INHERIT_MASK              0x0007fffff
-
-// Bits for inherited structs.
-#define NS_STYLE_INHERITED_STRUCT_MASK \
-  ((nsStyleStructID_size_t(1) << nsStyleStructID_Inherited_Count) - 1)
-// Bits for reset structs.
-#define NS_STYLE_RESET_STRUCT_MASK \
-  (((nsStyleStructID_size_t(1) << nsStyleStructID_Reset_Count) - 1) \
-   << nsStyleStructID_Inherited_Count)
-
-// Additional bits for ComputedStyle's mBits:
-// (free bit)                              0x000800000
-// See ComputedStyle::HasTextDecorationLines
-#define NS_STYLE_HAS_TEXT_DECORATION_LINES 0x001000000
-// See ComputedStyle::HasPseudoElementData.
-#define NS_STYLE_HAS_PSEUDO_ELEMENT_DATA   0x002000000
-// See ComputedStyle::RelevantLinkIsVisited
-#define NS_STYLE_RELEVANT_LINK_VISITED     0x004000000
-// See ComputedStyle::IsStyleIfVisited
-#define NS_STYLE_IS_STYLE_IF_VISITED       0x008000000
-// See ComputedStyle::HasChildThatUsesGrandancestorStyle
-#define NS_STYLE_CHILD_USES_GRANDANCESTOR_STYLE 0x010000000
-// See ComputedStyle::IsShared
-#define NS_STYLE_IS_SHARED                 0x020000000
-// See ComputedStyle::AssertStructsNotUsedElsewhere
-// (This bit is currently only used in #ifdef DEBUG code.)
-#define NS_STYLE_IS_GOING_AWAY             0x040000000
-// See ComputedStyle::ShouldSuppressLineBreak
-#define NS_STYLE_SUPPRESS_LINEBREAK        0x080000000
-// See ComputedStyle::IsInDisplayNoneSubtree
-#define NS_STYLE_IN_DISPLAY_NONE_SUBTREE   0x100000000
-// See ComputedStyle::FindChildWithRules
-#define NS_STYLE_INELIGIBLE_FOR_SHARING    0x200000000
-// See ComputedStyle::HasChildThatUsesResetStyle
-#define NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE 0x400000000
-// See ComputedStyle::IsTextCombined
-#define NS_STYLE_IS_TEXT_COMBINED          0x800000000
-// Whether a ComputedStyle is a Gecko or Servo context
-#define NS_STYLE_CONTEXT_IS_GECKO          0x1000000000
-// See ComputedStyle::GetPseudoEnum
-#define NS_STYLE_CONTEXT_TYPE_SHIFT        37
-
 class nsAtom;
 enum nsChangeHint : uint32_t;
 class nsIPresShell;
 class nsPresContext;
 class nsWindowSizes;
 
 #define STYLE_STRUCT(name_) struct nsStyle##name_;
 #include "nsStyleStructList.h"
@@ -103,18 +58,30 @@ class ComputedStyle;
  * ComputedStyles are reference counted. References are generally held by:
  *  1. the |nsIFrame|s that are using the ComputedStyle and
  *  2. any *child* ComputedStyle (this might be the reverse of
  *     expectation, but it makes sense in this case)
  *
  * FIXME(emilio): This comment is somewhat outdated now.
  */
 
+enum class ComputedStyleBit : uint8_t
+{
+  HasTextDecorationLines = 1 << 0,
+  HasPseudoElementData = 1 << 1,
+  SuppressLineBreak = 1 << 2,
+  IsTextCombined = 1 << 3,
+  RelevantLinkVisited = 1 << 4,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ComputedStyleBit)
+
 class ComputedStyle
 {
+  using Bit = ComputedStyleBit;
 public:
   ComputedStyle(nsPresContext* aPresContext,
                 nsAtom* aPseudoTag,
                 CSSPseudoElementType aPseudoType,
                 ServoComputedDataForgotten aComputedValues);
 
   // FIXME(emilio, bug 548397): This will need to go away. Don't add new callers
   // of this methed.
@@ -154,19 +121,19 @@ public:
 
   bool IsLazilyCascadedPseudoElement() const
   {
     return IsPseudoElement() &&
            !nsCSSPseudoElements::IsEagerlyCascadedInServo(GetPseudoType());
   }
 
   nsAtom* GetPseudo() const { return mPseudoTag; }
-  mozilla::CSSPseudoElementType GetPseudoType() const {
-    return static_cast<mozilla::CSSPseudoElementType>(
-             mBits >> NS_STYLE_CONTEXT_TYPE_SHIFT);
+  mozilla::CSSPseudoElementType GetPseudoType() const
+  {
+    return mPseudoType;
   }
 
   bool IsInheritingAnonBox() const {
     return GetPseudoType() == mozilla::CSSPseudoElementType::InheritingAnonBox;
   }
 
   bool IsNonInheritingAnonBox() const {
     return GetPseudoType() == mozilla::CSSPseudoElementType::NonInheritingAnonBox;
@@ -181,90 +148,59 @@ public:
   }
 
   bool IsAnonBox() const {
     return IsInheritingAnonBox() || IsNonInheritingAnonBox();
   }
 
   bool IsPseudoElement() const { return mPseudoTag && !IsAnonBox(); }
 
-
   // Does this ComputedStyle or any of its ancestors have text
   // decoration lines?
   // Differs from nsStyleTextReset::HasTextDecorationLines, which tests
   // only the data for a single context.
   bool HasTextDecorationLines() const
-    { return !!(mBits & NS_STYLE_HAS_TEXT_DECORATION_LINES); }
+  {
+    return bool(mBits & Bit::HasTextDecorationLines);
+  }
 
   // Whether any line break inside should be suppressed? If this returns
   // true, the line should not be broken inside, which means inlines act
   // as if nowrap is set, <br> is suppressed, and blocks are inlinized.
   // This bit is propogated to all children of line partitipants. It is
   // currently used by ruby to make its content frames unbreakable.
   // NOTE: for nsTextFrame, use nsTextFrame::ShouldSuppressLineBreak()
   // instead of this method.
   bool ShouldSuppressLineBreak() const
-    { return !!(mBits & NS_STYLE_SUPPRESS_LINEBREAK); }
-
-  // Does this ComputedStyle or any of its ancestors have display:none set?
-  bool IsInDisplayNoneSubtree() const
-    { return !!(mBits & NS_STYLE_IN_DISPLAY_NONE_SUBTREE); }
+  {
+    return bool(mBits & Bit::SuppressLineBreak);
+  }
 
   // Is this horizontal-in-vertical (tate-chu-yoko) text? This flag is
   // only set on ComputedStyles whose pseudo is nsCSSAnonBoxes::mozText.
   bool IsTextCombined() const
-    { return !!(mBits & NS_STYLE_IS_TEXT_COMBINED); }
+  {
+    return bool(mBits & Bit::IsTextCombined);
+  }
 
   // Does this ComputedStyle represent the style for a pseudo-element or
   // inherit data from such a ComputedStyle?  Whether this returns true
   // is equivalent to whether it or any of its ancestors returns
   // non-null for IsPseudoElement().
   bool HasPseudoElementData() const
-    { return !!(mBits & NS_STYLE_HAS_PSEUDO_ELEMENT_DATA); }
-
-  bool HasChildThatUsesResetStyle() const
-    { return mBits & NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE; }
+  {
+    return bool(mBits & Bit::HasPseudoElementData);
+  }
 
   // Is the only link whose visitedness is allowed to influence the
   // style of the node this ComputedStyle is for (which is that element
   // or its nearest ancestor that is a link) visited?
   bool RelevantLinkVisited() const
-    { return !!(mBits & NS_STYLE_RELEVANT_LINK_VISITED); }
-
-  // Is this a ComputedStyle for a link?
-  inline bool IsLinkContext() const;
-
-  // Is this ComputedStyle the GetStyleIfVisited() for some other style
-  // context?
-  bool IsStyleIfVisited() const
-    { return !!(mBits & NS_STYLE_IS_STYLE_IF_VISITED); }
-
-  // Tells this ComputedStyle that it should return true from
-  // IsStyleIfVisited.
-  void SetIsStyleIfVisited()
-    { mBits |= NS_STYLE_IS_STYLE_IF_VISITED; }
-
-  // Does any descendant of this ComputedStyle have any style values
-  // that were computed based on this ComputedStyle's ancestors?
-  bool HasChildThatUsesGrandancestorStyle() const
-    { return !!(mBits & NS_STYLE_CHILD_USES_GRANDANCESTOR_STYLE); }
-
-  // Is this ComputedStyle shared with a sibling or cousin?
-  // (See nsStyleSet::GetContext.)
-  bool IsShared() const
-    { return !!(mBits & NS_STYLE_IS_SHARED); }
-
-  /**
-   * Returns whether this ComputedStyle has cached style data for a
-   * given style struct and it does NOT own that struct.  This can
-   * happen because it was inherited from the parent ComputedStyle, or
-   * because it was stored conditionally on the rule node.
-   */
-  bool HasCachedDependentStyleData(nsStyleStructID aSID) {
-    return mBits & GetBitForSID(aSID);
+  {
+    return bool(mBits & Bit::RelevantLinkVisited);
   }
 
   ComputedStyle* GetCachedInheritingAnonBoxStyle(nsAtom* aAnonBox) const
   {
     MOZ_ASSERT(nsCSSAnonBoxes::IsInheritingAnonBox(aAnonBox));
     return mCachedInheritingStyles.Lookup(aAnonBox);
   }
 
@@ -294,18 +230,16 @@ public:
     // support state (like :hover). So we just avoid sharing in those cases.
     if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aStyle->GetPseudoType())) {
       return;
     }
 
     mCachedInheritingStyles.Insert(aStyle);
   }
 
-  void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; }
-
   /**
    * Define typesafe getter functions for each style struct by
    * preprocessing the list of style structs.  These functions are the
    * preferred way to get style data.  The macro creates functions like:
    *   const nsStyleBorder* StyleBorder();
    *   const nsStyleColor* StyleColor();
    */
   #define STYLE_STRUCT(name_) \
@@ -372,51 +306,58 @@ public:
 
   /**
    * aColors should be a two element array of nscolor in which the first
    * color is the unvisited color and the second is the visited color.
    *
    * Combine the R, G, and B components of whichever of aColors should
    * be used based on aLinkIsVisited with the A component of aColors[0].
    */
-  static nscolor CombineVisitedColors(nscolor *aColors,
-                                      bool aLinkIsVisited);
+  static nscolor CombineVisitedColors(nscolor* aColors, bool aLinkIsVisited);
 
   /**
    * Start the background image loads for this ComputedStyle.
    */
   inline void StartBackgroundImageLoads();
 
-  static uint32_t GetBitForSID(const nsStyleStructID aSID) { return 1 << aSID; }
-
 #ifdef DEBUG
   void List(FILE* out, int32_t aIndent);
-  static const char* StructName(nsStyleStructID aSID);
-  static bool LookupStruct(const nsACString& aName, nsStyleStructID& aResult);
+  static const char* StructName(StyleStructID aSID);
+  static Maybe<StyleStructID> LookupStruct(const nsACString& aName);
 #endif
 
   /**
    * Makes this context match |aOther| in terms of which style structs have
    * been resolved.
    */
   inline void ResolveSameStructsAs(const ComputedStyle* aOther);
 
   // The |aCVsSize| outparam on this function is where the actual CVs size
   // value is added. It's done that way because the callers know which value
   // the size should be added to.
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const;
 
 protected:
+  bool HasRequestedStruct(StyleStructID aID) const
+  {
+    return mRequestedStructs & StyleStructConstants::BitFor(aID);
+  }
+
+  void SetRequestedStruct(StyleStructID aID)
+  {
+    mRequestedStructs |= StyleStructConstants::BitFor(aID);
+  }
+
   // Needs to be friend so that it can call the destructor without making it
   // public.
   friend void ::Gecko_ComputedStyle_Destroy(ComputedStyle*);
 
   ~ComputedStyle() = default;
 
-  nsPresContext* mPresContext;
+  nsPresContext* const mPresContext;
 
   ServoComputedData mSource;
 
   // A cache of anonymous box and lazy pseudo styles inheriting from this style.
   CachedInheritingStyles mCachedInheritingStyles;
 
   // Helper functions for GetStyle* and PeekStyle*
   #define STYLE_STRUCT_INHERITED(name_)         \
@@ -427,21 +368,19 @@ protected:
     const nsStyle##name_ * DoGetStyle##name_();
 
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT_RESET
   #undef STYLE_STRUCT_INHERITED
 
   // If this ComputedStyle is for a pseudo-element or anonymous box,
   // the relevant atom.
-  RefPtr<nsAtom> mPseudoTag;
+  const RefPtr<nsAtom> mPseudoTag;
 
-  // mBits stores a number of things:
-  //  - It records (using the style struct bits) which structs have
-  //    been requested on this ComputedStyle.
-  //  - It also stores the additional bits listed at the top of
-  //    nsStyleStruct.h.
-  uint64_t                mBits;
+  // A bitfield with the structs that have been requested so far.
+  uint32_t mRequestedStructs = 0;
+  const Bit mBits;
+  const CSSPseudoElementType mPseudoType;
 };
 
 } // namespace mozilla
 
 #endif
--- a/layout/style/ComputedStyleInlines.h
+++ b/layout/style/ComputedStyleInlines.h
@@ -34,81 +34,60 @@ ComputedStyle::ThreadsafeStyle##name_() 
 }                                                           \
 const nsStyle##name_ * ComputedStyle::PeekStyle##name_() {  \
   return DoGetStyle##name_<false>();                        \
 }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 // Helper functions for GetStyle* and PeekStyle*
-#define STYLE_STRUCT_INHERITED(name_)                                       \
+#define STYLE_STRUCT(name_)                                                 \
 template<bool aComputeData>                                                 \
-const nsStyle##name_ * ComputedStyle::DoGetStyle##name_() {                 \
-  const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_));        \
+const nsStyle##name_* ComputedStyle::DoGetStyle##name_() {                  \
+  const auto kStructID = StyleStructID::name_;                              \
+  const bool needToCompute = !HasRequestedStruct(kStructID);                \
   if (!aComputeData && needToCompute) {                                     \
     return nullptr;                                                         \
   }                                                                         \
-                                                                            \
   const nsStyle##name_* data = ComputedData()->GetStyle##name_();           \
-                                                                            \
   /* perform any remaining main thread work on the struct */                \
   if (needToCompute) {                                                      \
     MOZ_ASSERT(NS_IsMainThread());                                          \
     MOZ_ASSERT(!mozilla::IsInServoTraversal());                             \
     const_cast<nsStyle##name_*>(data)->FinishStyle(mPresContext, nullptr);  \
     /* the ComputedStyle owns the struct */                                 \
-    AddStyleBit(NS_STYLE_INHERIT_BIT(name_));                               \
-  }                                                                         \
-  return data;                                                              \
-}
-
-#define STYLE_STRUCT_RESET(name_)                                           \
-template<bool aComputeData>                                                 \
-const nsStyle##name_ * ComputedStyle::DoGetStyle##name_() {                 \
-  const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_));        \
-  if (!aComputeData && needToCompute) {                                     \
-    return nullptr;                                                         \
-  }                                                                         \
-  const nsStyle##name_* data = ComputedData()->GetStyle##name_();           \
-  /* perform any remaining main thread work on the struct */                \
-  if (needToCompute) {                                                      \
-    const_cast<nsStyle##name_*>(data)->FinishStyle(mPresContext, nullptr);  \
-    /* the ComputedStyle owns the struct */                                 \
-    AddStyleBit(NS_STYLE_INHERIT_BIT(name_));                               \
+    SetRequestedStruct(kStructID);                                          \
   }                                                                         \
   return data;                                                              \
 }
 #include "nsStyleStructList.h"
-#undef STYLE_STRUCT_RESET
-#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT
 
 void
 ComputedStyle::StartBackgroundImageLoads()
 {
   // Just get our background struct; that should do the trick
   StyleBackground();
 }
 
 void
 ComputedStyle::ResolveSameStructsAs(const ComputedStyle* aOther)
 {
   // Only resolve structs that are not already resolved in this struct.
-  uint64_t ourBits = mBits & NS_STYLE_INHERIT_MASK;
-  uint64_t otherBits = aOther->mBits & NS_STYLE_INHERIT_MASK;
-  uint64_t newBits = otherBits & ~ourBits & NS_STYLE_INHERIT_MASK;
+  auto newBits = aOther->mRequestedStructs & ~mRequestedStructs;
 
 #define STYLE_STRUCT(name_)                                                    \
   if (nsStyle##name_::kHasFinishStyle &&                                       \
-      (newBits & NS_STYLE_INHERIT_BIT(name_))) {                               \
+      (newBits & StyleStructConstants::BitFor(StyleStructID::name_))) {        \
     const nsStyle##name_* data = ComputedData()->GetStyle##name_();            \
     const nsStyle##name_* oldData = aOther->ComputedData()->GetStyle##name_(); \
     const_cast<nsStyle##name_*>(data)->FinishStyle(mPresContext, oldData);     \
   }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
-  mBits |= newBits;
+  mRequestedStructs |= newBits;
 }
 
 } // namespace mozilla
 
 
 #endif // ComputedStyleInlines_h
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -688,17 +688,17 @@ SERVO_BINDING_FUNC(Servo_ComputedValues_
                    ComputedStyleBorrowedOrNull parent_style_or_null,
                    nsAtom* pseudo_tag,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_ComputedValues_Inherit, ComputedStyleStrong,
                    RawServoStyleSetBorrowed set,
                    nsAtom* pseudo_tag,
                    ComputedStyleBorrowedOrNull parent_style,
                    mozilla::InheritTarget target)
-SERVO_BINDING_FUNC(Servo_ComputedValues_GetStyleBits, uint64_t,
+SERVO_BINDING_FUNC(Servo_ComputedValues_GetStyleBits, uint8_t,
                    ComputedStyleBorrowed values)
 SERVO_BINDING_FUNC(Servo_ComputedValues_EqualCustomProperties, bool,
                    ServoComputedDataBorrowed first,
                    ServoComputedDataBorrowed second)
 // Gets the source style rules for the computed values. This returns
 // the result via rules, which would include a list of unowned pointers
 // to RawServoStyleRule.
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetStyleRuleList, void,
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -345,28 +345,25 @@ Gecko_CalcStyleDifference(ComputedStyleB
                           ComputedStyleBorrowed aNewStyle,
                           bool* aAnyStyleStructChanged,
                           bool* aOnlyResetStructsChanged)
 {
   MOZ_ASSERT(aOldStyle);
   MOZ_ASSERT(aNewStyle);
 
   uint32_t equalStructs;
-  nsChangeHint result = const_cast<mozilla::ComputedStyle*>(aOldStyle)->
-    CalcStyleDifference(
-      const_cast<mozilla::ComputedStyle*>(aNewStyle),
-      &equalStructs);
-
-  *aAnyStyleStructChanged = equalStructs != NS_STYLE_INHERIT_MASK;
-
-  const uint32_t kInheritStructsMask =
-    NS_STYLE_INHERIT_MASK & ~NS_STYLE_RESET_STRUCT_MASK;
-
+  nsChangeHint result = const_cast<ComputedStyle*>(aOldStyle)->
+    CalcStyleDifference(const_cast<ComputedStyle*>(aNewStyle), &equalStructs);
+
+  *aAnyStyleStructChanged =
+    equalStructs != StyleStructConstants::kAllStructsMask;
+
+  const auto kInheritedStructsMask = StyleStructConstants::kInheritedStructsMask;
   *aOnlyResetStructsChanged =
-    (equalStructs & kInheritStructsMask) == kInheritStructsMask;
+    (equalStructs & kInheritedStructsMask) == kInheritedStructsMask;
 
   return result;
 }
 
 const ServoElementSnapshot*
 Gecko_GetElementSnapshot(const ServoElementSnapshotTable* aTable,
                          const Element* aElement)
 {
@@ -2887,22 +2884,31 @@ Gecko_ContentList_AppendAll(
   aList->SetCapacity(aLength);
 
   for (size_t i = 0; i < aLength; ++i) {
     aList->AppendElement(const_cast<Element*>(aElements[i]));
   }
 }
 
 const nsTArray<Element*>*
-Gecko_GetElementsWithId(const nsIDocument* aDocument, nsAtom* aId)
+Gecko_Document_GetElementsWithId(const nsIDocument* aDoc, nsAtom* aId)
 {
-  MOZ_ASSERT(aDocument);
+  MOZ_ASSERT(aDoc);
   MOZ_ASSERT(aId);
 
-  return aDocument->GetAllElementsForId(nsDependentAtomString(aId));
+  return aDoc->GetAllElementsForId(nsDependentAtomString(aId));
+}
+
+const nsTArray<Element*>*
+Gecko_ShadowRoot_GetElementsWithId(const ShadowRoot* aShadowRoot, nsAtom* aId)
+{
+  MOZ_ASSERT(aShadowRoot);
+  MOZ_ASSERT(aId);
+
+  return aShadowRoot->GetAllElementsForId(nsDependentAtomString(aId));
 }
 
 bool
 Gecko_GetBoolPrefValue(const char* aPrefName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return Preferences::GetBool(aPrefName);
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -703,20 +703,27 @@ void Gecko_ReportUnexpectedCSSError(mozi
                                     uint32_t lineNumber,
                                     uint32_t colNumber);
 
 // DOM APIs.
 void Gecko_ContentList_AppendAll(nsSimpleContentList* aContentList,
                                  const RawGeckoElement** aElements,
                                  size_t aLength);
 
-const nsTArray<mozilla::dom::Element*>* Gecko_GetElementsWithId(
+// FIXME(emilio): These two below should be a single function that takes a
+// `const DocumentOrShadowRoot*`, but that doesn't make MSVC builds happy for a
+// reason I haven't really dug into.
+const nsTArray<mozilla::dom::Element*>* Gecko_Document_GetElementsWithId(
     const nsIDocument* aDocument,
     nsAtom* aId);
 
+const nsTArray<mozilla::dom::Element*>* Gecko_ShadowRoot_GetElementsWithId(
+    const mozilla::dom::ShadowRoot* aDocument,
+    nsAtom* aId);
+
 // Check the value of the given bool preference. The pref name needs to
 // be null-terminated.
 bool Gecko_GetBoolPrefValue(const char* pref_name);
 
 // Returns true if we're currently performing the servo traversal.
 bool Gecko_IsInServoTraversal();
 
 // Returns true if we're currently on the main thread.
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -453,16 +453,17 @@ raw-lines = [
 whitelist-functions = ["Servo_.*", "Gecko_.*"]
 structs-types = [
     "mozilla::css::GridTemplateAreasValue",
     "mozilla::css::ErrorReporter",
     "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
     "mozilla::css::URLValueData",
     "mozilla::dom::CallerType",
+    "mozilla::dom::ShadowRoot",
     "mozilla::AnonymousCounterStyle",
     "mozilla::AtomArray",
     "mozilla::FontStretch",
     "mozilla::FontSlantStyle",
     "mozilla::FontWeight",
     "mozilla::MallocSizeOf",
     "mozilla::OriginFlags",
     "mozilla::UniquePtr",
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -40,20 +40,16 @@
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include <algorithm>
 #include "ImageLoader.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-static_assert((((1 << nsStyleStructID_Length) - 1) &
-               ~(NS_STYLE_INHERIT_MASK)) == 0,
-              "Not enough bits in NS_STYLE_INHERIT_MASK");
-
 /* static */ const int32_t nsStyleGridLine::kMinLine;
 /* static */ const int32_t nsStyleGridLine::kMaxLine;
 
 // We set the size limit of style structs to 504 bytes so that when they
 // are allocated by Servo side with Arc, the total size doesn't exceed
 // 512 bytes, which minimizes allocator slop.
 static constexpr size_t kStyleStructSizeLimit = 504;
 #define STYLE_STRUCT(name_) \
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2484,22 +2484,19 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
    * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const;
 
   /**
    * The same as IsAbsPosContainingBlock, except skipping the tests that
    * are based on the frame rather than the ComputedStyle (thus
    * potentially returning a false positive).
-   *
-   * FIXME(stylo-everywhere): Pretty sure the template can go here.
    */
-  template<class ComputedStyleLike>
   inline bool IsAbsPosContainingBlockForAppropriateFrame(
-                ComputedStyleLike* aComputedStyle) const;
+    mozilla::ComputedStyle&) const;
 
   /**
    * Returns true when the element has the transform property
    * or a related property, and supports CSS transforms.
    * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool HasTransform(const nsIFrame* aContextFrame) const;
 
@@ -2516,46 +2513,38 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
    * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const;
 
   /**
    * The same as IsFixedPosContainingBlock, except skipping the tests that
    * are based on the frame rather than the ComputedStyle (thus
    * potentially returning a false positive).
-   *
-   * FIXME(stylo-everywhere): Pretty sure the template can go here.
    */
-  template<class ComputedStyleLike>
   inline bool IsFixedPosContainingBlockForAppropriateFrame(
-                ComputedStyleLike* aComputedStyle) const;
+    mozilla::ComputedStyle&) const;
 
   /**
    * Returns the final combined transform.
    **/
   already_AddRefed<nsCSSValueSharedList> GetCombinedTransform() const {
     if (mCombinedTransform) {
       return do_AddRef(mCombinedTransform);
     }
 
     // backward compatible to gecko-backed style system.
     return mSpecifiedTransform ? do_AddRef(mSpecifiedTransform) : nullptr;
   }
 
 private:
   // Helpers for above functions, which do some but not all of the tests
   // for them (since transform must be tested separately for each).
-  //
-  // FIXME(stylo-everywhere): Pretty sure the template can go here.
-  template<class ComputedStyleLike>
-  inline bool HasAbsPosContainingBlockStyleInternal(
-                ComputedStyleLike* aComputedStyle) const;
-  template<class ComputedStyleLike>
+  inline bool HasAbsPosContainingBlockStyleInternal() const;
   inline bool HasFixedPosContainingBlockStyleInternal(
-                ComputedStyleLike* aComputedStyle) const;
+    mozilla::ComputedStyle&) const;
   void GenerateCombinedTransform();
 public:
   // Return the 'float' and 'clear' properties, with inline-{start,end} values
   // resolved to {left,right} according to the given writing mode. These are
   // defined in WritingModes.h.
   inline mozilla::StyleFloat PhysicalFloats(mozilla::WritingMode aWM) const;
   inline mozilla::StyleClear PhysicalBreakType(mozilla::WritingMode aWM) const;
 };
--- a/layout/style/nsStyleStructFwd.h
+++ b/layout/style/nsStyleStructFwd.h
@@ -6,58 +6,74 @@
 
 /*
  * Forward declarations to avoid including all of nsStyleStruct.h.
  */
 
 #ifndef nsStyleStructFwd_h_
 #define nsStyleStructFwd_h_
 
-enum nsStyleStructID {
+namespace mozilla {
 
+enum class StyleStructID : uint32_t {
 /*
  * Define the constants eStyleStruct_Font, etc.
  *
  * The C++ standard, section 7.2, guarantees that enums begin with 0 and
  * increase by 1.
  *
- * We separate the IDs of Reset and Inherited structs so that we can use
- * the IDs as indices (offset by nsStyleStructID_*_Start) into arrays of
- * one type or the other.
+ * Note that we rely on the inherited structs being before the rest in
+ * ComputedStyle.
  */
-
-nsStyleStructID_None = -1,
-nsStyleStructID_Inherited_Start = 0,
-// a dummy value so the value after it is the same as ..._Inherited_Start
-nsStyleStructID_DUMMY1 = nsStyleStructID_Inherited_Start - 1,
-
-#define STYLE_STRUCT_INHERITED(name) eStyleStruct_##name,
+#define STYLE_STRUCT_INHERITED(name) name,
 #define STYLE_STRUCT_RESET(name)
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT_INHERITED
 #undef STYLE_STRUCT_RESET
 
-nsStyleStructID_Reset_Start,
-// a dummy value so the value after it is the same as ..._Reset_Start
-nsStyleStructID_DUMMY2 = nsStyleStructID_Reset_Start - 1,
-
-#define STYLE_STRUCT_RESET(name) eStyleStruct_##name,
+#define STYLE_STRUCT_RESET(name) name,
 #define STYLE_STRUCT_INHERITED(name)
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT_INHERITED
 #undef STYLE_STRUCT_RESET
-
-// one past the end; length of 0-based list
-nsStyleStructID_Length,
-
-nsStyleStructID_Inherited_Count =
-  nsStyleStructID_Reset_Start - nsStyleStructID_Inherited_Start,
-nsStyleStructID_Reset_Count =
-  nsStyleStructID_Length - nsStyleStructID_Reset_Start,
-
 };
 
-// A bit corresponding to each struct ID
-#define NS_STYLE_INHERIT_BIT(sid_)        (1 << uint64_t(eStyleStruct_##sid_))
+struct StyleStructConstants
+{
+  static const uint32_t kStyleStructCount =
+#define STYLE_STRUCT_RESET(name) 1 +
+#define STYLE_STRUCT_INHERITED(name) 1 +
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT_RESET
+    0;
+
+  static const uint32_t kInheritedStyleStructCount =
+#define STYLE_STRUCT_RESET(name)
+#define STYLE_STRUCT_INHERITED(name) 1 +
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT_RESET
+    0;
 
-typedef decltype(nsStyleStructID(0) + nsStyleStructID(0)) nsStyleStructID_size_t;
+  static const uint32_t kResetStyleStructCount =
+#define STYLE_STRUCT_RESET(name) 1 +
+#define STYLE_STRUCT_INHERITED(name)
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT_INHERITED
+#undef STYLE_STRUCT_RESET
+    0;
+
+  static_assert(kStyleStructCount <= 32, "Bitmasks must be bigger!");
+
+  static const uint32_t kAllStructsMask = (1 << kStyleStructCount) - 1;
+  static const uint32_t kInheritedStructsMask = (1 << kInheritedStyleStructCount) - 1;
+  static const uint32_t kResetStructsMask = kAllStructsMask & (~kInheritedStructsMask);
+
+  static uint32_t BitFor(StyleStructID aID)
+  {
+    return 1 << static_cast<uint32_t>(aID);
+  }
+};
+
+} // namespace mozilla
 
 #endif /* nsStyleStructFwd_h_ */
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -148,92 +148,87 @@ nsStyleDisplay::HasTransform(const nsIFr
 
 bool
 nsStyleDisplay::HasPerspective(const nsIFrame* aContextFrame) const
 {
   MOZ_ASSERT(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
   return HasPerspectiveStyle() && aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
 }
 
-template<class ComputedStyleLike>
 bool
 nsStyleDisplay::HasFixedPosContainingBlockStyleInternal(
-                  ComputedStyleLike* aComputedStyle) const
+  mozilla::ComputedStyle& aStyle) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
-  NS_ASSERTION(aComputedStyle->ThreadsafeStyleDisplay() == this,
-               "unexpected aComputedStyle");
+  NS_ASSERTION(aStyle.ThreadsafeStyleDisplay() == this, "unexpected aStyle");
 
   if (IsContainPaint()) {
     return true;
   }
 
   if (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) {
     return true;
   }
 
-  return aComputedStyle->ThreadsafeStyleEffects()->HasFilters();
+  return aStyle.ThreadsafeStyleEffects()->HasFilters();
 }
 
-template<class ComputedStyleLike>
 bool
 nsStyleDisplay::IsFixedPosContainingBlockForAppropriateFrame(
-                  ComputedStyleLike* aComputedStyle) const
+  mozilla::ComputedStyle& aStyle) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
-  return HasFixedPosContainingBlockStyleInternal(aComputedStyle) ||
+  return HasFixedPosContainingBlockStyleInternal(aStyle) ||
          HasTransformStyle() || HasPerspectiveStyle();
 }
 
 bool
 nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
-  if (!HasFixedPosContainingBlockStyleInternal(aContextFrame->Style()) &&
+  if (!HasFixedPosContainingBlockStyleInternal(*aContextFrame->Style()) &&
       !HasTransform(aContextFrame) && !HasPerspective(aContextFrame)) {
     return false;
   }
   return !nsSVGUtils::IsInSVGTextSubtree(aContextFrame);
 }
 
-template<class ComputedStyleLike>
 bool
-nsStyleDisplay::HasAbsPosContainingBlockStyleInternal(
-                  ComputedStyleLike* aComputedStyle) const
+nsStyleDisplay::HasAbsPosContainingBlockStyleInternal() const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
-  NS_ASSERTION(aComputedStyle->ThreadsafeStyleDisplay() == this,
-               "unexpected aComputedStyle");
   return IsAbsolutelyPositionedStyle() ||
          IsRelativelyPositionedStyle() ||
          (mWillChangeBitField & NS_STYLE_WILL_CHANGE_ABSPOS_CB);
 }
 
-template<class ComputedStyleLike>
 bool
-nsStyleDisplay::IsAbsPosContainingBlockForAppropriateFrame(ComputedStyleLike* aComputedStyle) const
+nsStyleDisplay::IsAbsPosContainingBlockForAppropriateFrame(
+  mozilla::ComputedStyle& aStyle) const
 {
+  NS_ASSERTION(aStyle.ThreadsafeStyleDisplay() == this, "unexpected aStyle");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
-  return HasAbsPosContainingBlockStyleInternal(aComputedStyle) ||
-         IsFixedPosContainingBlockForAppropriateFrame(aComputedStyle);
+  return HasAbsPosContainingBlockStyleInternal() ||
+         IsFixedPosContainingBlockForAppropriateFrame(aStyle);
 }
 
 bool
 nsStyleDisplay::IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const
 {
+  NS_ASSERTION(aContextFrame->Style()->ThreadsafeStyleDisplay() == this,
+               "unexpected aContextFrame");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
-  mozilla::ComputedStyle* sc = aContextFrame->Style();
-  if (!HasAbsPosContainingBlockStyleInternal(sc) &&
-      !HasFixedPosContainingBlockStyleInternal(sc) &&
+  if (!HasAbsPosContainingBlockStyleInternal() &&
+      !HasFixedPosContainingBlockStyleInternal(*aContextFrame->Style()) &&
       !HasTransform(aContextFrame) &&
       !HasPerspective(aContextFrame)) {
     return false;
   }
   return !nsSVGUtils::IsInSVGTextSubtree(aContextFrame);
 }
 
 bool
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-328d235fc7ee
+3d3e34bb7517
--- a/security/nss/cmd/certutil/certutil.c
+++ b/security/nss/cmd/certutil/certutil.c
@@ -31,16 +31,18 @@
 #include "pk11func.h"
 #include "secasn1.h"
 #include "cert.h"
 #include "cryptohi.h"
 #include "secoid.h"
 #include "certdb.h"
 #include "nss.h"
 #include "certutil.h"
+#include "basicutil.h"
+#include "ssl.h"
 
 #define MIN_KEY_BITS 512
 /* MAX_KEY_BITS should agree with RSA_MAX_MODULUS_BITS in freebl */
 #define MAX_KEY_BITS 8192
 #define DEFAULT_KEY_BITS 2048
 
 #define GEN_BREAK(e) \
     rv = e;          \
@@ -442,27 +444,36 @@ ChangeTrustAttributes(CERTCertDBHandle *
     }
     CERT_DestroyCertificate(cert);
     PORT_Free(trust);
 
     return SECSuccess;
 }
 
 static SECStatus
-DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii)
+DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii,
+          PRBool simpleSelfSigned)
 {
     CERTCertificate *the_cert;
     CERTCertificateList *chain;
     int i, j;
     the_cert = SECU_FindCertByNicknameOrFilename(handle, name,
                                                  ascii, NULL);
     if (!the_cert) {
         SECU_PrintError(progName, "Could not find: %s\n", name);
         return SECFailure;
     }
+    if (simpleSelfSigned &&
+        SECEqual == SECITEM_CompareItem(&the_cert->derIssuer,
+                                        &the_cert->derSubject)) {
+        printf("\"%s\" [%s]\n\n", the_cert->nickname, the_cert->subjectName);
+        CERT_DestroyCertificate(the_cert);
+        return SECSuccess;
+    }
+
     chain = CERT_CertChainFromCert(the_cert, 0, PR_TRUE);
     CERT_DestroyCertificate(the_cert);
     if (!chain) {
         SECU_PrintError(progName, "Could not obtain chain for: %s\n", name);
         return SECFailure;
     }
     for (i = chain->len - 1; i >= 0; i--) {
         CERTCertificate *c;
@@ -1110,17 +1121,19 @@ PrintSyntax()
     FPS "\t\t [-P targetDBPrefix] [--source-prefix sourceDBPrefix]\n");
     FPS "\t\t [-f targetPWfile] [-@ sourcePWFile]\n");
     FPS "\t%s -L [-n cert-name] [-h token-name] [--email email-address]\n",
         progName);
     FPS "\t\t [-X] [-r] [-a] [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n");
     FPS "\t%s --build-flags\n", progName);
     FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n",
         progName);
-    FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n", progName);
+    FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n"
+        "\t\t [--simple-self-signed]\n",
+        progName);
     FPS "\t%s -R -s subj -o cert-request-file [-d certdir] [-P dbprefix] [-p phone] [-a]\n"
         "\t\t [-7 emailAddrs] [-k key-type-or-id] [-h token-name] [-f pwfile]\n"
         "\t\t [-g key-size] [-Z hashAlg]\n",
         progName);
     FPS "\t%s -V -n cert-name -u usage [-b time] [-e] [-a]\n"
         "\t\t[-X] [-d certdir] [-P dbprefix]\n",
         progName);
     FPS "Usage:  %s -W [-d certdir] [-f pwfile] [-@newpwfile]\n",
@@ -1537,16 +1550,18 @@ luO(enum usage_level ul, const char *com
     FPS "%-20s Cert database directory (default is ~/.netscape)\n",
         "   -d certdir");
     FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n",
         "   -a");
     FPS "%-20s Cert & Key database prefix\n",
         "   -P dbprefix");
     FPS "%-20s force the database to open R/W\n",
         "   -X");
+    FPS "%-20s don't search for a chain if issuer name equals subject name\n",
+        "   --simple-self-signed");
     FPS "\n");
 }
 
 static void
 luR(enum usage_level ul, const char *command)
 {
     int is_my_command = (command && 0 == strcmp(command, "R"));
     if (ul == usage_all || !command || is_my_command)
@@ -1555,17 +1570,17 @@ luR(enum usage_level ul, const char *com
     if (ul == usage_selected && !is_my_command)
         return;
     FPS "%-20s Specify the subject name (using RFC1485)\n",
         "   -s subject");
     FPS "%-20s Output the cert request to this file\n",
         "   -o output-req");
     FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
         "   -k key-type-or-id");
-    FPS "%-20s or nickname of the cert key to use \n",
+    FPS "%-20s or nickname of the cert key to use, or key id obtained using -K\n",
         "");
     FPS "%-20s Name of token in which to generate key (default is internal)\n",
         "   -h token-name");
     FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n",
         "   -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
     FPS "%-20s Create a certificate request restricted to RSA-PSS (rsa only)\n",
         "   --pss");
     FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
@@ -2493,16 +2508,17 @@ enum certutilOpts {
     opt_EmptyPassword,
     opt_CertVersion,
     opt_AddSubjectAltNameExt,
     opt_DumpExtensionValue,
     opt_GenericExtensions,
     opt_NewNickname,
     opt_Pss,
     opt_PssSign,
+    opt_SimpleSelfSigned,
     opt_Help
 };
 
 static const secuCommandFlag commands_init[] =
     {
       { /* cmd_AddCert             */ 'A', PR_FALSE, 0, PR_FALSE },
       { /* cmd_CreateNewCert       */ 'C', PR_FALSE, 0, PR_FALSE },
       { /* cmd_DeleteCert          */ 'D', PR_FALSE, 0, PR_FALSE },
@@ -2617,16 +2633,18 @@ static const secuCommandFlag options_ini
       { /* opt_GenericExtensions   */ 0, PR_TRUE, 0, PR_FALSE,
         "extGeneric" },
       { /* opt_NewNickname         */ 0, PR_TRUE, 0, PR_FALSE,
         "new-n" },
       { /* opt_Pss                 */ 0, PR_FALSE, 0, PR_FALSE,
         "pss" },
       { /* opt_PssSign             */ 0, PR_FALSE, 0, PR_FALSE,
         "pss-sign" },
+      { /* opt_SimpleSelfSigned    */ 0, PR_FALSE, 0, PR_FALSE,
+        "simple-self-signed" },
     };
 #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0]))
 
 static secuCommandFlag certutil_commands[NUM_COMMANDS];
 static secuCommandFlag certutil_options[NUM_OPTIONS];
 
 static const secuCommand certutil = {
     NUM_COMMANDS,
@@ -3117,16 +3135,18 @@ certutil_main(int argc, char **argv, PRB
         }
         if (rv != SECSuccess) {
             SECU_PrintPRandOSError(progName);
             rv = SECFailure;
             goto shutdown;
         }
         initialized = PR_TRUE;
         SECU_RegisterDynamicOids();
+        /* Ensure the SSL error code table has been registered. Bug 1460284. */
+        SSL_OptionSetDefault(-1, 0);
     }
     certHandle = CERT_GetDefaultCertDB();
 
     if (certutil.commands[cmd_Version].activated) {
         printf("Certificate database content version: command not implemented.\n");
     }
 
     if (PL_strcmp(slotname, "internal") == 0)
@@ -3343,17 +3363,18 @@ certutil_main(int argc, char **argv, PRB
                            certutil.options[opt_BinaryDER].activated,
                            certutil.options[opt_ASCIIForIO].activated,
                            NULL, outFile, &pwdata);
         }
         goto shutdown;
     }
     if (certutil.commands[cmd_DumpChain].activated) {
         rv = DumpChain(certHandle, name,
-                       certutil.options[opt_ASCIIForIO].activated);
+                       certutil.options[opt_ASCIIForIO].activated,
+                       certutil.options[opt_SimpleSelfSigned].activated);
         goto shutdown;
     }
     /*  XXX needs work  */
     /*  List keys (-K)  */
     if (certutil.commands[cmd_ListKeys].activated) {
         rv = ListKeys(slot, name, 0 /*keyindex*/, keytype, PR_FALSE /*dopriv*/,
                       &pwdata);
         goto shutdown;
@@ -3437,47 +3458,90 @@ certutil_main(int argc, char **argv, PRB
     if (certutil.commands[cmd_CertReq].activated ||
         certutil.commands[cmd_CreateAndAddCert].activated ||
         certutil.commands[cmd_GenKeyPair].activated) {
         if (keysource) {
             CERTCertificate *keycert;
             keycert = CERT_FindCertByNicknameOrEmailAddr(certHandle, keysource);
             if (!keycert) {
                 keycert = PK11_FindCertFromNickname(keysource, NULL);
-                if (!keycert) {
-                    SECU_PrintError(progName,
-                                    "%s is neither a key-type nor a nickname", keysource);
+            }
+
+            if (keycert) {
+                privkey = PK11_FindKeyByDERCert(slot, keycert, &pwdata);
+            } else {
+                PLArenaPool *arena = NULL;
+                SECItem keyidItem = { 0 };
+                char *keysourcePtr = keysource;
+                /* Interpret keysource as CKA_ID */
+                if (PK11_NeedLogin(slot)) {
+                    rv = PK11_Authenticate(slot, PR_TRUE, &pwdata);
+                    if (rv != SECSuccess) {
+                        SECU_PrintError(progName, "could not authenticate to token %s.",
+                                        PK11_GetTokenName(slot));
+                        return SECFailure;
+                    }
+                }
+                if (0 == PL_strncasecmp("0x", keysource, 2)) {
+                    keysourcePtr = keysource + 2; // skip leading "0x"
+                }
+                arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+                if (!arena) {
+                    SECU_PrintError(progName, "unable to allocate arena");
                     return SECFailure;
                 }
+                if (SECU_HexString2SECItem(arena, &keyidItem, keysourcePtr)) {
+                    privkey = PK11_FindKeyByKeyID(slot, &keyidItem, &pwdata);
+                }
+                PORT_FreeArena(arena, PR_FALSE);
             }
-            privkey = PK11_FindKeyByDERCert(slot, keycert, &pwdata);
-            if (privkey)
-                pubkey = CERT_ExtractPublicKey(keycert);
+
+            if (!privkey) {
+                SECU_PrintError(
+                    progName,
+                    "%s is neither a key-type nor a nickname nor a key-id", keysource);
+                return SECFailure;
+            }
+
+            pubkey = SECKEY_ConvertToPublicKey(privkey);
             if (!pubkey) {
                 SECU_PrintError(progName,
                                 "Could not get keys from cert %s", keysource);
+                if (keycert) {
+                    CERT_DestroyCertificate(keycert);
+                }
                 rv = SECFailure;
-                CERT_DestroyCertificate(keycert);
                 goto shutdown;
             }
             keytype = privkey->keyType;
+
             /* On CertReq for renewal if no subject has been
              * specified obtain it from the certificate.
              */
             if (certutil.commands[cmd_CertReq].activated && !subject) {
-                subject = CERT_AsciiToName(keycert->subjectName);
-                if (!subject) {
-                    SECU_PrintError(progName,
-                                    "Could not get subject from certificate %s", keysource);
-                    CERT_DestroyCertificate(keycert);
+                if (keycert) {
+                    subject = CERT_AsciiToName(keycert->subjectName);
+                    if (!subject) {
+                        SECU_PrintError(
+                            progName,
+                            "Could not get subject from certificate %s",
+                            keysource);
+                        CERT_DestroyCertificate(keycert);
+                        rv = SECFailure;
+                        goto shutdown;
+                    }
+                } else {
+                    SECU_PrintError(progName, "Subject name not provided");
                     rv = SECFailure;
                     goto shutdown;
                 }
             }
-            CERT_DestroyCertificate(keycert);
+            if (keycert) {
+                CERT_DestroyCertificate(keycert);
+            }
         } else {
             privkey =
                 CERTUTIL_GeneratePrivateKey(keytype, slot, keysize,
                                             publicExponent,
                                             certutil.options[opt_NoiseFile].arg,
                                             &pubkey,
                                             certutil.options[opt_PQGFile].arg,
                                             keyAttrFlags,
@@ -3530,16 +3594,24 @@ certutil_main(int argc, char **argv, PRB
         if (keytype != rsaKey) {
             PR_fprintf(PR_STDERR,
                        "%s -%c: --pss-sign only works with RSA keys.\n",
                        progName, commandToRun);
             return 255;
         }
     }
 
+    if (certutil.options[opt_SimpleSelfSigned].activated &&
+        !certutil.commands[cmd_DumpChain].activated) {
+        PR_fprintf(PR_STDERR,
+                   "%s -%c: --simple-self-signed only works with -O.\n",
+                   progName, commandToRun);
+        return 255;
+    }
+
     /* If we need a list of extensions convert the flags into list format */
     if (certutil.commands[cmd_CertReq].activated ||
         certutil.commands[cmd_CreateAndAddCert].activated ||
         certutil.commands[cmd_CreateNewCert].activated) {
         certutil_extns[ext_keyUsage].activated =
             certutil.options[opt_AddCmdKeyUsageExt].activated;
         if (!certutil_extns[ext_keyUsage].activated) {
             certutil_extns[ext_keyUsage].activated =
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/freebl/Makefile
+++ b/security/nss/lib/freebl/Makefile
@@ -530,22 +530,26 @@ ifndef NSS_DISABLE_CHACHAPOLY
     VERIFIED_SRCS += Hacl_Chacha20.c
     VERIFIED_SRCS += Hacl_Chacha20_Vec128.c
 endif # NSS_DISABLE_CHACHAPOLY
 
 ifeq (,$(filter-out i386 x386 x86 x86_64 aarch64,$(CPU_ARCH)))
     # All intel architectures get the 64 bit version
     # With custom uint128 if necessary (faster than generic 32 bit version).
     ECL_SRCS += curve25519_64.c
-    VERIFIED_SRCS += Hacl_Curve25519.c FStar.c
+    VERIFIED_SRCS += Hacl_Curve25519.c
 else
     # All non intel architectures get the generic 32 bit implementation (slow!)
     ECL_SRCS += curve25519_32.c
 endif
 
+ifndef HAVE_INT128_SUPPORT
+    VERIFIED_SRCS += FStar.c
+endif
+
 #######################################################################
 # (5) Execute "global" rules. (OPTIONAL)                              #
 #######################################################################
 
 include $(CORE_DEPTH)/coreconf/rules.mk
 
 #######################################################################
 # (6) Execute "component" rules. (OPTIONAL)                           #
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -272,28 +272,20 @@
         },
       }],
       [ 'cc_use_gnu_ld==1 and OS=="win" and target_arch=="x64"', {
         # mingw x64
         'defines': [
           'MP_IS_LITTLE_ENDIAN',
          ],
       }],
-      [ 'OS!="win"', {
-        'conditions': [
-          [ 'target_arch=="x64" or target_arch=="arm64" or target_arch=="aarch64"', {
-            'defines': [
-              # The Makefile does version-tests on GCC, but we're not doing that here.
-              'HAVE_INT128_SUPPORT',
-            ],
-          }, {
-            'defines': [
-              'KRML_NOUINT128',
-            ],
-          }],
+      [ 'have_int128_support==1', {
+        'defines': [
+          # The Makefile does version-tests on GCC, but we're not doing that here.
+          'HAVE_INT128_SUPPORT',
         ],
       }, {
         'defines': [
           'KRML_NOUINT128',
         ],
       }],
       [ 'OS=="linux"', {
         'defines': [
@@ -345,10 +337,23 @@
             ],
           }],
         ],
       }],
     ],
   },
   'variables': {
     'module': 'nss',
+    'conditions': [
+      [ 'OS!="win"', {
+        'conditions': [
+          [ 'target_arch=="x64" or target_arch=="arm64" or target_arch=="aarch64"', {
+            'have_int128_support%': 1,
+          }, {
+            'have_int128_support%': 0,
+          }],
+        ],
+      }, {
+        'have_int128_support%': 0,
+      }],
+    ],
   }
 }
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -55,17 +55,16 @@
     'rsa.c',
     'rsapkcs.c',
     'seed.c',
     'sha512.c',
     'sha_fast.c',
     'shvfy.c',
     'sysrand.c',
     'tlsprfalg.c',
-    'verified/FStar.c',
   ],
   'conditions': [
     [ 'OS=="linux" or OS=="android"', {
       'conditions': [
         [ 'target_arch=="x64"', {
           'sources': [
             'arcfour-amd64-gas.s',
             'intel-aes.s',
@@ -215,13 +214,16 @@
             'MP_USE_UINT_DIGIT',
             'MP_ASSEMBLY_MULTIPLY',
             'MP_ASSEMBLY_SQUARE',
             'MP_ASSEMBLY_DIV_2DX1D',
           ],
         }],
       ],
     }],
+    [ 'have_int128_support==0', {
+        'sources': [ 'verified/FStar.c' ],
+    }],
   ],
  'ldflags': [
    '-Wl,-Bsymbolic'
  ],
 }
--- a/security/nss/lib/freebl/unix_urandom.c
+++ b/security/nss/lib/freebl/unix_urandom.c
@@ -27,17 +27,17 @@ RNG_SystemInfoForRNG(void)
 size_t
 RNG_SystemRNG(void *dest, size_t maxLen)
 {
     int fd;
     int bytes;
     size_t fileBytes = 0;
     unsigned char *buffer = dest;
 
-#if defined(LINUX) && defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25)))
+#if defined(__OpenBSD__) || (defined(LINUX) && defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25))))
     int result;
 
     while (fileBytes < maxLen) {
         size_t getBytes = maxLen - fileBytes;
         if (getBytes > GETENTROPY_MAX_BYTES) {
             getBytes = GETENTROPY_MAX_BYTES;
         }
         result = getentropy(buffer, getBytes);
--- a/security/nss/tests/cert/cert.sh
+++ b/security/nss/tests/cert/cert.sh
@@ -2470,16 +2470,41 @@ EOF
 
   CU_ACTION="Sign ${CERTNAME}'s Request"
   RETEXPECTED=255
   certu -C -c "TestCA-rsa-pss-sha1" --pss-sign -Z SHA256 -m "${CERTSERIAL}" -v 60 -d "${P_R_CADIR}" \
         -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
   RETEXPECTED=0
 }
 
+cert_test_orphan_key_reuse()
+{
+  CU_ACTION="Create orphan key in serverdir"
+  certu -G -f "${R_PWFILE}" -z ${R_NOISE_FILE} -d ${PROFILEDIR}
+  # Let's get the key ID of the first orphan key.
+  # The output of certutil -K (list keys) isn't well formatted.
+  # The initial <key-number> part may or may not contain white space, which
+  # makes the use of awk to filter the column unreliable.
+  # To fix that, we remove the initial <number> field using sed, then select the
+  # column that contains the key ID.
+  ORPHAN=`${BINDIR}/certutil -d ${PROFILEDIR} -K -f ${R_PWFILE} | \
+          sed 's/^<.*>//g' | grep -w orphan | head -1 | awk '{print $2}'`
+  CU_ACTION="Create cert request for orphan key"
+  certu -R -f "${R_PWFILE}" -k ${ORPHAN} -s "CN=orphan" -d ${PROFILEDIR} \
+        -o ${SERVERDIR}/orphan.req
+  # Ensure that creating the request really works by listing it, and check
+  # if listing was successful.
+  ${BINDIR}/pp -t certificate-request -i ${SERVERDIR}/orphan.req
+  RET=$?
+  if [ "$RET" -ne 0 ]; then
+    html_failed "Listing cert request for orphan key ($RET)"
+    cert_log "ERROR: Listing cert request for orphan key failed $RET"
+  fi
+}
+
 ############################## cert_cleanup ############################
 # local shell function to finish this script (no exit since it might be
 # sourced)
 ########################################################################
 cert_cleanup()
 {
   cert_log "$SCRIPTNAME: finished $SCRIPTNAME"
   html "</TABLE><BR>"
@@ -2489,16 +2514,17 @@ cert_cleanup()
 
 ################## main #################################################
 
 cert_init
 cert_all_CA
 cert_test_implicit_db_init
 cert_extended_ssl
 cert_ssl
+cert_test_orphan_key_reuse
 cert_smime_client
 IS_FIPS_DISABLED=`certutil --build-flags |grep -cw NSS_FIPS_DISABLED`
 if [ $IS_FIPS_DISABLED -ne 0 ]; then
   cert_rsa_exponent_nonfips
 else
   cert_fips
 fi
 cert_eccurves
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -1528,18 +1528,21 @@ impl<'a, ConcreteThreadSafeLayoutNode> P
     //
     // TODO: This should actually consult the table in that section to get the
     // final computed value for 'display'.
     fn process(&mut self, node: &ConcreteThreadSafeLayoutNode) {
         node.insert_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
 
         let style = node.style(self.style_context());
 
-        // Bail out if this node has an ancestor with display: none.
-        if style.is_in_display_none_subtree() {
+        // Bail out if this node has display: none.
+        //
+        // We shouldn't try to construct flows for nodes in this subtree, the
+        // style system guarantees this.
+        if style.get_box().display.is_none() {
             self.set_flow_construction_result(node, ConstructionResult::None);
             return;
         }
 
         // Get the `display` property for this node, and determine whether this node is floated.
         let (display, float, positioning) = match node.type_id() {
             None => {
                 // Pseudo-element.
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -130,24 +130,27 @@ pub trait TDocument: Sized + Copy + Clon
 
     /// Returns whether this document is an HTML document.
     fn is_html_document(&self) -> bool;
 
     /// Returns the quirks mode of this document.
     fn quirks_mode(&self) -> QuirksMode;
 
     /// Get a list of elements with a given ID in this document, sorted by
-    /// document position.
+    /// tree position.
     ///
     /// Can return an error to signal that this list is not available, or also
     /// return an empty slice.
-    fn elements_with_id(
+    fn elements_with_id<'a>(
         &self,
         _id: &Atom,
-    ) -> Result<&[<Self::ConcreteNode as TNode>::ConcreteElement], ()> {
+    ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
+    where
+        Self: 'a,
+    {
         Err(())
     }
 }
 
 /// The `TNode` trait. This is the main generic trait over which the style
 /// system can be implemented.
 pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
     /// The concrete `TElement` type.
@@ -337,16 +340,31 @@ pub trait TShadowRoot: Sized + Copy + Cl
 
     /// Get the shadow host that hosts this ShadowRoot.
     fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement;
 
     /// Get the style data for this ShadowRoot.
     fn style_data<'a>(&self) -> &'a CascadeData
     where
         Self: 'a;
+
+    /// Get a list of elements with a given ID in this shadow root, sorted by
+    /// tree position.
+    ///
+    /// Can return an error to signal that this list is not available, or also
+    /// return an empty slice.
+    fn elements_with_id<'a>(
+        &self,
+        _id: &Atom,
+    ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
+    where
+        Self: 'a,
+    {
+        Err(())
+    }
 }
 
 /// The element trait, the main abstraction the style crate acts over.
 pub trait TElement:
     Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + SelectorsElement<Impl = SelectorImpl>
 {
     /// The concrete node type.
     type ConcreteNode: TNode<ConcreteElement = Self>;
--- a/servo/components/style/dom_apis.rs
+++ b/servo/components/style/dom_apis.rs
@@ -216,93 +216,114 @@ where
 
         Q::append_element(results, element);
         if Q::should_stop_after_first_match() {
             return;
         }
     }
 }
 
-/// Returns whether a given element is descendant of a given `root` node.
+/// Returns whether a given element connected to `root` is descendant of `root`.
 ///
 /// NOTE(emilio): if root == element, this returns false.
-fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
+fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
 where
     E: TElement,
 {
-    if element.as_node().is_in_document() && root == root.owner_doc().as_node() {
+    // Optimize for when the root is a document or a shadow root and the element
+    // is connected to that root.
+    if root.as_document().is_some() {
+        debug_assert!(element.as_node().is_in_document(), "Not connected?");
+        debug_assert_eq!(
+            root,
+            root.owner_doc().as_node(),
+            "Where did this element come from?",
+        );
+        return true;
+    }
+
+    if root.as_shadow_root().is_some() {
+        debug_assert_eq!(
+            element.containing_shadow().unwrap().as_node(),
+            root,
+            "Not connected?"
+        );
         return true;
     }
 
     let mut current = element.as_node().parent_node();
     while let Some(n) = current.take() {
         if n == root {
             return true;
         }
 
         current = n.parent_node();
     }
     false
 }
 
 /// Fast path for iterating over every element with a given id in the document
-/// that `root` is connected to.
-fn fast_connected_elements_with_id<'a, D>(
-    doc: &'a D,
-    root: D::ConcreteNode,
+/// or shadow root that `root` is connected to.
+fn fast_connected_elements_with_id<'a, N>(
+    root: N,
     id: &Atom,
     quirks_mode: QuirksMode,
-) -> Result<&'a [<D::ConcreteNode as TNode>::ConcreteElement], ()>
+) -> Result<&'a [N::ConcreteElement], ()>
 where
-    D: TDocument,
+    N: TNode + 'a,
 {
-    debug_assert_eq!(root.owner_doc().as_node(), doc.as_node());
-
     let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
     if case_sensitivity != CaseSensitivity::CaseSensitive {
         return Err(());
     }
 
-    if !root.is_in_document() {
-        return Err(());
+    if root.is_in_document() {
+        return root.owner_doc().elements_with_id(id);
     }
 
-    doc.elements_with_id(id)
+    if let Some(shadow) = root.as_shadow_root() {
+        return shadow.elements_with_id(id);
+    }
+
+    if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
+        return shadow.elements_with_id(id);
+    }
+
+    Err(())
 }
 
 /// Collects elements with a given id under `root`, that pass `filter`.
 fn collect_elements_with_id<E, Q, F>(
     root: E::ConcreteNode,
     id: &Atom,
     results: &mut Q::Output,
     quirks_mode: QuirksMode,
     mut filter: F,
 ) where
     E: TElement,
     Q: SelectorQuery<E>,
     F: FnMut(E) -> bool,
 {
-    let doc = root.owner_doc();
-    let elements = match fast_connected_elements_with_id(&doc, root, id, quirks_mode) {
+    let elements = match fast_connected_elements_with_id(root, id, quirks_mode) {
         Ok(elements) => elements,
         Err(()) => {
             let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
 
             collect_all_elements::<E, Q, _>(root, results, |e| {
                 e.has_id(id, case_sensitivity) && filter(e)
             });
 
             return;
         },
     };
 
     for element in elements {
         // If the element is not an actual descendant of the root, even though
         // it's connected, we don't really care about it.
-        if !element_is_descendant_of(*element, root) {
+        if !connected_element_is_descendant_of(*element, root) {
             continue;
         }
 
         if !filter(*element) {
             continue;
         }
 
         Q::append_element(results, *element);
@@ -400,19 +421,18 @@ where
                         // root that match the selector list with that id.
                         collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |e| {
                             matching::matches_selector_list(selector_list, &e, matching_context)
                         });
 
                         return Ok(());
                     }
 
-                    let doc = root.owner_doc();
-                    let elements = fast_connected_elements_with_id(&doc, root, id, quirks_mode)?;
-
+                    let elements =
+                        fast_connected_elements_with_id(root, id, quirks_mode)?;
                     if elements.is_empty() {
                         return Ok(());
                     }
 
                     // Results need to be in document order. Let's not bother
                     // reordering or deduplicating nodes, which we would need to
                     // do if one element with the given id were a descendant of
                     // another element with that given id.
@@ -427,17 +447,17 @@ where
                         // that match our selector that are _not_.
                         //
                         // So we can't just walk over the element's descendants
                         // and match the selector against all of them, nor can
                         // we skip looking at this element's descendants.
                         //
                         // Give up on trying to optimize based on this id and
                         // keep walking our selector.
-                        if !element_is_descendant_of(*element, root) {
+                        if !connected_element_is_descendant_of(*element, root) {
                             continue 'component_loop;
                         }
 
                         query_selector_slow::<E, Q>(
                             element.as_node(),
                             selector_list,
                             results,
                             matching_context,
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -82,16 +82,39 @@ use shared_lock::Locked;
 use std::cell::RefCell;
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::mem;
 use std::ptr;
 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
 use stylist::CascadeData;
 
+
+#[inline]
+fn elements_with_id<'a, 'le>(
+    array: *const structs::nsTArray<*mut RawGeckoElement>,
+) -> &'a [GeckoElement<'le>] {
+    unsafe {
+        if array.is_null() {
+            return &[];
+        }
+
+        let elements: &[*mut RawGeckoElement] = &**array;
+
+        // NOTE(emilio): We rely on the in-memory representation of
+        // GeckoElement<'ld> and *mut RawGeckoElement being the same.
+        #[allow(dead_code)]
+        unsafe fn static_assert() {
+            mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
+        }
+
+        mem::transmute(elements)
+    }
+}
+
 /// A simple wrapper over `nsIDocument`.
 #[derive(Clone, Copy)]
 pub struct GeckoDocument<'ld>(pub &'ld structs::nsIDocument);
 
 impl<'ld> TDocument for GeckoDocument<'ld> {
     type ConcreteNode = GeckoNode<'ld>;
 
     #[inline]
@@ -104,34 +127,24 @@ impl<'ld> TDocument for GeckoDocument<'l
         self.0.mType == structs::root::nsIDocument_Type::eHTML
     }
 
     #[inline]
     fn quirks_mode(&self) -> QuirksMode {
         self.0.mCompatMode.into()
     }
 
-    fn elements_with_id(&self, id: &Atom) -> Result<&[GeckoElement<'ld>], ()> {
-        unsafe {
-            let array = bindings::Gecko_GetElementsWithId(self.0, id.as_ptr());
-            if array.is_null() {
-                return Ok(&[]);
-            }
-
-            let elements: &[*mut RawGeckoElement] = &**array;
-
-            // NOTE(emilio): We rely on the in-memory representation of
-            // GeckoElement<'ld> and *mut RawGeckoElement being the same.
-            #[allow(dead_code)]
-            unsafe fn static_assert() {
-                mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
-            }
-
-            Ok(mem::transmute(elements))
-        }
+    #[inline]
+    fn elements_with_id<'a>(&self, id: &Atom) -> Result<&'a [GeckoElement<'ld>], ()>
+    where
+        Self: 'a,
+    {
+        Ok(elements_with_id(unsafe {
+            bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())
+        }))
     }
 }
 
 /// A simple wrapper over `ShadowRoot`.
 #[derive(Clone, Copy)]
 pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
 
 impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
@@ -171,16 +184,26 @@ impl<'lr> TShadowRoot for GeckoShadowRoo
         debug_assert!(!author_styles.stylesheets.dirty());
         debug_assert!(
             author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() ||
                 author_styles.stylesheets.is_empty()
         );
 
         &author_styles.data
     }
+
+    #[inline]
+    fn elements_with_id<'a>(&self, id: &Atom) -> Result<&'a [GeckoElement<'lr>], ()>
+    where
+        Self: 'a,
+    {
+        Ok(elements_with_id(unsafe {
+            bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr())
+        }))
+    }
 }
 
 /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
 ///
 /// Important: We don't currently refcount the DOM, because the wrapper lifetime
 /// magic guarantees that our LayoutFoo references won't outlive the root, and
 /// we don't mutate any of the references on the Gecko side during restyle.
 ///
--- a/servo/components/style/properties/computed_value_flags.rs
+++ b/servo/components/style/properties/computed_value_flags.rs
@@ -36,20 +36,16 @@ bitflags! {
 
         /// A flag used to mark styles under a relevant link that is also
         /// visited.
         const IS_RELEVANT_LINK_VISITED = 1 << 3;
 
         /// A flag used to mark styles which are a pseudo-element or under one.
         const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;
 
-        /// A flag used to mark styles which are in a display: none subtree, or
-        /// under one.
-        const IS_IN_DISPLAY_NONE_SUBTREE = 1 << 5;
-
         /// Whether this style inherits the `display` property.
         ///
         /// This is important because it may affect our optimizations to avoid
         /// computing the style of pseudo-elements, given whether the
         /// pseudo-element is generated depends on the `display` value.
         const INHERITS_DISPLAY = 1 << 6;
 
         /// Whether this style inherits the `content` property.
@@ -72,17 +68,16 @@ bitflags! {
 
 impl ComputedValueFlags {
     /// Flags that are unconditionally propagated to descendants.
     #[inline]
     fn inherited_flags() -> Self {
         ComputedValueFlags::IS_STYLE_IF_VISITED |
         ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
         ComputedValueFlags::CAN_BE_FRAGMENTED |
-        ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE |
         ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
         ComputedValueFlags::HAS_TEXT_DECORATION_LINES
     }
 
     /// Flags that may be propagated to descendants.
     #[inline]
     fn maybe_inherited_flags() -> Self {
         Self::inherited_flags() | ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -36,17 +36,16 @@ use gecko_bindings::bindings::Gecko_SetL
 use gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use gecko_bindings::structs;
 use gecko_bindings::structs::nsCSSPropertyID;
 use gecko_bindings::structs::mozilla::CSSPseudoElementType;
 use gecko_bindings::structs::mozilla::CSSPseudoElementType_InheritingAnonBox;
-use gecko_bindings::structs::root::NS_STYLE_CONTEXT_TYPE_SHIFT;
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use gecko_bindings::sugar::refptr::RefPtr;
 use gecko::values::convert_nscolor_to_rgba;
 use gecko::values::convert_rgba_to_nscolor;
 use gecko::values::GeckoStyleCoordConvertible;
 use gecko::values::round_border_to_device_pixels;
 use logical_geometry::WritingMode;
 use media_queries::Device;
@@ -132,22 +131,22 @@ impl ComputedValues {
         if atom.is_null() {
             return None;
         }
 
         let atom = unsafe { Atom::from_raw(atom) };
         PseudoElement::from_atom(&atom)
     }
 
+    #[inline]
     fn get_pseudo_type(&self) -> CSSPseudoElementType {
-        let bits = (self.0).mBits;
-        let our_type = bits >> NS_STYLE_CONTEXT_TYPE_SHIFT;
-        unsafe { transmute(our_type as u8) }
-    }
-
+        self.0.mPseudoType
+    }
+
+    #[inline]
     pub fn is_anon_box(&self) -> bool {
         let our_type = self.get_pseudo_type();
         return our_type == CSSPseudoElementType_InheritingAnonBox ||
                our_type == CSSPseudoElementType::NonInheritingAnonBox;
     }
 
     /// Returns true if the display property is changed from 'none' to others.
     pub fn is_display_property_changed_from_none(
@@ -2980,17 +2979,16 @@ fn static_assert() {
 
 <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
     #[allow(non_snake_case)]
     pub fn set_animation_${ident}<I>(&mut self, v: I)
         where I: IntoIterator<Item = longhands::animation_${ident}::computed_value::single_value::T>,
               I::IntoIter: ExactSizeIterator + Clone
     {
         use properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
-        use gecko_bindings::structs;
 
         let v = v.into_iter();
 
         debug_assert_ne!(v.len(), 0);
         let input_len = v.len();
         self.gecko.mAnimations.ensure_len(input_len);
 
         self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32;
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1284,35 +1284,31 @@ impl Animate for ComputedTransformOperat
                 ))
             },
             (
                 &TransformOperation::Scale(ref fx, ref fy),
                 &TransformOperation::Scale(ref tx, ref ty),
             ) => {
                 Ok(TransformOperation::Scale(
                     animate_multiplicative_factor(*fx, *tx, procedure)?,
-                    Some(animate_multiplicative_factor(fy.unwrap_or(*fx), ty.unwrap_or(*tx), procedure)?),
+                    Some(animate_multiplicative_factor(
+                        fy.unwrap_or(*fx),
+                        ty.unwrap_or(*tx),
+                        procedure
+                    )?),
                 ))
             },
             (
                 &TransformOperation::Rotate3D(fx, fy, fz, fa),
                 &TransformOperation::Rotate3D(tx, ty, tz, ta),
             ) => {
-                let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
-                let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
-                if (fx, fy, fz) == (tx, ty, tz) {
-                    let ia = fa.animate(&ta, procedure)?;
-                    Ok(TransformOperation::Rotate3D(fx, fy, fz, ia))
-                } else {
-                    let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
-                    let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
-                    Ok(TransformOperation::Matrix3D(
-                        matrix_f.animate(&matrix_t, procedure)?,
-                    ))
-                }
+                let animated = Rotate::Rotate3D(fx, fy, fz, fa)
+                    .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
+                let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
+                Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
             },
             (
                 &TransformOperation::RotateX(fa),
                 &TransformOperation::RotateX(ta),
             ) => {
                 Ok(TransformOperation::RotateX(
                     fa.animate(&ta, procedure)?
                 ))
@@ -1366,16 +1362,19 @@ impl Animate for ComputedTransformOperat
                 ))
             },
             _ if self.is_translate() && other.is_translate() => {
                 self.to_translate_3d().animate(&other.to_translate_3d(), procedure)
             }
             _ if self.is_scale() && other.is_scale() => {
                 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
             }
+            _ if self.is_rotate() && other.is_rotate() => {
+                self.to_rotate_3d().animate(&other.to_rotate_3d(), procedure)
+            }
             _ => Err(()),
         }
     }
 }
 
 fn is_matched_operation(first: &ComputedTransformOperation, second: &ComputedTransformOperation) -> bool {
     match (first, second) {
         (&TransformOperation::Matrix(..),
@@ -1398,50 +1397,22 @@ fn is_matched_operation(first: &Computed
          &TransformOperation::RotateY(..)) |
         (&TransformOperation::RotateZ(..),
          &TransformOperation::RotateZ(..)) |
         (&TransformOperation::Perspective(..),
          &TransformOperation::Perspective(..)) => true,
         // we animate scale and translate operations against each other
         (a, b) if a.is_translate() && b.is_translate() => true,
         (a, b) if a.is_scale() && b.is_scale() => true,
+        (a, b) if a.is_rotate() && b.is_rotate() => true,
         // InterpolateMatrix and AccumulateMatrix are for mismatched transform.
         _ => false
     }
 }
 
-/// <https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined>
-fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> Matrix3D {
-    let half_rad = a.radians() / 2.0;
-    let sc = (half_rad).sin() * (half_rad).cos();
-    let sq = (half_rad).sin().powi(2);
-
-    Matrix3D {
-        m11: 1.0 - 2.0 * (y * y + z * z) * sq,
-        m12: 2.0 * (x * y * sq + z * sc),
-        m13: 2.0 * (x * z * sq - y * sc),
-        m14: 0.0,
-
-        m21: 2.0 * (x * y * sq - z * sc),
-        m22: 1.0 - 2.0 * (x * x + z * z) * sq,
-        m23: 2.0 * (y * z * sq + x * sc),
-        m24: 0.0,
-
-        m31: 2.0 * (x * z * sq + y * sc),
-        m32: 2.0 * (y * z * sq - x * sc),
-        m33: 1.0 - 2.0 * (x * x + y * y) * sq,
-        m34: 0.0,
-
-        m41: 0.0,
-        m42: 0.0,
-        m43: 0.0,
-        m44: 1.0
-    }
-}
-
 /// A 2d matrix for interpolation.
 #[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 #[allow(missing_docs)]
 // FIXME: We use custom derive for ComputeSquaredDistance. However, If possible, we should convert
 // the InnerMatrix2D into types with physical meaning. This custom derive computes the squared
 // distance from each matrix item, and this makes the result different from that in Gecko if we
 // have skew factor in the Matrix3D.
@@ -1527,20 +1498,20 @@ impl Animate for MatrixDecomposed2D {
 
         // Interpolate all values.
         let translate = self.translate.animate(&other.translate, procedure)?;
         let scale = scale.animate(&other.scale, procedure)?;
         let angle = angle.animate(&other_angle, procedure)?;
         let matrix = self.matrix.animate(&other.matrix, procedure)?;
 
         Ok(MatrixDecomposed2D {
-            translate: translate,
-            scale: scale,
-            angle: angle,
-            matrix: matrix,
+            translate,
+            scale,
+            angle,
+            matrix,
         })
     }
 }
 
 impl ComputeSquaredDistance for MatrixDecomposed2D {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         // Use Radian to compute the distance.
@@ -1844,22 +1815,26 @@ impl Quaternion {
     }
 }
 
 impl Animate for Quaternion {
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         use std::f64;
 
         let (this_weight, other_weight) = procedure.weights();
-        debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
-                      other_weight == 1.0f64 || other_weight == 0.0f64,
-                      "animate should only be used for interpolating or accumulating transforms");
+        debug_assert!(
+            (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
+            other_weight == 1.0f64 || other_weight == 0.0f64,
+            "animate should only be used for interpolating or accumulating transforms"
+        );
 
-        // We take a specialized code path for accumulation (where other_weight is 1)
-        if other_weight == 1.0 {
+        // We take a specialized code path for accumulation (where other_weight
+        // is 1).
+        if let Procedure::Accumulate { .. } = procedure {
+            debug_assert_eq!(other_weight, 1.0);
             if this_weight == 0.0 {
                 return Ok(*other);
             }
 
             let clamped_w = self.3.min(1.0).max(-1.0);
 
             // Determine the scale factor.
             let mut theta = clamped_w.acos();
@@ -1880,124 +1855,115 @@ impl Animate for Quaternion {
             return Ok(Quaternion(
                 a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
                 a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
                 a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
                 a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
             ));
         }
 
-        let mut product = self.0 * other.0 +
-                          self.1 * other.1 +
-                          self.2 * other.2 +
-                          self.3 * other.3;
+        // Straight from gfxQuaternion::Slerp.
+        //
+        // Dot product, clamped between -1 and 1.
+        let dot =
+            (self.0 * other.0 +
+             self.1 * other.1 +
+             self.2 * other.2 +
+             self.3 * other.3)
+            .min(1.0).max(-1.0);
 
-        // Clamp product to -1.0 <= product <= 1.0
-        product = product.min(1.0);
-        product = product.max(-1.0);
-
-        if product == 1.0 {
+        if dot == 1.0 {
             return Ok(*self);
         }
 
-        let theta = product.acos();
-        let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
+        let theta = dot.acos();
+        let rsintheta = 1.0 / (1.0 - dot * dot).sqrt();
+
+        let right_weight = (other_weight * theta).sin() * rsintheta;
+        let left_weight = (other_weight * theta).cos() - dot * right_weight;
 
-        let mut a = *self;
-        let mut b = *other;
-        let mut result = Quaternion(0., 0., 0., 0.,);
+        let mut left = *self;
+        let mut right = *other;
         % for i in range(4):
-            a.${i} *= (other_weight * theta).cos() - product * w;
-            b.${i} *= w;
-            result.${i} = a.${i} + b.${i};
+            left.${i} *= left_weight;
+            right.${i} *= right_weight;
         % endfor
 
-        Ok(result)
+        Ok(Quaternion(
+            left.0 + right.0,
+            left.1 + right.1,
+            left.2 + right.2,
+            left.3 + right.3,
+        ))
     }
 }
 
 impl ComputeSquaredDistance for Quaternion {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors,
         // so we can get their angle difference by:
         // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
         let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
         Ok(SquaredDistance::from_sqrt(distance))
     }
 }
 
 /// Decompose a 3D matrix.
-/// <https://drafts.csswg.org/css-transforms/#decomposing-a-3d-matrix>
+/// https://drafts.csswg.org/css-transforms-2/#decomposing-a-3d-matrix
+/// http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c
 fn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {
     // Normalize the matrix.
     if matrix.m44 == 0.0 {
         return Err(());
     }
 
     let scaling_factor = matrix.m44;
+
+    // Normalize the matrix.
     % for i in range(1, 5):
         % for j in range(1, 5):
             matrix.m${i}${j} /= scaling_factor;
         % endfor
     % endfor
 
     // perspective_matrix is used to solve for perspective, but it also provides
     // an easy way to test for singularity of the upper 3x3 component.
     let mut perspective_matrix = matrix;
 
-    % for i in range(1, 4):
-        perspective_matrix.m${i}4 = 0.0;
-    % endfor
+    perspective_matrix.m14 = 0.0;
+    perspective_matrix.m24 = 0.0;
+    perspective_matrix.m34 = 0.0;
     perspective_matrix.m44 = 1.0;
 
     if perspective_matrix.determinant() == 0.0 {
         return Err(());
     }
 
     // First, isolate perspective.
     let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
         let right_hand_side: [f32; 4] = [
             matrix.m14,
             matrix.m24,
             matrix.m34,
             matrix.m44
         ];
 
-        perspective_matrix = perspective_matrix.inverse().unwrap();
-
-        // Transpose perspective_matrix
-        perspective_matrix = Matrix3D {
-            % for i in range(1, 5):
-                % for j in range(1, 5):
-                    m${i}${j}: perspective_matrix.m${j}${i},
-                % endfor
-            % endfor
-        };
-
-        // Multiply right_hand_side with perspective_matrix
-        let mut tmp: [f32; 4] = [0.0; 4];
-        % for i in range(1, 5):
-            tmp[${i - 1}] = (right_hand_side[0] * perspective_matrix.m1${i}) +
-                            (right_hand_side[1] * perspective_matrix.m2${i}) +
-                            (right_hand_side[2] * perspective_matrix.m3${i}) +
-                            (right_hand_side[3] * perspective_matrix.m4${i});
-        % endfor
-
-        Perspective(tmp[0], tmp[1], tmp[2], tmp[3])
+        perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
+        let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);
+        // NOTE(emilio): Even though the reference algorithm clears the
+        // fourth column here (matrix.m14..matrix.m44), they're not used below
+        // so it's not really needed.
+        Perspective(perspective[0], perspective[1], perspective[2], perspective[3])
     } else {
         Perspective(0.0, 0.0, 0.0, 1.0)
     };
 
-    // Next take care of translation
-    let translate = Translate3D (
-        matrix.m41,
-        matrix.m42,
-        matrix.m43
-    );
+    // Next take care of translation (easy).
+    let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);
 
     // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
     let mut row: [[f32; 3]; 3] = [[0.0; 3]; 3];
     % for i in range(1, 4):
         row[${i - 1}][0] = matrix.m${i}1;
         row[${i - 1}][1] = matrix.m${i}2;
         row[${i - 1}][2] = matrix.m${i}3;
     % endfor
@@ -2028,28 +1994,27 @@ fn decompose_3d_matrix(mut matrix: Matri
     scale.2 = row2len;
     row[2] = [row[2][0] / row2len, row[2][1] / row2len, row[2][2] / row2len];
     skew.1 /= scale.2;
     skew.2 /= scale.2;
 
     // At this point, the matrix (in rows) is orthonormal.
     // Check for a coordinate system flip.  If the determinant
     // is -1, then negate the matrix and the scaling factors.
-    let pdum3 = cross(row[1], row[2]);
-    if dot(row[0], pdum3) < 0.0 {
+    if dot(row[0], cross(row[1], row[2])) < 0.0 {
         % for i in range(3):
             scale.${i} *= -1.0;
             row[${i}][0] *= -1.0;
             row[${i}][1] *= -1.0;
             row[${i}][2] *= -1.0;
         % endfor
     }
 
-    // Now, get the rotations out
-    let mut quaternion = Quaternion (
+    // Now, get the rotations out.
+    let mut quaternion = Quaternion(
         0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
         0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
         0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
         0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt()
     );
 
     if row[2][1] > row[1][2] {
         quaternion.0 = -quaternion.0
@@ -2057,21 +2022,21 @@ fn decompose_3d_matrix(mut matrix: Matri
     if row[0][2] > row[2][0] {
         quaternion.1 = -quaternion.1
     }
     if row[1][0] > row[0][1] {
         quaternion.2 = -quaternion.2
     }
 
     Ok(MatrixDecomposed3D {
-        translate: translate,
-        scale: scale,
-        skew: skew,
-        perspective: perspective,
-        quaternion: quaternion
+        translate,
+        scale,
+        skew,
+        perspective,
+        quaternion,
     })
 }
 
 /// Decompose a 2D matrix for Gecko.
 // Use the algorithm from nsStyleTransformMatrix::Decompose2DMatrix() in Gecko.
 #[cfg(feature = "gecko")]
 fn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {
     // The index is column-major, so the equivalent transform matrix is:
@@ -2184,85 +2149,90 @@ impl From<MatrixDecomposed3D> for Matrix
         let mut matrix = Matrix3D::identity();
 
         // Apply perspective
         % for i in range(1, 5):
             matrix.m${i}4 = decomposed.perspective.${i - 1};
         % endfor
 
         // Apply translation
-        % for i in range(1, 4):
+        % for i in range(1, 5):
             % for j in range(1, 4):
                 matrix.m4${i} += decomposed.translate.${j - 1} * matrix.m${j}${i};
             % endfor
         % endfor
 
         // Apply rotation
-        let x = decomposed.quaternion.0;
-        let y = decomposed.quaternion.1;
-        let z = decomposed.quaternion.2;
-        let w = decomposed.quaternion.3;
+        {
+            let x = decomposed.quaternion.0;
+            let y = decomposed.quaternion.1;
+            let z = decomposed.quaternion.2;
+            let w = decomposed.quaternion.3;
 
-        // Construct a composite rotation matrix from the quaternion values
-        // rotationMatrix is a identity 4x4 matrix initially
-        let mut rotation_matrix = Matrix3D::identity();
-        rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
-        rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
-        rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
-        rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
-        rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
-        rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
-        rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
-        rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
-        rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
+            // Construct a composite rotation matrix from the quaternion values
+            // rotationMatrix is a identity 4x4 matrix initially
+            let mut rotation_matrix = Matrix3D::identity();
+            rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
+            rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
+            rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
+            rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
+            rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
+            rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
+            rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
+            rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
+            rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
 
-        matrix = multiply(rotation_matrix, matrix);
+            matrix = multiply(rotation_matrix, matrix);
+        }
 
         // Apply skew
-        let mut temp = Matrix3D::identity();
-        if decomposed.skew.2 != 0.0 {
-            temp.m32 = decomposed.skew.2;
-            matrix = multiply(temp, matrix);
-        }
+        {
+            let mut temp = Matrix3D::identity();
+            if decomposed.skew.2 != 0.0 {
+                temp.m32 = decomposed.skew.2;
+                matrix = multiply(temp, matrix);
+                temp.m32 = 0.0;
+            }
 
-        if decomposed.skew.1 != 0.0 {
-            temp.m32 = 0.0;
-            temp.m31 = decomposed.skew.1;
-            matrix = multiply(temp, matrix);
-        }
+            if decomposed.skew.1 != 0.0 {
+                temp.m31 = decomposed.skew.1;
+                matrix = multiply(temp, matrix);
+                temp.m31 = 0.0;
+            }
 
-        if decomposed.skew.0 != 0.0 {
-            temp.m31 = 0.0;
-            temp.m21 = decomposed.skew.0;
-            matrix = multiply(temp, matrix);
+            if decomposed.skew.0 != 0.0 {
+                temp.m21 = decomposed.skew.0;
+                matrix = multiply(temp, matrix);
+            }
         }
 
         // Apply scale
         % for i in range(1, 4):
-            % for j in range(1, 4):
-                matrix.m${i}${j} *= decomposed.scale.${i - 1};
-            % endfor
+        % for j in range(1, 5):
+        matrix.m${i}${j} *= decomposed.scale.${i - 1};
+        % endfor
         % endfor
 
         matrix
     }
 }
 
 // Multiplication of two 4x4 matrices.
 fn multiply(a: Matrix3D, b: Matrix3D) -> Matrix3D {
-    let mut a_clone = a;
+    Matrix3D {
     % for i in range(1, 5):
-        % for j in range(1, 5):
-            a_clone.m${i}${j} = (a.m${i}1 * b.m1${j}) +
-                               (a.m${i}2 * b.m2${j}) +
-                               (a.m${i}3 * b.m3${j}) +
-                               (a.m${i}4 * b.m4${j});
-        % endfor
+    % for j in range(1, 5):
+        m${i}${j}:
+            a.m${i}1 * b.m1${j} +
+            a.m${i}2 * b.m2${j} +
+            a.m${i}3 * b.m3${j} +
+            a.m${i}4 * b.m4${j},
     % endfor
-    a_clone
+    % endfor
+    }
 }
 
 impl Matrix3D {
     fn is_3d(&self) -> bool {
         self.m13 != 0.0 || self.m14 != 0.0 ||
         self.m23 != 0.0 || self.m24 != 0.0 ||
         self.m31 != 0.0 || self.m32 != 0.0 || self.m33 != 1.0 || self.m34 != 0.0 ||
         self.m43 != 0.0 || self.m44 != 1.0
@@ -2290,21 +2260,32 @@ impl Matrix3D {
         self.m13 * self.m22 * self.m31 * self.m44 +
         self.m12 * self.m23 * self.m31 * self.m44 +
         self.m13 * self.m21 * self.m32 * self.m44 -
         self.m11 * self.m23 * self.m32 * self.m44 -
         self.m12 * self.m21 * self.m33 * self.m44 +
         self.m11 * self.m22 * self.m33 * self.m44
     }
 
-    fn inverse(&self) -> Option<Matrix3D> {
+    /// Transpose a matrix.
+    fn transpose(&self) -> Self {
+        Self {
+            % for i in range(1, 5):
+            % for j in range(1, 5):
+            m${i}${j}: self.m${j}${i},
+            % endfor
+            % endfor
+        }
+    }
+
+    fn inverse(&self) -> Result<Matrix3D, ()> {
         let mut det = self.determinant();
 
         if det == 0.0 {
-            return None;
+            return Err(());
         }
 
         det = 1.0 / det;
         let x = Matrix3D {
             m11: det *
             (self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
              self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
              self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44),
@@ -2365,17 +2346,29 @@ impl Matrix3D {
              self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
              self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43),
             m44: det *
             (self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
              self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
              self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
         };
 
-        Some(x)
+        Ok(x)
+    }
+
+    /// Multiplies `pin * self`.
+    fn pre_mul_point4(&self, pin: &[f32; 4]) -> [f32; 4] {
+        [
+        % for i in range(1, 5):
+            pin[0] * self.m1${i} +
+            pin[1] * self.m2${i} +
+            pin[2] * self.m3${i} +
+            pin[3] * self.m4${i},
+        % endfor
+        ]
     }
 }
 
 /// <https://drafts.csswg.org/css-transforms-2/#propdef-rotate>
 impl ComputedRotate {
     fn resolve(rotate: &ComputedRotate) -> (Number, Number, Number, Angle) {
         // According to the spec:
         // https://drafts.csswg.org/css-transforms-2/#individual-transforms
@@ -2485,16 +2478,18 @@ impl Animate for ComputedScale {
     fn animate(
         &self,
         other: &Self,
         procedure: Procedure,
     ) -> Result<Self, ()> {
         let from = ComputedScale::resolve(self);
         let to = ComputedScale::resolve(other);
 
+        // FIXME(emilio, bug 1464791): why does this do something different than
+        // Scale3D / TransformOperation::Scale3D?
         if procedure == Procedure::Add {
             // scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)
             return Ok(Scale::Scale3D(from.0 * to.0, from.1 * to.1, from.2 * to.2));
         }
 
         Ok(Scale::Scale3D(
             animate_multiplicative_factor(from.0, to.0, procedure)?,
             animate_multiplicative_factor(from.1, to.1, procedure)?,
@@ -2729,16 +2724,19 @@ impl ComputeSquaredDistance for Computed
             // functions (See ToPrimitive in layout/style/StyleAnimationValue.cpp)
             // without falling back to InterpolateMatrix
             _ if self.is_translate() && other.is_translate() => {
                 self.to_translate_3d().compute_squared_distance(&other.to_translate_3d())
             }
             _ if self.is_scale() && other.is_scale() => {
                 self.to_scale_3d().compute_squared_distance(&other.to_scale_3d())
             }
+            _ if self.is_rotate() && other.is_rotate() => {
+                self.to_rotate_3d().compute_squared_distance(&other.to_rotate_3d())
+            }
             _ => Err(()),
         }
     }
 }
 
 impl ComputeSquaredDistance for ComputedTransform {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2532,23 +2532,16 @@ impl ComputedValues {
         self.visited_style.as_ref().map(|s| &**s)
     }
 
     /// Returns the visited rules, if applicable.
     pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
         self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
     }
 
-    /// Returns whether we're in a display: none subtree.
-    pub fn is_in_display_none_subtree(&self) -> bool {
-        use properties::computed_value_flags::ComputedValueFlags;
-
-        self.flags.contains(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE)
-    }
-
     /// Gets a reference to the custom properties map (if one exists).
     pub fn custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>> {
         self.custom_properties.as_ref()
     }
 }
 
 #[cfg(feature = "servo")]
 impl ComputedValues {
--- a/servo/components/style/servo/restyle_damage.rs
+++ b/servo/components/style/servo/restyle_damage.rs
@@ -186,17 +186,18 @@ impl fmt::Display for ServoRestyleDamage
 }
 
 fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage {
     let mut damage = ServoRestyleDamage::empty();
 
     // This should check every CSS property, as enumerated in the fields of
     // http://doc.servo.org/style/properties/struct.ComputedValues.html
 
-    restyle_damage_rebuild_and_reflow!(
+    // This uses short-circuiting boolean OR for its side effects and ignores the result.
+    let _ = restyle_damage_rebuild_and_reflow!(
         old,
         new,
         damage,
         [
             ServoRestyleDamage::REPAINT,
             ServoRestyleDamage::REPOSITION,
             ServoRestyleDamage::STORE_OVERFLOW,
             ServoRestyleDamage::BUBBLE_ISIZES,
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -199,22 +199,16 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
                 .clone_text_decoration_line()
                 .is_empty()
         {
             self.style
                 .flags
                 .insert(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
         }
 
-        if display == Display::None {
-            self.style
-                .flags
-                .insert(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE);
-        }
-
         if self.style.is_pseudo_element() {
             self.style
                 .flags
                 .insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
         }
 
         #[cfg(feature = "servo")]
         {
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -6,21 +6,21 @@
 //!
 //! Some values, notably colors, cannot be interpolated directly with their
 //! computed values and need yet another intermediate representation. This
 //! module's raison d'ĂȘtre is to ultimately contain all these types.
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use smallvec::SmallVec;
-use values::computed::length::CalcLengthOrPercentage;
 use values::computed::Angle as ComputedAngle;
 use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
 use values::computed::MaxLength as ComputedMaxLength;
 use values::computed::MozLength as ComputedMozLength;
+use values::computed::length::CalcLengthOrPercentage;
 use values::computed::url::ComputedUrl;
 
 pub mod color;
 pub mod effects;
 
 /// Animate from one value to another.
 ///
 /// This trait is derivable with `#[derive(Animate)]`. The derived
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -146,16 +146,37 @@ impl TransformOperation {
                     LengthOrPercentage::zero(),
                     LengthOrPercentage::zero(),
                     z.clone(),
                 )
             },
             _ => unreachable!(),
         }
     }
+
+    /// Convert to a Rotate3D.
+    ///
+    /// Must be called on a Rotate function.
+    pub fn to_rotate_3d(&self) -> Self {
+        match *self {
+            generic::TransformOperation::Rotate3D(..) => self.clone(),
+            generic::TransformOperation::RotateZ(ref angle) |
+            generic::TransformOperation::Rotate(ref angle) => {
+                generic::TransformOperation::Rotate3D(0., 0., 1., angle.clone())
+            }
+            generic::TransformOperation::RotateX(ref angle) => {
+                generic::TransformOperation::Rotate3D(1., 0., 0., angle.clone())
+            }
+            generic::TransformOperation::RotateY(ref angle) => {
+                generic::TransformOperation::Rotate3D(0., 1., 0., angle.clone())
+            }
+            _ => unreachable!(),
+        }
+    }
+
     /// Convert to a Scale3D.
     ///
     /// Must be called on a Scale function
     pub fn to_scale_3d(&self) -> Self {
         match *self {
             generic::TransformOperation::Scale3D(..) => self.clone(),
             generic::TransformOperation::Scale(s, None) => {
                 generic::TransformOperation::Scale3D(s, s, 1.)
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -271,16 +271,29 @@ pub enum TransformOperation<Angle, Numbe
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
          ToComputedValue, ToCss)]
 /// A value of the `transform` property
 pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
 
 impl<Angle, Number, Length, Integer, LengthOrPercentage>
     TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage>
 {
+    /// Check if it is any rotate function.
+    pub fn is_rotate(&self) -> bool {
+        use self::TransformOperation::*;
+        matches!(
+            *self,
+            Rotate(..) |
+            Rotate3D(..) |
+            RotateX(..) |
+            RotateY(..) |
+            RotateZ(..)
+        )
+    }
+
     /// Check if it is any translate function
     pub fn is_translate(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
             Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
                 true
             },
             _ => false,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -3092,38 +3092,35 @@ pub unsafe extern "C" fn Servo_ComputedV
     if for_text {
         StyleAdjuster::new(&mut style).adjust_for_text();
     }
 
     style.build().into()
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_ComputedValues_GetStyleBits(values: ComputedStyleBorrowed) -> u64 {
+pub extern "C" fn Servo_ComputedValues_GetStyleBits(values: ComputedStyleBorrowed) -> u8 {
     use style::properties::computed_value_flags::ComputedValueFlags;
     // FIXME(emilio): We could do this more efficiently I'm quite sure.
     let flags = values.flags;
     let mut result = 0;
     if flags.contains(ComputedValueFlags::IS_RELEVANT_LINK_VISITED) {
-        result |= structs::NS_STYLE_RELEVANT_LINK_VISITED as u64;
+        result |= structs::ComputedStyleBit_RelevantLinkVisited;
     }
     if flags.contains(ComputedValueFlags::HAS_TEXT_DECORATION_LINES) {
-        result |= structs::NS_STYLE_HAS_TEXT_DECORATION_LINES as u64;
+        result |= structs::ComputedStyleBit_HasTextDecorationLines;
     }
     if flags.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK) {
-        result |= structs::NS_STYLE_SUPPRESS_LINEBREAK as u64;
+        result |= structs::ComputedStyleBit_SuppressLineBreak;
     }
     if flags.contains(ComputedValueFlags::IS_TEXT_COMBINED) {
-        result |= structs::NS_STYLE_IS_TEXT_COMBINED as u64;
+        result |= structs::ComputedStyleBit_IsTextCombined;
     }
     if flags.contains(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE) {
-        result |= structs::NS_STYLE_HAS_PSEUDO_ELEMENT_DATA as u64;
-    }
-    if flags.contains(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE) {
-        result |= structs::NS_STYLE_IN_DISPLAY_NONE_SUBTREE as u64;
+        result |= structs::ComputedStyleBit_HasPseudoElementData;
     }
     result
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
     values: ComputedStyleBorrowed,
 ) -> bool {
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -783,17 +783,18 @@ def split_e10s(config, tests):
 
 
 @transforms.add
 def split_chunks(config, tests):
     """Based on the 'chunks' key, split tests up into chunks by duplicating
     them and assigning 'this-chunk' appropriately and updating the treeherder
     symbol."""
     for test in tests:
-        if test['suite'].startswith('test-verify'):
+        if test['suite'].startswith('test-verify') or \
+           test['suite'].startswith('test-coverage'):
             env = config.params.get('try_task_config', {}) or {}
             env = env.get('templates', {}).get('env', {})
             test['chunks'] = perfile_number_of_chunks(env.get('MOZHARNESS_TEST_PATHS', ''),
                                                       config.params.get('head_repository', ''),
                                                       config.params.get('head_rev', ''),
                                                       test['test-name'])
 
             # limit the number of chunks we run for test-verify mode because
--- a/taskcluster/taskgraph/util/perfile.py
+++ b/taskcluster/taskgraph/util/perfile.py
@@ -17,26 +17,26 @@ from .. import GECKO
 logger = logging.getLogger(__name__)
 
 
 @memoize
 def perfile_number_of_chunks(try_task_config, head_repository, head_rev, type):
     # TODO: Make this flexible based on coverage vs verify || test type
     tests_per_chunk = 10.0
 
-    if type.startswith('test-verify-wpt'):
+    if type.startswith('test-verify-wpt') or type.startswith('test-coverage-wpt'):
         file_patterns = ['testing/web-platform/tests/**',
                          'testing/web-platform/mozilla/tests/**']
-    elif type.startswith('test-verify-gpu'):
+    elif type.startswith('test-verify-gpu') or type.startswith('test-coverage-gpu'):
         file_patterns = ['**/*webgl*/**/test_*',
                          '**/dom/canvas/**/test_*',
                          '**/gfx/tests/**/test_*',
                          '**/devtools/canvasdebugger/**/browser_*',
                          '**/reftest*/**']
-    elif type.startswith('test-verify'):
+    elif type.startswith('test-verify') or type.startswith('test-coverage'):
         file_patterns = ['**/test_*',
                          '**/browser_*',
                          '**/crashtest*/**',
                          'js/src/test/test/**',
                          'js/src/test/non262/**',
                          'js/src/test/test262/**']
     else:
         # Returning 0 means no tests to run, this captures non test-verify tasks
@@ -64,17 +64,17 @@ def perfile_number_of_chunks(try_task_co
     for pattern in file_patterns:
         for path in changed_files:
             # TODO: consider running tests if a manifest changes
             if path.endswith('.list') or path.endswith('.ini'):
                 continue
 
             if mozpackmatch(path, pattern):
                 gpu = False
-                if type == 'test-verify-e10s':
+                if type == 'test-verify-e10s' or type == 'test-coverage-e10s':
                     # file_patterns for test-verify will pick up some gpu tests, lets ignore
                     # in the case of reftest, we will not have any in the regular case
                     gpu_dirs = ['dom/canvas', 'gfx/tests', 'devtools/canvasdebugger', 'webgl']
                     for gdir in gpu_dirs:
                         if len(path.split(gdir)) > 1:
                             gpu = True
 
                 if not gpu:
--- a/testing/mochitest/runrobocop.py
+++ b/testing/mochitest/runrobocop.py
@@ -234,17 +234,16 @@ class RobocopTestRunner(MochitestDesktop
         self.options.extraPrefs.append('browser.snippets.enabled=false')
         self.options.extraPrefs.append('extensions.autoupdate.enabled=false')
 
         # Override the telemetry init delay for integration testing.
         self.options.extraPrefs.append('toolkit.telemetry.initDelay=1')
 
         self.options.extensionsToExclude.extend([
             'mochikit@mozilla.org',
-            'indexedDB-test@mozilla.org.xpi',
         ])
 
         manifest = MochitestDesktop.buildProfile(self, self.options)
         self.localProfile = self.options.profilePath
         self.log.debug("Profile created at %s" % self.localProfile)
         # some files are not needed for robocop; save time by not pushing
         os.remove(os.path.join(self.localProfile, 'userChrome.css'))
         try:
--- a/testing/mozbase/mozlog/mozlog/handlers/statushandler.py
+++ b/testing/mozbase/mozlog/mozlog/handlers/statushandler.py
@@ -25,22 +25,26 @@ class StatusHandler(object):
         # The count of each type of unexpected result status (includes tests and subtests)
         self.unexpected_statuses = defaultdict(int)
         # The count of each type of expected result status (includes tests and subtests)
         self.expected_statuses = defaultdict(int)
         # The count of actions logged
         self.action_counts = defaultdict(int)
         # The count of messages logged at each log level
         self.log_level_counts = defaultdict(int)
+        # The count of "No tests run" error messages seen
+        self.no_tests_run_count = 0
 
     def __call__(self, data):
         action = data['action']
         self.action_counts[action] += 1
 
         if action == 'log':
+            if data['level'] == 'ERROR' and data['message'] == 'No tests ran':
+                self.no_tests_run_count += 1
             self.log_level_counts[data['level']] += 1
 
         if action in ('test_status', 'test_end'):
             status = data['status']
             if 'expected' in data:
                 self.unexpected_statuses[status] += 1
             else:
                 self.expected_statuses[status] += 1
--- a/testing/mozharness/mozharness/mozilla/structuredlog.py
+++ b/testing/mozharness/mozharness/mozilla/structuredlog.py
@@ -63,17 +63,17 @@ class StructuredOutputParser(OutputParse
         """
         level = INFO
         tbpl_level = TBPL_SUCCESS
 
         data = None
         try:
             candidate_data = json.loads(line)
             if (isinstance(candidate_data, dict) and
-                'action' in candidate_data and candidate_data['action'] in self.log_actions):
+               'action' in candidate_data and candidate_data['action'] in self.log_actions):
                 data = candidate_data
         except ValueError:
             pass
 
         if data is None:
             if self.strict:
                 self.critical(("Test harness output was not a valid structured log message: "
                               "\n%s") % line)
@@ -119,37 +119,47 @@ class StructuredOutputParser(OutputParse
 
         """
           We can run evaluate_parser multiple times, it will duplicate failures
           and status which can mean that future tests will fail if a previous test fails.
           When we have a previous summary, we want to do 2 things:
             1) Remove previous data from the new summary to only look at new data
             2) Build a joined summary to include the previous + new data
         """
+        RunSummary = namedtuple("RunSummary",
+                                ("unexpected_statuses",
+                                 "expected_statuses",
+                                 "log_level_counts",
+                                 "action_counts"))
+        if previous_summary == {}:
+            previous_summary = RunSummary(defaultdict(int),
+                                          defaultdict(int),
+                                          defaultdict(int),
+                                          defaultdict(int))
         if previous_summary:
-            RunSummary = namedtuple("RunSummary",
-                                    ("unexpected_statuses",
-                                     "expected_statuses",
-                                     "log_level_counts",
-                                     "action_counts"))
-
             self.tbpl_status = TBPL_SUCCESS
-
             joined_summary = summary
 
             # Remove previously known status messages
-            summary = RunSummary(self._subtract_tuples(previous_summary.unexpected_statuses, summary.unexpected_statuses),
-                                 self._subtract_tuples(previous_summary.expected_statuses, summary.expected_statuses),
-                                 summary.log_level_counts,
+            if 'ERROR' in summary.log_level_counts:
+                summary.log_level_counts['ERROR'] -= self.handler.no_tests_run_count
+
+            summary = RunSummary(self._subtract_tuples(previous_summary.unexpected_statuses,
+                                                       summary.unexpected_statuses),
+                                 self._subtract_tuples(previous_summary.expected_statuses,
+                                                       summary.expected_statuses),
+                                 self._subtract_tuples(previous_summary.log_level_counts,
+                                                       summary.log_level_counts),
                                  summary.action_counts)
 
-            # If we have previous data to ignore, cache it so we don't parse the log multiple times
+            # If we have previous data to ignore,
+            # cache it so we don't parse the log multiple times
             self.summary = summary
         else:
-           joined_summary = summary
+            joined_summary = summary
 
         fail_pair = TBPL_WARNING, WARNING
         error_pair = TBPL_FAILURE, ERROR
 
         # These are warning/orange statuses.
         failure_conditions = [
             sum(summary.unexpected_statuses.values()) > 0,
             summary.action_counts.get('crash', 0) > summary.expected_statuses.get('CRASH', 0),
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -769,17 +769,17 @@ class AndroidEmulatorTest(TestingMixin, 
             cwd = self._query_tests_dir(self.test_suite)
             env = self.query_env()
             if minidump:
                 env['MINIDUMP_STACKWALK'] = minidump
             env['MOZ_UPLOAD_DIR'] = self.query_abs_dirs()['abs_blob_upload_dir']
             env['MINIDUMP_SAVE_PATH'] = self.query_abs_dirs()['abs_blob_upload_dir']
             env['RUST_BACKTRACE'] = 'full'
 
-            summary = None
+            summary = {}
             for per_test_args in self.query_args(per_test_suite):
                 if (datetime.datetime.now() - self.start_time) > max_per_test_time:
                     # Running tests has run out of time. That is okay! Stop running
                     # them so that a task timeout is not triggered, and so that
                     # (partial) results are made available in a timely manner.
                     self.info("TinderboxPrint: Running tests took too long: "
                               "Not all tests were executed.<br/>")
                     # Signal per-test time exceeded, to break out of suites and
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -843,17 +843,17 @@ class DesktopUnittest(TestingMixin, Merc
                 if self.config['single_stylo_traversal']:
                     env['STYLO_THREADS'] = '1'
                 else:
                     env['STYLO_THREADS'] = '4'
 
                 env = self.query_env(partial_env=env, log_level=INFO)
                 cmd_timeout = self.get_timeout_for_category(suite_category)
 
-                summary = None
+                summary = {}
                 executed_too_many_tests = False
                 for per_test_args in self.query_args(suite):
                     # Make sure baseline code coverage tests are never
                     # skipped and that having them run has no influence
                     # on the max number of actual tests that are to be run.
                     is_baseline_test = 'baselinecoverage' in per_test_args[-1] \
                                        if self.per_test_coverage else False
                     if executed_too_many_tests and not is_baseline_test:
--- a/testing/mozharness/scripts/web_platform_tests.py
+++ b/testing/mozharness/scripts/web_platform_tests.py
@@ -330,17 +330,17 @@ class WebPlatformTest(TestingMixin, Merc
                     self.info("Skipping 'wdspec' tests - no geckodriver")
         else:
             test_types = self.config.get("test_type", [])
             suites = [None]
         for suite in suites:
             if suite:
                 test_types = [suite]
 
-            summary = None
+            summary = {}
             executed_too_many_tests = False
             for per_test_args in self.query_args(suite):
                 # Make sure baseline code coverage tests are never
                 # skipped and that having them run has no influence
                 # on the max number of actual tests that are to be run.
                 is_baseline_test = 'baselinecoverage' in per_test_args[-1] \
                                    if self.per_test_coverage else False
                 if executed_too_many_tests and not is_baseline_test:
--- a/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini
@@ -1,7 +1,10 @@
 [labelable-elements.html]
+  max-asserts:
+    if webrender: 12
+    3
   [Check if the keygen element is not a labelable element]
     expected: FAIL
 
   [Check if the keygen element can access 'labels']
     expected: FAIL
 
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -577,16 +577,25 @@ class XPIStateLocation extends Map {
       this.path = path.path;
     } else {
       this.path = path;
       this.dir = this.path && new nsIFile(this.path);
     }
     this.staged = {};
     this.changed = false;
 
+    // The profile extensions directory is whitelisted for access by the
+    // content process sandbox if, and only if it already exists. Since
+    // we want it to be available for newly-installed extensions even if
+    // no profile extensions were present at startup, make sure it
+    // exists now.
+    if (name === KEY_APP_PROFILE) {
+      OS.File.makeDir(this.path, {ignoreExisting: true});
+    }
+
     if (saved) {
       this.restore(saved);
     }
 
     this._installler = undefined;
   }
 
   get installer() {
--- a/toolkit/mozapps/extensions/test/browser/browser_legacy.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_legacy.js
@@ -3,19 +3,17 @@ add_task(async function() {
   const INFO_URL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "webextensions";
 
   // The mochitest framework installs a bunch of legacy extensions.
   // Fortunately, the extensions.legacy.exceptions preference exists to
   // avoid treating some extensions as legacy for the purposes of the UI.
   const IGNORE = [
     "special-powers@mozilla.org",
     "mochikit@mozilla.org",
-    "workerbootstrap-test@mozilla.org",
     "mozscreenshots@mozilla.org",
-    "indexedDB-test@mozilla.org",
   ];
 
   let exceptions = Services.prefs.getCharPref("extensions.legacy.exceptions");
   exceptions = [ exceptions, ...IGNORE ].join(",");
 
   await SpecialPowers.pushPrefEnv({
     set: [
       ["extensions.legacy.enabled", false],
--- a/toolkit/mozapps/extensions/test/browser/browser_legacy_themes.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_legacy_themes.js
@@ -1,17 +1,16 @@
 
 add_task(async function() {
   // The mochitest framework installs a bunch of legacy extensions.
   // Fortunately, the extensions.legacy.exceptions preference exists to
   // avoid treating some extensions as legacy for the purposes of the UI.
   const IGNORE = [
     "special-powers@mozilla.org",
     "mochikit@mozilla.org",
-    "workerbootstrap-test@mozilla.org",
   ];
 
   let exceptions = Services.prefs.getCharPref("extensions.legacy.exceptions");
   exceptions = [ exceptions, ...IGNORE ].join(",");
 
   await SpecialPowers.pushPrefEnv({
     set: [
       ["extensions.legacy.enabled", false],
--- a/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
@@ -784,17 +784,18 @@ add_task(async function() {
 
   notEqual(addon, null);
   equal(addon.temporarilyInstalled, false);
 
   await promiseRestartManager();
 });
 
 // Tests that XPIs with a .zip extension work when loaded temporarily.
-add_task(async function test_zip_extension() {
+add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" },
+         async function test_zip_extension() {
   let xpi = createTempWebExtensionFile({
     background() {
       /* globals browser */
       browser.test.sendMessage("started", "Hello.");
     },
   });
   xpi.moveTo(null, xpi.leafName.replace(/\.xpi$/, ".zip"));
 
--- a/toolkit/mozapps/extensions/test/xpinstall/browser.ini
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser.ini
@@ -60,17 +60,17 @@ skip-if = true # Bug 1084646
 [browser_hash.js]
 [browser_hash2.js]
 [browser_httphash.js]
 [browser_httphash2.js]
 [browser_httphash3.js]
 [browser_httphash4.js]
 [browser_httphash5.js]
 [browser_httphash6.js]
-skip-if = (os == 'win' || os == 'mac') && (verify || !debug)
+skip-if = true # Bug 1449788
 [browser_installchrome.js]
 [browser_localfile.js]
 [browser_localfile2.js]
 [browser_localfile3.js]
 [browser_localfile4.js]
 [browser_offline.js]
 [browser_relative.js]
 [browser_softwareupdate.js]