Merge inbound to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 19 Sep 2018 13:00:20 +0300
changeset 492934 57a1f59a6646
parent 492921 a0f7c46838a0 (current diff)
parent 492933 aee03f432203 (diff)
child 492944 11354663213f
child 492964 f153921d64c0
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
57a1f59a6646 / 64.0a1 / 20180919123806 / files
nightly linux64
57a1f59a6646 / 64.0a1 / 20180919123806 / files
nightly mac
57a1f59a6646 / 64.0a1 / 20180919123806 / files
nightly win32
57a1f59a6646 / 64.0a1 / 20180919123806 / files
nightly win64
57a1f59a6646 / 64.0a1 / 20180919123806 / 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/interfaces/xul/nsIDOMXULLabeledControlEl.idl
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -5,17 +5,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTextEquivUtils.h"
 
 #include "Accessible-inl.h"
 #include "AccIterator.h"
 #include "nsCoreUtils.h"
-#include "nsIDOMXULLabeledControlEl.h"
 #include "mozilla/dom/Text.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 /**
  * The accessible for which we are computing a text equivalent. It is useful
  * for bailing out during recursive text computation, or for special cases
@@ -302,30 +301,27 @@ nsTextEquivUtils::AppendFromDOMNode(nsIC
   nsresult rv = AppendTextEquivFromTextContent(aContent, aString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
     return NS_OK;
 
   if (aContent->IsXULElement()) {
     nsAutoString textEquivalent;
-    nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
-      do_QueryInterface(aContent);
-
-    if (labeledEl) {
-      labeledEl->GetLabel(textEquivalent);
+    if (aContent->NodeInfo()->Equals(nsGkAtoms::label, kNameSpaceID_XUL)) {
+      aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
+                                     textEquivalent);
     } else {
-      if (aContent->NodeInfo()->Equals(nsGkAtoms::label,
-                                       kNameSpaceID_XUL))
-        aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
-                                       textEquivalent);
+      aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label,
+                                     textEquivalent);
+    }
 
-      if (textEquivalent.IsEmpty())
-        aContent->AsElement()->GetAttr(kNameSpaceID_None,
-                                       nsGkAtoms::tooltiptext, textEquivalent);
+    if (textEquivalent.IsEmpty()) {
+      aContent->AsElement()->GetAttr(kNameSpaceID_None,
+                                     nsGkAtoms::tooltiptext, textEquivalent);
     }
 
     AppendString(aString, textEquivalent);
   }
 
   return AppendFromDOMChildren(aContent, aString);
 }
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -799,30 +799,25 @@ Accessible::XULElmName(DocAccessible* aD
    *        - label has either value="foo" or children
    * Once a label is found, the search is discontinued, so a control
    *  that has a label child as well as having a label external to
    *  the control that uses the control="controlID" syntax will use
    *  the child label for its Name.
    */
 
   // CASE #1 (via label attribute) -- great majority of the cases
-  nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
-  if (labeledEl) {
-    labeledEl->GetLabel(aName);
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
+  if (itemEl) {
+    itemEl->GetLabel(aName);
   } else {
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
-    if (itemEl) {
-      itemEl->GetLabel(aName);
-    } else {
-      nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
-      // Use label if this is not a select control element which
-      // uses label attribute to indicate which option is selected
-      if (!select && aElm->IsElement()) {
-        aElm->AsElement()->GetAttribute(NS_LITERAL_STRING("label"), aName);
-      }
+    // Use @label if this is not a select control element, which uses label
+    // attribute to indicate, which option is selected.
+    nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
+    if (!select) {
+      aElm->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
     }
   }
 
   // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
   if (aName.IsEmpty()) {
     Accessible* label = nullptr;
     XULLabelIterator iter(aDocument, aElm);
     while ((label = iter.Next())) {
--- a/accessible/tests/mochitest/name/test_general.xul
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -67,20 +67,20 @@
       testName("btn_labelledby_mixed_hidden_child", "nomore text2");
 
       // Gets the name from hidden text nodes contained by nested elements,
       // (label element is hidden entirely), (bug 443081)
       testName("btn_labelledby_mixed_hidden", "lala more hidden text");
 
 
       //////////////////////////////////////////////////////////////////////////
-      // Name for nsIDOMXULLabeledControlElement.
+      // Name from @label attribute.
 
       // Gets the name from @label attribute.
-      testName("btn_nsIDOMXULLabeledControlElement", "labeled element");
+      testName("btn_labelattr", "labeled element");
 
 
       //////////////////////////////////////////////////////////////////////////
       // Name for nsIDOMXULSelectControlItemElement.
 
       // Gets the name from @label attribute.
       testName("li_nsIDOMXULSelectControlItemElement", "select control item");
 
@@ -266,18 +266,18 @@
         <menuitem label="item"/>
       </menupopup>
     </menulist>
     more text
   </description>
   <button id="btn_labelledby_mixed_menulist"
           aria-labelledby="labelledby_mixed_menulist"/>
 
-  <!-- nsIDOMXULLabeledControlElement -->
-  <button id="btn_nsIDOMXULLabeledControlElement"
+  <!-- @label -->
+  <button id="btn_labelattr"
           label="labeled element"/>
 
   <!-- nsIDOMXULSelectControlItemElement -->
   <richlistbox>
     <richlistitem id="li_nsIDOMXULSelectControlItemElement">
       <label value="select control item"/>
     </richlistitem>
   </richlistbox>
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5290,17 +5290,18 @@ AssertContentPrivilegedAboutPageHasCSP(n
   NS_ENSURE_SUCCESS_VOID(rv);
 
   if (!(aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT)) {
     return;
   }
 
   // Potentially init the legacy whitelist of about URIs without a CSP.
   static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
-  if (!sLegacyAboutPagesWithNoCSP) {
+  if (!sLegacyAboutPagesWithNoCSP ||
+      Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) {
     sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>();
     nsAutoCString legacyAboutPages;
     Preferences::GetCString("csp.content_privileged_about_uris_without_csp",
       legacyAboutPages);
     for (const nsACString& hostString : legacyAboutPages.Split(',')) {
       // please note that for the actual whitelist we only store the path of
       // about: URI. Let's reassemble the full about URI here so we don't
       // have to remove query arguments later.
@@ -5329,16 +5330,20 @@ AssertContentPrivilegedAboutPageHasCSP(n
   nsAutoString parsedPolicyStr;
   if (csp) {
     uint32_t policyCount = 0;
      csp->GetPolicyCount(&policyCount);
      if (policyCount > 0) {
        csp->GetPolicyString(0, parsedPolicyStr);
      }
   }
+  if (Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) {
+    NS_ASSERTION(parsedPolicyStr.Find("default-src") >= 0, "about: page must have a CSP");
+    return;
+  }
   MOZ_ASSERT(parsedPolicyStr.Find("default-src") >= 0,
     "about: page must contain a CSP including default-src");
 }
 #endif
 
 void
 nsDocument::EndLoad()
 {
--- a/dom/interfaces/xul/moz.build
+++ b/dom/interfaces/xul/moz.build
@@ -7,17 +7,16 @@
 with Files("**"):
     BUG_COMPONENT = ("Core", "XUL")
 
 XPIDL_SOURCES += [
     'nsIDOMXULButtonElement.idl',
     'nsIDOMXULCommandDispatcher.idl',
     'nsIDOMXULContainerElement.idl',
     'nsIDOMXULControlElement.idl',
-    'nsIDOMXULLabeledControlEl.idl',
     'nsIDOMXULMenuListElement.idl',
     'nsIDOMXULMultSelectCntrlEl.idl',
     'nsIDOMXULRelatedElement.idl',
     'nsIDOMXULSelectCntrlEl.idl',
     'nsIDOMXULSelectCntrlItemEl.idl',
 ]
 
 XPIDL_MODULE = 'dom_xul'
--- a/dom/interfaces/xul/nsIDOMXULButtonElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULButtonElement.idl
@@ -1,22 +1,21 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMXULLabeledControlEl.idl"
+#include "nsIDOMXULControlElement.idl"
 
 [scriptable, uuid(6ed53cfb-9e59-424c-af8d-e74582381951)]
-interface nsIDOMXULButtonElement : nsIDOMXULLabeledControlElement {
+interface nsIDOMXULButtonElement : nsIDOMXULControlElement {
   attribute DOMString type;
   attribute DOMString dlgType;
 
   // For buttons of type="menu" only.
   attribute boolean open;
   
   // For buttons of type="checkbox" only.
   attribute boolean checked;
 
   // For buttons of type="radio" only.
   attribute DOMString group;
 };
-
deleted file mode 100644
--- a/dom/interfaces/xul/nsIDOMXULLabeledControlEl.idl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMXULControlElement.idl"
-
-[scriptable, uuid(c58a159f-e27d-40c8-865a-d4dcfd928f62)]
-interface nsIDOMXULLabeledControlElement : nsIDOMXULControlElement {
-  attribute DOMString crop;
-  attribute DOMString image;
-  attribute DOMString label;
-  attribute DOMString accessKey;
-  attribute DOMString command;
-
-//  void doCommand();
-};
-
--- a/dom/security/test/general/mochitest.ini
+++ b/dom/security/test/general/mochitest.ini
@@ -37,8 +37,10 @@ skip-if = toolkit == 'android'
 [test_same_site_cookies_subrequest.html]
 [test_same_site_cookies_toplevel_nav.html]
 [test_same_site_cookies_cross_origin_context.html]
 [test_same_site_cookies_from_script.html]
 [test_same_site_cookies_redirect.html]
 [test_same_site_cookies_toplevel_set_cookie.html]
 [test_same_site_cookies_iframe.html]
 [test_same_site_cookies_about.html]
+[test_assert_about_page_no_csp.html]
+skip-if = !debug || toolkit == 'android'
new file mode 100644
--- /dev/null
+++ b/dom/security/test/general/test_assert_about_page_no_csp.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1490977: Test Assertion if content privileged about: page has no CSP</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe id="testframe"></iframe>
+<script class="testbody" type="text/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.expectAssertions(0, 1);
+
+  // Test Setup:
+  // The test overrules the whitelist of about: pages that are allowed to load without a CSP
+  // and makes sure to hit the assertion within AssertContentPrivilegedAboutPageHasCSP().
+  // However, due to the caching mechanism within AssertContentPrivilegedAboutPageHasCSP this
+  // test loads a second dummy data: URI to reset the old cache and finally resets the pref
+  // used for testing purposes.
+
+  let origWhiteList = SpecialPowers.getCharPref("csp.content_privileged_about_uris_without_csp");
+
+  SpecialPowers.setCharPref("csp.content_privileged_about_uris_without_csp", "");
+  SpecialPowers.setBoolPref("csp.overrule_content_privileged_about_uris_without_csp_whitelist", true);
+
+  ok(true, "sanity: prefs flipped and test runs");
+  let myFrame = document.getElementById("testframe");
+  myFrame.src = "about:blank";
+  // booom :-)
+
+  SpecialPowers.setCharPref("csp.content_privileged_about_uris_without_csp", origWhiteList);
+  myFrame.src = "data:text/html,<body>just a dumy data: URI</body>";
+
+  SpecialPowers.setBoolPref("csp.overrule_content_privileged_about_uris_without_csp_whitelist", false);
+
+  SimpleTest.finish();
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -61,18 +61,18 @@ pub enum SourceTexture {
     Invalid,
     TextureCache(CacheTextureId),
     External(ExternalImageData),
     CacheA8,
     CacheRGBA8,
     RenderTaskCache(SavedTargetIndex),
 }
 
-pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
-pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
+pub const ORTHO_NEAR_PLANE: f32 = -100000.0;
+pub const ORTHO_FAR_PLANE: f32 = 100000.0;
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderTargetInfo {
     pub has_depth: bool,
 }
 
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-6fa046c936b9d720726d27c3b0514ee66a305b69
+5b5b4145ecef117acb02fd1f9b72bf02e85c650b
--- a/js/src/gc/AllocKind.h
+++ b/js/src/gc/AllocKind.h
@@ -113,16 +113,19 @@ IsAllocKind(AllocKind kind)
 }
 
 inline bool
 IsValidAllocKind(AllocKind kind)
 {
     return kind >= AllocKind::FIRST && kind <= AllocKind::LAST;
 }
 
+const char*
+AllocKindName(AllocKind kind);
+
 inline bool
 IsObjectAllocKind(AllocKind kind)
 {
     return kind >= AllocKind::OBJECT_FIRST && kind <= AllocKind::OBJECT_LAST;
 }
 
 inline bool
 IsShapeAllocKind(AllocKind kind)
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1263,18 +1263,18 @@ GCRuntime::parseAndSetZeal(const char* s
         }
 
         setZeal(mode, frequency);
     }
 
     return true;
 }
 
-static const char*
-AllocKindName(AllocKind kind)
+const char*
+js::gc::AllocKindName(AllocKind kind)
 {
     static const char* const names[] = {
 #define EXPAND_THING_NAME(allocKind, _1, _2, _3, _4, _5, _6) \
         #allocKind,
 FOR_EACH_ALLOCKIND(EXPAND_THING_NAME)
 #undef EXPAND_THING_NAME
     };
     static_assert(ArrayLength(names) == size_t(AllocKind::LIMIT),
--- a/js/src/jit-test/tests/debug/wasm-binary-sources.js
+++ b/js/src/jit-test/tests/debug/wasm-binary-sources.js
@@ -27,11 +27,11 @@ assertThrowsInstanceOf(() => source.bina
 dbg.allowWasmBinarySource = true;
 
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
 assertEq(s.format, "wasm");
 
 var source2 = s.source;
 
 // The text is predefined if wasm binary sources are enabled.
-assertEq(source2.text, '[wasm]');
+assertEq(source2.text, '[debugger missing wasm binary-to-text conversion]');
 // The binary contains Uint8Array which is equal to wasm bytecode;
 arraysEqual(source2.binary, wasmTextToBinary('(module (func) (export "" 0))'));
--- a/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
+++ b/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
@@ -61,14 +61,14 @@ assertEq(gotScript.source.sourceMapURL, 
 // The sourceMappingURL section is present
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(toWasm('(module (func) (export "a" 0))', 'http://example.org/test')));`);
 assertEq(gotScript.format, "wasm");
 assertEq(gotScript.source.sourceMapURL, 'http://example.org/test');
 
 // The sourceMapURL is read-only for wasm
 assertThrowsInstanceOf(() => gotScript.source.sourceMapURL = 'foo', Error);
 
-// The sourceMappingURL section is present, but is not available when wasm
+// The sourceMappingURL section is present, and is still available when wasm
 // binary source is disabled.
 dbg.allowWasmBinarySource = false;
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(toWasm('(module (func) (export "a" 0))', 'http://example.org/test2')));`);
 assertEq(gotScript.format, "wasm");
-assertEq(gotScript.source.sourceMapURL, null);
+assertEq(gotScript.source.sourceMapURL, 'http://example.org/test2');
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7002,17 +7002,17 @@ JS::IsWasmModuleObject(HandleObject obj)
     }
     return unwrapped->is<WasmModuleObject>();
 }
 
 JS_PUBLIC_API(RefPtr<JS::WasmModule>)
 JS::GetWasmModule(HandleObject obj)
 {
     MOZ_ASSERT(JS::IsWasmModuleObject(obj));
-    return &CheckedUnwrap(obj)->as<WasmModuleObject>().module();
+    return const_cast<wasm::Module*>(&CheckedUnwrap(obj)->as<WasmModuleObject>().module());
 }
 
 JS_PUBLIC_API(RefPtr<JS::WasmModule>)
 JS::DeserializeWasmModule(PRFileDesc* bytecode, UniqueChars filename, unsigned line)
 {
     return wasm::DeserializeModule(bytecode, std::move(filename), line);
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6574,17 +6574,17 @@ enum class MailboxTag {
 
 struct SharedObjectMailbox
 {
     union Value {
         struct {
             SharedArrayRawBuffer* buffer;
             uint32_t              length;
         } sarb;
-        wasm::Module*             module;
+        const wasm::Module*       module;
     };
 
     SharedObjectMailbox() : tag(MailboxTag::Empty) {}
 
     MailboxTag tag;
     Value      val;
 };
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2780,17 +2780,17 @@ UpdateExecutionObservabilityOfScriptsInZ
     // Iterate through all wasm instances to find ones that need to be updated.
     for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
         for (wasm::Instance* instance : r->wasm.instances()) {
             if (!instance->debugEnabled()) {
                 continue;
             }
 
             bool enableTrap = observing == Debugger::IsObserving::Observing;
-            instance->ensureEnterFrameTrapsState(cx, enableTrap);
+            instance->debug().ensureEnterFrameTrapsState(cx, enableTrap);
         }
     }
 
     return true;
 }
 
 /* static */ bool
 Debugger::updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
@@ -5926,22 +5926,23 @@ struct DebuggerScriptGetLineCountMatcher
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        uint32_t result;
-        if (!wasmInstance->instance().debug().totalSourceLines(cx_, &result)) {
-            return false;
-        }
-        totalLines = double(result);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (instance.debugEnabled()) {
+            totalLines = double(instance.debug().totalSourceLines());
+        } else {
+            totalLines = 0;
+        }
         return true;
     }
 };
 
 static bool
 DebuggerScript_getLineCount(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj, referent);
@@ -6385,25 +6386,26 @@ class DebuggerScriptGetOffsetLocationMat
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
+            JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
+            return false;
+        }
+
         size_t lineno;
         size_t column;
-        bool found;
-        if (!instance->instance().debug().getOffsetLocation(cx_, offset_, &found, &lineno, &column)) {
-            return false;
-        }
-
-        if (!found) {
+        if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
             JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
             return false;
         }
 
         result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
         if (!result_) {
             return false;
         }
@@ -6691,19 +6693,21 @@ class DebuggerScriptGetAllColumnOffsetsM
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+
         Vector<wasm::ExprLoc> offsets(cx_);
-        if (!instance->instance().debug().getAllColumnOffsets(cx_, &offsets)) {
+        if (instance.debugEnabled() && !instance.debug().getAllColumnOffsets(cx_, &offsets)) {
             return false;
         }
 
         result_.set(NewDenseEmptyArray(cx_));
         if (!result_) {
             return false;
         }
 
@@ -6780,19 +6784,21 @@ class DebuggerScriptGetLineOffsetsMatche
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+
         Vector<uint32_t> offsets(cx_);
-        if (!instance->instance().debug().getLineOffsets(cx_, lineno_, &offsets)) {
+        if (instance.debugEnabled() && !instance.debug().getLineOffsets(cx_, lineno_, &offsets)) {
             return false;
         }
 
         result_.set(NewDenseEmptyArray(cx_));
         if (!result_) {
             return false;
         }
 
@@ -7103,17 +7109,17 @@ struct DebuggerScriptSetBreakpointMatche
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
     ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
         wasm::Instance& instance = wasmInstance->instance();
-        if (!instance.debug().hasBreakpointTrapAtOffset(offset_)) {
+        if (!instance.debugEnabled() || !instance.debug().hasBreakpointTrapAtOffset(offset_)) {
             JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
             return false;
         }
         WasmBreakpointSite* site = instance.debug().getOrCreateBreakpointSite(cx_, offset_);
         if (!site) {
             return false;
         }
         site->inc(cx_->runtime()->defaultFreeOp());
@@ -7211,18 +7217,22 @@ class DebuggerScriptClearBreakpointMatch
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
-        return instance->instance().debug().clearBreakpointsIn(cx_, instance, dbg_, handler_);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
+            return true;
+        }
+        return instance.debug().clearBreakpointsIn(cx_, instanceObj, dbg_, handler_);
     }
 };
 
 
 static bool
 DebuggerScript_clearBreakpoint(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearBreakpoint", args, obj, referent);
@@ -7648,23 +7658,25 @@ class DebuggerSourceGetTextMatcher
 
         if (ss->isFunctionBody()) {
             return ss->functionBodyString(cx_);
         }
 
         return ss->substring(cx_, 0, ss->length());
     }
 
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        if (wasmInstance->instance().debug().maybeBytecode() &&
-            wasmInstance->instance().debug().binarySource())
-        {
-            return NewStringCopyZ<CanGC>(cx_, "[wasm]");
-        }
-        return wasmInstance->instance().debug().createText(cx_);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        const char* msg;
+        if (!instance.debugEnabled()) {
+            msg = "Restart with developer tools open to view WebAssembly source.";
+        } else {
+            msg = "[debugger missing wasm binary-to-text conversion]";
+        }
+        return NewStringCopyZ<CanGC>(cx_, msg);
     }
 };
 
 static bool
 DebuggerSource_getText(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, referent);
     Value textv = obj->getReservedSlot(JSSLOT_DEBUGSOURCE_TEXT);
@@ -7691,32 +7703,32 @@ DebuggerSource_getBinary(JSContext* cx, 
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get binary)", args, obj, referent);
 
     if (!referent.is<WasmInstanceObject*>()) {
         ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, args.thisv(), nullptr,
                          "a wasm source");
         return false;
     }
 
-    RootedWasmInstanceObject wasmInstance(cx, referent.as<WasmInstanceObject*>());
-    if (!wasmInstance->instance().debug().binarySource()) {
+    RootedWasmInstanceObject instanceObj(cx, referent.as<WasmInstanceObject*>());
+    wasm::Instance& instance = instanceObj->instance();
+
+    if (!instance.debugEnabled() || !instance.debug().binarySource()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_DEBUG_NO_BINARY_SOURCE);
         return false;
     }
 
-    auto bytecode = wasmInstance->instance().debug().maybeBytecode();
-    size_t arrLength = bytecode ? bytecode->length() : 0;
-    RootedObject arr(cx, JS_NewUint8Array(cx, arrLength));
+    const wasm::Bytes& bytecode = instance.debug().bytecode();
+    RootedObject arr(cx, JS_NewUint8Array(cx, bytecode.length()));
     if (!arr) {
         return false;
     }
-    if (bytecode) {
-        memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode->begin(), arrLength);
-    }
+
+    memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode.begin(), bytecode.length());
 
     args.rval().setObject(*arr);
     return true;
 }
 
 class DebuggerSourceGetURLMatcher
 {
     JSContext* cx_;
@@ -7730,28 +7742,18 @@ class DebuggerSourceGetURLMatcher
         ScriptSource* ss = sourceObject->source();
         MOZ_ASSERT(ss);
         if (ss->filename()) {
             JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
             return Some(str);
         }
         return Nothing();
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        if (wasmInstance->instance().metadata().filenameIsURL) {
-            JSString* str = NewStringCopyZ<CanGC>(cx_, wasmInstance->instance().metadata().filename.get());
-            if (!str) {
-                return Nothing();
-            }
-            return Some(str);
-        }
-        if (JSString* str = wasmInstance->instance().debug().debugDisplayURL(cx_)) {
-            return Some(str);
-        }
-        return Nothing();
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        return Some(instanceObj->instance().createDisplayURL(cx_));
     }
 };
 
 static bool
 DebuggerSource_getURL(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
 
@@ -8004,27 +8006,28 @@ class DebuggerSourceGetSourceMapURLMatch
         }
         JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
         if (!str) {
             return false;
         }
         result_.set(str);
         return true;
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        // sourceMapURL is not available if debugger was not in
-        // allowWasmBinarySource mode.
-        if (!wasmInstance->instance().debug().binarySource()) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
             result_.set(nullptr);
             return true;
         }
+
         RootedString str(cx_);
-        if (!wasmInstance->instance().debug().getSourceMappingURL(cx_, &str)) {
-            return false;
-        }
+        if (!instance.debug().getSourceMappingURL(cx_, &str)) {
+            return false;
+        }
+
         result_.set(str);
         return true;
     }
 };
 
 static bool
 DebuggerSource_getSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -463,17 +463,17 @@ StatsCellCallback(JSRuntime* rt, void* d
         JS::ClassInfo info;        // This zeroes all the sizes.
         info.objectsGCHeap += thingSize;
 
         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
         // These classes require special handling due to shared resources which
         // we must be careful not to report twice.
         if (obj->is<WasmModuleObject>()) {
-            wasm::Module& module = obj->as<WasmModuleObject>().module();
+            const wasm::Module& module = obj->as<WasmModuleObject>().module();
             if (ScriptSource* ss = module.metadata().maybeScriptSource()) {
                 CollectScriptSourceStats<granularity>(closure, ss);
             }
             module.addSizeOfMisc(rtStats->mallocSizeOf_,
                                  &closure->wasmSeenMetadata,
                                  &closure->wasmSeenBytes,
                                  &closure->wasmSeenCode,
                                  &info.objectsNonHeapCodeWasm,
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -479,17 +479,17 @@ class ObjectGroupRealm::NewTable : publi
     using Table = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
     using Base = JS::WeakCache<Table>;
 
   public:
     explicit NewTable(Zone* zone) : Base(zone) {}
 };
 
 /* static*/ ObjectGroupRealm&
-ObjectGroupRealm::get(ObjectGroup* group)
+ObjectGroupRealm::get(const ObjectGroup* group)
 {
     return group->realm()->objectGroups_;
 }
 
 /* static*/ ObjectGroupRealm&
 ObjectGroupRealm::getForNewObject(JSContext* cx)
 {
     return cx->realm()->objectGroups_;
@@ -1691,17 +1691,17 @@ ObjectGroup::getCopyOnWriteObject(JSScri
     // IonBuilder.
     ArrayObject* obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();
     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
 
     return obj;
 }
 
 /* static */ bool
-ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
+ObjectGroup::findAllocationSite(JSContext* cx, const ObjectGroup* group,
                                 JSScript** script, uint32_t* offset)
 {
     *script = nullptr;
     *offset = 0;
 
     ObjectGroupRealm& realm = ObjectGroupRealm::get(group);
     const ObjectGroupRealm::AllocationSiteTable* table = realm.allocationSiteTable;
 
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -583,17 +583,17 @@ class ObjectGroup : public gc::TenuredCe
     setAllocationSiteObjectGroup(JSContext* cx, HandleScript script, jsbytecode* pc,
                                  HandleObject obj, bool singleton);
 
     static ArrayObject* getOrFixupCopyOnWriteObject(JSContext* cx, HandleScript script,
                                                     jsbytecode* pc);
     static ArrayObject* getCopyOnWriteObject(JSScript* script, jsbytecode* pc);
 
     // Returns false if not found.
-    static bool findAllocationSite(JSContext* cx, ObjectGroup* group,
+    static bool findAllocationSite(JSContext* cx, const ObjectGroup* group,
                                    JSScript** script, uint32_t* offset);
 
   private:
     static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
 };
 
 // Structure used to manage the groups in a realm.
 class ObjectGroupRealm
@@ -688,17 +688,17 @@ class ObjectGroupRealm
     struct NewEntry;
 
     ObjectGroupRealm() = default;
     ~ObjectGroupRealm();
 
     ObjectGroupRealm(ObjectGroupRealm&) = delete;
     void operator=(ObjectGroupRealm&) = delete;
 
-    static ObjectGroupRealm& get(ObjectGroup* group);
+    static ObjectGroupRealm& get(const ObjectGroup* group);
     static ObjectGroupRealm& getForNewObject(JSContext* cx);
 
     void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
                                     JSProtoKey kind, ObjectGroup* group);
 
     void removeDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated);
     void replaceDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated,
                                 ObjectGroup* group);
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -315,17 +315,17 @@ class JS::Realm : public JS::shadow::Rea
 
     // Note: this is private to enforce use of ObjectRealm::get(obj).
     js::ObjectRealm objects_;
     friend js::ObjectRealm& js::ObjectRealm::get(const JSObject*);
 
     // Object group tables and other state in the realm. This is private to
     // enforce use of ObjectGroupRealm::get(group)/getForNewObject(cx).
     js::ObjectGroupRealm objectGroups_;
-    friend js::ObjectGroupRealm& js::ObjectGroupRealm::get(js::ObjectGroup* group);
+    friend js::ObjectGroupRealm& js::ObjectGroupRealm::get(const js::ObjectGroup* group);
     friend js::ObjectGroupRealm& js::ObjectGroupRealm::getForNewObject(JSContext* cx);
 
     // The global environment record's [[VarNames]] list that contains all
     // names declared using FunctionDeclaration, GeneratorDeclaration, and
     // VariableDeclaration declarations in global code in this realm.
     // Names are only removed from this list by a |delete IdentifierReference|
     // that successfully removes that global property.
     using VarNamesSet = JS::GCHashSet<JSAtom*,
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -134,35 +134,35 @@ TypeSet::ObjectKey::maybeCompartment()
     if (isSingleton()) {
         return singletonNoBarrier()->compartment();
     }
 
     return groupNoBarrier()->compartment();
 }
 
 /* static */ inline TypeSet::Type
-TypeSet::ObjectType(JSObject* obj)
+TypeSet::ObjectType(const JSObject* obj)
 {
     if (obj->isSingleton()) {
         return Type(uintptr_t(obj) | 1);
     }
     return Type(uintptr_t(obj->group()));
 }
 
 /* static */ inline TypeSet::Type
-TypeSet::ObjectType(ObjectGroup* group)
+TypeSet::ObjectType(const ObjectGroup* group)
 {
     if (group->singleton()) {
         return Type(uintptr_t(group->singleton()) | 1);
     }
     return Type(uintptr_t(group));
 }
 
 /* static */ inline TypeSet::Type
-TypeSet::ObjectType(ObjectKey* obj)
+TypeSet::ObjectType(const ObjectKey* obj)
 {
     return Type(uintptr_t(obj));
 }
 
 inline TypeSet::Type
 TypeSet::GetValueType(const Value& val)
 {
     if (val.isDouble()) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -140,17 +140,17 @@ static UniqueChars MakeStringCopy(const 
     char* copy = strdup(s);
     if (!copy) {
         oomUnsafe.crash("Could not copy string");
     }
     return UniqueChars(copy);
 }
 
 /* static */ UniqueChars
-TypeSet::TypeString(TypeSet::Type type)
+TypeSet::TypeString(const TypeSet::Type type)
 {
     if (type.isPrimitive() || type.isUnknown() || type.isAnyObject()) {
         return MakeStringCopy(NonObjectTypeString(type));
     }
 
     char buf[100];
     if (type.isSingleton()) {
         JSObject* singleton = type.singletonNoBarrier();
@@ -158,17 +158,17 @@ TypeSet::TypeString(TypeSet::Type type)
     } else {
         SprintfLiteral(buf, "[%s * %#" PRIxPTR "]", type.groupNoBarrier()->clasp()->name, uintptr_t(type.groupNoBarrier()));
     }
 
     return MakeStringCopy(buf);
 }
 
 /* static */ UniqueChars
-TypeSet::ObjectGroupString(ObjectGroup* group)
+TypeSet::ObjectGroupString(const ObjectGroup* group)
 {
     return TypeString(TypeSet::ObjectType(group));
 }
 
 #ifdef DEBUG
 
 bool
 js::InferSpewActive(SpewChannel channel)
--- a/js/src/vm/TypeSet.h
+++ b/js/src/vm/TypeSet.h
@@ -430,24 +430,24 @@ class TypeSet
     static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
     static inline Type UnknownType()   { return Type(JSVAL_TYPE_UNKNOWN); }
 
     static inline Type PrimitiveType(JSValueType type) {
         MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
         return Type(type);
     }
 
-    static inline Type ObjectType(JSObject* obj);
-    static inline Type ObjectType(ObjectGroup* group);
-    static inline Type ObjectType(ObjectKey* key);
+    static inline Type ObjectType(const JSObject* obj);
+    static inline Type ObjectType(const ObjectGroup* group);
+    static inline Type ObjectType(const ObjectKey* key);
 
     static const char* NonObjectTypeString(Type type);
 
-    static JS::UniqueChars TypeString(Type type);
-    static JS::UniqueChars ObjectGroupString(ObjectGroup* group);
+    static JS::UniqueChars TypeString(const Type type);
+    static JS::UniqueChars ObjectGroupString(const ObjectGroup* group);
 
   public:
     void print(FILE* fp = stderr);
 
     /* Whether this set contains a specific type. */
     MOZ_ALWAYS_INLINE bool hasType(Type type) const;
 
     TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -362,19 +362,17 @@ struct js::AsmJSMetadata : Metadata, Asm
         return scriptSource.get()->mutedErrors();
     }
     const char16_t* displayURL() const override {
         return scriptSource.get()->hasDisplayURL() ? scriptSource.get()->displayURL() : nullptr;
     }
     ScriptSource* maybeScriptSource() const override {
         return scriptSource.get();
     }
-    bool getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                     UTF8Bytes* name) const override
-    {
+    bool getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const override {
         const char* p = asmJSFuncNames[funcIndex].get();
         if (!p) {
             return true;
         }
         return name->append(p, strlen(p));
     }
 
     AsmJSMetadataCacheablePod& pod() { return *this; }
@@ -2399,17 +2397,17 @@ IsLiteralInt(ModuleValidator& m, ParseNo
     return IsNumericLiteral(m, pn) &&
            IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
 }
 
 /*****************************************************************************/
 
 namespace {
 
-typedef Vector<PropertyName*, 4, SystemAllocPolicy> NameVector;
+typedef Vector<PropertyName*, 4, SystemAllocPolicy> LabelVector;
 
 // Encapsulates the building of an asm bytecode function from an asm.js function
 // source code, packing the asm.js code into the asm bytecode form that can
 // be decoded and compiled with a FunctionCompiler.
 class MOZ_STACK_CLASS FunctionValidator
 {
   public:
     struct Local
@@ -2529,29 +2527,29 @@ class MOZ_STACK_CLASS FunctionValidator
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
                breakableStack_.append(blockDepth_++);
     }
     bool popBreakableBlock() {
         MOZ_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
         return encoder().writeOp(Op::End);
     }
 
-    bool pushUnbreakableBlock(const NameVector* labels = nullptr) {
+    bool pushUnbreakableBlock(const LabelVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels) {
                 if (!breakLabels_.putNew(label, blockDepth_)) {
                     return false;
                 }
             }
         }
         blockDepth_++;
         return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void));
     }
-    bool popUnbreakableBlock(const NameVector* labels = nullptr) {
+    bool popUnbreakableBlock(const LabelVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels) {
                 removeLabel(label, &breakLabels_);
             }
         }
         --blockDepth_;
         return encoder().writeOp(Op::End);
     }
@@ -2617,30 +2615,30 @@ class MOZ_STACK_CLASS FunctionValidator
     }
     bool writeUnlabeledBreakOrContinue(bool isBreak) {
         return writeBr(isBreak? breakableStack_.back() : continuableStack_.back());
     }
     bool writeContinue() {
         return writeBr(continuableStack_.back());
     }
 
-    bool addLabels(const NameVector& labels, uint32_t relativeBreakDepth,
+    bool addLabels(const LabelVector& labels, uint32_t relativeBreakDepth,
                    uint32_t relativeContinueDepth)
     {
         for (PropertyName* label : labels) {
             if (!breakLabels_.putNew(label, blockDepth_ + relativeBreakDepth)) {
                 return false;
             }
             if (!continueLabels_.putNew(label, blockDepth_ + relativeContinueDepth)) {
                 return false;
             }
         }
         return true;
     }
-    void removeLabels(const NameVector& labels) {
+    void removeLabels(const LabelVector& labels) {
         for (PropertyName* label : labels) {
             removeLabel(label, &breakLabels_);
             removeLabel(label, &continueLabels_);
         }
     }
     bool writeLabeledBreakOrContinue(PropertyName* label, bool isBreak) {
         LabelMap& map = isBreak ? breakLabels_ : continueLabels_;
         if (LabelMap::Ptr p = map.lookup(label)) {
@@ -5166,17 +5164,17 @@ CheckLoopConditionOnEntry(FunctionValida
     if (!f.writeBreakIf()) {
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
+CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::While));
     ParseNode* cond = BinaryLeft(whileStmt);
     ParseNode* body = BinaryRight(whileStmt);
 
     // A while loop `while(#cond) #body` is equivalent to:
     // (block $after_loop
     //    (loop $top
@@ -5208,17 +5206,17 @@ CheckWhile(FunctionValidator& f, ParseNo
     }
     if (labels) {
         f.removeLabels(*labels);
     }
     return true;
 }
 
 static bool
-CheckFor(FunctionValidator& f, ParseNode* forStmt, const NameVector* labels = nullptr)
+CheckFor(FunctionValidator& f, ParseNode* forStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(forStmt->isKind(ParseNodeKind::For));
     ParseNode* forHead = BinaryLeft(forStmt);
     ParseNode* body = BinaryRight(forStmt);
 
     if (!forHead->isKind(ParseNodeKind::ForHead)) {
         return f.fail(forHead, "unsupported for-loop statement");
     }
@@ -5294,17 +5292,17 @@ CheckFor(FunctionValidator& f, ParseNode
     if (labels) {
         f.removeLabels(*labels);
     }
 
     return true;
 }
 
 static bool
-CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
+CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::DoWhile));
     ParseNode* body = BinaryLeft(whileStmt);
     ParseNode* cond = BinaryRight(whileStmt);
 
     // A do-while loop `do { #body } while (#cond)` is equivalent to:
     // (block $after_loop           // depth X
     //   (loop $top                 // depth X+1
@@ -5351,24 +5349,24 @@ CheckDoWhile(FunctionValidator& f, Parse
         return false;
     }
     if (labels) {
         f.removeLabels(*labels);
     }
     return true;
 }
 
-static bool CheckStatementList(FunctionValidator& f, ParseNode*, const NameVector* = nullptr);
+static bool CheckStatementList(FunctionValidator& f, ParseNode*, const LabelVector* = nullptr);
 
 static bool
 CheckLabel(FunctionValidator& f, ParseNode* labeledStmt)
 {
     MOZ_ASSERT(labeledStmt->isKind(ParseNodeKind::Label));
 
-    NameVector labels;
+    LabelVector labels;
     ParseNode* innermost = labeledStmt;
     do {
         if (!labels.append(LabeledStatementLabel(innermost))) {
             return false;
         }
         innermost = LabeledStatementStatement(innermost);
     } while (innermost->getKind() == ParseNodeKind::Label);
 
@@ -5746,17 +5744,17 @@ CheckReturn(FunctionValidator& f, ParseN
     if (!f.encoder().writeOp(Op::Return)) {
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* labels /*= nullptr */)
+CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const LabelVector* labels /*= nullptr */)
 {
     MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
 
     if (!f.pushUnbreakableBlock(labels)) {
         return false;
     }
 
     for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
@@ -6625,17 +6623,17 @@ GetImports(JSContext* cx, const AsmJSMet
             return false;
         }
     }
 
     return true;
 }
 
 static bool
-TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
+TryInstantiate(JSContext* cx, CallArgs args, const Module& module, const AsmJSMetadata& metadata,
                MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
 {
     HandleValue globalVal = args.get(0);
     HandleValue importVal = args.get(1);
     HandleValue bufferVal = args.get(2);
 
     RootedArrayBufferObjectMaybeShared buffer(cx);
     RootedWasmMemoryObject memory(cx);
@@ -6729,32 +6727,32 @@ HandleInstantiationFailure(JSContext* cx
         return false;
     }
 
     // Call the function we just recompiled.
     args.setCallee(ObjectValue(*fun));
     return InternalCallOrConstruct(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
 }
 
-static Module&
+static const Module&
 AsmJSModuleFunctionToModule(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSModule(fun));
     const Value& v = fun->getExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT);
     return v.toObject().as<WasmModuleObject>().module();
 }
 
 // Implements the semantics of an asm.js module function that has been successfully validated.
 bool
 js::InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSFunction* callee = &args.callee().as<JSFunction>();
-    Module& module = AsmJSModuleFunctionToModule(callee);
+    const Module& module = AsmJSModuleFunctionToModule(callee);
     const AsmJSMetadata& metadata = module.metadata().asAsmJS();
 
     RootedWasmInstanceObject instanceObj(cx);
     RootedObject exportObj(cx);
     if (!TryInstantiate(cx, args, module, metadata, &instanceObj, &exportObj)) {
         // Link-time validation checks failed, so reparse the entire asm.js
         // module from scratch to get normal interpreted bytecode which we can
         // simply Invoke. Very slow.
@@ -7146,25 +7144,24 @@ struct ScopedCacheEntryOpenedForRead
             cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
         }
     }
 };
 
 } // unnamed namespace
 
 static JS::AsmJSCacheResult
-StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, const LinkData& linkData, JSContext* cx)
+StoreAsmJSModuleInCache(AsmJSParser& parser, const Module& module, const LinkData& linkData,
+                        JSContext* cx)
 {
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser)) {
         return JS::AsmJSCache_InternalError;
     }
 
-    MOZ_RELEASE_ASSERT(module.bytecode().length() == 0);
-
     size_t moduleSize = module.serializedSize(linkData);
     MOZ_RELEASE_ASSERT(moduleSize <= UINT32_MAX);
 
     Assumptions assumptions;
     if (!assumptions.init()) {
         return JS::AsmJSCache_InternalError;
     }
 
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -84,17 +84,17 @@ WasmHandleDebugTrap()
     const CallSite* site = code.lookupCallSite(fp->returnAddress);
     MOZ_ASSERT(site);
 
     // Advance to the actual trapping frame.
     fp = fp->callerFP;
     DebugFrame* debugFrame = DebugFrame::from(fp);
 
     if (site->kind() == CallSite::EnterFrame) {
-        if (!instance->enterFrameTrapsEnabled()) {
+        if (!instance->debug().enterFrameTrapsEnabled()) {
             return true;
         }
         debugFrame->setIsDebuggee();
         debugFrame->observe(cx);
         ResumeMode mode = Debugger::onEnterFrame(cx, debugFrame);
         if (mode == ResumeMode::Return) {
             // Ignoring forced return (ResumeMode::Return) -- changing code execution
             // order is not yet implemented in the wasm baseline.
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -276,17 +276,17 @@ StaticallyUnlink(uint8_t* base, const Li
 static bool
 AppendToString(const char* str, UTF8Bytes* bytes)
 {
     return bytes->append(str, strlen(str)) && bytes->append('\0');
 }
 #endif
 
 static void
-SendCodeRangesToProfiler(const ModuleSegment& ms, const Bytes& bytecode, const Metadata& metadata,
+SendCodeRangesToProfiler(const ModuleSegment& ms, const Metadata& metadata,
                          const CodeRangeVector& codeRanges)
 {
     bool enabled = false;
 #ifdef JS_ION_PERF
     enabled |= PerfFuncEnabled();
 #endif
 #ifdef MOZ_VTUNE
     enabled |= vtune::IsProfilingActive();
@@ -299,17 +299,17 @@ SendCodeRangesToProfiler(const ModuleSeg
         if (!codeRange.hasFuncIndex()) {
             continue;
         }
 
         uintptr_t start = uintptr_t(ms.base() + codeRange.begin());
         uintptr_t size = codeRange.end() - codeRange.begin();
 
         UTF8Bytes name;
-        if (!metadata.getFuncNameStandalone(&bytecode, codeRange.funcIndex(), &name)) {
+        if (!metadata.getFuncNameStandalone(codeRange.funcIndex(), &name)) {
             return;
         }
 
         // Avoid "unused" warnings
         (void)start;
         (void)size;
 
 #ifdef JS_ION_PERF
@@ -399,33 +399,32 @@ ModuleSegment::create(Tier tier, const B
 
     memcpy(codeBytes.get(), unlinkedBytes.begin(), codeLength);
 
     return js::MakeUnique<ModuleSegment>(tier, std::move(codeBytes), codeLength, linkData);
 }
 
 bool
 ModuleSegment::initialize(const CodeTier& codeTier,
-                          const ShareableBytes& bytecode,
                           const LinkData& linkData,
                           const Metadata& metadata,
                           const MetadataTier& metadataTier)
 {
     if (!StaticallyLink(*this, linkData)) {
         return false;
     }
 
     ExecutableAllocator::cacheFlush(base(), RoundupCodeLength(length()));
 
     // Reprotect the whole region to avoid having separate RW and RX mappings.
     if (!ExecutableAllocator::makeExecutable(base(), RoundupCodeLength(length()))) {
         return false;
     }
 
-    SendCodeRangesToProfiler(*this, bytecode.bytes, metadata, metadataTier.codeRanges);
+    SendCodeRangesToProfiler(*this, metadata, metadataTier.codeRanges);
 
     // See comments in CodeSegment::initialize() for why this must be last.
     return CodeSegment::initialize(codeTier);
 }
 
 size_t
 ModuleSegment::serializedSize() const
 {
@@ -971,66 +970,65 @@ MetadataTier::clone(const MetadataTier& 
 
 size_t
 Metadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(funcTypeIds) +
            SerializedPodVectorSize(globals) +
            SerializedPodVectorSize(tables) +
+           sizeof(moduleName) +
            SerializedPodVectorSize(funcNames) +
-           SerializedPodVectorSize(customSections) +
            filename.serializedSize() +
            sourceMapURL.serializedSize();
 }
 
-size_t
-Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return SizeOfVectorExcludingThis(funcTypeIds, mallocSizeOf) +
-           globals.sizeOfExcludingThis(mallocSizeOf) +
-           tables.sizeOfExcludingThis(mallocSizeOf) +
-           funcNames.sizeOfExcludingThis(mallocSizeOf) +
-           customSections.sizeOfExcludingThis(mallocSizeOf) +
-           filename.sizeOfExcludingThis(mallocSizeOf) +
-           sourceMapURL.sizeOfExcludingThis(mallocSizeOf);
-}
-
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
     MOZ_ASSERT(!debugEnabled && debugFuncArgTypes.empty() && debugFuncReturnTypes.empty());
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcTypeIds);
     cursor = SerializePodVector(cursor, globals);
     cursor = SerializePodVector(cursor, tables);
+    cursor = WriteBytes(cursor, &moduleName, sizeof(moduleName));
     cursor = SerializePodVector(cursor, funcNames);
-    cursor = SerializePodVector(cursor, customSections);
     cursor = filename.serialize(cursor);
     cursor = sourceMapURL.serialize(cursor);
     return cursor;
 }
 
 /* static */ const uint8_t*
 Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcTypeIds)) &&
     (cursor = DeserializePodVector(cursor, &globals)) &&
     (cursor = DeserializePodVector(cursor, &tables)) &&
+    (cursor = ReadBytes(cursor, &moduleName, sizeof(moduleName))) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
-    (cursor = DeserializePodVector(cursor, &customSections)) &&
     (cursor = filename.deserialize(cursor)) &&
     (cursor = sourceMapURL.deserialize(cursor));
     debugEnabled = false;
     debugFuncArgTypes.clear();
     debugFuncReturnTypes.clear();
     return cursor;
 }
 
+size_t
+Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return SizeOfVectorExcludingThis(funcTypeIds, mallocSizeOf) +
+           globals.sizeOfExcludingThis(mallocSizeOf) +
+           tables.sizeOfExcludingThis(mallocSizeOf) +
+           funcNames.sizeOfExcludingThis(mallocSizeOf) +
+           filename.sizeOfExcludingThis(mallocSizeOf) +
+           sourceMapURL.sizeOfExcludingThis(mallocSizeOf);
+}
+
 struct ProjectFuncIndex
 {
     const FuncExportVector& funcExports;
     explicit ProjectFuncIndex(const FuncExportVector& funcExports)
       : funcExports(funcExports)
     {}
     uint32_t operator[](size_t index) const {
         return funcExports[index].funcIndex();
@@ -1052,21 +1050,21 @@ MetadataTier::lookupFuncExport(uint32_t 
 
 const FuncExport&
 MetadataTier::lookupFuncExport(uint32_t funcIndex, size_t* funcExportIndex) const
 {
     return const_cast<MetadataTier*>(this)->lookupFuncExport(funcIndex, funcExportIndex);
 }
 
 static bool
-AppendNameInBytecode(const Bytes* maybeBytecode, const NameInBytecode& name, UTF8Bytes* bytes)
+AppendName(const Bytes& namePayload, const Name& name, UTF8Bytes* bytes)
 {
-    MOZ_RELEASE_ASSERT(name.offset <= maybeBytecode->length());
-    MOZ_RELEASE_ASSERT(name.length <= maybeBytecode->length() - name.offset);
-    return bytes->append((const char*)maybeBytecode->begin() + name.offset, name.length);
+    MOZ_RELEASE_ASSERT(name.offsetInNamePayload <= namePayload.length());
+    MOZ_RELEASE_ASSERT(name.length <= namePayload.length() - name.offsetInNamePayload);
+    return bytes->append((const char*)namePayload.begin() + name.offsetInNamePayload, name.length);
 }
 
 static bool
 AppendFunctionIndexName(uint32_t funcIndex, UTF8Bytes* bytes)
 {
     const char beforeFuncIndex[] = "wasm-function[";
     const char afterFuncIndex[] = "]";
 
@@ -1075,39 +1073,57 @@ AppendFunctionIndexName(uint32_t funcInd
     MOZ_ASSERT(funcIndexStr);
 
     return bytes->append(beforeFuncIndex, strlen(beforeFuncIndex)) &&
            bytes->append(funcIndexStr, strlen(funcIndexStr)) &&
            bytes->append(afterFuncIndex, strlen(afterFuncIndex));
 }
 
 bool
-Metadata::getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                      UTF8Bytes* name) const
+Metadata::getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const
 {
     if (moduleName && moduleName->length != 0) {
-        if (!AppendNameInBytecode(maybeBytecode, *moduleName, name)) {
+        if (!AppendName(namePayload->bytes, *moduleName, name)) {
             return false;
         }
         if (!name->append('.')) {
             return false;
         }
     }
 
     if (funcIndex < funcNames.length() && funcNames[funcIndex].length != 0) {
-        return AppendNameInBytecode(maybeBytecode, funcNames[funcIndex], name);
+        return AppendName(namePayload->bytes, funcNames[funcIndex], name);
     }
 
     if (ctx == NameContext::BeforeLocation) {
         return true;
     }
 
     return AppendFunctionIndexName(funcIndex, name);
 }
 
+bool
+CodeTier::initialize(const Code& code,
+                     const LinkData& linkData,
+                     const Metadata& metadata)
+{
+    MOZ_ASSERT(!initialized());
+    code_ = &code;
+
+    MOZ_ASSERT(lazyStubs_.lock()->empty());
+
+    // See comments in CodeSegment::initialize() for why this must be last.
+    if (!segment_->initialize(*this, linkData, metadata, *metadata_)) {
+        return false;
+    }
+
+    MOZ_ASSERT(initialized());
+    return true;
+}
+
 size_t
 CodeTier::serializedSize() const
 {
     return segment_->serializedSize() +
            metadata_->serializedSize();
 }
 
 uint8_t*
@@ -1217,36 +1233,35 @@ JumpTables::init(CompileMode mode, const
 Code::Code(UniqueCodeTier tier1, const Metadata& metadata, JumpTables&& maybeJumpTables)
   : tier1_(std::move(tier1)),
     metadata_(&metadata),
     profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector()),
     jumpTables_(std::move(maybeJumpTables))
 {}
 
 bool
-Code::initialize(const ShareableBytes& bytecode, const LinkData& linkData)
+Code::initialize(const LinkData& linkData)
 {
     MOZ_ASSERT(!initialized());
 
-    if (!tier1_->initialize(*this, bytecode, linkData, *metadata_)) {
+    if (!tier1_->initialize(*this, linkData, *metadata_)) {
         return false;
     }
 
     MOZ_ASSERT(initialized());
     return true;
 }
 
 bool
-Code::setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
-               const LinkData& linkData) const
+Code::setTier2(UniqueCodeTier tier2, const LinkData& linkData) const
 {
     MOZ_RELEASE_ASSERT(!hasTier2());
     MOZ_RELEASE_ASSERT(tier2->tier() == Tier::Ion && tier1_->tier() == Tier::Baseline);
 
-    if (!tier2->initialize(*this, bytecode, linkData, *metadata_)) {
+    if (!tier2->initialize(*this, linkData, *metadata_)) {
         return false;
     }
 
     tier2_ = std::move(tier2);
 
     return true;
 }
 
@@ -1410,17 +1425,17 @@ Code::lookupTrap(void* pc, Trap* trapOut
     return false;
 }
 
 // When enabled, generate profiling labels for every name in funcNames_ that is
 // the name of some Function CodeRange. This involves malloc() so do it now
 // since, once we start sampling, we'll be in a signal-handing context where we
 // cannot malloc.
 void
-Code::ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const
+Code::ensureProfilingLabels(bool profilingEnabled) const
 {
     auto labels = profilingLabels_.lock();
 
     if (!profilingEnabled) {
         labels->clear();
         return;
     }
 
@@ -1436,17 +1451,17 @@ Code::ensureProfilingLabels(const Bytes*
             continue;
         }
 
         ToCStringBuf cbuf;
         const char* bytecodeStr = NumberToCString(nullptr, &cbuf, codeRange.funcLineOrBytecode());
         MOZ_ASSERT(bytecodeStr);
 
         UTF8Bytes name;
-        if (!metadata().getFuncNameStandalone(maybeBytecode, codeRange.funcIndex(), &name)) {
+        if (!metadata().getFuncNameStandalone(codeRange.funcIndex(), &name)) {
             return;
         }
         if (!name.append(" (", 2)) {
             return;
         }
 
         if (const char* filename = metadata().filename.get()) {
             if (!name.append(filename, strlen(filename))) {
@@ -1510,36 +1525,16 @@ Code::addSizeOfMiscIfNotSeen(MallocSizeO
              profilingLabels_.lock()->sizeOfExcludingThis(mallocSizeOf) +
              jumpTables_.sizeOfMiscExcludingThis();
 
     for (auto t : tiers()) {
         codeTier(t).addSizeOfMisc(mallocSizeOf, code, data);
     }
 }
 
-bool
-CodeTier::initialize(const Code& code,
-                     const ShareableBytes& bytecode,
-                     const LinkData& linkData,
-                     const Metadata& metadata)
-{
-    MOZ_ASSERT(!initialized());
-    code_ = &code;
-
-    MOZ_ASSERT(lazyStubs_.lock()->empty());
-
-    // See comments in CodeSegment::initialize() for why this must be last.
-    if (!segment_->initialize(*this, bytecode, linkData, metadata, *metadata_)) {
-        return false;
-    }
-
-    MOZ_ASSERT(initialized());
-    return true;
-}
-
 size_t
 Code::serializedSize() const
 {
     return metadata().serializedSize() +
            codeTier(Tier::Serialized).serializedSize();
 }
 
 uint8_t*
@@ -1549,17 +1544,16 @@ Code::serialize(uint8_t* cursor, const L
 
     cursor = metadata().serialize(cursor);
     cursor = codeTier(Tier::Serialized).serialize(cursor, linkData);
     return cursor;
 }
 
 /* static */ const uint8_t*
 Code::deserialize(const uint8_t* cursor,
-                  const ShareableBytes& bytecode,
                   const LinkData& linkData,
                   Metadata& metadata,
                   SharedCode* out)
 {
     cursor = metadata.deserialize(cursor);
     if (!cursor) {
         return nullptr;
     }
@@ -1571,15 +1565,15 @@ Code::deserialize(const uint8_t* cursor,
     }
 
     JumpTables jumpTables;
     if (!jumpTables.init(CompileMode::Once, codeTier->segment(), codeTier->metadata().codeRanges)) {
         return nullptr;
     }
 
     MutableCode code = js_new<Code>(std::move(codeTier), metadata, std::move(jumpTables));
-    if (!code || !code->initialize(bytecode, linkData)) {
+    if (!code || !code->initialize(linkData)) {
         return nullptr;
     }
 
     *out = code;
     return cursor;
 }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -173,17 +173,16 @@ class ModuleSegment : public CodeSegment
     static UniqueModuleSegment create(Tier tier,
                                       jit::MacroAssembler& masm,
                                       const LinkData& linkData);
     static UniqueModuleSegment create(Tier tier,
                                       const Bytes& unlinkedBytes,
                                       const LinkData& linkData);
 
     bool initialize(const CodeTier& codeTier,
-                    const ShareableBytes& bytecode,
                     const LinkData& linkData,
                     const Metadata& metadata,
                     const MetadataTier& metadataTier);
 
     Tier tier() const { return tier_; }
 
     // Pointers to stubs to which PC is redirected from the signal-handler.
 
@@ -322,54 +321,16 @@ typedef Vector<FuncImport, 0, SystemAllo
 
 enum class MemoryUsage
 {
     None = false,
     Unshared = 1,
     Shared = 2
 };
 
-// NameInBytecode represents a name that is embedded in the wasm bytecode.
-// The presence of NameInBytecode implies that bytecode has been kept.
-
-struct NameInBytecode
-{
-    uint32_t offset;
-    uint32_t length;
-
-    NameInBytecode()
-      : offset(UINT32_MAX), length(0)
-    {}
-    NameInBytecode(uint32_t offset, uint32_t length)
-      : offset(offset), length(length)
-    {}
-};
-
-typedef Vector<NameInBytecode, 0, SystemAllocPolicy> NameInBytecodeVector;
-
-// CustomSection represents a custom section in the bytecode which can be
-// extracted via Module.customSections. The (offset, length) pair does not
-// include the custom section name.
-
-struct CustomSection
-{
-    NameInBytecode name;
-    uint32_t offset;
-    uint32_t length;
-
-    CustomSection() = default;
-    CustomSection(NameInBytecode name, uint32_t offset, uint32_t length)
-      : name(name), offset(offset), length(length)
-    {}
-};
-
-typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
-typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
-typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
-
 // Metadata holds all the data that is needed to describe compiled wasm code
 // at runtime (as opposed to data that is only used to statically link or
 // instantiate a module).
 //
 // Metadata is built incrementally by ModuleGenerator and then shared immutably
 // between modules.
 //
 // The Metadata structure is split into tier-invariant and tier-variant parts;
@@ -381,41 +342,48 @@ struct MetadataCacheablePod
 {
     ModuleKind            kind;
     MemoryUsage           memoryUsage;
     HasGcTypes            temporaryGcTypesConfigured;
     uint32_t              minMemoryLength;
     uint32_t              globalDataLength;
     Maybe<uint32_t>       maxMemoryLength;
     Maybe<uint32_t>       startFuncIndex;
-    Maybe<NameInBytecode> moduleName;
+    Maybe<uint32_t>       nameCustomSectionIndex;
     bool                  filenameIsURL;
 
     explicit MetadataCacheablePod(ModuleKind kind)
       : kind(kind),
         memoryUsage(MemoryUsage::None),
         temporaryGcTypesConfigured(HasGcTypes::False),
         minMemoryLength(0),
         globalDataLength(0),
         filenameIsURL(false)
     {}
 };
 
 typedef uint8_t ModuleHash[8];
+typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
+typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
 
 struct Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
 {
     FuncTypeWithIdVector  funcTypeIds;
     GlobalDescVector      globals;
     TableDescVector       tables;
-    NameInBytecodeVector  funcNames;
-    CustomSectionVector   customSections;
     CacheableChars        filename;
     CacheableChars        sourceMapURL;
 
+    // namePayload points at the name section's CustomSection::payload so that
+    // the Names (which are use payload-relative offsets) can be used
+    // independently of the Module without duplicating the name section.
+    SharedBytes           namePayload;
+    Maybe<Name>           moduleName;
+    NameVector            funcNames;
+
     // Debug-enabled code is not serialized.
     bool                  debugEnabled;
     FuncArgTypesVector    debugFuncArgTypes;
     FuncReturnTypesVector debugFuncReturnTypes;
     ModuleHash            debugHash;
 
     explicit Metadata(ModuleKind kind = ModuleKind::Wasm)
       : MetadataCacheablePod(kind),
@@ -454,24 +422,23 @@ struct Metadata : public ShareableBase<M
 
     // The Developer-Facing Display Conventions section of the WebAssembly Web
     // API spec defines two cases for displaying a wasm function name:
     //  1. the function name stands alone
     //  2. the function name precedes the location
 
     enum NameContext { Standalone, BeforeLocation };
 
-    virtual bool getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                             UTF8Bytes* name) const;
+    virtual bool getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const;
 
-    bool getFuncNameStandalone(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const {
-        return getFuncName(NameContext::Standalone, maybeBytecode, funcIndex, name);
+    bool getFuncNameStandalone(uint32_t funcIndex, UTF8Bytes* name) const {
+        return getFuncName(NameContext::Standalone, funcIndex, name);
     }
-    bool getFuncNameBeforeLocation(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const {
-        return getFuncName(NameContext::BeforeLocation, maybeBytecode, funcIndex, name);
+    bool getFuncNameBeforeLocation(uint32_t funcIndex, UTF8Bytes* name) const {
+        return getFuncName(NameContext::BeforeLocation, funcIndex, name);
     }
 
     WASM_DECLARE_SERIALIZABLE_VIRTUAL(Metadata);
 };
 
 typedef RefPtr<Metadata> MutableMetadata;
 typedef RefPtr<const Metadata> SharedMetadata;
 
@@ -629,21 +596,17 @@ class CodeTier
     CodeTier(UniqueMetadataTier metadata, UniqueModuleSegment segment)
       : code_(nullptr),
         metadata_(std::move(metadata)),
         segment_(std::move(segment)),
         lazyStubs_(mutexForTier(segment_->tier()))
     {}
 
     bool initialized() const { return !!code_ && segment_->initialized(); }
-
-    bool initialize(const Code& code,
-                    const ShareableBytes& bytecode,
-                    const LinkData& linkData,
-                    const Metadata& metadata);
+    bool initialize(const Code& code, const LinkData& linkData, const Metadata& metadata);
 
     Tier tier() const { return segment_->tier(); }
     const ExclusiveData<LazyStubTier>& lazyStubs() const { return lazyStubs_; }
     const MetadataTier& metadata() const { return *metadata_.get(); }
     const ModuleSegment& segment() const { return *segment_.get(); }
     const Code& code() const { MOZ_ASSERT(initialized()); return *code_; }
 
     const CodeRange* lookupRange(const void* pc) const;
@@ -725,27 +688,26 @@ class Code : public ShareableBase<Code>
     JumpTables                          jumpTables_;
 
   public:
     Code(UniqueCodeTier tier1,
          const Metadata& metadata,
          JumpTables&& maybeJumpTables);
     bool initialized() const { return tier1_->initialized(); }
 
-    bool initialize(const ShareableBytes& bytecode, const LinkData& linkData);
+    bool initialize(const LinkData& linkData);
 
     void setTieringEntry(size_t i, void* target) const { jumpTables_.setTieringEntry(i, target); }
     void** tieringJumpTable() const { return jumpTables_.tiering(); }
 
     void setJitEntry(size_t i, void* target) const { jumpTables_.setJitEntry(i, target); }
     void** getAddressOfJitEntry(size_t i) const { return jumpTables_.getAddressOfJitEntry(i); }
     uint32_t getFuncIndex(JSFunction* fun) const;
 
-    bool setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
-                  const LinkData& linkData) const;
+    bool setTier2(UniqueCodeTier tier2, const LinkData& linkData) const;
     void commitTier2() const;
 
     bool hasTier2() const { return hasTier2_; }
     Tiers tiers() const;
     bool hasTier(Tier t) const;
 
     Tier stableTier() const;    // This is stable during a run
     Tier bestTier() const;      // This may transition from Baseline -> Ion at any time
@@ -765,17 +727,17 @@ class Code : public ShareableBase<Code>
     const CallSite* lookupCallSite(void* returnAddress) const;
     const CodeRange* lookupFuncRange(void* pc) const;
     bool containsCodePC(const void* pc) const;
     bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const;
 
     // To save memory, profilingLabels_ are generated lazily when profiling mode
     // is enabled.
 
-    void ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const;
+    void ensureProfilingLabels(bool profilingEnabled) const;
     const char* profilingLabel(uint32_t funcIndex) const;
 
     // about:memory reporting:
 
     void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf,
                                 Metadata::SeenSet* seenMetadata,
                                 Code::SeenSet* seenCode,
                                 size_t* code,
@@ -783,17 +745,16 @@ class Code : public ShareableBase<Code>
 
     // A Code object is serialized as the length and bytes of the machine code
     // after statically unlinking it; the Code is then later recreated from the
     // machine code and other parts.
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
     static const uint8_t* deserialize(const uint8_t* cursor,
-                                      const ShareableBytes& bytecode,
                                       const LinkData& linkData,
                                       Metadata& metadata,
                                       SharedCode* code);
 };
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -538,22 +538,23 @@ wasm::CompileBuffer(const CompileArgs& a
     if (!DecodeModuleTail(d, &env, mg.deferredValidationState())) {
         return nullptr;
     }
 
     return mg.finishModule(bytecode);
 }
 
 void
-wasm::CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled)
+wasm::CompileTier2(const CompileArgs& args, const Bytes& bytecode, const Module& module,
+                   Atomic<bool>* cancelled)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
     UniqueChars error;
-    Decoder d(module.bytecode().bytes, 0, &error);
+    Decoder d(bytecode, 0, &error);
 
     HasGcTypes gcTypesConfigured = HasGcTypes::False; // No Ion support yet
     CompilerEnvironment compilerEnv(CompileMode::Tier2, Tier::Ion, DebugEnabled::False,
                                     gcTypesConfigured);
     ModuleEnvironment env(gcTypesConfigured,
                           &compilerEnv,
                           args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env)) {
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -87,17 +87,18 @@ SharedModule
 CompileBuffer(const CompileArgs& args,
               const ShareableBytes& bytecode,
               UniqueChars* error,
               UniqueCharsVector* warnings);
 
 // Attempt to compile the second tier of the given wasm::Module.
 
 void
-CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled);
+CompileTier2(const CompileArgs& args, const Bytes& bytecode, const Module& module,
+             Atomic<bool>* cancelled);
 
 // Compile the given WebAssembly module which has been broken into three
 // partitions:
 //  - envBytes contains a complete ModuleEnvironment that has already been
 //    copied in from the stream.
 //  - codeBytes is pre-sized to hold the complete code section when the stream
 //    completes.
 //  - The range [codeBytes.begin(), codeStreamEnd) contains the bytes currently
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -19,163 +19,109 @@
 #include "wasm/WasmDebug.h"
 
 #include "mozilla/BinarySearch.h"
 
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/MacroAssembler.h"
-#include "util/StringBuffer.h"
-#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::BinarySearchIf;
 
-DebugState::DebugState(SharedCode code,
-                       const ShareableBytes* maybeBytecode,
-                       bool binarySource)
-  : code_(std::move(code)),
-    maybeBytecode_(maybeBytecode),
+DebugState::DebugState(const Code& code, const Module& module, bool binarySource)
+  : code_(&code),
+    module_(&module),
     binarySource_(binarySource),
+    enterFrameTrapsEnabled_(false),
     enterAndLeaveFrameTrapsCounter_(0)
 {
-    MOZ_ASSERT_IF(debugEnabled(), maybeBytecode);
+    MOZ_ASSERT(code.metadata().debugEnabled);
 }
 
-const char enabledMessage[] =
-    "Restart with developer tools open to view WebAssembly source.";
-
-const char noBinarySource[] =
-    "Configure the debugger to display WebAssembly bytecode.";
-
-const char notGeneratedMessage[] =
-    "WebAssembly text generation was disabled.";
-
 static const uint32_t DefaultBinarySourceColumnNumber = 1;
 
 static const CallSite*
 SlowCallSiteSearchByOffset(const MetadataTier& metadata, uint32_t offset)
 {
     for (const CallSite& callSite : metadata.callSites) {
         if (callSite.lineOrBytecode() == offset && callSite.kind() == CallSiteDesc::Breakpoint) {
             return &callSite;
         }
     }
     return nullptr;
 }
 
-JSString*
-DebugState::createText(JSContext* cx)
-{
-    StringBuffer buffer(cx);
-    if (!maybeBytecode_) {
-        if (!buffer.append(enabledMessage)) {
-            return nullptr;
-        }
-    } else if (binarySource_) {
-        if (!buffer.append(notGeneratedMessage)) {
-            return nullptr;
-        }
-    } else {
-        if (!buffer.append(noBinarySource)) {
-            return nullptr;
-        }
-    }
-    return buffer.finishString();
-}
-
 bool
 DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets)
 {
-    if (!debugEnabled()) {
-        return true;
-    }
     if (!binarySource_) {
         return true;
     }
     const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
     if (callsite && !offsets->append(lineno)) {
         return false;
     }
     return true;
 }
 
 bool
 DebugState::getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets)
 {
-    if (!metadata().debugEnabled) {
-        return true;
-    }
     if (!binarySource_) {
         return true;
     }
     for (const CallSite& callSite : metadata(Tier::Debug).callSites) {
         if (callSite.kind() != CallSite::Breakpoint) {
             continue;
         }
         uint32_t offset = callSite.lineOrBytecode();
         if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) {
             return false;
         }
     }
     return true;
 }
 
 bool
-DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column)
+DebugState::getOffsetLocation(uint32_t offset, size_t* lineno, size_t* column)
 {
-    *found = false;
-    if (!debugEnabled()) {
-        return true;
-    }
     if (!binarySource_) {
-        return true;
+        return false;
     }
     if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) {
-        return true; // offset was not found
+        return false;
     }
-    *found = true;
     *lineno = offset;
     *column = DefaultBinarySourceColumnNumber;
     return true;
 }
 
-bool
-DebugState::totalSourceLines(JSContext* cx, uint32_t* count)
+uint32_t
+DebugState::totalSourceLines()
 {
-    *count = 0;
-    if (!debugEnabled()) {
-        return true;
-    }
-    if (!binarySource_) {
-        return true;
-    }
-    if (maybeBytecode_) {
-        *count = maybeBytecode_->length();
-    }
-    return true;
+    return binarySource_ ? bytecode().length() : 0;
 }
 
 bool
 DebugState::stepModeEnabled(uint32_t funcIndex) const
 {
     return stepModeCounters_.lookup(funcIndex).found();
 }
 
 bool
 DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     const CodeRange& codeRange = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     MOZ_ASSERT(codeRange.isFunction());
 
     StepModeCounters::AddPtr p = stepModeCounters_.lookupForAdd(funcIndex);
     if (p) {
         MOZ_ASSERT(p->value() > 0);
         p->value()++;
         return true;
@@ -199,17 +145,16 @@ DebugState::incrementStepModeCount(JSCon
         }
     }
     return true;
 }
 
 bool
 DebugState::decrementStepModeCount(FreeOp* fop, uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     const CodeRange& codeRange = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     MOZ_ASSERT(codeRange.isFunction());
 
     MOZ_ASSERT(!stepModeCounters_.empty());
     StepModeCounters::Ptr p = stepModeCounters_.lookup(funcIndex);
     MOZ_ASSERT(p);
     if (--p->value()) {
         return true;
@@ -232,26 +177,22 @@ DebugState::decrementStepModeCount(FreeO
         }
     }
     return true;
 }
 
 bool
 DebugState::hasBreakpointTrapAtOffset(uint32_t offset)
 {
-    if (!debugEnabled()) {
-        return false;
-    }
     return SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
 }
 
 void
 DebugState::toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled)
 {
-    MOZ_ASSERT(debugEnabled());
     const CallSite* callSite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
     if (!callSite) {
         return;
     }
     size_t debugTrapOffset = callSite->returnAddressOffset();
 
     const ModuleSegment& codeSegment = code_->segment(Tier::Debug);
     const CodeRange* codeRange = code_->lookupFuncRange(codeSegment.base() + debugTrapOffset);
@@ -359,17 +300,16 @@ DebugState::toggleDebugTrap(uint32_t off
     } else {
         MacroAssembler::patchCallToNop(trap);
     }
 }
 
 void
 DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled)
 {
-    MOZ_ASSERT(debugEnabled());
     MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);
 
     bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
     if (enabled) {
         ++enterAndLeaveFrameTrapsCounter_;
     } else {
         --enterAndLeaveFrameTrapsCounter_;
     }
@@ -385,41 +325,49 @@ DebugState::adjustEnterAndLeaveFrameTrap
     for (const CallSite& callSite : callSites(Tier::Debug)) {
         if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame) {
             continue;
         }
         toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled);
     }
 }
 
+void
+DebugState::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
+{
+    if (enterFrameTrapsEnabled_ == enabled) {
+        return;
+    }
+
+    adjustEnterAndLeaveFrameTrapsState(cx, enabled);
+
+    enterFrameTrapsEnabled_ = enabled;
+}
+
 bool
 DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength)
 {
-    MOZ_ASSERT(debugEnabled());
-
     const ValTypeVector& args = metadata().debugFuncArgTypes[funcIndex];
     *argsLength = args.length();
     if (!locals->appendAll(args)) {
         return false;
     }
 
     // Decode local var types from wasm binary function body.
     const CodeRange& range = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     // In wasm, the Code points to the function start via funcLineOrBytecode.
-    MOZ_ASSERT(!metadata().isAsmJS() && maybeBytecode_);
     size_t offsetInModule = range.funcLineOrBytecode();
-    Decoder d(maybeBytecode_->begin() + offsetInModule,  maybeBytecode_->end(),
-              offsetInModule, /* error = */ nullptr);
+    Decoder d(bytecode().begin() + offsetInModule,  bytecode().end(), offsetInModule,
+              /* error = */ nullptr);
     return DecodeValidatedLocalEntries(d, locals);
 }
 
 ExprType
 DebugState::debugGetResultType(uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     return metadata().debugFuncReturnTypes[funcIndex];
 }
 
 bool
 DebugState::getGlobal(Instance& instance, uint32_t globalIndex, MutableHandleValue vp)
 {
     const GlobalDesc& global = metadata().globals[globalIndex];
 
@@ -470,87 +418,31 @@ DebugState::getGlobal(Instance& instance
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
 
-
-JSString*
-DebugState::debugDisplayURL(JSContext* cx) const
-{
-    // Build wasm module URL from following parts:
-    // - "wasm:" as protocol;
-    // - URI encoded filename from metadata (if can be encoded), plus ":";
-    // - 64-bit hash of the module bytes (as hex dump).
-
-    js::StringBuffer result(cx);
-    if (!result.append("wasm:")) {
-        return nullptr;
-    }
-
-    if (const char* filename = metadata().filename.get()) {
-        // EncodeURI returns false due to invalid chars or OOM -- fail only
-        // during OOM.
-        JSString* filenamePrefix = EncodeURI(cx, filename, strlen(filename));
-        if (!filenamePrefix) {
-            if (cx->isThrowingOutOfMemory()) {
-                return nullptr;
-            }
-
-            MOZ_ASSERT(!cx->isThrowingOverRecursed());
-            cx->clearPendingException(); // ignore invalid URI
-        } else if (!result.append(filenamePrefix)) {
-            return nullptr;
-        }
-    }
-
-    if (metadata().debugEnabled) {
-        if (!result.append(":")) {
-            return nullptr;
-        }
-
-        const ModuleHash& hash = metadata().debugHash;
-        for (size_t i = 0; i < sizeof(ModuleHash); i++) {
-            char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
-            if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) {
-                return nullptr;
-            }
-            if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) {
-                return nullptr;
-            }
-        }
-    }
-
-    return result.finishString();
-}
-
 bool
 DebugState::getSourceMappingURL(JSContext* cx, MutableHandleString result) const
 {
     result.set(nullptr);
-    if (!maybeBytecode_) {
-        return true;
-    }
 
-    for (const CustomSection& customSection : metadata().customSections) {
-        const NameInBytecode& sectionName = customSection.name;
-        if (strlen(SourceMappingURLSectionName) != sectionName.length ||
-            memcmp(SourceMappingURLSectionName, maybeBytecode_->begin() + sectionName.offset,
-                   sectionName.length) != 0)
+    for (const CustomSection& customSection : module_->customSections()) {
+        const Bytes& sectionName = customSection.name;
+        if (strlen(SourceMappingURLSectionName) != sectionName.length() ||
+            memcmp(SourceMappingURLSectionName, sectionName.begin(), sectionName.length()) != 0)
         {
             continue;
         }
 
         // Parse found "SourceMappingURL" custom section.
-        Decoder d(maybeBytecode_->begin() + customSection.offset,
-                  maybeBytecode_->begin() + customSection.offset + customSection.length,
-                  customSection.offset,
+        Decoder d(customSection.payload->begin(), customSection.payload->end(), 0,
                   /* error = */ nullptr);
         uint32_t nchars;
         if (!d.readVarU32(&nchars)) {
             return true; // ignoring invalid section data
         }
         const uint8_t* chars;
         if (!d.readBytes(nchars, &chars) || d.currentPosition() != d.end()) {
             return true; // ignoring invalid section data
@@ -582,12 +474,10 @@ void
 DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                           Metadata::SeenSet* seenMetadata,
                           ShareableBytes::SeenSet* seenBytes,
                           Code::SeenSet* seenCode,
                           size_t* code,
                           size_t* data) const
 {
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
-    if (maybeBytecode_) {
-        *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
-    }
+    module_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
 }
--- a/js/src/wasm/WasmDebug.h
+++ b/js/src/wasm/WasmDebug.h
@@ -15,17 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_debug_h
 #define wasm_debug_h
 
 #include "js/HashTable.h"
-#include "wasm/WasmCode.h"
+#include "wasm/WasmModule.h"
 #include "wasm/WasmTypes.h"
 
 namespace js {
 
 class Debugger;
 class WasmBreakpoint;
 class WasmBreakpointSite;
 class WasmInstanceObject;
@@ -50,48 +50,48 @@ struct ExprLoc
 
 typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> StepModeCounters;
 typedef HashMap<uint32_t, WasmBreakpointSite*, DefaultHasher<uint32_t>, SystemAllocPolicy>
     WasmBreakpointSiteMap;
 
 class DebugState
 {
     const SharedCode         code_;
-    const SharedBytes        maybeBytecode_;
+    const SharedModule       module_;
     bool                     binarySource_;
 
     // State maintained when debugging is enabled. In this case, the Code is
     // not actually shared, but is referenced uniquely by the instance that is
     // being debugged.
 
+    bool                     enterFrameTrapsEnabled_;
     uint32_t                 enterAndLeaveFrameTrapsCounter_;
     WasmBreakpointSiteMap    breakpointSites_;
     StepModeCounters         stepModeCounters_;
 
     void toggleDebugTrap(uint32_t offset, bool enabled);
 
   public:
-    DebugState(SharedCode code,
-               const ShareableBytes* maybeBytecode,
-               bool binarySource);
+    DebugState(const Code& code, const Module& module, bool binarySource);
 
-    const Bytes* maybeBytecode() const { return maybeBytecode_ ? &maybeBytecode_->bytes : nullptr; }
+    const Bytes& bytecode() const { return module_->debugBytecode(); }
     bool binarySource() const { return binarySource_; }
 
-    JSString* createText(JSContext* cx);
     bool getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets);
     bool getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets);
-    bool getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column);
-    bool totalSourceLines(JSContext* cx, uint32_t* count);
+    bool getOffsetLocation(uint32_t offset, size_t* lineno, size_t* column);
+    uint32_t totalSourceLines();
 
     // The Code can track enter/leave frame events. Any such event triggers
     // debug trap. The enter/leave frame events enabled or disabled across
     // all functions.
 
     void adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled);
+    void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
+    bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
 
     // When the Code is debugEnabled, individual breakpoints can be enabled or
     // disabled at instruction offsets.
 
     bool hasBreakpointTrapAtOffset(uint32_t offset);
     void toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled);
     WasmBreakpointSite* getOrCreateBreakpointSite(JSContext* cx, uint32_t offset);
     bool hasBreakpointSite(uint32_t offset);
@@ -108,24 +108,22 @@ class DebugState
     // Stack inspection helpers.
 
     bool debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength);
     ExprType debugGetResultType(uint32_t funcIndex);
     bool getGlobal(Instance& instance, uint32_t globalIndex, MutableHandleValue vp);
 
     // Debug URL helpers.
 
-    JSString* debugDisplayURL(JSContext* cx) const;
     bool getSourceMappingURL(JSContext* cx, MutableHandleString result) const;
 
     // Accessors for commonly used elements of linked structures.
 
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
-    bool debugEnabled() const { return metadata().debugEnabled; }
     const CodeRangeVector& codeRanges(Tier t) const { return metadata(t).codeRanges; }
     const CallSiteVector& callSites(Tier t) const { return metadata(t).callSites; }
 
     uint32_t funcToCodeRangeIndex(uint32_t funcIndex) const {
         return metadata(Tier::Debug).funcToCodeRange[funcIndex];
     }
 
     // about:memory reporting:
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -444,27 +444,21 @@ typedef HashMap<uint32_t, uint32_t, Defa
 typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapMaybeOffsetArray;
 
 bool
 ModuleGenerator::linkCallSites()
 {
     masm_.haltingAlign(CodeAlignment);
 
     // Create far jumps for calls that have relative offsets that may otherwise
-    // go out of range. Far jumps are created for two cases: direct calls
-    // between function definitions and calls to trap exits by trap out-of-line
-    // paths. Far jump code is shared when possible to reduce bloat. This method
-    // is called both between function bodies (at a frequency determined by the
-    // ISA's jump range) and once at the very end of a module's codegen after
-    // all possible calls/traps have been emitted.
+    // go out of range. This method is called both between function bodies (at a
+    // frequency determined by the ISA's jump range) and once at the very end of
+    // a module's codegen after all possible calls/traps have been emitted.
 
     OffsetMap existingCallFarJumps;
-
-    TrapMaybeOffsetArray existingTrapFarJumps;
-
     for (; lastPatchedCallSite_ < metadataTier_->callSites.length(); lastPatchedCallSite_++) {
         const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
         const CallSiteTarget& target = callSiteTargets_[lastPatchedCallSite_];
         uint32_t callerOffset = callSite.returnAddressOffset();
         switch (callSite.kind()) {
           case CallSiteDesc::Dynamic:
           case CallSiteDesc::Symbolic:
             break;
@@ -833,17 +827,17 @@ ModuleGenerator::finishFuncDefs()
         return false;
     }
 
     finishedFuncDefs_ = true;
     return true;
 }
 
 bool
-ModuleGenerator::finishCode()
+ModuleGenerator::finishCodegen()
 {
     // Now that all functions and stubs are generated and their CodeRanges
     // known, patch all calls (which can emit far jumps) and far jumps. Linking
     // can emit tiny far-jump stubs, so there is an ordering dependency here.
 
     if (!linkCallSites()) {
         return false;
     }
@@ -865,17 +859,17 @@ ModuleGenerator::finishCode()
     MOZ_ASSERT(masm_.symbolicAccesses().empty());
     MOZ_ASSERT(masm_.codeLabels().empty());
 
     masm_.finish();
     return !masm_.oom();
 }
 
 bool
-ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
+ModuleGenerator::finishMetadataTier()
 {
     // Assert all sorted metadata is sorted.
 #ifdef DEBUG
     uint32_t last = 0;
     for (const CodeRange& codeRange : metadataTier_->codeRanges) {
         MOZ_ASSERT(codeRange.begin() >= last);
         last = codeRange.end();
     }
@@ -896,79 +890,33 @@ ModuleGenerator::finishMetadata(const Sh
 
     last = 0;
     for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
         MOZ_ASSERT(debugTrapFarJumpOffset >= last);
         last = debugTrapFarJumpOffset;
     }
 #endif
 
-    // Copy over data from the ModuleEnvironment.
-
-    metadata_->memoryUsage = env_->memoryUsage;
-    metadata_->temporaryGcTypesConfigured = env_->gcTypesConfigured;
-    metadata_->minMemoryLength = env_->minMemoryLength;
-    metadata_->maxMemoryLength = env_->maxMemoryLength;
-    metadata_->startFuncIndex = env_->startFuncIndex;
-    metadata_->moduleName = env_->moduleName;
-    metadata_->tables = std::move(env_->tables);
-    metadata_->globals = std::move(env_->globals);
-    metadata_->funcNames = std::move(env_->funcNames);
-    metadata_->customSections = std::move(env_->customSections);
-
-    // Inflate the global bytes up to page size so that the total bytes are a
-    // page size (as required by the allocator functions).
-
-    metadata_->globalDataLength = AlignBytes(metadata_->globalDataLength, gc::SystemPageSize());
-
     // These Vectors can get large and the excess capacity can be significant,
     // so realloc them down to size.
 
     metadataTier_->funcToCodeRange.podResizeToFit();
     metadataTier_->codeRanges.podResizeToFit();
     metadataTier_->callSites.podResizeToFit();
     metadataTier_->trapSites.podResizeToFit();
     metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
     for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
         metadataTier_->trapSites[trap].podResizeToFit();
     }
 
-    // Copy over additional debug information.
-
-    if (env_->debugEnabled()) {
-        metadata_->debugEnabled = true;
-
-        const size_t numFuncTypes = env_->funcTypes.length();
-        if (!metadata_->debugFuncArgTypes.resize(numFuncTypes)) {
-            return false;
-        }
-        if (!metadata_->debugFuncReturnTypes.resize(numFuncTypes)) {
-            return false;
-        }
-        for (size_t i = 0; i < numFuncTypes; i++) {
-            if (!metadata_->debugFuncArgTypes[i].appendAll(env_->funcTypes[i]->args())) {
-                return false;
-            }
-            metadata_->debugFuncReturnTypes[i] = env_->funcTypes[i]->ret();
-        }
-
-        static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash),
-                      "The ModuleHash size shall not exceed the SHA1 hash size.");
-        mozilla::SHA1Sum::Hash hash;
-        mozilla::SHA1Sum sha1Sum;
-        sha1Sum.update(bytecode.begin(), bytecode.length());
-        sha1Sum.finish(hash);
-        memcpy(metadata_->debugHash, hash, sizeof(ModuleHash));
-    }
-
     return true;
 }
 
-UniqueModuleSegment
-ModuleGenerator::finish(const ShareableBytes& bytecode)
+UniqueCodeTier
+ModuleGenerator::finishCodeTier()
 {
     MOZ_ASSERT(finishedFuncDefs_);
 
     while (outstanding_ > 0) {
         if (!finishOutstandingTask()) {
             return nullptr;
         }
     }
@@ -996,144 +944,230 @@ ModuleGenerator::finish(const ShareableB
     // All functions and stubs have been compiled.  Perform module-end
     // validation.
 
     if (!deferredValidationState_.lock()->performDeferredValidation(*env_, error_))
         return nullptr;
 
     // Finish linking and metadata.
 
-    if (!finishCode()) {
+    if (!finishCodegen()) {
+        return nullptr;
+    }
+
+    if (!finishMetadataTier()) {
+        return nullptr;
+    }
+
+    UniqueModuleSegment segment = ModuleSegment::create(tier(), masm_, *linkData_);
+    if (!segment) {
         return nullptr;
     }
 
-    if (!finishMetadata(bytecode)) {
-        return nullptr;
+    return js::MakeUnique<CodeTier>(std::move(metadataTier_), std::move(segment));
+}
+
+bool
+ModuleGenerator::finishMetadata(const Bytes& bytecode)
+{
+    // Finish initialization of Metadata, which is only needed for constructing
+    // the initial Module, not for tier-2 compilation.
+    MOZ_ASSERT(mode() != CompileMode::Tier2);
+
+    // Copy over data from the ModuleEnvironment.
+
+    metadata_->memoryUsage = env_->memoryUsage;
+    metadata_->temporaryGcTypesConfigured = env_->gcTypesConfigured;
+    metadata_->minMemoryLength = env_->minMemoryLength;
+    metadata_->maxMemoryLength = env_->maxMemoryLength;
+    metadata_->startFuncIndex = env_->startFuncIndex;
+    metadata_->tables = std::move(env_->tables);
+    metadata_->globals = std::move(env_->globals);
+    metadata_->nameCustomSectionIndex = env_->nameCustomSectionIndex;
+    metadata_->moduleName = env_->moduleName;
+    metadata_->funcNames = std::move(env_->funcNames);
+
+    // Copy over additional debug information.
+
+    if (env_->debugEnabled()) {
+        metadata_->debugEnabled = true;
+
+        const size_t numFuncTypes = env_->funcTypes.length();
+        if (!metadata_->debugFuncArgTypes.resize(numFuncTypes)) {
+            return false;
+        }
+        if (!metadata_->debugFuncReturnTypes.resize(numFuncTypes)) {
+            return false;
+        }
+        for (size_t i = 0; i < numFuncTypes; i++) {
+            if (!metadata_->debugFuncArgTypes[i].appendAll(env_->funcTypes[i]->args())) {
+                return false;
+            }
+            metadata_->debugFuncReturnTypes[i] = env_->funcTypes[i]->ret();
+        }
+
+        static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash),
+                      "The ModuleHash size shall not exceed the SHA1 hash size.");
+        mozilla::SHA1Sum::Hash hash;
+        mozilla::SHA1Sum sha1Sum;
+        sha1Sum.update(bytecode.begin(), bytecode.length());
+        sha1Sum.finish(hash);
+        memcpy(metadata_->debugHash, hash, sizeof(ModuleHash));
     }
 
-    return ModuleSegment::create(tier(), masm_, *linkData_);
+    return true;
 }
 
 SharedModule
 ModuleGenerator::finishModule(const ShareableBytes& bytecode, UniqueLinkData* linkData)
 {
     MOZ_ASSERT(mode() == CompileMode::Once || mode() == CompileMode::Tier1);
 
-    UniqueModuleSegment moduleSegment = finish(bytecode);
-    if (!moduleSegment) {
+    UniqueCodeTier codeTier = finishCodeTier();
+    if (!codeTier) {
         return nullptr;
     }
 
     JumpTables jumpTables;
-    if (!jumpTables.init(mode(), *moduleSegment, metadataTier_->codeRanges)) {
+    if (!jumpTables.init(mode(), codeTier->segment(), codeTier->metadata().codeRanges)) {
         return nullptr;
     }
 
-    auto codeTier = js::MakeUnique<CodeTier>(std::move(metadataTier_), std::move(moduleSegment));
-    if (!codeTier) {
+    if (!finishMetadata(bytecode.bytes)) {
         return nullptr;
     }
 
     MutableCode code = js_new<Code>(std::move(codeTier), *metadata_, std::move(jumpTables));
-    if (!code || !code->initialize(bytecode, *linkData_)) {
+    if (!code || !code->initialize(*linkData_)) {
         return nullptr;
     }
 
     StructTypeVector structTypes;
     for (TypeDef& td : env_->types) {
         if (td.isStructType() && !structTypes.append(std::move(td.structType()))) {
             return nullptr;
         }
     }
 
+    // Copy over data from the Bytecode, which is going away at the end of
+    // compilation.
+
     DataSegmentVector dataSegments;
     if (!dataSegments.reserve(env_->dataSegments.length())) {
         return nullptr;
     }
-    for (DataSegmentEnv& srcSeg : env_->dataSegments) {
+    for (const DataSegmentEnv& srcSeg : env_->dataSegments) {
         MutableDataSegment dstSeg = js_new<DataSegment>(srcSeg);
         if (!dstSeg) {
             return nullptr;
         }
         if (!dstSeg->bytes.append(bytecode.begin() + srcSeg.bytecodeOffset, srcSeg.length)) {
             return nullptr;
         }
         dataSegments.infallibleAppend(std::move(dstSeg));
     }
 
+    CustomSectionVector customSections;
+    if (!customSections.reserve(env_->customSections.length())) {
+        return nullptr;
+    }
+    for (const CustomSectionEnv& srcSec : env_->customSections) {
+        CustomSection sec;
+        if (!sec.name.append(bytecode.begin() + srcSec.nameOffset, srcSec.nameLength)) {
+            return nullptr;
+        }
+        MutableBytes payload = js_new<ShareableBytes>();
+        if (!payload) {
+            return nullptr;
+        }
+        if (!payload->append(bytecode.begin() + srcSec.payloadOffset, srcSec.payloadLength)) {
+            return nullptr;
+        }
+        sec.payload = std::move(payload);
+        customSections.infallibleAppend(std::move(sec));
+    }
+
+    if (env_->nameCustomSectionIndex) {
+        metadata_->namePayload = customSections[*env_->nameCustomSectionIndex].payload;
+    }
+
+    // See Module debugCodeClaimed_ comments for why we need to make a separate
+    // debug copy.
+
     UniqueBytes debugUnlinkedCode;
     UniqueLinkData debugLinkData;
+    const ShareableBytes* debugBytecode = nullptr;
     if (env_->debugEnabled()) {
         MOZ_ASSERT(mode() == CompileMode::Once);
         MOZ_ASSERT(tier() == Tier::Debug);
 
         debugUnlinkedCode = js::MakeUnique<Bytes>();
         if (!debugUnlinkedCode || !debugUnlinkedCode->resize(masm_.bytesNeeded())) {
             return nullptr;
         }
 
         masm_.executableCopy(debugUnlinkedCode->begin(), /* flushICache = */ false);
 
         debugLinkData = std::move(linkData_);
+        debugBytecode = &bytecode;
     }
 
-    SharedModule module(js_new<Module>(*code,
-                                       std::move(env_->imports),
-                                       std::move(env_->exports),
-                                       std::move(structTypes),
-                                       std::move(dataSegments),
-                                       std::move(env_->elemSegments),
-                                       bytecode,
-                                       std::move(debugUnlinkedCode),
-                                       std::move(debugLinkData)));
+    // All the components are finished, so create the complete Module and start
+    // tier-2 compilation if requested.
+
+    MutableModule module = js_new<Module>(*code,
+                                          std::move(env_->imports),
+                                          std::move(env_->exports),
+                                          std::move(structTypes),
+                                          std::move(dataSegments),
+                                          std::move(env_->elemSegments),
+                                          std::move(customSections),
+                                          std::move(debugUnlinkedCode),
+                                          std::move(debugLinkData),
+                                          debugBytecode);
     if (!module) {
         return nullptr;
     }
 
     if (mode() == CompileMode::Tier1) {
-        module->startTier2(*compileArgs_);
+        module->startTier2(*compileArgs_, bytecode);
     }
 
     if (linkData) {
         MOZ_ASSERT(isAsmJS());
         MOZ_ASSERT(!env_->debugEnabled());
         *linkData = std::move(linkData_);
     }
 
     return module;
 }
 
 bool
-ModuleGenerator::finishTier2(Module& module)
+ModuleGenerator::finishTier2(const Module& module)
 {
     MOZ_ASSERT(mode() == CompileMode::Tier2);
     MOZ_ASSERT(tier() == Tier::Ion);
     MOZ_ASSERT(!env_->debugEnabled());
 
     if (cancelled_ && *cancelled_) {
         return false;
     }
 
-    UniqueModuleSegment moduleSegment = finish(module.bytecode());
-    if (!moduleSegment) {
-        return false;
-    }
-
-    auto code = js::MakeUnique<CodeTier>(std::move(metadataTier_), std::move(moduleSegment));
-    if (!code) {
+    UniqueCodeTier codeTier = finishCodeTier();
+    if (!codeTier) {
         return false;
     }
 
     if (MOZ_UNLIKELY(JitOptions.wasmDelayTier2)) {
         // Introduce an artificial delay when testing wasmDelayTier2, since we
         // want to exercise both tier1 and tier2 code in this case.
         std::this_thread::sleep_for(std::chrono::milliseconds(500));
     }
 
-    return module.finishTier2(*linkData_, std::move(code));
+    return module.finishTier2(*linkData_, std::move(codeTier));
 }
 
 size_t
 CompiledCode::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
     size_t trapSitesSize = 0;
     for (const TrapSiteVector& vec : trapSites) {
         trapSitesSize += vec.sizeOfExcludingThis(mallocSizeOf);
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -194,19 +194,20 @@ class MOZ_STACK_CLASS ModuleGenerator
     bool funcIsCompiled(uint32_t funcIndex) const;
     const CodeRange& funcCodeRange(uint32_t funcIndex) const;
     bool linkCallSites();
     void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange);
     bool linkCompiledCode(const CompiledCode& code);
     bool finishTask(CompileTask* task);
     bool launchBatchCompile();
     bool finishOutstandingTask();
-    bool finishCode();
-    bool finishMetadata(const ShareableBytes& bytecode);
-    UniqueModuleSegment finish(const ShareableBytes& bytecode);
+    bool finishCodegen();
+    bool finishMetadataTier();
+    UniqueCodeTier finishCodeTier();
+    bool finishMetadata(const Bytes& bytecode);
 
     bool isAsmJS() const { return env_->isAsmJS(); }
     Tier tier() const { return env_->tier(); }
     CompileMode mode() const { return env_->mode(); }
     bool debugEnabled() const { return env_->debugEnabled(); }
 
   public:
     ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
@@ -226,17 +227,17 @@ class MOZ_STACK_CLASS ModuleGenerator
 
     MOZ_MUST_USE bool finishFuncDefs();
 
     // If env->mode is Once or Tier1, finishModule() must be called to generate
     // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be
     // called to augment the given Module with tier 2 code.
 
     SharedModule finishModule(const ShareableBytes& bytecode, UniqueLinkData* linkData = nullptr);
-    MOZ_MUST_USE bool finishTier2(Module& module);
+    MOZ_MUST_USE bool finishTier2(const Module& module);
 
     ExclusiveDeferredValidationState& deferredValidationState() {
         return deferredValidationState_;
     }
 };
 
 } // namespace wasm
 } // namespace js
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -18,16 +18,18 @@
 
 #include "wasm/WasmInstance.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
 #include "jit/JitRealm.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
@@ -692,32 +694,33 @@ Instance::postBarrier(Instance* instance
     MOZ_ASSERT(location);
     TlsContext.get()->runtime()->gc.storeBuffer().putCell(location);
 }
 #endif // ENABLE_WASM_GC
 
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    SharedCode code,
-                   UniqueDebugState debug,
                    UniqueTlsData tlsDataIn,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
                    HandleValVector globalImportValues,
-                   const WasmGlobalObjectVector& globalObjs)
+                   const WasmGlobalObjectVector& globalObjs,
+                   UniqueDebugState maybeDebug)
   : realm_(cx->realm()),
     object_(object),
     code_(code),
-    debug_(std::move(debug)),
     tlsData_(std::move(tlsDataIn)),
     memory_(memory),
     tables_(std::move(tables)),
-    enterFrameTrapsEnabled_(false)
+    maybeDebug_(std::move(maybeDebug))
 {
+    MOZ_ASSERT(!!maybeDebug_ == metadata().debugEnabled);
+
 #ifdef DEBUG
     for (auto t : code_->tiers()) {
         MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
     }
 #endif
     MOZ_ASSERT(tables_.length() == metadata().tables.length());
 
     tlsData()->memoryBase = memory ? memory->buffer().dataPointerEither().unwrap() : nullptr;
@@ -1140,27 +1143,27 @@ Instance::callExport(JSContext* cx, uint
 }
 
 JSAtom*
 Instance::getFuncDisplayAtom(JSContext* cx, uint32_t funcIndex) const
 {
     // The "display name" of a function is primarily shown in Error.stack which
     // also includes location, so use getFuncNameBeforeLocation.
     UTF8Bytes name;
-    if (!metadata().getFuncNameBeforeLocation(debug_->maybeBytecode(), funcIndex, &name)) {
+    if (!metadata().getFuncNameBeforeLocation(funcIndex, &name)) {
         return nullptr;
     }
 
     return AtomizeUTF8Chars(cx, name.begin(), name.length());
 }
 
 void
 Instance::ensureProfilingLabels(bool profilingEnabled) const
 {
-    return code_->ensureProfilingLabels(debug_->maybeBytecode(), profilingEnabled);
+    return code_->ensureProfilingLabels(profilingEnabled);
 }
 
 void
 Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
 {
     MOZ_ASSERT(!isAsmJS());
     MOZ_ASSERT(!memory_->isShared());
 
@@ -1186,25 +1189,73 @@ Instance::deoptimizeImportExit(uint32_t 
 {
     Tier t = code().bestTier();
     const FuncImport& fi = metadata(t).funcImports[funcImportIndex];
     FuncImportTls& import = funcImportTls(fi);
     import.code = codeBase(t) + fi.interpExitCodeOffset();
     import.baselineScript = nullptr;
 }
 
-void
-Instance::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
+JSString*
+Instance::createDisplayURL(JSContext* cx)
 {
-    if (enterFrameTrapsEnabled_ == enabled) {
-        return;
+    // In the best case, we simply have a URL, from a streaming compilation of a
+    // fetched Response.
+
+    if (metadata().filenameIsURL) {
+        return NewStringCopyZ<CanGC>(cx, metadata().filename.get());
+    }
+
+    // Otherwise, build wasm module URL from following parts:
+    // - "wasm:" as protocol;
+    // - URI encoded filename from metadata (if can be encoded), plus ":";
+    // - 64-bit hash of the module bytes (as hex dump).
+
+    StringBuffer result(cx);
+    if (!result.append("wasm:")) {
+        return nullptr;
     }
 
-    debug_->adjustEnterAndLeaveFrameTrapsState(cx, enabled);
-    enterFrameTrapsEnabled_ = enabled;
+    if (const char* filename = metadata().filename.get()) {
+        // EncodeURI returns false due to invalid chars or OOM -- fail only
+        // during OOM.
+        JSString* filenamePrefix = EncodeURI(cx, filename, strlen(filename));
+        if (!filenamePrefix) {
+            if (cx->isThrowingOutOfMemory()) {
+                return nullptr;
+            }
+
+            MOZ_ASSERT(!cx->isThrowingOverRecursed());
+            cx->clearPendingException();
+            return nullptr;
+        }
+
+        if (!result.append(filenamePrefix)) {
+            return nullptr;
+        }
+    }
+
+    if (metadata().debugEnabled) {
+        if (!result.append(":")) {
+            return nullptr;
+        }
+
+        const ModuleHash& hash = metadata().debugHash;
+        for (size_t i = 0; i < sizeof(ModuleHash); i++) {
+            char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
+            if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) {
+                return nullptr;
+            }
+            if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) {
+                return nullptr;
+            }
+        }
+    }
+
+    return result.finishString();
 }
 
 void
 Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                         Metadata::SeenSet* seenMetadata,
                         ShareableBytes::SeenSet* seenBytes,
                         Code::SeenSet* seenCode,
                         Table::SeenSet* seenTables,
@@ -1212,11 +1263,14 @@ Instance::addSizeOfMisc(MallocSizeOf mal
                         size_t* data) const
 {
     *data += mallocSizeOf(this);
     *data += mallocSizeOf(tlsData_.get());
     for (const SharedTable& table : tables_) {
          *data += table->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenTables);
     }
 
-    debug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
+    if (maybeDebug_) {
+        maybeDebug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
+    }
+
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
 }
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -46,23 +46,22 @@ class Instance
     JS::Realm* const                realm_;
     ReadBarrieredWasmInstanceObject object_;
     jit::TrampolinePtr              jsJitArgsRectifier_;
     jit::TrampolinePtr              jsJitExceptionHandler_;
 #ifdef ENABLE_WASM_GC
     jit::TrampolinePtr              preBarrierCode_;
 #endif
     const SharedCode                code_;
-    const UniqueDebugState          debug_;
     const UniqueTlsData             tlsData_;
     GCPtrWasmMemoryObject           memory_;
-    SharedTableVector               tables_;
+    const SharedTableVector         tables_;
     DataSegmentVector               passiveDataSegments_;
     ElemSegmentVector               passiveElemSegments_;
-    bool                            enterFrameTrapsEnabled_;
+    const UniqueDebugState          maybeDebug_;
 
     // Internal helpers:
     const void** addressOfFuncTypeId(const FuncTypeIdDesc& funcTypeId) const;
     FuncImportTls& funcImportTls(const FuncImport& fi);
     TableTls& tableTls(const TableDesc& td) const;
 
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
@@ -70,34 +69,34 @@ class Instance
 
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
 
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              SharedCode code,
-             UniqueDebugState debug,
              UniqueTlsData tlsData,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
              HandleValVector globalImportValues,
-             const WasmGlobalObjectVector& globalObjs);
+             const WasmGlobalObjectVector& globalObjs,
+             UniqueDebugState maybeDebug);
     ~Instance();
     bool init(JSContext* cx,
               const DataSegmentVector& dataSegments,
               const ElemSegmentVector& elemSegments);
     void trace(JSTracer* trc);
 
     JS::Realm* realm() const { return realm_; }
     const Code& code() const { return *code_; }
     const CodeTier& code(Tier t) const { return code_->codeTier(t); }
-    DebugState& debug() { return *debug_; }
-    const DebugState& debug() const { return *debug_; }
+    bool debugEnabled() const { return !!maybeDebug_; }
+    DebugState& debug() { return *maybeDebug_; }
     const ModuleSegment& moduleSegment(Tier t) const { return code_->segment(t); }
     TlsData* tlsData() const { return tlsData_.get(); }
     uint8_t* globalData() const { return (uint8_t*)&tlsData_->globalArea; }
     uint8_t* codeBase(Tier t) const { return code_->segment(t).base(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     bool isAsmJS() const { return metadata().isAsmJS(); }
     const SharedTableVector& tables() const { return tables_; }
@@ -153,21 +152,19 @@ class Instance
     void onMovingGrowMemory(uint8_t* prevMemoryBase);
     void onMovingGrowTable();
 
     // Called to apply a single ElemSegment at a given offset, assuming
     // that all bounds validation has already been performed.
 
     void initElems(const ElemSegment& seg, uint32_t dstOffset, uint32_t srcOffset, uint32_t len);
 
-    // Debug support:
+    // Debugger support:
 
-    bool debugEnabled() const { return metadata().debugEnabled; }
-    bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
-    void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
+    JSString* createDisplayURL(JSContext* cx);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Code::SeenSet* seenCode,
                        Table::SeenSet* seenTables,
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -593,29 +593,29 @@ const JSFunctionSpec WasmModuleObject::s
 
 /* static */ void
 WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
 {
     obj->as<WasmModuleObject>().module().Release();
 }
 
 static bool
-IsModuleObject(JSObject* obj, Module** module)
+IsModuleObject(JSObject* obj, const Module** module)
 {
     JSObject* unwrapped = CheckedUnwrap(obj);
     if (!unwrapped || !unwrapped->is<WasmModuleObject>()) {
         return false;
     }
 
     *module = &unwrapped->as<WasmModuleObject>().module();
     return true;
 }
 
 static bool
-GetModuleArg(JSContext* cx, CallArgs args, const char* name, Module** module)
+GetModuleArg(JSContext* cx, CallArgs args, const char* name, const Module** module)
 {
     if (!args.requireAtLeast(cx, name, 1)) {
         return false;
     }
 
     if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
         return false;
@@ -727,17 +727,17 @@ UTF8CharsToString(JSContext* cx, const c
     return NewStringCopyUTF8Z<CanGC>(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
 }
 
 /* static */ bool
 WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.imports", &module)) {
         return false;
     }
 
     KindNames names(cx);
     if (!InitKindNames(cx, &names)) {
         return false;
     }
@@ -801,17 +801,17 @@ WasmModuleObject::imports(JSContext* cx,
     return true;
 }
 
 /* static */ bool
 WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.exports", &module)) {
         return false;
     }
 
     KindNames names(cx);
     if (!InitKindNames(cx, &names)) {
         return false;
     }
@@ -869,17 +869,17 @@ WasmModuleObject::exports(JSContext* cx,
     return true;
 }
 
 /* static */ bool
 WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.customSections", &module)) {
         return false;
     }
 
     Vector<char, 8> name(cx);
     {
         RootedString str(cx, ToString(cx, args.get(1)));
         if (!str) {
@@ -893,58 +893,56 @@ WasmModuleObject::customSections(JSConte
 
         if (!name.initLengthUninitialized(JS::GetDeflatedUTF8StringLength(flat))) {
             return false;
         }
 
         JS::DeflateStringToUTF8Buffer(flat, RangedPtr<char>(name.begin(), name.length()));
     }
 
-    const uint8_t* bytecode = module->bytecode().begin();
-
     AutoValueVector elems(cx);
     RootedArrayBufferObject buf(cx);
-    for (const CustomSection& sec : module->metadata().customSections) {
-        if (name.length() != sec.name.length) {
+    for (const CustomSection& cs : module->customSections()) {
+        if (name.length() != cs.name.length()) {
             continue;
         }
-        if (memcmp(name.begin(), bytecode + sec.name.offset, name.length())) {
+        if (memcmp(name.begin(), cs.name.begin(), name.length())) {
             continue;
         }
 
-        buf = ArrayBufferObject::create(cx, sec.length);
+        buf = ArrayBufferObject::create(cx, cs.payload->length());
         if (!buf) {
             return false;
         }
 
-        memcpy(buf->dataPointer(), bytecode + sec.offset, sec.length);
+        memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
         if (!elems.append(ObjectValue(*buf))) {
             return false;
         }
     }
 
     JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
     if (!arr) {
         return false;
     }
 
     args.rval().setObject(*arr);
     return true;
 }
 
 /* static */ WasmModuleObject*
-WasmModuleObject::create(JSContext* cx, Module& module, HandleObject proto)
+WasmModuleObject::create(JSContext* cx, const Module& module, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
     auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
     if (!obj) {
         return nullptr;
     }
 
-    obj->initReservedSlot(MODULE_SLOT, PrivateValue(&module));
+    obj->initReservedSlot(MODULE_SLOT, PrivateValue(const_cast<Module*>(&module)));
     module.AddRef();
     // We account for the first tier here; the second tier, if different, will be
     // accounted for separately when it's been compiled.
     cx->zone()->updateJitCodeMallocBytes(module.codeLength(module.code().stableTier()));
     return obj;
 }
 
 static bool
@@ -1054,21 +1052,21 @@ WasmModuleObject::construct(JSContext* c
     if (!moduleObj) {
         return false;
     }
 
     callArgs.rval().setObject(*moduleObj);
     return true;
 }
 
-Module&
+const Module&
 WasmModuleObject::module() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
-    return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
+    return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
 // ============================================================================
 // WebAssembly.Instance class and methods
 
 const ClassOps WasmInstanceObject::classOps_ =
 {
     nullptr, /* addProperty */
@@ -1154,25 +1152,25 @@ WasmInstanceObject::trace(JSTracer* trc,
     }
 }
 
 /* static */ WasmInstanceObject*
 WasmInstanceObject::create(JSContext* cx,
                            SharedCode code,
                            const DataSegmentVector& dataSegments,
                            const ElemSegmentVector& elemSegments,
-                           UniqueDebugState debug,
                            UniqueTlsData tlsData,
                            HandleWasmMemoryObject memory,
                            SharedTableVector&& tables,
                            Handle<FunctionVector> funcImports,
                            const GlobalDescVector& globals,
                            HandleValVector globalImportValues,
                            const WasmGlobalObjectVector& globalObjs,
-                           HandleObject proto)
+                           HandleObject proto,
+                           UniqueDebugState maybeDebug)
 {
     UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
     if (!exports) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone());
@@ -1222,23 +1220,23 @@ WasmInstanceObject::create(JSContext* cx
     // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
     // leading to an observable "newborn" state in tracing/finalization.
     MOZ_ASSERT(obj->isNewborn());
 
     // Root the Instance via WasmInstanceObject before any possible GC.
     auto* instance = cx->new_<Instance>(cx,
                                         obj,
                                         code,
-                                        std::move(debug),
                                         std::move(tlsData),
                                         memory,
                                         std::move(tables),
                                         funcImports,
                                         globalImportValues,
-                                        globalObjs);
+                                        globalObjs,
+                                        std::move(maybeDebug));
     if (!instance) {
         return nullptr;
     }
 
     obj->initReservedSlot(INSTANCE_SLOT, PrivateValue(instance));
     MOZ_ASSERT(!obj->isNewborn());
 
     if (!instance->init(cx, dataSegments, elemSegments)) {
@@ -1295,17 +1293,17 @@ WasmInstanceObject::construct(JSContext*
     if (!ThrowIfNotConstructing(cx, args, "Instance")) {
         return false;
     }
 
     if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
         return false;
     }
 
-    Module* module;
+    const Module* module;
     if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
         return false;
     }
 
     RootedObject importObj(cx);
     if (!GetImportArg(cx, args, &importObj)) {
         return false;
@@ -2681,17 +2679,17 @@ Reject(JSContext* cx, const CompileArgs&
         return false;
     }
 
     RootedValue rejectionValue(cx, ObjectValue(*errorObj));
     return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
 static bool
-Resolve(JSContext* cx, Module& module, Handle<PromiseObject*> promise, bool instantiate,
+Resolve(JSContext* cx, const Module& module, Handle<PromiseObject*> promise, bool instantiate,
         HandleObject importObj, const UniqueCharsVector& warnings)
 {
     if (!ReportCompileWarnings(cx, warnings)) {
         return false;
     }
 
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
     RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
@@ -2874,17 +2872,17 @@ WebAssembly_instantiate(JSContext* cx, u
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     RootedObject firstArg(cx);
     RootedObject importObj(cx);
     if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
         return RejectWithPendingException(cx, promise, callArgs);
     }
 
-    Module* module;
+    const Module* module;
     if (IsModuleObject(firstArg, &module)) {
         RootedWasmInstanceObject instanceObj(cx);
         if (!Instantiate(cx, *module, importObj, &instanceObj)) {
             return RejectWithPendingException(cx, promise, callArgs);
         }
 
         RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
         if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -102,19 +102,19 @@ class WasmModuleObject : public NativeOb
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmModuleObject* create(JSContext* cx,
-                                    wasm::Module& module,
+                                    const wasm::Module& module,
                                     HandleObject proto = nullptr);
-    wasm::Module& module() const;
+    const wasm::Module& module() const;
 };
 
 // The class of WebAssembly.Global.  This wraps a storage location, and there is
 // a per-agent one-to-one relationship between the WasmGlobalObject and the
 // storage location (the Cell) it wraps: if a module re-exports an imported
 // global, the imported and exported WasmGlobalObjects are the same, and if a
 // module exports a global twice, the two exported WasmGlobalObjects are the
 // same.
@@ -211,25 +211,25 @@ class WasmInstanceObject : public Native
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmInstanceObject* create(JSContext* cx,
                                       RefPtr<const wasm::Code> code,
                                       const wasm::DataSegmentVector& dataSegments,
                                       const wasm::ElemSegmentVector& elemSegments,
-                                      UniquePtr<wasm::DebugState> debug,
                                       wasm::UniqueTlsData tlsData,
                                       HandleWasmMemoryObject memory,
                                       Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
                                       Handle<FunctionVector> funcImports,
                                       const wasm::GlobalDescVector& globals,
                                       wasm::HandleValVector globalImportValues,
                                       const WasmGlobalObjectVector& globalObjs,
-                                      HandleObject proto);
+                                      HandleObject proto,
+                                      UniquePtr<wasm::DebugState> maybeDebug);
     void initExportsObj(JSObject& exportsObj);
 
     wasm::Instance& instance() const;
     JSObject& exportsObj() const;
 
     static bool getExportedFunction(JSContext* cx,
                                     HandleWasmInstanceObject instanceObj,
                                     uint32_t funcIndex,
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -34,66 +34,69 @@
 #include "vm/JSAtom-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask
 {
+    SharedCompileArgs       compileArgs_;
+    SharedBytes             bytecode_;
     SharedModule            module_;
-    SharedCompileArgs       compileArgs_;
     Atomic<bool>            cancelled_;
 
   public:
-    Tier2GeneratorTaskImpl(Module& module, const CompileArgs& compileArgs)
-      : module_(&module),
-        compileArgs_(&compileArgs),
+    Tier2GeneratorTaskImpl(const CompileArgs& compileArgs, const ShareableBytes& bytecode,
+                           Module& module)
+      : compileArgs_(&compileArgs),
+        bytecode_(&bytecode),
+        module_(&module),
         cancelled_(false)
     {}
 
     ~Tier2GeneratorTaskImpl() override {
         module_->testingTier2Active_ = false;
     }
 
     void cancel() override {
         cancelled_ = true;
     }
 
     void execute() override {
-        CompileTier2(*compileArgs_, *module_, &cancelled_);
+        CompileTier2(*compileArgs_, bytecode_->bytes, *module_, &cancelled_);
     }
 };
 
 void
-Module::startTier2(const CompileArgs& args)
+Module::startTier2(const CompileArgs& args, const ShareableBytes& bytecode)
 {
     MOZ_ASSERT(!testingTier2Active_);
 
-    auto task = MakeUnique<Tier2GeneratorTaskImpl>(*this, args);
+    auto task = MakeUnique<Tier2GeneratorTaskImpl>(args, bytecode, *this);
     if (!task) {
         return;
     }
 
     // This flag will be cleared asynchronously by ~Tier2GeneratorTaskImpl()
     // on success or failure.
     testingTier2Active_ = true;
 
     StartOffThreadWasmTier2Generator(std::move(task));
 }
 
 bool
-Module::finishTier2(const LinkData& linkData2, UniqueCodeTier code2)
+Module::finishTier2(const LinkData& linkData2, UniqueCodeTier code2) const
 {
     MOZ_ASSERT(code().bestTier() == Tier::Baseline && code2->tier() == Tier::Ion);
 
     // Install the data in the data structures. They will not be visible
     // until commitTier2().
 
-    if (!code().setTier2(std::move(code2), *bytecode_, linkData2)) {
+    if (!code().setTier2(std::move(code2), linkData2)) {
         return false;
     }
 
     // Before we can make tier-2 live, we need to compile tier2 versions of any
     // extant tier1 lazy stubs (otherwise, tiering would break the assumption
     // that any extant exported wasm function has had a lazy entry stub already
     // compiled for it).
     {
@@ -168,16 +171,17 @@ Module::testingBlockOnTier2Complete() co
 Module::serializedSize(const LinkData& linkData) const
 {
     return linkData.serializedSize() +
            SerializedVectorSize(imports_) +
            SerializedVectorSize(exports_) +
            SerializedVectorSize(structTypes_) +
            SerializedVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
+           SerializedVectorSize(customSections_) +
            code_->serializedSize();
 }
 
 /* virtual */ void
 Module::serialize(const LinkData& linkData, uint8_t* begin, size_t size) const
 {
     MOZ_RELEASE_ASSERT(!testingTier2Active_);
     MOZ_RELEASE_ASSERT(!metadata().debugEnabled);
@@ -185,40 +189,34 @@ Module::serialize(const LinkData& linkDa
 
     uint8_t* cursor = begin;
     cursor = linkData.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializeVector(cursor, structTypes_);
     cursor = SerializeVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
+    cursor = SerializeVector(cursor, customSections_);
     cursor = code_->serialize(cursor, linkData);
     MOZ_RELEASE_ASSERT(cursor == begin + size);
 }
 
-/* static */ SharedModule
+/* static */ MutableModule
 Module::deserialize(const uint8_t* begin, size_t size, Metadata* maybeMetadata)
 {
     MutableMetadata metadata(maybeMetadata);
     if (!metadata) {
         metadata = js_new<Metadata>();
         if (!metadata) {
             return nullptr;
         }
     }
 
     const uint8_t* cursor = begin;
 
-    // Temporary. (asm.js doesn't save bytecode)
-    MOZ_RELEASE_ASSERT(maybeMetadata->isAsmJS());
-    MutableBytes bytecode = js_new<ShareableBytes>();
-    if (!bytecode) {
-        return nullptr;
-    }
-
     LinkData linkData(Tier::Serialized);
     cursor = linkData.deserialize(cursor);
     if (!cursor) {
         return nullptr;
     }
 
     ImportVector imports;
     cursor = DeserializeVector(cursor, &imports);
@@ -245,32 +243,45 @@ Module::deserialize(const uint8_t* begin
     }
 
     ElemSegmentVector elemSegments;
     cursor = DeserializeVector(cursor, &elemSegments);
     if (!cursor) {
         return nullptr;
     }
 
+    CustomSectionVector customSections;
+    cursor = DeserializeVector(cursor, &customSections);
+    if (!cursor) {
+        return nullptr;
+    }
+
+    if (metadata->nameCustomSectionIndex) {
+        metadata->namePayload = customSections[*metadata->nameCustomSectionIndex].payload;
+    } else {
+        MOZ_RELEASE_ASSERT(!metadata->moduleName);
+        MOZ_RELEASE_ASSERT(metadata->funcNames.empty());
+    }
+
     SharedCode code;
-    cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code);
+    cursor = Code::deserialize(cursor, linkData, *metadata, &code);
     if (!cursor) {
         return nullptr;
     }
 
     MOZ_RELEASE_ASSERT(cursor == begin + size);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
 
     return js_new<Module>(*code,
                           std::move(imports),
                           std::move(exports),
                           std::move(structTypes),
                           std::move(dataSegments),
                           std::move(elemSegments),
-                          *bytecode);
+                          std::move(customSections));
 }
 
 /* virtual */ JSObject*
 Module::createObject(JSContext* cx)
 {
     if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
         return nullptr;
     }
@@ -304,17 +315,17 @@ MapFile(PRFileDesc* file, PRFileInfo* in
     // PRFileMap objects do not need to be kept alive after the memory has been
     // mapped, so unconditionally close the PRFileMap, regardless of whether
     // PR_MemMap succeeds.
     uint8_t* memory = (uint8_t*)PR_MemMap(map, 0, info->size);
     PR_CloseFileMap(map);
     return UniqueMapping(memory, MemUnmap(info->size));
 }
 
-SharedModule
+RefPtr<JS::WasmModule>
 wasm::DeserializeModule(PRFileDesc* bytecodeFile, UniqueChars filename, unsigned line)
 {
     PRFileInfo bytecodeInfo;
     UniqueMapping bytecodeMapping = MapFile(bytecodeFile, &bytecodeInfo);
     if (!bytecodeMapping) {
         return nullptr;
     }
 
@@ -344,17 +355,23 @@ wasm::DeserializeModule(PRFileDesc* byte
     // (We would prefer to store this value with the Assumptions when
     // serializing, and for the caller of the deserialization machinery to
     // provide the value from the originating context.)
 
     args->sharedMemoryEnabled = true;
 
     UniqueChars error;
     UniqueCharsVector warnings;
-    return CompileBuffer(*args, *bytecode, &error, &warnings);
+    SharedModule module = CompileBuffer(*args, *bytecode, &error, &warnings);
+    if (!module) {
+        return nullptr;
+    }
+
+    // The public interface is effectively const.
+    return RefPtr<JS::WasmModule>(const_cast<Module*>(module.get()));
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
                       Code::SeenSet* seenCode,
                       size_t* code,
@@ -362,17 +379,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
 {
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
     *data += mallocSizeOf(this) +
              SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(structTypes_, mallocSizeOf) +
              SizeOfVectorExcludingThis(dataSegments_, mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
-             bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
+             SizeOfVectorExcludingThis(customSections_, mallocSizeOf);
 
     if (debugUnlinkedCode_) {
         *data += debugUnlinkedCode_->sizeOfExcludingThis(mallocSizeOf);
     }
 }
 
 // Extracting machine code as JS object. The result has the "code" property, as
 // a Uint8Array, and the "segments" property as array objects. The objects
@@ -880,17 +897,17 @@ Module::getDebugEnabledCode() const
     }
 
     JumpTables jumpTables;
     if (!jumpTables.init(CompileMode::Once, codeTier->segment(), metadata(tier).codeRanges)) {
         return nullptr;
     }
 
     MutableCode debugCode = js_new<Code>(std::move(codeTier), metadata(), std::move(jumpTables));
-    if (!debugCode || !debugCode->initialize(*bytecode_, *debugLinkData_)) {
+    if (!debugCode || !debugCode->initialize(*debugLinkData_)) {
         return nullptr;
     }
 
     return debugCode;
 }
 
 static bool
 GetFunctionExport(JSContext* cx,
@@ -1016,66 +1033,47 @@ Module::instantiate(JSContext* cx,
     }
 
     UniqueTlsData tlsData = CreateTlsData(metadata().globalDataLength);
     if (!tlsData) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    // Debugging mutates code (for traps, stepping, etc) and thus may need to
-    // clone the code on each instantiation.
-
-    SharedCode code(code_);
+    SharedCode code;
+    UniqueDebugState maybeDebug;
     if (metadata().debugEnabled) {
         code = getDebugEnabledCode();
         if (!code) {
             ReportOutOfMemory(cx);
             return false;
         }
-    }
 
-    // To support viewing the source of an instance (Instance::createText), the
-    // instance must hold onto a ref of the bytecode (keeping it alive). This
-    // wastes memory for most users, so we try to only save the source when a
-    // developer actually cares: when the realm is debuggable (which is true
-    // when the web console is open), has code compiled with debug flag
-    // enabled or a names section is present (since this going to be stripped
-    // for non-developer builds).
-
-    const ShareableBytes* maybeBytecode = nullptr;
-    if (cx->realm()->isDebuggee() || metadata().debugEnabled ||
-        !metadata().funcNames.empty() || !!metadata().moduleName)
-    {
-        maybeBytecode = bytecode_.get();
-    }
-
-    // The debug object must be present even when debugging is not enabled: It
-    // provides the lazily created source text for the program, even if that
-    // text is a placeholder message when debugging is not enabled.
-
-    bool binarySource = cx->realm()->debuggerObservesBinarySource();
-    auto debug = cx->make_unique<DebugState>(code, maybeBytecode, binarySource);
-    if (!debug) {
-        return false;
+        bool binarySource = cx->realm()->debuggerObservesBinarySource();
+        maybeDebug = cx->make_unique<DebugState>(*code, *this, binarySource);
+        if (!maybeDebug) {
+            return false;
+        }
+    } else {
+        code = code_;
     }
 
     instance.set(WasmInstanceObject::create(cx,
                                             code,
                                             dataSegments_,
                                             elemSegments_,
-                                            std::move(debug),
                                             std::move(tlsData),
                                             memory,
                                             std::move(tables),
                                             funcImports,
                                             metadata().globals,
                                             globalImportValues,
                                             globalObjs,
-                                            instanceProto));
+                                            instanceProto,
+                                            std::move(maybeDebug)));
     if (!instance) {
         return false;
     }
 
     if (!CreateExportObject(cx, instance, funcImports, table, memory, globalObjs, exports_)) {
         return false;
     }
 
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -43,35 +43,36 @@ struct CompileArgs;
 // ModuleSegment) can be shared between instances, provided none of those
 // instances are being debugged. If patchable code is needed then each instance
 // must have its own Code. Module eagerly creates a new Code and gives it to the
 // first instance; it then instantiates new Code objects from a copy of the
 // unlinked code that it keeps around for that purpose.
 
 class Module : public JS::WasmModule
 {
-    const SharedCode        code_;
-    const ImportVector      imports_;
-    const ExportVector      exports_;
-    const StructTypeVector  structTypes_;
-    const DataSegmentVector dataSegments_;
-    const ElemSegmentVector elemSegments_;
-    const SharedBytes       bytecode_;
+    const SharedCode          code_;
+    const ImportVector        imports_;
+    const ExportVector        exports_;
+    const StructTypeVector    structTypes_;
+    const DataSegmentVector   dataSegments_;
+    const ElemSegmentVector   elemSegments_;
+    const CustomSectionVector customSections_;
 
     // These fields are only meaningful when code_->metadata().debugEnabled.
     // `debugCodeClaimed_` is set to false initially and then to true when
     // `code_` is already being used for an instance and can't be shared because
     // it may be patched by the debugger. Subsequent instances must then create
     // copies by linking the `debugUnlinkedCode_` using `debugLinkData_`.
     // This could all be removed if debugging didn't need to perform
     // per-instance code patching.
 
     mutable Atomic<bool>    debugCodeClaimed_;
     const UniqueConstBytes  debugUnlinkedCode_;
     const UniqueLinkData    debugLinkData_;
+    const SharedBytes       debugBytecode_;
 
     // This flag is only used for testing purposes and is racily updated as soon
     // as tier-2 compilation finishes (in success or failure).
 
     mutable Atomic<bool>    testingTier2Active_;
 
     bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
@@ -91,42 +92,45 @@ class Module : public JS::WasmModule
 
   public:
     Module(const Code& code,
            ImportVector&& imports,
            ExportVector&& exports,
            StructTypeVector&& structTypes,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
-           const ShareableBytes& bytecode,
+           CustomSectionVector&& customSections,
            UniqueConstBytes debugUnlinkedCode = nullptr,
-           UniqueLinkData debugLinkData = nullptr)
+           UniqueLinkData debugLinkData = nullptr,
+           const ShareableBytes* debugBytecode = nullptr)
       : code_(&code),
         imports_(std::move(imports)),
         exports_(std::move(exports)),
         structTypes_(std::move(structTypes)),
         dataSegments_(std::move(dataSegments)),
         elemSegments_(std::move(elemSegments)),
-        bytecode_(&bytecode),
+        customSections_(std::move(customSections)),
         debugCodeClaimed_(false),
         debugUnlinkedCode_(std::move(debugUnlinkedCode)),
         debugLinkData_(std::move(debugLinkData)),
+        debugBytecode_(debugBytecode),
         testingTier2Active_(false)
     {
         MOZ_ASSERT_IF(metadata().debugEnabled, debugUnlinkedCode_ && debugLinkData_);
     }
     ~Module() override { /* Note: can be called on any thread */ }
 
     const Code& code() const { return *code_; }
     const ModuleSegment& moduleSegment(Tier t) const { return code_->segment(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
-    const ShareableBytes& bytecode() const { return *bytecode_; }
+    const CustomSectionVector& customSections() const { return customSections_; }
+    const Bytes& debugBytecode() const { return debugBytecode_->bytes; }
     uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
@@ -135,18 +139,18 @@ class Module : public JS::WasmModule
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Tier-2 compilation may be initiated after the Module is constructed at
     // most once. When tier-2 compilation completes, ModuleGenerator calls
     // finishTier2() from a helper thread, passing tier-variant data which will
     // be installed and made visible.
 
-    void startTier2(const CompileArgs& args);
-    bool finishTier2(const LinkData& linkData2, UniqueCodeTier code2);
+    void startTier2(const CompileArgs& args, const ShareableBytes& bytecode);
+    bool finishTier2(const LinkData& linkData2, UniqueCodeTier code2) const;
 
     void testingBlockOnTier2Complete() const;
     bool testingTier2Active() const { return testingTier2Active_; }
 
     // Currently dead, but will be ressurrected with shell tests (bug 1330661)
     // and HTTP cache integration.
 
     size_t serializedSize(const LinkData& linkData) const;
@@ -166,19 +170,20 @@ class Module : public JS::WasmModule
                        Code::SeenSet* seenCode,
                        size_t* code, size_t* data) const;
 
     // Generated code analysis support:
 
     bool extractCode(JSContext* cx, Tier tier, MutableHandleValue vp) const;
 };
 
-typedef RefPtr<Module> SharedModule;
+typedef RefPtr<Module> MutableModule;
+typedef RefPtr<const Module> SharedModule;
 
 // JS API implementations:
 
-SharedModule
+RefPtr<JS::WasmModule>
 DeserializeModule(PRFileDesc* bytecode, UniqueChars filename, unsigned line);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_module_h
--- a/js/src/wasm/WasmRealm.cpp
+++ b/js/src/wasm/WasmRealm.cpp
@@ -68,17 +68,17 @@ wasm::Realm::registerInstance(JSContext*
     MOZ_ASSERT(runtime_ == cx->runtime());
 
     Instance& instance = instanceObj->instance();
     MOZ_ASSERT(this == &instance.realm()->wasm);
 
     instance.ensureProfilingLabels(cx->runtime()->geckoProfiler().enabled());
 
     if (instance.debugEnabled() && instance.realm()->debuggerObservesAllExecution()) {
-        instance.ensureEnterFrameTrapsState(cx, true);
+        instance.debug().ensureEnterFrameTrapsState(cx, true);
     }
 
     {
         if (!instances_.reserve(instances_.length() + 1)) {
             return false;
         }
 
         auto runtimeInstances = cx->runtime()->wasmInstances.lock();
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -512,16 +512,60 @@ DataSegment::deserialize(const uint8_t* 
 }
 
 size_t
 DataSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return bytes.sizeOfExcludingThis(mallocSizeOf);
 }
 
+size_t
+CustomSection::serializedSize() const
+{
+    return SerializedPodVectorSize(name) +
+           SerializedPodVectorSize(payload->bytes);
+}
+
+uint8_t*
+CustomSection::serialize(uint8_t* cursor) const
+{
+    cursor = SerializePodVector(cursor, name);
+    cursor = SerializePodVector(cursor, payload->bytes);
+    return cursor;
+}
+
+const uint8_t*
+CustomSection::deserialize(const uint8_t* cursor)
+{
+    cursor = DeserializePodVector(cursor, &name);
+    if (!cursor) {
+        return nullptr;
+    }
+
+    Bytes bytes;
+    cursor = DeserializePodVector(cursor, &bytes);
+    if (!cursor) {
+        return nullptr;
+    }
+    payload = js_new<ShareableBytes>(std::move(bytes));
+    if (!payload) {
+        return nullptr;
+    }
+
+    return cursor;
+}
+
+size_t
+CustomSection::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return name.sizeOfExcludingThis(mallocSizeOf) +
+           sizeof(*payload) +
+           payload->sizeOfExcludingThis(mallocSizeOf);
+}
+
 //  Heap length on ARM should fit in an ARM immediate. We approximate the set
 //  of valid ARM immediates with the predicate:
 //    2^n for n in [16, 24)
 //  or
 //    2^24 * n for n >= 1.
 bool
 wasm::IsValidARMImmediate(uint32_t i)
 {
@@ -917,18 +961,16 @@ wasm::LookupInSorted(const CodeRangeVect
     }
 
     return &codeRanges[match];
 }
 
 UniqueTlsData
 wasm::CreateTlsData(uint32_t globalDataLength)
 {
-    MOZ_ASSERT(globalDataLength % gc::SystemPageSize() == 0);
-
     void* allocatedBase = js_calloc(TlsDataAlign + offsetof(TlsData, globalArea) + globalDataLength);
     if (!allocatedBase) {
         return nullptr;
     }
 
     auto* tlsData = reinterpret_cast<TlsData*>(AlignBytes(uintptr_t(allocatedBase), TlsDataAlign));
     tlsData->allocatedBase = allocatedBase;
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1174,16 +1174,58 @@ struct DataSegment : AtomicRefCounted<Da
 
     WASM_DECLARE_SERIALIZABLE(DataSegment)
 };
 
 typedef RefPtr<DataSegment> MutableDataSegment;
 typedef SerializableRefPtr<const DataSegment> SharedDataSegment;
 typedef Vector<SharedDataSegment, 0, SystemAllocPolicy> DataSegmentVector;
 
+// The CustomSection(Env) structs are like DataSegment(Env): CustomSectionEnv is
+// stored in the ModuleEnvironment and CustomSection holds a copy of the payload
+// and is stored in the wasm::Module.
+
+struct CustomSectionEnv
+{
+    uint32_t nameOffset;
+    uint32_t nameLength;
+    uint32_t payloadOffset;
+    uint32_t payloadLength;
+};
+
+typedef Vector<CustomSectionEnv, 0, SystemAllocPolicy> CustomSectionEnvVector;
+
+struct CustomSection
+{
+    Bytes name;
+    SharedBytes payload;
+
+    WASM_DECLARE_SERIALIZABLE(CustomSection)
+};
+
+typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
+
+// A Name represents a string of utf8 chars embedded within the name custom
+// section. The offset of a name is expressed relative to the beginning of the
+// name section's payload so that Names can stored in wasm::Code, which only
+// holds the name section's bytes, not the whole bytecode.
+
+struct Name
+{
+    // All fields are treated as cacheable POD:
+    uint32_t offsetInNamePayload;
+    uint32_t length;
+
+    Name()
+      : offsetInNamePayload(UINT32_MAX), length(0)
+    {}
+};
+
+typedef Vector<Name, 0, SystemAllocPolicy> NameVector;
+
 // FuncTypeIdDesc describes a function type that can be used by call_indirect
 // and table-entry prologues to structurally compare whether the caller and
 // callee's signatures *structurally* match. To handle the general case, a
 // FuncType is allocated and stored in a process-wide hash table, so that
 // pointer equality implies structural equality. As an optimization for the 99%
 // case where the FuncType has a small number of parameters, the FuncType is
 // bit-packed into a uint32 immediate value so that integer equality implies
 // structural equality. Both cases can be handled with a single comparison by
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -199,39 +199,42 @@ Decoder::startCustomSection(const char* 
         if (!*range) {
             goto rewind;
         }
 
         if (bytesRemain() < (*range)->size) {
             goto fail;
         }
 
-        NameInBytecode name;
-        if (!readVarU32(&name.length) || name.length > bytesRemain()) {
+        CustomSectionEnv sec;
+        if (!readVarU32(&sec.nameLength) || sec.nameLength > bytesRemain()) {
             goto fail;
         }
 
-        name.offset = currentOffset();
-        uint32_t payloadOffset = name.offset + name.length;
+        sec.nameOffset = currentOffset();
+        sec.payloadOffset = sec.nameOffset + sec.nameLength;
+
         uint32_t payloadEnd = (*range)->start + (*range)->size;
-        if (payloadOffset > payloadEnd) {
+        if (sec.payloadOffset > payloadEnd) {
             goto fail;
         }
 
+        sec.payloadLength = payloadEnd - sec.payloadOffset;
+
         // Now that we have a valid custom section, record its offsets in the
         // metadata which can be queried by the user via Module.customSections.
         // Note: after an entry is appended, it may be popped if this loop or
         // the loop in startSection needs to rewind.
-        if (!env->customSections.emplaceBack(name, payloadOffset, payloadEnd - payloadOffset)) {
+        if (!env->customSections.append(sec)) {
             return false;
         }
 
         // If this is the expected custom section, we're done.
-        if (!expected || (expectedLength == name.length && !memcmp(cur_, expected, name.length))) {
-            cur_ += name.length;
+        if (!expected || (expectedLength == sec.nameLength && !memcmp(cur_, expected, sec.nameLength))) {
+            cur_ += sec.nameLength;
             return true;
         }
 
         // Otherwise, blindly skip the custom section and keep looking.
         skipAndFinishCustomSection(**range);
         range->reset();
     }
     MOZ_CRASH("unreachable");
@@ -2500,93 +2503,90 @@ DecodeDataSection(Decoder& d, ModuleEnvi
             return false;
         }
     }
 
     return d.finishSection(*range, "data");
 }
 
 static bool
-DecodeModuleNameSubsection(Decoder& d, ModuleEnvironment* env)
+DecodeModuleNameSubsection(Decoder& d, const CustomSectionEnv& nameSection, ModuleEnvironment* env)
 {
     Maybe<uint32_t> endOffset;
     if (!d.startNameSubsection(NameType::Module, &endOffset)) {
         return false;
     }
     if (!endOffset) {
         return true;
     }
 
-    // Don't use NameInBytecode for module name; instead store a copy of the
-    // string. This way supplying a module name doesn't need to save the whole
-    // bytecode. While function names are likely to be stripped in practice,
-    // module names aren't necessarily.
-
-    uint32_t nameLength;
-    if (!d.readVarU32(&nameLength)) {
+    Name moduleName;
+    if (!d.readVarU32(&moduleName.length)) {
         return d.fail("failed to read module name length");
     }
 
-    NameInBytecode moduleName(d.currentOffset(), nameLength);
+    MOZ_ASSERT(d.currentOffset() >= nameSection.payloadOffset);
+    moduleName.offsetInNamePayload = d.currentOffset() - nameSection.payloadOffset;
 
     const uint8_t* bytes;
-    if (!d.readBytes(nameLength, &bytes)) {
+    if (!d.readBytes(moduleName.length, &bytes)) {
         return d.fail("failed to read module name bytes");
     }
 
     env->moduleName.emplace(moduleName);
 
     return d.finishNameSubsection(*endOffset);
 }
 
 static bool
-DecodeFunctionNameSubsection(Decoder& d, ModuleEnvironment* env)
+DecodeFunctionNameSubsection(Decoder& d, const CustomSectionEnv& nameSection, ModuleEnvironment* env)
 {
     Maybe<uint32_t> endOffset;
     if (!d.startNameSubsection(NameType::Function, &endOffset)) {
         return false;
     }
     if (!endOffset) {
         return true;
     }
 
     uint32_t nameCount = 0;
     if (!d.readVarU32(&nameCount) || nameCount > MaxFuncs) {
         return d.fail("bad function name count");
     }
 
-    NameInBytecodeVector funcNames;
+    NameVector funcNames;
 
     for (uint32_t i = 0; i < nameCount; ++i) {
         uint32_t funcIndex = 0;
         if (!d.readVarU32(&funcIndex)) {
             return d.fail("unable to read function index");
         }
 
         // Names must refer to real functions and be given in ascending order.
         if (funcIndex >= env->numFuncs() || funcIndex < funcNames.length()) {
             return d.fail("invalid function index");
         }
 
-        uint32_t nameLength = 0;
-        if (!d.readVarU32(&nameLength) || nameLength > MaxStringLength) {
+        Name funcName;
+        if (!d.readVarU32(&funcName.length) || funcName.length > MaxStringLength) {
             return d.fail("unable to read function name length");
         }
 
-        if (!nameLength) {
+        if (!funcName.length) {
             continue;
         }
 
         if (!funcNames.resize(funcIndex + 1)) {
             return false;
         }
 
-        NameInBytecode funcName(d.currentOffset(), nameLength);
-
-        if (!d.readBytes(nameLength)) {
+        MOZ_ASSERT(d.currentOffset() >= nameSection.payloadOffset);
+        funcName.offsetInNamePayload = d.currentOffset() - nameSection.payloadOffset;
+
+        if (!d.readBytes(funcName.length)) {
             return d.fail("unable to read function name bytes");
         }
 
         funcNames[funcIndex] = funcName;
     }
 
     if (!d.finishNameSubsection(*endOffset)) {
         return false;
@@ -2604,23 +2604,26 @@ DecodeNameSection(Decoder& d, ModuleEnvi
     MaybeSectionRange range;
     if (!d.startCustomSection(NameSectionName, env, &range)) {
         return false;
     }
     if (!range) {
         return true;
     }
 
+    env->nameCustomSectionIndex = Some(env->customSections.length() - 1);
+    const CustomSectionEnv& nameSection = env->customSections.back();
+
     // Once started, custom sections do not report validation errors.
 
-    if (!DecodeModuleNameSubsection(d, env)) {
+    if (!DecodeModuleNameSubsection(d, nameSection, env)) {
         goto finish;
     }
 
-    if (!DecodeFunctionNameSubsection(d, env)) {
+    if (!DecodeFunctionNameSubsection(d, nameSection, env)) {
         goto finish;
     }
 
     while (d.currentOffset() < range->end()) {
         if (!d.skipNameSubsection()) {
             goto finish;
         }
     }
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -174,19 +174,20 @@ struct ModuleEnvironment
     ImportVector              imports;
     ExportVector              exports;
     Maybe<uint32_t>           startFuncIndex;
     ElemSegmentVector         elemSegments;
     MaybeSectionRange         codeSection;
 
     // Fields decoded as part of the wasm module tail:
     DataSegmentEnvVector      dataSegments;
-    Maybe<NameInBytecode>     moduleName;
-    NameInBytecodeVector      funcNames;
-    CustomSectionVector       customSections;
+    CustomSectionEnvVector    customSections;
+    Maybe<uint32_t>           nameCustomSectionIndex;
+    Maybe<Name>               moduleName;
+    NameVector                funcNames;
 
     explicit ModuleEnvironment(HasGcTypes gcTypesConfigured,
                                CompilerEnvironment* compilerEnv,
                                Shareable sharedMemoryEnabled,
                                ModuleKind kind = ModuleKind::Wasm)
       : kind(kind),
         sharedMemoryEnabled(sharedMemoryEnabled),
         gcTypesConfigured(gcTypesConfigured),
@@ -431,17 +432,17 @@ class Encoder
     // of bytes. When used for strings, bytes are to be interpreted as utf8.
 
     MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
         return writeVarU32(numBytes) &&
                bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
     }
 
     // A "section" is a contiguous range of bytes that stores its own size so
-    // that it may be trivially skipped without examining the contents. Sections
+    // that it may be trivially skipped without examining the payload. Sections
     // require backpatching since the size of the section is only known at the
     // end while the size's varU32 must be stored at the beginning. Immediately
     // after the section length is the string id of the section.
 
     MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
         MOZ_ASSERT(uint32_t(id) < 128);
         return writeVarU32(uint32_t(id)) &&
                writePatchableVarU32(offset);
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -319,16 +319,36 @@ NS_NewBlockFormattingContext(nsIPresShel
 
 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
 
 nsBlockFrame::~nsBlockFrame()
 {
 }
 
 void
+nsBlockFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aWindowSizes) const
+{
+  nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes);
+
+  // Add the size of any nsLineBox::mFrames hashtables we might have:
+  for (ConstLineIterator line = LinesBegin(), line_end = LinesEnd();
+       line != line_end; ++line) {
+    line->AddSizeOfExcludingThis(aWindowSizes);
+  }
+  const FrameLines* overflowLines = GetOverflowLines();
+  if (overflowLines) {
+    ConstLineIterator line = overflowLines->mLines.begin(),
+                      line_end = overflowLines->mLines.end();
+    for (; line != line_end; ++line) {
+      line->AddSizeOfExcludingThis(aWindowSizes);
+    }
+  }
+}
+
+void
 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
 {
   ClearLineCursor();
   DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
   mFloats.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
   nsPresContext* presContext = PresContext();
   nsIPresShell* shell = presContext->PresShell();
   nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -590,16 +590,20 @@ public:
     }
     return false;
   }
 
   virtual bool RenumberChildFrames(int32_t* aOrdinal,
                                    int32_t aDepth,
                                    int32_t aIncrement,
                                    bool aForCounting) override;
+
+  // @see nsIFrame::AddSizeOfExcludingThisForTree
+  void AddSizeOfExcludingThisForTree(nsWindowSizes&) const override;
+
 protected:
   /** @see DoRemoveFrame */
   void DoRemoveFrameInternal(nsIFrame* aDeletedFrame, uint32_t aFlags,
                              PostDestroyData& data);
 
   /** grab overflow lines from this block's prevInFlow, and make them
     * part of this block's mLines list.
     * @return true if any lines were drained.
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -3605,17 +3605,17 @@ public:
   void DeleteAllProperties()
   {
     mProperties.DeleteAll(this);
   }
 
   // nsIFrames themselves are in the nsPresArena, and so are not measured here.
   // Instead, this measures heap-allocated things hanging off the nsIFrame, and
   // likewise for its descendants.
-  void AddSizeOfExcludingThisForTree(nsWindowSizes& aWindowSizes) const;
+  virtual void AddSizeOfExcludingThisForTree(nsWindowSizes& aWindowSizes) const;
 
   /**
    * Return true if and only if this frame obeys visibility:hidden.
    * if it does not, then nsContainerFrame will hide its view even though
    * this means children can't be made visible again.
    */
   virtual bool SupportsVisibilityHidden() { return true; }
 
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -6,23 +6,24 @@
 
 /* representation of one line within a block frame, a CSS line box */
 
 #include "nsLineBox.h"
 
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Likely.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/WritingModes.h"
 #include "nsBidiPresUtils.h"
 #include "nsFrame.h"
 #include "nsIFrameInlines.h"
 #include "nsPresArena.h"
 #include "nsPrintfCString.h"
-#include "mozilla/Sprintf.h"
+#include "nsWindowSizes.h"
 
 #ifdef DEBUG
 static int32_t ctorCount;
 int32_t nsLineBox::GetCtorCount() { return ctorCount; }
 #endif
 
 #ifndef _MSC_VER
 // static nsLineBox constant; initialized in the header file.
@@ -89,16 +90,25 @@ NS_NewLineBox(nsIPresShell* aPresShell, 
 {
   nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
   newLine->NoteFramesMovedFrom(aFromLine);
   newLine->mContainerSize = aFromLine->mContainerSize;
   return newLine;
 }
 
 void
+nsLineBox::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const
+{
+  if (mFlags.mHasHashedFrames) {
+    aSizes.mLayoutFramePropertiesSize +=
+      mFrames->ShallowSizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
+  }
+}
+
+void
 nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
 {
   MOZ_ASSERT(!mFlags.mHasHashedFrames);
   MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
   mFrames = aFromLine->mFrames;
   mFlags.mHasHashedFrames = 1;
   aFromLine->mFlags.mHasHashedFrames = 0;
   aFromLine->mChildCount = aFromLineNewCount;
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -15,16 +15,17 @@
 #include "nsILineIterator.h"
 #include "nsIFrame.h"
 #include <algorithm>
 
 class nsLineBox;
 class nsFloatCache;
 class nsFloatCacheList;
 class nsFloatCacheFreeList;
+class nsWindowSizes;
 
 // State cached after reflowing a float. This state is used during
 // incremental reflow when we avoid reflowing a float.
 class nsFloatCache {
 public:
   nsFloatCache();
 #ifdef NS_BUILD_REFCNT_LOGGING
   ~nsFloatCache();
@@ -577,16 +578,18 @@ public:
   static const char* BreakTypeToString(StyleClear aBreakType);
   char* StateToString(char* aBuf, int32_t aBufSize) const;
 
   void List(FILE* out, int32_t aIndent, uint32_t aFlags = 0) const;
   void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const;
   nsIFrame* LastChild() const;
 #endif
 
+  void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const;
+
 private:
   int32_t IndexOf(nsIFrame* aFrame) const;
 public:
 
   bool Contains(nsIFrame* aFrame) const {
     return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame)
                                                 : IndexOf(aFrame) >= 0;
   }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2578,16 +2578,18 @@ pref("font.blacklist.underline_offset", 
 pref("security.directory",              "");
 
 // security-sensitive dialogs should delay button enabling. In milliseconds.
 pref("security.dialog_enable_delay", 1000);
 pref("security.notification_enable_delay", 500);
 
 #if defined(DEBUG) && !defined(ANDROID)
 pref("csp.content_privileged_about_uris_without_csp", "blank,printpreview,srcdoc");
+// the following pref is for testing purposes only.
+pref("csp.overrule_content_privileged_about_uris_without_csp_whitelist", false);
 #endif
 
 // Default Content Security Policy to apply to signed contents.
 pref("security.signed_content.CSP.default", "script-src 'self'; style-src 'self'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1201,17 +1201,17 @@ class BootstrapScope {
     AsyncShutdown.profileChangeTeardown.addBlocker(
       `Uninstalling add-on: ${data.id}`,
       Management.emit("uninstall", {id: data.id}).then(() => {
         Management.emit("uninstall-complete", {id: data.id});
       }));
   }
 
   update(data, reason) {
-    Management.emit("update", {id: data.id, resourceURI: data.resourceURI});
+    return Management.emit("update", {id: data.id, resourceURI: data.resourceURI});
   }
 
   startup(data, reason) {
     // eslint-disable-next-line no-use-before-define
     this.extension = new Extension(data, this.BOOTSTRAP_REASON_TO_STRING_MAP[reason]);
     return this.extension.startup();
   }
 
--- a/toolkit/content/widgets/general.xml
+++ b/toolkit/content/widgets/general.xml
@@ -18,17 +18,17 @@
                                 onget="return this.getAttribute('disabled') == 'true';"/>
       <property name="tabIndex" onget="return parseInt(this.getAttribute('tabindex')) || 0"
                                 onset="if (val) this.setAttribute('tabindex', val);
                                        else this.removeAttribute('tabindex'); return val;"/>
     </implementation>
   </binding>
 
   <binding id="basetext" extends="chrome://global/content/bindings/general.xml#basecontrol">
-    <implementation implements="nsIDOMXULLabeledControlElement">
+    <implementation>
       <!-- public implementation -->
       <property name="label"      onset="this.setAttribute('label',val); return val;"
                                   onget="return this.getAttribute('label');"/>
       <property name="crop"       onset="this.setAttribute('crop',val); return val;"
                                   onget="return this.getAttribute('crop');"/>
       <property name="image"      onset="this.setAttribute('image',val); return val;"
                                   onget="return this.getAttribute('image');"/>
       <property name="command"    onset="this.setAttribute('command',val); return val;"
--- a/toolkit/content/widgets/text.xml
+++ b/toolkit/content/widgets/text.xml
@@ -65,17 +65,17 @@
 
       <method name="formatAccessKey">
         <parameter name="firstTime"/>
         <body>
           <![CDATA[
             var control = this.labeledControlElement;
             if (!control) {
               var bindingParent = document.getBindingParent(this);
-              if (bindingParent instanceof Ci.nsIDOMXULLabeledControlElement) {
+              if ("accessKey" in bindingParent) {
                 control = bindingParent; // For controls that make the <label> an anon child
               }
             }
             if (control) {
               control.labelElement = this;
               var controlAccessKey = control.getAttribute("accesskey");
               if (controlAccessKey) {
                 this.setAttribute("accesskey", controlAccessKey);
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -27,17 +27,17 @@
     <content>
       <children/>
       <xul:moz-input-box anonid="moz-input-box" flex="1" xbl:inherits="context,spellcheck">
         <html:input class="textbox-input" anonid="input"
                     xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck"/>
       </xul:moz-input-box>
     </content>
 
-    <implementation implements="nsIDOMXULLabeledControlElement">
+    <implementation>
       <!-- nsIDOMXULLabeledControlElement -->
       <field name="crop">""</field>
       <field name="image">""</field>
       <field name="command">""</field>
       <field name="accessKey">""</field>
 
       <field name="mInputField">null</field>
       <field name="mIgnoreClick">false</field>
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -1792,16 +1792,20 @@ class AddonInstall {
         if (isTheme(this.addon.type) && this.addon.active)
           AddonManagerPrivate.notifyAddonChanged(this.addon.id, this.addon.type);
       };
 
       this._startupPromise = (async () => {
         if (this.existingAddon) {
           await XPIInternal.BootstrapScope.get(this.existingAddon).update(
             this.addon, !this.addon.disabled, install);
+
+          if (this.addon.disabled) {
+            flushJarCache(this.file);
+          }
         } else {
           await install();
           await XPIInternal.BootstrapScope.get(this.addon).install(undefined, true);
         }
       })();
 
       await this._startupPromise;
     })().catch((e) => {
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1763,17 +1763,17 @@ class BootstrapScope {
    *        startup is required.
    */
   install(reason = BOOTSTRAP_REASONS.ADDON_INSTALL, startup, extraArgs) {
     return this._install(reason, false, startup, extraArgs);
   }
 
   async _install(reason, callUpdate, startup, extraArgs) {
     if (callUpdate) {
-      this.callBootstrapMethod("update", reason, extraArgs);
+      await this.callBootstrapMethod("update", reason, extraArgs);
     } else {
       this.callBootstrapMethod("install", reason, extraArgs);
     }
 
     if (startup && this.addon.active) {
       await this.startup(reason, extraArgs);
     } else if (this.addon.disabled) {
       this.unloadBootstrapScope();