Merge inbound to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Thu, 30 Aug 2018 19:58:52 +0300
changeset 482421 c317d6b31d9c951c9357fb9a49d2686a3efcfe2f
parent 482420 91dc93fd924586ad3db866bb5ea0db0260c8209d (current diff)
parent 482374 54c5a8af9831a0a36ed39c6f4581101acdcae79b (diff)
child 482423 baccdb95340e7e84b741836cc83a155ca2c8a310
child 482449 7e58b028df95389c1994c268161a0c7ba6e7f957
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmerge
milestone63.0a1
Merge inbound to mozilla-central. a=merge
browser/components/extensions/test/browser/browser-common.ini
--- a/accessible/tests/mochitest/events/test_focus_general.xul
+++ b/accessible/tests/mochitest/events/test_focus_general.xul
@@ -69,37 +69,16 @@
       gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
       // select first menu item ("item 1"), focus on menu item
       gQueue.push(new synthDownKey("popupbutton", new focusChecker("bp_item1")));
       // choose select menu item, focus gets back to menubutton
       gQueue.push(new synthEnterKey("bp_item1", new focusChecker("menubutton")));
       // show popup again for the next test
       gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
 
-if (!MAC) {
-      // click menubutton of the 'menubutton' button while popup of button open.
-      gQueue.push(new synthClick("mbb", new focusChecker("mbb"), { where: "right" }));
-      // close popup, focus stays on menubutton, fire focus event
-      gQueue.push(new synthEscapeKey("mbb", new focusChecker("mbb")));
-      // click menubutton, open popup, focus stays on menubutton
-      gQueue.push(new synthClick("mbb", new nofocusChecker(), { where: "right" }));
-      // select first menu item ("item 1"), focus on menu item
-      gQueue.push(new synthDownKey("mbb", new focusChecker("mbb_item1")));
-      // choose select menu item, focus gets back to menubutton
-      gQueue.push(new synthEnterKey("mbb_item1", new focusChecker("mbb")));
-      // open popup, focus stays on menubutton
-      gQueue.push(new synthOpenComboboxKey("mbb", new nofocusChecker()));
-      // select second menu item ("item 2"), focus on menu item
-      gQueue.push(new synthUpKey("menubutton", new focusChecker("mbb_item2")));
-      // click on menu item of menubutton menu, focus menubutton
-      gQueue.push(new synthClick("mbb_item2", new focusChecker("mbb")));
-} else {
-      todo(false, "mbb tests time out on OS X, fix bug 746970 and reenable!");
-}
-
       // focus colorpicker button
       gQueue.push(new synthFocus("colorpicker"));
       // click on button, open popup, focus goes to current color button
       var btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 0 };
       var checker = new focusChecker(getColorBtn, btnObj);
       gQueue.push(new synthClick("colorpicker", checker));
       // select sibling color button, focus on it
       btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 1 };
@@ -134,31 +113,26 @@ if (!MAC) {
       <iframe id="editabledoc" src="focus.html"/>
       <radiogroup id="radioclothes">
         <radio id="radiosweater" label="radiosweater"/>
         <radio id="radiocap" label="radiocap" disabled="true"/>
         <radio id="radiojacket" label="radiojacket"/>
       </radiogroup>
       <checkbox id="checkbox" label="checkbox"/>
       <button id="button" label="button"/>
-      <button id="checkbutton" type="checkbox" label="checkbutton"/>
-      <button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
 
       <button id="menubutton" type="menu" label="menubutton">
         <menupopup>
           <menuitem id="mb_item1" label="item1"/>
           <menuitem id="mb_item2" label="item2"/>
         </menupopup>
       </button>
-      <button id="mbb" type="menu-button" label="menubutton button">
-        <menupopup>
-          <menuitem id="mbb_item1" label="item1"/>
-          <menuitem id="mbb_item2" label="item2"/>
-        </menupopup>
-      </button>
+
+      <button id="checkbutton" type="checkbox" label="checkbutton"/>
+      <button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
 
       <colorpicker id="colorpicker" type="button" label="color picker"
                    color="#FFFFFF"/>
 
       <popupset>
         <menupopup id="backpopup" position="after_start">
           <menuitem id="bp_item1" label="Page 1"/>
           <menuitem id="bp_item2" label="Page 2"/>
--- a/accessible/tests/mochitest/role/test_aria.xul
+++ b/accessible/tests/mochitest/role/test_aria.xul
@@ -20,17 +20,16 @@
     {
       ok(!isAccessible("presentation_label"),
                       "Presentation label shouldn't be accessible.");
       ok(!isAccessible("presentation_descr"),
                       "Presentation description shouldn't be accessible.");
 
       // aria-pressed
       testRole("pressed_button", ROLE_TOGGLE_BUTTON);
-      testRole("pressed_menu_button", ROLE_TOGGLE_BUTTON);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
@@ -54,19 +53,14 @@
     <pre id="test">
     </pre>
   </body>
 
   <vbox flex="1">
     <label id="presentation_label" role="presentation" value="label"/>
     <description id="presentation_descr" role="presentation" value="description"/>
     <button id="pressed_button" aria-pressed="true" label="I am pressed" />
-    <button id="pressed_menu_button" aria-pressed="true" label="I am pressed" type="menu-button">
-      <menupopup>
-        <menuitem label="I am a menu item" />
-      </menupopup>
-    </button>
   </vbox>
 
   
   </hbox>  
 </window>
 
--- a/accessible/tests/mochitest/states/test_aria.xul
+++ b/accessible/tests/mochitest/states/test_aria.xul
@@ -17,17 +17,16 @@
           src="../states.js" />
 
   <script type="application/javascript">
   <![CDATA[
     function doTest()
     {
       // aria-pressed
       testStates("pressed_button", STATE_PRESSED, 0, STATE_CHECKABLE);
-      testStates("pressed_menu_button", STATE_PRESSED | STATE_HASPOPUP, 0, STATE_CHECKABLE);
 
       testStates("tabs", STATE_MULTISELECTABLE);
       // Make sure XUL selection works, since aria-selected defaults to false.
       testStates("tab1", STATE_SELECTED);
       // aria-selected="true".
       testStates("tab2", STATE_SELECTED);
       // Neither.
       testStates("tab3", 0, 0, STATE_SELECTED);
@@ -51,21 +50,16 @@
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
       <button id="pressed_button" aria-pressed="true" label="I am pressed" />
-      <button id="pressed_menu_button" aria-pressed="true" label="I am pressed" type="menu-button">
-        <menupopup>
-          <menuitem label="I am a menu item" />
-        </menupopup>
-      </button>
 
       <tabbox>
         <tabs id="tabs" aria-multiselectable="true">
           <tab id="tab1" label="tab1" selected="true"/>
           <tab id="tab2" label="tab2" aria-selected="true"/>
           <tab id="tab3" label="tab3"/>
         </tabs>
       </tabbox>
--- a/accessible/xul/XULFormControlAccessible.cpp
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -152,49 +152,29 @@ XULButtonAccessible::ContainerWidget() c
     return mParent;
   return nullptr;
 }
 
 bool
 XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const
 {
   // In general XUL button has not accessible children. Nevertheless menu
-  // buttons can have button (@type="menu-button") and popup accessibles
-  // (@type="menu-button", @type="menu" or columnpicker.
-
-  // Get an accessible for menupopup or popup elements.
-  if (aEl->IsXULElement(nsGkAtoms::menupopup) ||
-      aEl->IsXULElement(nsGkAtoms::popup)) {
-    return true;
-  }
-
-  // Button and toolbarbutton are real buttons. Get an accessible
-  // for it. Ignore dropmarker button which is placed as a last child.
-  if ((!aEl->IsXULElement(nsGkAtoms::button) &&
-       !aEl->IsXULElement(nsGkAtoms::toolbarbutton)) ||
-      aEl->IsXULElement(nsGkAtoms::dropMarker)) {
-    return false;
-  }
-
-  return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                            nsGkAtoms::menuButton, eCaseMatters);
+  // buttons can have popup accessibles (@type="menu" or columnpicker).
+  return aEl->IsXULElement(nsGkAtoms::menupopup) ||
+         aEl->IsXULElement(nsGkAtoms::popup);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULButtonAccessible protected
 
 bool
 XULButtonAccessible::ContainsMenu() const
 {
-  static Element::AttrValuesArray strings[] =
-    {&nsGkAtoms::menu, &nsGkAtoms::menuButton, nullptr};
-
-  return mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None,
-                                                nsGkAtoms::type,
-                                                strings, eCaseMatters) >= 0;
+  return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                                            nsGkAtoms::menu, eCaseMatters);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULDropmarkerAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULDropmarkerAccessible::
   XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
--- a/browser/components/downloads/DownloadsSubview.jsm
+++ b/browser/components/downloads/DownloadsSubview.jsm
@@ -365,16 +365,25 @@ DownloadsSubview.Button = class extends 
     super();
     this.download = download;
 
     this.element = document.createElement("toolbarbutton");
     this.element._shell = this;
 
     this.element.classList.add("subviewbutton", "subviewbutton-iconic", "download",
       "download-state");
+
+    let hover = event => {
+      if (event.originalTarget.classList.contains("action-button")) {
+        this.element.classList.toggle("downloadHoveringButton",
+                                      event.type == "mouseover");
+      }
+    };
+    this.element.addEventListener("mouseover", hover);
+    this.element.addEventListener("mouseout", hover);
   }
 
   get browserWindow() {
     return this.element.ownerGlobal;
   }
 
   async refresh() {
     if (this._targetFileChecked)
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -106,19 +106,18 @@
         <xul:button class="downloadButton downloadShowBlockedInfo"
                     tooltiptext="&cmd.chooseUnblock.label;"
                     oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_showBlockedInfo');"/>
       </xul:stack>
     </content>
   </binding>
 
   <binding id="download-subview-toolbarbutton"
-           extends="chrome://global/content/bindings/button.xml#menu-button-base">
+           extends="chrome://global/content/bindings/button.xml#button-base">
     <content>
-      <children includes="observes|template|menupopup|panel|tooltip"/>
       <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
       <xul:vbox class="toolbarbutton-text" flex="1">
         <xul:label crop="end" xbl:inherits="value=label,accesskey,crop,wrap"/>
         <xul:label class="status-text status-full" crop="end" xbl:inherits="value=fullStatus"/>
         <xul:label class="status-text status-open" crop="end" xbl:inherits="value=openLabel"/>
         <xul:label class="status-text status-retry" crop="end" xbl:inherits="value=retryLabel"/>
         <xul:label class="status-text status-show" crop="end" xbl:inherits="value=showLabel"/>
       </xul:vbox>
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -225,20 +225,20 @@
 
 /* When a Download is _not_ hovered, display the full status message. */
 .subviewbutton.download:not(:hover) > .toolbarbutton-text > .status-full,
 /* When a Download is hovered when the file doesn't exist and cannot be retried,
    keep showing the full status message. */
 .subviewbutton.download:hover:-moz-any(:not([openLabel]),:not([exists])):not([retryLabel]) > .toolbarbutton-text > .status-full,
 /* When a Download is hovered and the it can be retried, but the action button
    is _not_ hovered, keep showing the full status message. */
-.subviewbutton.download:hover[retryLabel]:not([buttonover]) > .toolbarbutton-text > .status-full,
+.subviewbutton.download:hover[retryLabel]:not(.downloadHoveringButton) > .toolbarbutton-text > .status-full,
 /* When a Download is hovered and the file can be opened, but the action button
    is _not_ hovered, show the 'Open File' status label. */
-.subviewbutton.download:hover[openLabel][exists]:not([buttonover]) > .toolbarbutton-text > .status-open,
+.subviewbutton.download:hover[openLabel][exists]:not(.downloadHoveringButton) > .toolbarbutton-text > .status-open,
 /* When a Download is hovered - its action button explicitly - and it can be
    retried, show the 'Retry Download' label. */
-.subviewbutton.download:hover[retryLabel][buttonover] > .toolbarbutton-text > .status-retry,
+.subviewbutton.download:hover[retryLabel].downloadHoveringButton > .toolbarbutton-text > .status-retry,
 /* When a Download is hovered - its action button explicitly - and the file can
    be shown in the OS's shell, show the 'Open Containing Folder' label. */
-.subviewbutton.download:hover[openLabel][exists][buttonover] > .toolbarbutton-text > .status-show {
+.subviewbutton.download:hover[openLabel][exists].downloadHoveringButton > .toolbarbutton-text > .status-show {
   display: inline;
 }
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -154,17 +154,16 @@ skip-if = !e10s || !crashreporter # the 
 [browser_ext_sessions_getRecentlyClosed.js]
 [browser_ext_sessions_getRecentlyClosed_private.js]
 [browser_ext_sessions_getRecentlyClosed_tabs.js]
 [browser_ext_sessions_restore.js]
 [browser_ext_sessions_restoreTab.js]
 [browser_ext_sessions_window_tab_value.js]
 [browser_ext_settings_overrides_default_search.js]
 [browser_ext_sidebarAction.js]
-skip-if = debug #Bug 1483325
 [browser_ext_sidebarAction_browser_style.js]
 [browser_ext_sidebarAction_context.js]
 [browser_ext_sidebarAction_contextMenu.js]
 [browser_ext_sidebarAction_runtime.js]
 [browser_ext_sidebarAction_tabs.js]
 [browser_ext_sidebarAction_windows.js]
 [browser_ext_simple.js]
 [browser_ext_slow_script.js]
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
@@ -65,16 +65,17 @@ async function sendMessage(ext, msg, dat
   ext.sendMessage({msg, data});
   await ext.awaitMessage("done");
 }
 
 add_task(async function sidebar_initial_install() {
   ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
   let extension = ExtensionTestUtils.loadExtension(getExtData());
   await extension.startup();
+  await extension.awaitMessage("sidebar");
 
   // Test sidebar is opened on install
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
 
   await extension.unload();
   // Test that the sidebar was closed on unload.
   ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
 });
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -149,17 +149,17 @@ public:
 
   void AssertIsOnTargetThread() const
   {
     MOZ_ASSERT(IsTargetThread());
   }
 
   bool IsTargetThread() const
   {
-    return NS_IsMainThread() == mIsMainThread;
+    return NS_GetCurrentThread() == mTargetThread;
   }
 
   uint16_t ReadyState()
   {
     MutexAutoLock lock(mMutex);
     if (mEventSource) {
       return mEventSource->mReadyState;
     }
@@ -307,16 +307,24 @@ public:
   // These attributes are used for error reporting. Should only be accessed on
   // target thread
   nsString mScriptFile;
   uint32_t mScriptLine;
   uint32_t mScriptColumn;
   uint64_t mInnerWindowID;
 
 private:
+
+  // Pointer to the target thread for checking whether we are
+  // on the target thread. This is intentionally a non-owning
+  // pointer in order not to affect the thread destruction
+  // sequence. This pointer must only be compared for equality
+  // and must not be dereferenced.
+  nsIThread* mTargetThread;
+
   // prevent bad usage
   EventSourceImpl(const EventSourceImpl& x) = delete;
   EventSourceImpl& operator=(const EventSourceImpl& x) = delete;
   ~EventSourceImpl()
   {
     if (IsClosed()) {
       return;
     }
@@ -343,16 +351,17 @@ EventSourceImpl::EventSourceImpl(EventSo
   , mMutex("EventSourceImpl::mMutex")
   , mFrozen(false)
   , mGoingToDispatchAllMessages(false)
   , mIsMainThread(NS_IsMainThread())
   , mIsShutDown(false)
   , mScriptLine(0)
   , mScriptColumn(0)
   , mInnerWindowID(0)
+  , mTargetThread(NS_GetCurrentThread())
 {
   MOZ_ASSERT(mEventSource);
   if (!mIsMainThread) {
     mEventSource->mIsMainThread = false;
   }
   SetReadyState(CONNECTING);
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4101,17 +4101,17 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
     default:
       return gfx::ExtendMode::CLAMP;
     }
   }
 
   already_AddRefed<gfxPattern> GetPatternFor(Style aStyle)
   {
     const CanvasPattern* pat = mCtx->CurrentState().patternStyles[aStyle];
-    RefPtr<gfxPattern> pattern = new gfxPattern(pat->mSurface, Matrix());
+    RefPtr<gfxPattern> pattern = new gfxPattern(pat->mSurface, pat->mTransform);
     pattern->SetExtend(CvtCanvasRepeatToGfxRepeat(pat->mRepeat));
     return pattern.forget();
   }
 
   virtual void DrawText(nscoord aXOffset, nscoord aWidth) override
   {
     gfx::Point point = mPt;
     bool rtl = mTextRun->IsRightToLeft();
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1481,24 +1481,19 @@ EventStateManager::CreateClickHoldTimer(
   // just to be anal (er, safe)
   if (mClickHoldTimer) {
     mClickHoldTimer->Cancel();
     mClickHoldTimer = nullptr;
   }
 
   // if content clicked on has a popup, don't even start the timer
   // since we'll end up conflicting and both will show.
-  if (mGestureDownContent) {
-    // check for the |popup| attribute
-    if (nsContentUtils::HasNonEmptyAttr(mGestureDownContent, kNameSpaceID_None,
-                                        nsGkAtoms::popup))
-      return;
-
-    // check for a <menubutton> like bookmarks
-    if (mGestureDownContent->IsXULElement(nsGkAtoms::menubutton))
+  if (mGestureDownContent &&
+      nsContentUtils::HasNonEmptyAttr(mGestureDownContent, kNameSpaceID_None,
+                                      nsGkAtoms::popup)) {
       return;
   }
 
   int32_t clickHoldDelay =
     Preferences::GetInt("ui.click_hold_context_menus.delay", 500);
   NS_NewTimerWithFuncCallback(getter_AddRefs(mClickHoldTimer),
                               sClickHoldCallback,
                               this,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -901,17 +901,17 @@ JS_TransplantObject(JSContext* cx, Handl
 
     JS::Compartment* destination = target->compartment();
 
     if (origobj->compartment() == destination) {
         // If the original object is in the same compartment as the
         // destination, then we know that we won't find a wrapper in the
         // destination's cross compartment map and that the same
         // object will continue to work.
-        AutoRealmUnchecked ar(cx, origobj->deprecatedRealm());
+        AutoRealmUnchecked ar(cx, origobj->nonCCWRealm());
         if (!JSObject::swap(cx, origobj, target))
             MOZ_CRASH();
         newIdentity = origobj;
     } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
         // There might already be a wrapper for the original object in
         // the new compartment. If there is, we use its identity and swap
         // in the contents of |target|.
         newIdentity = &p->value().get().toObject();
@@ -935,17 +935,17 @@ JS_TransplantObject(JSContext* cx, Handl
     // `newIdentity == origobj`, because this process also clears out any
     // cached wrapper state.
     if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
         MOZ_CRASH();
 
     // Lastly, update the original object to point to the new one.
     if (origobj->compartment() != destination) {
         RootedObject newIdentityWrapper(cx, newIdentity);
-        AutoRealmUnchecked ar(cx, origobj->deprecatedRealm());
+        AutoRealmUnchecked ar(cx, origobj->nonCCWRealm());
         if (!JS_WrapObject(cx, &newIdentityWrapper))
             MOZ_CRASH();
         MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
         if (!JSObject::swap(cx, origobj, newIdentityWrapper))
             MOZ_CRASH();
         if (!origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv))
             MOZ_CRASH();
     }
@@ -3596,25 +3596,26 @@ CloneFunctionObject(JSContext* cx, Handl
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     cx->check(env);
     MOZ_ASSERT(env);
     // Note that funobj can be in a different compartment.
 
     if (!funobj->is<JSFunction>()) {
+        MOZ_RELEASE_ASSERT(!IsCrossCompartmentWrapper(funobj));
         AutoRealm ar(cx, funobj);
         RootedValue v(cx, ObjectValue(*funobj));
         ReportIsNotFunction(cx, v);
         return nullptr;
     }
 
     RootedFunction fun(cx, &funobj->as<JSFunction>());
     if (fun->isInterpretedLazy()) {
-        AutoRealm ar(cx, funobj);
+        AutoRealm ar(cx, fun);
         if (!JSFunction::getOrCreateScript(cx, fun))
             return nullptr;
     }
 
     // Only allow cloning normal, interpreted functions.
     if (fun->isNative() ||
         fun->isBoundFunction() ||
         fun->kind() != JSFunction::NormalFunction ||
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -817,16 +817,18 @@ FormatValue(JSContext* cx, const Value& 
 
     /*
      * We could use Maybe<AutoRealm> here, but G++ can't quite follow
      * that, and warns about uninitialized members being used in the
      * destructor.
      */
     RootedString str(cx);
     if (v.isObject()) {
+        if (IsCrossCompartmentWrapper(&v.toObject()))
+            return "[cross-compartment wrapper]";
         AutoRealm ar(cx, &v.toObject());
         str = ToString<CanGC>(cx, v);
     } else {
         str = ToString<CanGC>(cx, v);
     }
 
     if (!str)
         return nullptr;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -990,18 +990,18 @@ RunModule(JSContext* cx, const char* fil
     return JS_CallFunction(cx, loaderObj, importFun, args, &value);
 }
 
 static bool
 EnqueueJob(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (!IsCallable(args.get(0))) {
-        JS_ReportErrorASCII(cx, "EnqueueJob's first argument must be callable");
+    if (!IsFunctionObject(args.get(0))) {
+        JS_ReportErrorASCII(cx, "EnqueueJob's first argument must be a function");
         return false;
     }
 
     args.rval().setUndefined();
 
     RootedObject job(cx, &args[0].toObject());
     return js::EnqueueJob(cx, job);
 }
@@ -1049,17 +1049,17 @@ ForwardingPromiseRejectionTrackerCallbac
         JS_ClearPendingException(cx);
 }
 
 static bool
 SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (!IsCallable(args.get(0))) {
+    if (!IsFunctionObject(args.get(0))) {
         JS_ReportErrorASCII(cx,
                             "setPromiseRejectionTrackerCallback expects a function as its sole "
                             "argument");
         return false;
     }
 
     GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];
     JS::SetPromiseRejectionTrackerCallback(cx, ForwardingPromiseRejectionTrackerCallback);
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -369,17 +369,17 @@ def load_wpt_tests(requested_paths, excl
         os.path.join(wpt, "resources", "testharness.js"),
         os.path.join(here, "testharnessreport.js"),
     ]
 
     def resolve(test_path, script):
         if script.startswith("/"):
             return os.path.join(wpt, script[1:])
 
-        return os.path.join(test_path, script)
+        return os.path.join(wpt, os.path.dirname(test_path), script)
 
     return [
         RefTestCase(
             wpt,
             test_path,
             extra_helper_paths=extra_helper_paths + [resolve(test_path, s) for s in test.scripts],
             wpt=test
         )
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -388,17 +388,17 @@ JSContext::setZone(js::Zone *zone, JSCon
         freeLists_ = &zone_->arenas.freeLists();
     }
 }
 
 inline void
 JSContext::enterRealmOf(JSObject* target)
 {
     MOZ_ASSERT(JS::CellIsNotGray(target));
-    enterRealm(target->deprecatedRealm());
+    enterRealm(target->nonCCWRealm());
 }
 
 inline void
 JSContext::enterRealmOf(JSScript* target)
 {
     MOZ_ASSERT(JS::CellIsNotGray(target));
     enterRealm(target->realm());
 }
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1188,17 +1188,17 @@ js::RunJobs(JSContext* cx)
 
             cx->jobQueue->get()[i] = nullptr;
 
             // If the next job is the last job in the job queue, allow
             // skipping the standard job queuing behavior.
             if (i == cx->jobQueue->length() - 1)
                 JS::JobQueueIsEmpty(cx);
 
-            AutoRealm ar(cx, job);
+            AutoRealm ar(cx, &job->as<JSFunction>());
             {
                 if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
                     // Nothing we can do about uncatchable exceptions.
                     if (!cx->isExceptionPending())
                         continue;
                     RootedValue exn(cx);
                     if (cx->getPendingException(&exn)) {
                         /*
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -392,22 +392,16 @@ SetNewObjectMetadata(JSContext* cx, T* o
     }
 
     return obj;
 }
 
 } // namespace js
 
 inline js::GlobalObject&
-JSObject::deprecatedGlobal() const
-{
-    return *deprecatedRealm()->unsafeUnbarrieredMaybeGlobal();
-}
-
-inline js::GlobalObject&
 JSObject::nonCCWGlobal() const
 {
     /*
      * The global is read-barriered so that it is kept live by access through
      * the Realm. When accessed through a JSObject, however, the global will be
      * already kept live by the black JSObject's group pointer, so does not
      * need to be read-barriered.
      */
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -425,19 +425,16 @@ class JSObject : public js::gc::Cell
     /*
      * Get the enclosing environment of an object. When called on a
      * non-EnvironmentObject, this will just be the global (the name
      * "enclosing environment" still applies in this situation because
      * non-EnvironmentObjects can be on the environment chain).
      */
     inline JSObject* enclosingEnvironment() const;
 
-    // Deprecated: call nonCCWGlobal or NativeObject::global() instead!
-    inline js::GlobalObject& deprecatedGlobal() const;
-
     // Cross-compartment wrappers are not associated with a single realm/global,
     // so these methods assert the object is not a CCW.
     inline js::GlobalObject& nonCCWGlobal() const;
 
     JS::Realm* nonCCWRealm() const {
         MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
         return group_->realm();
     }
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -884,16 +884,19 @@ class MOZ_RAII AssertRealmUnchanged
     }
 
   protected:
     JSContext* const cx;
     JS::Realm* const oldRealm;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+// AutoRealm can be used to enter the realm of a JSObject, JSScript or
+// ObjectGroup. It must not be used with cross-compartment wrappers, because
+// CCWs are not associated with a single realm.
 class AutoRealm
 {
     JSContext* const cx_;
     JS::Realm* const origin_;
 
   public:
     template <typename T>
     inline AutoRealm(JSContext* cx, const T& target);
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -236,17 +236,16 @@ GetClickableAncestor(nsIFrame* aFrame, n
     }
 
     // See nsCSSFrameConstructor::FindXULTagData. This code is not
     // really intended to be used with XUL, though.
     if (content->IsAnyOfXULElements(nsGkAtoms::button,
                                     nsGkAtoms::checkbox,
                                     nsGkAtoms::radio,
                                     nsGkAtoms::menu,
-                                    nsGkAtoms::menubutton,
                                     nsGkAtoms::menuitem,
                                     nsGkAtoms::menulist,
                                     nsGkAtoms::scrollbarbutton,
                                     nsGkAtoms::resizer)) {
       return content;
     }
 
     static Element::AttrValuesArray clickableRoles[] =
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1784,24 +1784,20 @@ ConsumeOutsideClicksResult nsMenuPopupFr
       return ConsumeOutsideClicks_True;  // Consume outside clicks for combo boxes on all platforms
     }
 #if defined(XP_WIN)
     // Don't consume outside clicks for menus in Windows
     if (ni->Equals(nsGkAtoms::menu, kNameSpaceID_XUL) ||
         ni->Equals(nsGkAtoms::popupset, kNameSpaceID_XUL) ||
         ((ni->Equals(nsGkAtoms::button, kNameSpaceID_XUL) ||
           ni->Equals(nsGkAtoms::toolbarbutton, kNameSpaceID_XUL)) &&
-         (parentContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                                  nsGkAtoms::type,
-                                                  nsGkAtoms::menu,
-                                                  eCaseMatters) ||
-          parentContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                                  nsGkAtoms::type,
-                                                  nsGkAtoms::menuButton,
-                                                  eCaseMatters)))) {
+         parentContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+                                                 nsGkAtoms::type,
+                                                 nsGkAtoms::menu,
+                                                 eCaseMatters))) {
       return ConsumeOutsideClicks_Never;
     }
 #endif
     if (ni->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL)) {
       // Don't consume outside clicks for autocomplete widget
       if (parentContent->AsElement()->AttrValueIs(kNameSpaceID_None,
                                                   nsGkAtoms::type,
                                                   nsGkAtoms::autocomplete,
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -1117,16 +1117,35 @@ nsJARChannel::GetZipEntry(nsIZipEntry **
 NS_IMETHODIMP
 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
 {
     LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
 
     mRequest = req;
     nsresult rv = mListener->OnStartRequest(this, mListenerContext);
     mRequest = nullptr;
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Restrict loadable content types.
+    nsAutoCString contentType;
+    GetContentType(contentType);
+    auto contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
+    if (contentType.Equals(APPLICATION_HTTP_INDEX_FORMAT) &&
+        contentPolicyType != nsIContentPolicy::TYPE_DOCUMENT &&
+        contentPolicyType != nsIContentPolicy::TYPE_FETCH) {
+      return NS_ERROR_CORRUPTED_CONTENT;
+    }
+    if (contentPolicyType == nsIContentPolicy::TYPE_STYLESHEET &&
+        !contentType.EqualsLiteral(TEXT_CSS)) {
+      return NS_ERROR_CORRUPTED_CONTENT;
+    }
+    if (contentPolicyType == nsIContentPolicy::TYPE_SCRIPT &&
+        !nsContentUtils::IsJavascriptMIMEType(NS_ConvertUTF8toUTF16(contentType))) {
+      return NS_ERROR_CORRUPTED_CONTENT;
+    }
 
     return rv;
 }
 
 NS_IMETHODIMP
 nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
 {
     LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n",
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -310194,16 +310194,21 @@
      {}
     ]
    ],
    "wasm/jsapi/bad-imports.js": [
     [
      {}
     ]
    ],
+   "wasm/jsapi/table/assertions.js": [
+    [
+     {}
+    ]
+   ],
    "wasm/jsapi/wasm-constants.js": [
     [
      {}
     ]
    ],
    "wasm/jsapi/wasm-module-builder.js": [
     [
      {}
@@ -394817,16 +394822,80 @@
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/constructor/instantiate-bad-imports.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/global/constructor.any.js": [
+    [
+     "/wasm/jsapi/global/constructor.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/global/constructor.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/global/constructor.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/global/toString.any.js": [
+    [
+     "/wasm/jsapi/global/toString.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/global/toString.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/global/toString.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/global/value-set.any.js": [
+    [
+     "/wasm/jsapi/global/value-set.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/global/value-set.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/global/value-set.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/global/valueOf.any.js": [
+    [
+     "/wasm/jsapi/global/valueOf.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/global/valueOf.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/global/valueOf.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/instance/constructor-bad-imports.any.js": [
     [
      "/wasm/jsapi/instance/constructor-bad-imports.any.html",
      {}
     ],
     [
      "/wasm/jsapi/instance/constructor-bad-imports.any.js",
      {
@@ -394849,48 +394918,128 @@
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/instance/constructor.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/instance/exports.any.js": [
+    [
+     "/wasm/jsapi/instance/exports.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/instance/exports.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/instance/exports.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/instance/toString.any.js": [
+    [
+     "/wasm/jsapi/instance/toString.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/instance/toString.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/instance/toString.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/interface.any.js": [
     [
      "/wasm/jsapi/interface.any.html",
      {}
     ],
     [
      "/wasm/jsapi/interface.any.js",
      {
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/interface.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/memory/buffer.any.js": [
+    [
+     "/wasm/jsapi/memory/buffer.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/memory/buffer.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/memory/buffer.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/memory/constructor.any.js": [
     [
      "/wasm/jsapi/memory/constructor.any.html",
      {}
     ],
     [
      "/wasm/jsapi/memory/constructor.any.js",
      {
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/memory/constructor.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/memory/grow.any.js": [
+    [
+     "/wasm/jsapi/memory/grow.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/memory/grow.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/memory/grow.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/memory/toString.any.js": [
+    [
+     "/wasm/jsapi/memory/toString.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/memory/toString.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/memory/toString.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/module/constructor.any.js": [
     [
      "/wasm/jsapi/module/constructor.any.html",
      {}
     ],
     [
      "/wasm/jsapi/module/constructor.any.js",
      {
@@ -394945,32 +395094,112 @@
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/module/imports.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/module/toString.any.js": [
+    [
+     "/wasm/jsapi/module/toString.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/module/toString.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/module/toString.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/table/constructor.any.js": [
     [
      "/wasm/jsapi/table/constructor.any.html",
      {}
     ],
     [
      "/wasm/jsapi/table/constructor.any.js",
      {
       "jsshell": true
      }
     ],
     [
      "/wasm/jsapi/table/constructor.any.worker.html",
      {}
     ]
    ],
+   "wasm/jsapi/table/get-set.any.js": [
+    [
+     "/wasm/jsapi/table/get-set.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/table/get-set.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/table/get-set.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/table/grow.any.js": [
+    [
+     "/wasm/jsapi/table/grow.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/table/grow.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/table/grow.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/table/length.any.js": [
+    [
+     "/wasm/jsapi/table/length.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/table/length.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/table/length.any.worker.html",
+     {}
+    ]
+   ],
+   "wasm/jsapi/table/toString.any.js": [
+    [
+     "/wasm/jsapi/table/toString.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/table/toString.any.js",
+     {
+      "jsshell": true
+     }
+    ],
+    [
+     "/wasm/jsapi/table/toString.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/wasm_local_iframe_test.html": [
     [
      "/wasm/wasm_local_iframe_test.html",
      {}
     ]
    ],
    "wasm/wasm_serialization_tests.html": [
     [
@@ -647668,50 +647897,110 @@
   "wasm/jsapi/bad-imports.js": [
    "f076baacca8b3e6addf49f6841874d11bfcfe5a2",
    "support"
   ],
   "wasm/jsapi/constructor/instantiate-bad-imports.any.js": [
    "86700298dfae66de6f4d026baa29e6e3584320f7",
    "testharness"
   ],
+  "wasm/jsapi/global/constructor.any.js": [
+   "7a45cc4191c55684cde187fc73fb9741d6f5c2c5",
+   "testharness"
+  ],
+  "wasm/jsapi/global/toString.any.js": [
+   "ca025576c2b49604f053c83002f3a9cc8ef41a7b",
+   "testharness"
+  ],
+  "wasm/jsapi/global/value-set.any.js": [
+   "b4e6770f10ed9e3ad55b9ae18ee96deda3ff171e",
+   "testharness"
+  ],
+  "wasm/jsapi/global/valueOf.any.js": [
+   "176c5a784698399351eedeaac0ec305aa8ab7b81",
+   "testharness"
+  ],
   "wasm/jsapi/instance/constructor-bad-imports.any.js": [
    "24c51c10dc5df9d52c06bfb0715e435b17f24f7a",
    "testharness"
   ],
   "wasm/jsapi/instance/constructor.any.js": [
-   "93a3ffda033729d64562a583e823fab05f35f6fe",
+   "61a8f53a014c192f28a0c25252cf3702561e7191",
+   "testharness"
+  ],
+  "wasm/jsapi/instance/exports.any.js": [
+   "31423918720543da2ba25e392267bf541b756242",
+   "testharness"
+  ],
+  "wasm/jsapi/instance/toString.any.js": [
+   "08dcb14a50d04f6db196626ddb93f2b50da8f055",
    "testharness"
   ],
   "wasm/jsapi/interface.any.js": [
-   "64c1f60da1c7888be994f222af69f401402ae5f4",
+   "5d76ac56ec5fafde8dde3924df863a2694bd6691",
+   "testharness"
+  ],
+  "wasm/jsapi/memory/buffer.any.js": [
+   "b04460b6c5e56cf1fe990e3107aa9efcb4964ed5",
    "testharness"
   ],
   "wasm/jsapi/memory/constructor.any.js": [
-   "33256f85e45749cc46842dccbd1ee7c40db41ae5",
+   "f9907ca6104d8ec76861e43b6b981042d86fb865",
+   "testharness"
+  ],
+  "wasm/jsapi/memory/grow.any.js": [
+   "95300399f192b7eab70dd8f07c43f4db37eebe01",
+   "testharness"
+  ],
+  "wasm/jsapi/memory/toString.any.js": [
+   "4e15d75ea20f1ebfeba5dc7c8a9a52c253dd01bf",
    "testharness"
   ],
   "wasm/jsapi/module/constructor.any.js": [
-   "0f5eecf957e8ca6af851ce12f5c18266a2eb0460",
+   "32f183fac8738d30cc8a432768da315949320257",
    "testharness"
   ],
   "wasm/jsapi/module/customSections.any.js": [
-   "146aa7fd332ca9b061fef51a7378d29f8c9c165e",
+   "58ac63b61c93a015bfa9d5daab39f8d5b48548da",
    "testharness"
   ],
   "wasm/jsapi/module/exports.any.js": [
-   "c7ecdcf6b619b4ab93cf4e878addeb9bed736d4e",
+   "e63a885a4c34add0f6787d3642de83d9766568d1",
    "testharness"
   ],
   "wasm/jsapi/module/imports.any.js": [
-   "522b262f549b9a07c0a426cd474151d3d3e02749",
-   "testharness"
+   "640da591d21d8924d261fdc58b8e7cc762187a11",
+   "testharness"
+  ],
+  "wasm/jsapi/module/toString.any.js": [
+   "d9231a132ca8bf965f69c3cc81070a2ffe179efa",
+   "testharness"
+  ],
+  "wasm/jsapi/table/assertions.js": [
+   "dde2fd770904207a1f9f287fa48d82954a418f2e",
+   "support"
   ],
   "wasm/jsapi/table/constructor.any.js": [
-   "4aeac10f7adc6e0ec0abc56fa66c0259102798e2",
+   "e924bdb2ba42c67bcc6d4a949c2eeb50eac63e31",
+   "testharness"
+  ],
+  "wasm/jsapi/table/get-set.any.js": [
+   "2bb43a9308d732b5b6fa689c181ac411880c3733",
+   "testharness"
+  ],
+  "wasm/jsapi/table/grow.any.js": [
+   "d3efb511e4b1db1efa089322c0a3079705dfbdbd",
+   "testharness"
+  ],
+  "wasm/jsapi/table/length.any.js": [
+   "a6a9661dbaddc800cb99b7b8e2b804cb0c8e3c62",
+   "testharness"
+  ],
+  "wasm/jsapi/table/toString.any.js": [
+   "e576477910ad3198b446b4addf89ba9a571d020b",
    "testharness"
   ],
   "wasm/jsapi/wasm-constants.js": [
    "f056f9cbfcfbac52d0506edddd01c8fad8636ebb",
    "support"
   ],
   "wasm/jsapi/wasm-module-builder.js": [
    "6e9284e773105db5751c5483ed9333a45272b180",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/wasm/jsapi/global/constructor.any.js.ini
@@ -0,0 +1,30 @@
+[constructor.any.html]
+  [Order of evaluation]
+    expected: FAIL
+
+  [Explicit value undefined for type f32]
+    expected: FAIL
+
+  [Explicit value undefined for type f64]
+    expected: FAIL
+
+[constructor.any.worker.html]
+  [Order of evaluation]
+    expected: FAIL
+
+  [Explicit value undefined for type f32]
+    expected: FAIL
+
+  [Explicit value undefined for type f64]
+    expected: FAIL
+
+[constructor.any.js]
+  [Order of evaluation]
+    expected: FAIL
+
+  [Explicit value undefined for type f32]
+    expected: FAIL
+
+  [Explicit value undefined for type f64]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/wasm/jsapi/global/value-set.any.js.ini
@@ -0,0 +1,12 @@
+[value-set.any.html]
+  [Calling setter without argument]
+    expected: FAIL
+
+[value-set.any.worker.html]
+  [Calling setter without argument]
+    expected: FAIL
+
+[value-set.any.js]
+  [Calling setter without argument]
+    expected: FAIL
+
--- a/testing/web-platform/meta/wasm/jsapi/memory/constructor.any.js.ini
+++ b/testing/web-platform/meta/wasm/jsapi/memory/constructor.any.js.ini
@@ -1,10 +1,10 @@
 [constructor.any.html]
-  [Empty descriptor]
+  [Invalid descriptor argument]
     expected: FAIL
 
   [Undefined initial value in descriptor]
     expected: FAIL
 
   [Out-of-range initial value in descriptor: NaN]
     expected: FAIL
 
@@ -40,17 +40,17 @@
 
   [Out-of-range maximum value in descriptor: 68719476736]
     expected: FAIL
 
   [Proxy descriptor]
     expected: FAIL
 
 [constructor.any.worker.html]
-  [Empty descriptor]
+  [Invalid descriptor argument]
     expected: FAIL
 
   [Undefined initial value in descriptor]
     expected: FAIL
 
   [Out-of-range initial value in descriptor: NaN]
     expected: FAIL
 
@@ -86,17 +86,17 @@
 
   [Out-of-range maximum value in descriptor: 68719476736]
     expected: FAIL
 
   [Proxy descriptor]
     expected: FAIL
 
 [constructor.any.js]
-  [Empty descriptor]
+  [Invalid descriptor argument]
     expected: FAIL
 
   [Undefined initial value in descriptor]
     expected: FAIL
 
   [Out-of-range initial value in descriptor: NaN]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/wasm/jsapi/memory/grow.any.js.ini
@@ -0,0 +1,93 @@
+[grow.any.html]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[grow.any.worker.html]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[grow.any.js]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
--- a/testing/web-platform/meta/wasm/jsapi/table/constructor.any.js.ini
+++ b/testing/web-platform/meta/wasm/jsapi/table/constructor.any.js.ini
@@ -36,16 +36,22 @@
     expected: FAIL
 
   [Out-of-range maximum value in descriptor: 68719476736]
     expected: FAIL
 
   [Proxy descriptor]
     expected: FAIL
 
+  [Type conversion for descriptor.element]
+    expected: FAIL
+
+  [Order of evaluation for descriptor]
+    expected: FAIL
+
 [constructor.any.worker.html]
   [Undefined initial value in descriptor]
     expected: FAIL
 
   [Out-of-range initial value in descriptor: NaN]
     expected: FAIL
 
   [Out-of-range maximum value in descriptor: NaN]
@@ -79,16 +85,22 @@
     expected: FAIL
 
   [Out-of-range maximum value in descriptor: 68719476736]
     expected: FAIL
 
   [Proxy descriptor]
     expected: FAIL
 
+  [Type conversion for descriptor.element]
+    expected: FAIL
+
+  [Order of evaluation for descriptor]
+    expected: FAIL
+
 [constructor.any.js]
   [Undefined initial value in descriptor]
     expected: FAIL
 
   [Out-of-range initial value in descriptor: NaN]
     expected: FAIL
 
   [Out-of-range maximum value in descriptor: NaN]
@@ -121,8 +133,15 @@
   [Out-of-range initial value in descriptor: 68719476736]
     expected: FAIL
 
   [Out-of-range maximum value in descriptor: 68719476736]
     expected: FAIL
 
   [Proxy descriptor]
     expected: FAIL
+
+  [Type conversion for descriptor.element]
+    expected: FAIL
+
+  [Order of evaluation for descriptor]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/wasm/jsapi/table/get-set.any.js.ini
@@ -0,0 +1,174 @@
+[get-set.any.html]
+  [Missing arguments: get]
+    expected: FAIL
+
+  [Getting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Setting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Getting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Setting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Getting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -1]
+    expected: FAIL
+
+  [Setting out-of-range argument: -1]
+    expected: FAIL
+
+  [Getting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Setting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Getting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Setting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Getting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Setting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Getting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+  [Setting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[get-set.any.worker.html]
+  [Missing arguments: get]
+    expected: FAIL
+
+  [Getting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Setting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Getting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Setting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Getting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -1]
+    expected: FAIL
+
+  [Setting out-of-range argument: -1]
+    expected: FAIL
+
+  [Getting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Setting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Getting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Setting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Getting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Setting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Getting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+  [Setting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[get-set.any.js]
+  [Missing arguments: get]
+    expected: FAIL
+
+  [Getting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Setting out-of-range argument: undefined]
+    expected: FAIL
+
+  [Getting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Setting out-of-range argument: NaN]
+    expected: FAIL
+
+  [Getting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Setting out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Getting out-of-range argument: -1]
+    expected: FAIL
+
+  [Setting out-of-range argument: -1]
+    expected: FAIL
+
+  [Getting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Setting out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Getting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Setting out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Getting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Setting out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Getting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+  [Setting out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/wasm/jsapi/table/grow.any.js.ini
@@ -0,0 +1,93 @@
+[grow.any.html]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[grow.any.worker.html]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
+[grow.any.js]
+  [Missing arguments]
+    expected: FAIL
+
+  [Out-of-range argument: undefined]
+    expected: FAIL
+
+  [Out-of-range argument: NaN]
+    expected: FAIL
+
+  [Out-of-range argument: Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -Infinity]
+    expected: FAIL
+
+  [Out-of-range argument: -1]
+    expected: FAIL
+
+  [Out-of-range argument: 4294967296]
+    expected: FAIL
+
+  [Out-of-range argument: 68719476736]
+    expected: FAIL
+
+  [Out-of-range argument: "0x100000000"]
+    expected: FAIL
+
+  [Out-of-range argument: object "[object Object\]"]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/constructor.any.js
@@ -0,0 +1,121 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_Global(actual, expected) {
+  assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype,
+                "prototype");
+  assert_true(Object.isExtensible(actual), "extensible");
+
+  assert_equals(actual.value, expected, "value");
+  assert_equals(actual.valueOf(), expected, "valueOf");
+}
+
+test(() => {
+  assert_function_name(WebAssembly.Global, "Global", "WebAssembly.Global");
+}, "name");
+
+test(() => {
+  assert_function_length(WebAssembly.Global, 1, "WebAssembly.Global");
+}, "length");
+
+test(() => {
+  assert_throws(new TypeError(), () => new WebAssembly.Global());
+}, "No arguments");
+
+test(() => {
+  const argument = { "value": "i32" };
+  assert_throws(new TypeError(), () => WebAssembly.Global(argument));
+}, "Calling");
+
+test(() => {
+  const order = [];
+
+  new WebAssembly.Global({
+    get value() {
+      order.push("descriptor value");
+      return {
+        toString() {
+          order.push("descriptor value toString");
+          return "f64";
+        },
+      };
+    },
+
+    get mutable() {
+      order.push("descriptor mutable");
+      return false;
+    },
+  }, {
+    valueOf() {
+      order.push("value valueOf()");
+    }
+  });
+
+  assert_array_equals(order, [
+    "descriptor mutable",
+    "descriptor value",
+    "descriptor value toString",
+    "value valueOf()",
+  ]);
+}, "Order of evaluation");
+
+test(() => {
+  const invalidArguments = [
+    undefined,
+    null,
+    false,
+    true,
+    "",
+    "test",
+    Symbol(),
+    1,
+    NaN,
+    {},
+  ];
+  for (const invalidArgument of invalidArguments) {
+    assert_throws(new TypeError(),
+                  () => new WebAssembly.Global(invalidArgument),
+                  `new Global(${format_value(invalidArgument)})`);
+  }
+}, "Invalid descriptor argument");
+
+test(() => {
+  const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"];
+  for (const value of invalidTypes) {
+    const argument = { value };
+    assert_throws(new TypeError(), () => new WebAssembly.Global(argument));
+  }
+}, "Invalid type argument");
+
+test(() => {
+  const argument = { "value": "i64" };
+  const global = new WebAssembly.Global(argument);
+  assert_throws(new TypeError(), () => global.value);
+  assert_throws(new TypeError(), () => global.valueOf());
+}, "i64 with default");
+
+for (const type of ["i32", "f32", "f64"]) {
+  test(() => {
+    const argument = { "value": type };
+    const global = new WebAssembly.Global(argument);
+    assert_Global(global, 0);
+  }, `Default value for type ${type}`);
+
+  const valueArguments = [
+    [undefined, 0],
+    [null, 0],
+    [true, 1],
+    [false, 0],
+    [2, 2],
+    ["3", 3],
+    [{ toString() { return "5" } }, 5, "object with toString"],
+    [{ valueOf() { return "8" } }, 8, "object with valueOf"],
+  ];
+  for (const [value, expected, name = format_value(value)] of valueArguments) {
+    test(() => {
+      const argument = { "value": type };
+      const global = new WebAssembly.Global(argument, value);
+      assert_Global(global, expected);
+    }, `Explicit value ${name} for type ${type}`);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/toString.any.js
@@ -0,0 +1,7 @@
+// META: global=jsshell
+
+test(() => {
+  const argument = { "value": "i32" };
+  const global = new WebAssembly.Global(argument);
+  assert_class_string(global, "WebAssembly.Global");
+}, "Object.prototype.toString on an Global");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/value-set.any.js
@@ -0,0 +1,94 @@
+// META: global=jsshell
+
+test(() => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Global,
+    WebAssembly.Global.prototype,
+  ];
+
+  const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value");
+  assert_equals(typeof desc, "object");
+
+  const getter = desc.get;
+  assert_equals(typeof getter, "function");
+
+  const setter = desc.set;
+  assert_equals(typeof setter, "function");
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => getter.call(thisValue), `getter with this=${format_value(thisValue)}`);
+    assert_throws(new TypeError(), () => setter.call(thisValue, 1), `setter with this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+for (const type of ["i32", "f32", "f64"]) {
+  const immutableOptions = [
+    [{}, "missing"],
+    [{ "mutable": undefined }, "undefined"],
+    [{ "mutable": null }, "null"],
+    [{ "mutable": false }, "false"],
+    [{ "mutable": "" }, "empty string"],
+    [{ "mutable": 0 }, "zero"],
+  ];
+  for (const [opts, name] of immutableOptions) {
+    test(() => {
+      opts.value = type;
+      const global = new WebAssembly.Global(opts);
+      assert_equals(global.value, 0, "initial value");
+      assert_equals(global.valueOf(), 0, "initial valueOf");
+
+      assert_throws(new TypeError(), () => global.value = 1);
+
+      assert_equals(global.value, 0, "post-set value");
+      assert_equals(global.valueOf(), 0, "post-set valueOf");
+    }, `Immutable ${type} (${name})`);
+  }
+
+  const mutableOptions = [
+    [{ "mutable": true }, "true"],
+    [{ "mutable": 1 }, "one"],
+    [{ "mutable": "x" }, "string"],
+    [Object.create({ "mutable": true }), "true on prototype"],
+  ];
+  for (const [opts, name] of mutableOptions) {
+    test(() => {
+      opts.value = type;
+      const global = new WebAssembly.Global(opts);
+      assert_equals(global.value, 0, "initial value");
+      assert_equals(global.valueOf(), 0, "initial valueOf");
+
+      global.value = 1;
+
+      assert_equals(global.value, 1, "post-set value");
+      assert_equals(global.valueOf(), 1, "post-set valueOf");
+    }, `Mutable ${type} (${name})`);
+  }
+}
+
+test(() => {
+  const argument = { "value": "i64", "mutable": true };
+  const global = new WebAssembly.Global(argument);
+  assert_throws(new TypeError(), () => global.value);
+  assert_throws(new TypeError(), () => global.value = 0);
+  assert_throws(new TypeError(), () => global.valueOf());
+}, "i64 with default");
+
+
+test(() => {
+  const argument = { "value": "i32", "mutable": true };
+  const global = new WebAssembly.Global(argument);
+  const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value");
+  assert_equals(typeof desc, "object");
+
+  const setter = desc.set;
+  assert_equals(typeof setter, "function");
+
+  assert_throws(new TypeError(), () => setter.call(global));
+}, "Calling setter without argument");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/valueOf.any.js
@@ -0,0 +1,22 @@
+// META: global=jsshell
+
+test(() => {
+  const argument = { "value": "i32" };
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Global,
+    WebAssembly.Global.prototype,
+  ];
+
+  const fn = WebAssembly.Global.prototype.valueOf;
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => fn.call(thisValue), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
--- a/testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js
@@ -23,16 +23,17 @@ function assert_Instance(instance, expec
   assert_false(Object.isExtensible(exports), "extensible exports");
   for (const [key, expected] of Object.entries(expected_exports)) {
     const property = Object.getOwnPropertyDescriptor(exports, key);
     assert_equals(typeof property, "object", `${key} should be present`);
     assert_false(property.writable, `${key}: writable`);
     assert_true(property.enumerable, `${key}: enumerable`);
     assert_false(property.configurable, `${key}: configurable`);
     const actual = property.value;
+    assert_true(Object.isExtensible(actual), `${key}: extensible`);
 
     switch (expected.kind) {
     case "function":
       assert_exported_function(actual, expected, `value of ${key}`);
       break;
     case "global":
       assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype,
                     `value of ${key}: prototype`);
@@ -71,16 +72,49 @@ test(() => {
   assert_function_length(WebAssembly.Instance, 1, "WebAssembly.Instance");
 }, "length");
 
 test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Instance());
 }, "No arguments");
 
 test(() => {
+  const invalidArguments = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Module,
+    WebAssembly.Module.prototype,
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => new WebAssembly.Instance(argument),
+                  `new Instance(${format_value(argument)})`);
+  }
+}, "Non-Module arguments");
+
+test(() => {
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  const invalidArguments = [
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => new WebAssembly.Instance(module, argument),
+                  `new Instance(module, ${format_value(argument)})`);
+  }
+}, "Non-object imports");
+
+test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   assert_throws(new TypeError(), () => WebAssembly.Instance(module));
 }, "Calling");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const arguments = [
     [],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/exports.any.js
@@ -0,0 +1,53 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/wasm-constants.js
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+let emptyModuleBinary;
+setup(() => {
+  emptyModuleBinary = new WasmModuleBuilder().toBuffer();
+});
+
+test(() => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Instance,
+    WebAssembly.Instance.prototype,
+  ];
+
+  const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports");
+  assert_equals(typeof desc, "object");
+
+  const getter = desc.get;
+  assert_equals(typeof getter, "function");
+
+  assert_equals(typeof desc.set, "undefined");
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => getter.call(thisValue), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+test(() => {
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  const instance = new WebAssembly.Instance(module);
+  const exports = instance.exports;
+  instance.exports = {};
+  assert_equals(instance.exports, exports, "Should not change the exports");
+}, "Setting (sloppy mode)");
+
+test(() => {
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  const instance = new WebAssembly.Instance(module);
+  const exports = instance.exports;
+  assert_throws(new TypeError(), () => {
+    "use strict";
+    instance.exports = {};
+  });
+  assert_equals(instance.exports, exports, "Should not change the exports");
+}, "Setting (strict mode)");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/toString.any.js
@@ -0,0 +1,10 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/wasm-constants.js
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+test(() => {
+  const emptyModuleBinary = new WasmModuleBuilder().toBuffer();
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  const instance = new WebAssembly.Instance(module);
+  assert_class_string(instance, "WebAssembly.Instance");
+}, "Object.prototype.toString on an Instance");
--- a/testing/web-platform/tests/wasm/jsapi/interface.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/interface.any.js
@@ -58,16 +58,20 @@ test(() => {
   assert_equals(typeof propdesc, "object");
   assert_true(propdesc.writable, "writable");
   assert_false(propdesc.enumerable, "enumerable");
   assert_true(propdesc.configurable, "configurable");
   assert_equals(propdesc.value, this.WebAssembly);
 }, "WebAssembly: property descriptor");
 
 test(() => {
+  assert_throws(new TypeError(), () => WebAssembly());
+}, "WebAssembly: calling");
+
+test(() => {
   assert_throws(new TypeError(), () => new WebAssembly());
 }, "WebAssembly: constructing");
 
 const interfaces = [
   "Module",
   "Instance",
   "Memory",
   "Table",
@@ -84,24 +88,33 @@ for (const name of interfaces) {
     assert_true(propdesc.writable, "writable");
     assert_false(propdesc.enumerable, "enumerable");
     assert_true(propdesc.configurable, "configurable");
     assert_equals(propdesc.value, WebAssembly[name]);
   }, `WebAssembly.${name}: property descriptor`);
 
   test(() => {
     const interface_object = WebAssembly[name];
+    const propdesc = Object.getOwnPropertyDescriptor(interface_object, "prototype");
+    assert_equals(typeof propdesc, "object");
+    assert_false(propdesc.writable, "writable");
+    assert_false(propdesc.enumerable, "enumerable");
+    assert_false(propdesc.configurable, "configurable");
+  }, `WebAssembly.${name}: prototype`);
+
+  test(() => {
+    const interface_object = WebAssembly[name];
     const interface_prototype_object = interface_object.prototype;
     const propdesc = Object.getOwnPropertyDescriptor(interface_prototype_object, "constructor");
     assert_equals(typeof propdesc, "object");
     assert_true(propdesc.writable, "writable");
     assert_false(propdesc.enumerable, "enumerable");
     assert_true(propdesc.configurable, "configurable");
     assert_equals(propdesc.value, interface_object);
-  }, `WebAssembly.${name}: prototype`);
+  }, `WebAssembly.${name}: prototype.constructor`);
 }
 
 test_operations(WebAssembly, "WebAssembly", [
   ["validate", 1],
   ["compile", 1],
   ["instantiate", 1],
 ]);
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/buffer.any.js
@@ -0,0 +1,50 @@
+// META: global=jsshell
+
+test(() => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Memory,
+    WebAssembly.Memory.prototype,
+  ];
+
+  const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer");
+  assert_equals(typeof desc, "object");
+
+  const getter = desc.get;
+  assert_equals(typeof getter, "function");
+
+  assert_equals(typeof desc.set, "undefined");
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => getter.call(thisValue), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+test(() => {
+  const argument = { "initial": 0 };
+  const memory = new WebAssembly.Memory(argument);
+  const memory2 = new WebAssembly.Memory(argument);
+  const buffer = memory.buffer;
+  assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers");
+  memory.buffer = memory2.buffer;
+  assert_equals(memory.buffer, buffer, "Should not change the buffer");
+}, "Setting (sloppy mode)");
+
+test(() => {
+  const argument = { "initial": 0 };
+  const memory = new WebAssembly.Memory(argument);
+  const memory2 = new WebAssembly.Memory(argument);
+  const buffer = memory.buffer;
+  assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers");
+  assert_throws(new TypeError(), () => {
+    "use strict";
+    memory.buffer = memory2.buffer;
+  });
+  assert_equals(memory.buffer, buffer, "Should not change the buffer");
+}, "Setting (strict mode)");
--- a/testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js
@@ -1,17 +1,28 @@
 // META: global=jsshell
-// META: script=/wasm/jsapi/wasm-constants.js
-// META: script=/wasm/jsapi/wasm-module-builder.js
 // META: script=/wasm/jsapi/assertions.js
 
-let emptyModuleBinary;
-setup(() => {
-  emptyModuleBinary = new WasmModuleBuilder().toBuffer();
-});
+function assert_Memory(memory, expected) {
+  assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype,
+                "prototype");
+  assert_true(Object.isExtensible(memory), "extensible");
+
+  // https://github.com/WebAssembly/spec/issues/840
+  assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent");
+  assert_equals(Object.getPrototypeOf(memory.buffer), ArrayBuffer.prototype,
+                "prototype of buffer");
+  assert_true(Object.isExtensible(memory.buffer), "buffer extensibility");
+  assert_equals(memory.buffer.byteLength, 0x10000 * expected.size, "size of buffer");
+  if (expected.size > 0) {
+    const array = new Uint8Array(memory.buffer);
+    assert_equals(array[0], 0, "first element of buffer");
+    assert_equals(array[array.byteLength - 1], 0, "last element of buffer");
+  }
+}
 
 test(() => {
   assert_function_name(WebAssembly.Memory, "Memory", "WebAssembly.Memory");
 }, "name");
 
 test(() => {
   assert_function_length(WebAssembly.Memory, 1, "WebAssembly.Memory");
 }, "length");
@@ -21,18 +32,34 @@ test(() => {
 }, "No arguments");
 
 test(() => {
   const argument = { "initial": 0 };
   assert_throws(new TypeError(), () => WebAssembly.Memory(argument));
 }, "Calling");
 
 test(() => {
-  assert_throws(new TypeError(), () => new WebAssembly.Memory({}));
-}, "Empty descriptor");
+  const invalidArguments = [
+    undefined,
+    null,
+    false,
+    true,
+    "",
+    "test",
+    Symbol(),
+    1,
+    NaN,
+    {},
+  ];
+  for (const invalidArgument of invalidArguments) {
+    assert_throws(new TypeError(),
+                  () => new WebAssembly.Memory(invalidArgument),
+                  `new Memory(${format_value(invalidArgument)})`);
+  }
+}, "Invalid descriptor argument");
 
 test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Memory({ "initial": undefined }));
 }, "Undefined initial value in descriptor");
 
 const outOfRangeValues = [
   NaN,
   Infinity,
@@ -60,12 +87,51 @@ test(() => {
     get(o, x) {
       return 0;
     },
   });
   new WebAssembly.Memory(proxy);
 }, "Proxy descriptor");
 
 test(() => {
+  const order = [];
+
+  new WebAssembly.Memory({
+    get maximum() {
+      order.push("maximum");
+      return {
+        valueOf() {
+          order.push("maximum valueOf");
+          return 1;
+        },
+      };
+    },
+
+    get initial() {
+      order.push("initial");
+      return {
+        valueOf() {
+          order.push("initial valueOf");
+          return 1;
+        },
+      };
+    },
+  });
+
+  assert_array_equals(order, [
+    "initial",
+    "initial valueOf",
+    "maximum",
+    "maximum valueOf",
+  ]);
+}, "Order of evaluation for descriptor");
+
+test(() => {
   const argument = { "initial": 0 };
   const memory = new WebAssembly.Memory(argument);
-  assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype);
-}, "Prototype");
+  assert_Memory(memory, { "size": 0 });
+}, "Zero initial");
+
+test(() => {
+  const argument = { "initial": 4 };
+  const memory = new WebAssembly.Memory(argument);
+  assert_Memory(memory, { "size": 4 });
+}, "Non-zero initial");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/grow.any.js
@@ -0,0 +1,170 @@
+// META: global=jsshell
+
+function assert_ArrayBuffer(actual, expected, message) {
+  // https://github.com/WebAssembly/spec/issues/840
+  assert_equals(Object.getPrototypeOf(actual), ArrayBuffer.prototype,
+                `${message}: prototype`);
+  if (expected.detached) {
+    // https://github.com/tc39/ecma262/issues/678
+    let byteLength;
+    try {
+      byteLength = actual.byteLength;
+    } catch (e) {
+      byteLength = 0;
+    }
+    assert_equals(byteLength, 0, `${message}: detached size`);
+  } else {
+    assert_equals(actual.byteLength, 0x10000 * expected.size, `${message}: size`);
+    if (expected.size > 0) {
+      const array = new Uint8Array(actual);
+      assert_equals(array[0], 0, `${message}: first element`);
+      assert_equals(array[array.byteLength - 1], 0, `${message}: last element`);
+    }
+  }
+}
+
+test(() => {
+  const argument = { "initial": 0 };
+  const memory = new WebAssembly.Memory(argument);
+  assert_throws(new TypeError(), () => memory.grow());
+}, "Missing arguments");
+
+test(t => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Memory,
+    WebAssembly.Memory.prototype,
+  ];
+
+  const argument = {
+    valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
+    toString: t.unreached_func("Should not touch the argument (toString)"),
+  };
+
+  const fn = WebAssembly.Memory.prototype.grow;
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+test(() => {
+  const argument = { "initial": 0 };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
+
+  const result = memory.grow(2);
+  assert_equals(result, 0);
+
+  const newMemory = memory.buffer;
+  assert_not_equals(oldMemory, newMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
+  assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
+}, "Zero initial");
+
+test(() => {
+  const argument = { "initial": { valueOf() { return 0 } } };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
+
+  const result = memory.grow({ valueOf() { return 2 } });
+  assert_equals(result, 0);
+
+  const newMemory = memory.buffer;
+  assert_not_equals(oldMemory, newMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
+  assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
+}, "Zero initial with valueOf");
+
+test(() => {
+  const argument = { "initial": 3 };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 3 }, "Buffer before growing");
+
+  const result = memory.grow(2);
+  assert_equals(result, 3);
+
+  const newMemory = memory.buffer;
+  assert_not_equals(oldMemory, newMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
+  assert_ArrayBuffer(newMemory, { "size": 5 }, "New buffer after growing");
+}, "Non-zero initial");
+
+test(() => {
+  const argument = { "initial": 0, "maximum": 2 };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
+
+  const result = memory.grow(2);
+  assert_equals(result, 0);
+
+  const newMemory = memory.buffer;
+  assert_not_equals(oldMemory, newMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
+  assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
+}, "Zero initial with respected maximum");
+
+test(() => {
+  const argument = { "initial": 0, "maximum": 2 };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
+
+  const result = memory.grow(1);
+  assert_equals(result, 0);
+
+  const newMemory = memory.buffer;
+  assert_not_equals(oldMemory, newMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing once");
+  assert_ArrayBuffer(newMemory, { "size": 1 }, "New buffer after growing once");
+
+  const result2 = memory.grow(1);
+  assert_equals(result2, 1);
+
+  const newestMemory = memory.buffer;
+  assert_not_equals(newMemory, newestMemory);
+  assert_ArrayBuffer(oldMemory, { "detached": true }, "New buffer after growing twice");
+  assert_ArrayBuffer(newMemory, { "detached": true }, "New buffer after growing twice");
+  assert_ArrayBuffer(newestMemory, { "size": 2 }, "Newest buffer after growing twice");
+}, "Zero initial with respected maximum grown twice");
+
+test(() => {
+  const argument = { "initial": 1, "maximum": 2 };
+  const memory = new WebAssembly.Memory(argument);
+  const oldMemory = memory.buffer;
+  assert_ArrayBuffer(oldMemory, { "size": 1 }, "Buffer before growing");
+
+  assert_throws(new RangeError(), () => memory.grow(2));
+  assert_equals(memory.buffer, oldMemory);
+  assert_ArrayBuffer(memory.buffer, { "size": 1 }, "Buffer before trying to grow");
+}, "Zero initial growing too much");
+
+const outOfRangeValues = [
+  undefined,
+  NaN,
+  Infinity,
+  -Infinity,
+  -1,
+  0x100000000,
+  0x1000000000,
+  "0x100000000",
+  { valueOf() { return 0x100000000; } },
+];
+
+for (const value of outOfRangeValues) {
+  test(() => {
+    const argument = { "initial": 0 };
+    const memory = new WebAssembly.Memory(argument);
+    assert_throws(new TypeError(), () => memory.grow(value));
+  }, `Out-of-range argument: ${format_value(value)}`);
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/toString.any.js
@@ -0,0 +1,7 @@
+// META: global=jsshell
+
+test(() => {
+  const argument = { "initial": 0 };
+  const memory = new WebAssembly.Memory(argument);
+  assert_class_string(memory, "WebAssembly.Memory");
+}, "Object.prototype.toString on an Memory");
--- a/testing/web-platform/tests/wasm/jsapi/module/constructor.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/module/constructor.any.js
@@ -20,16 +20,41 @@ test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Module());
 }, "No arguments");
 
 test(() => {
   assert_throws(new TypeError(), () => WebAssembly.Module(emptyModuleBinary));
 }, "Calling");
 
 test(() => {
+  const invalidArguments = [
+    undefined,
+    null,
+    true,
+    "test",
+    Symbol(),
+    7,
+    NaN,
+    {},
+    ArrayBuffer,
+    ArrayBuffer.prototype,
+    Array.from(emptyModuleBinary),
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => new WebAssembly.Module(argument),
+                  `new Module(${format_value(argument)})`);
+  }
+}, "Invalid arguments");
+
+test(() => {
   const buffer = new Uint8Array();
   assert_throws(new WebAssembly.CompileError(), () => new WebAssembly.Module(buffer));
 }, "Empty buffer");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype);
 }, "Prototype");
+
+test(() => {
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  assert_true(Object.isExtensible(module));
+}, "Extensibility");
--- a/testing/web-platform/tests/wasm/jsapi/module/customSections.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/module/customSections.any.js
@@ -1,20 +1,22 @@
 // META: global=jsshell
 // META: script=/wasm/jsapi/wasm-constants.js
 // META: script=/wasm/jsapi/wasm-module-builder.js
 
 function assert_ArrayBuffer(buffer, expected) {
   assert_equals(Object.getPrototypeOf(buffer), ArrayBuffer.prototype, "Prototype");
+  assert_true(Object.isExtensible(buffer), "isExtensible");
   assert_array_equals(new Uint8Array(buffer), expected);
 }
 
 function assert_sections(sections, expected) {
   assert_true(Array.isArray(sections), "Should be array");
   assert_equals(Object.getPrototypeOf(sections), Array.prototype, "Prototype");
+  assert_true(Object.isExtensible(sections), "isExtensible");
 
   assert_equals(sections.length, expected.length);
   for (let i = 0; i < expected.length; ++i) {
     assert_ArrayBuffer(sections[i], expected[i]);
   }
 }
 
 let emptyModuleBinary;
@@ -24,20 +26,31 @@ setup(() => {
 
 test(() => {
   assert_throws(new TypeError(), () => WebAssembly.Module.customSections());
   const module = new WebAssembly.Module(emptyModuleBinary);
   assert_throws(new TypeError(), () => WebAssembly.Module.customSections(module));
 }, "Missing arguments");
 
 test(() => {
-  assert_throws(new TypeError(), () => WebAssembly.Module.customSections({}, ""));
-  assert_throws(new TypeError(), () => WebAssembly.Module.customSections("", ""));
-  assert_throws(new TypeError(), () => WebAssembly.Module.customSections(undefined, ""));
-  assert_throws(new TypeError(), () => WebAssembly.Module.customSections(null, ""));
+  const invalidArguments = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Module,
+    WebAssembly.Module.prototype,
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => WebAssembly.Module.customSections(argument, ""),
+                  `customSections(${format_value(argument)})`);
+  }
 }, "Non-Module arguments");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const fn = WebAssembly.Module.customSections;
   const thisValues = [
     undefined,
     null,
@@ -97,8 +110,53 @@ test(() => {
     bytes1,
   ])
 
   assert_sections(WebAssembly.Module.customSections(module, ""), [])
   assert_sections(WebAssembly.Module.customSections(module, "\0"), [])
   assert_sections(WebAssembly.Module.customSections(module, "name\0"), [])
   assert_sections(WebAssembly.Module.customSections(module, "foo\0"), [])
 }, "Custom sections");
+
+test(() => {
+  const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121];
+  const name = "yee\uD801\uDC37eey"
+
+  const binary = new Binary;
+  binary.emit_section(kUnknownSectionCode, section => {
+    section.emit_string(name);
+    section.emit_bytes(bytes);
+  });
+
+  const builder = new WasmModuleBuilder();
+  builder.addExplicitSection(binary);
+  const buffer = builder.toBuffer();
+  const module = new WebAssembly.Module(buffer);
+
+  assert_sections(WebAssembly.Module.customSections(module, name), [
+    bytes,
+  ]);
+  assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFDeey"), []);
+  assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFD\uFFFDeey"), []);
+}, "Custom sections with surrogate pairs");
+
+test(() => {
+  const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121];
+
+  const binary = new Binary;
+  binary.emit_section(kUnknownSectionCode, section => {
+    section.emit_string("na\uFFFDme");
+    section.emit_bytes(bytes);
+  });
+
+  const builder = new WasmModuleBuilder();
+  builder.addExplicitSection(binary);
+  const buffer = builder.toBuffer();
+  const module = new WebAssembly.Module(buffer);
+
+  assert_sections(WebAssembly.Module.customSections(module, "name"), []);
+  assert_sections(WebAssembly.Module.customSections(module, "na\uFFFDme"), [
+    bytes,
+  ]);
+  assert_sections(WebAssembly.Module.customSections(module, "na\uDC01me"), [
+    bytes,
+  ]);
+}, "Custom sections with U+FFFD");
--- a/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
@@ -2,25 +2,64 @@
 // META: script=/wasm/jsapi/wasm-constants.js
 // META: script=/wasm/jsapi/wasm-module-builder.js
 
 let emptyModuleBinary;
 setup(() => {
   emptyModuleBinary = new WasmModuleBuilder().toBuffer();
 });
 
+function assert_ModuleExportDescriptor(export_, expected) {
+  assert_equals(Object.getPrototypeOf(export_), Object.prototype, "Prototype");
+  assert_true(Object.isExtensible(export_), "isExtensible");
+
+  const name = Object.getOwnPropertyDescriptor(export_, "name");
+  assert_true(name.writable, "name: writable");
+  assert_true(name.enumerable, "name: enumerable");
+  assert_true(name.configurable, "name: configurable");
+  assert_equals(name.value, expected.name);
+
+  const kind = Object.getOwnPropertyDescriptor(export_, "kind");
+  assert_true(kind.writable, "kind: writable");
+  assert_true(kind.enumerable, "kind: enumerable");
+  assert_true(kind.configurable, "kind: configurable");
+  assert_equals(kind.value, expected.kind);
+}
+
+function assert_exports(exports, expected) {
+  assert_true(Array.isArray(exports), "Should be array");
+  assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype");
+  assert_true(Object.isExtensible(exports), "isExtensible");
+
+  assert_equals(exports.length, expected.length);
+  for (let i = 0; i < expected.length; ++i) {
+    assert_ModuleExportDescriptor(exports[i], expected[i]);
+  }
+}
+
 test(() => {
   assert_throws(new TypeError(), () => WebAssembly.Module.exports());
 }, "Missing arguments");
 
 test(() => {
-  assert_throws(new TypeError(), () => WebAssembly.Module.exports({}));
-  assert_throws(new TypeError(), () => WebAssembly.Module.exports(""));
-  assert_throws(new TypeError(), () => WebAssembly.Module.exports(undefined));
-  assert_throws(new TypeError(), () => WebAssembly.Module.exports(null));
+  const invalidArguments = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Module,
+    WebAssembly.Module.prototype,
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => WebAssembly.Module.exports(argument),
+                  `exports(${format_value(argument)})`);
+  }
 }, "Non-Module arguments");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const fn = WebAssembly.Module.exports;
   const thisValues = [
     undefined,
     null,
@@ -35,48 +74,24 @@ test(() => {
   for (const thisValue of thisValues) {
     assert_array_equals(fn.call(thisValue, module), []);
   }
 }, "Branding");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const exports = WebAssembly.Module.exports(module);
-  assert_true(Array.isArray(exports));
-}, "Return type");
-
-test(() => {
-  const module = new WebAssembly.Module(emptyModuleBinary);
-  const exports = WebAssembly.Module.exports(module);
-  assert_true(Array.isArray(exports), "Should be array");
-  assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype");
-  assert_array_equals(exports, []);
+  assert_exports(exports, []);
 }, "Empty module");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module));
 }, "Empty module: array caching");
 
-function assert_ModuleExportDescriptor(export_, expected) {
-  assert_equals(Object.getPrototypeOf(export_), Object.prototype, "Prototype");
-
-  const name = Object.getOwnPropertyDescriptor(export_, "name");
-  assert_true(name.writable, "name: writable");
-  assert_true(name.enumerable, "name: enumerable");
-  assert_true(name.configurable, "name: configurable");
-  assert_equals(name.value, expected.name);
-
-  const kind = Object.getOwnPropertyDescriptor(export_, "kind");
-  assert_true(kind.writable, "kind: writable");
-  assert_true(kind.enumerable, "kind: enumerable");
-  assert_true(kind.configurable, "kind: configurable");
-  assert_equals(kind.value, expected.kind);
-}
-
 test(() => {
   const builder = new WasmModuleBuilder();
 
   builder
     .addFunction("fn", kSig_v_v)
     .addBody([
         kExprEnd
     ])
@@ -98,24 +113,18 @@ test(() => {
     .exportAs("global2")
     .init = 1.2;
 
   builder.addMemory(0, 256, true);
 
   const buffer = builder.toBuffer()
   const module = new WebAssembly.Module(buffer);
   const exports = WebAssembly.Module.exports(module);
-  assert_true(Array.isArray(exports), "Should be array");
-  assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype");
-
   const expected = [
     { "kind": "function", "name": "fn" },
     { "kind": "function", "name": "fn2" },
     { "kind": "table", "name": "table" },
     { "kind": "global", "name": "global" },
     { "kind": "global", "name": "global2" },
     { "kind": "memory", "name": "memory" },
   ];
-  assert_equals(exports.length, expected.length);
-  for (let i = 0; i < expected.length; ++i) {
-    assert_ModuleExportDescriptor(exports[i], expected[i]);
-  }
+  assert_exports(exports, expected);
 }, "exports");
--- a/testing/web-platform/tests/wasm/jsapi/module/imports.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/module/imports.any.js
@@ -1,26 +1,71 @@
 // META: global=jsshell
 // META: script=/wasm/jsapi/wasm-constants.js
 // META: script=/wasm/jsapi/wasm-module-builder.js
 
+function assert_ModuleImportDescriptor(import_, expected) {
+  assert_equals(Object.getPrototypeOf(import_), Object.prototype, "Prototype");
+  assert_true(Object.isExtensible(import_), "isExtensible");
+
+  const module = Object.getOwnPropertyDescriptor(import_, "module");
+  assert_true(module.writable, "module: writable");
+  assert_true(module.enumerable, "module: enumerable");
+  assert_true(module.configurable, "module: configurable");
+  assert_equals(module.value, expected.module);
+
+  const name = Object.getOwnPropertyDescriptor(import_, "name");
+  assert_true(name.writable, "name: writable");
+  assert_true(name.enumerable, "name: enumerable");
+  assert_true(name.configurable, "name: configurable");
+  assert_equals(name.value, expected.name);
+
+  const kind = Object.getOwnPropertyDescriptor(import_, "kind");
+  assert_true(kind.writable, "kind: writable");
+  assert_true(kind.enumerable, "kind: enumerable");
+  assert_true(kind.configurable, "kind: configurable");
+  assert_equals(kind.value, expected.kind);
+}
+
+function assert_imports(imports, expected) {
+  assert_true(Array.isArray(imports), "Should be array");
+  assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype");
+  assert_true(Object.isExtensible(imports), "isExtensible");
+
+  assert_equals(imports.length, expected.length);
+  for (let i = 0; i < expected.length; ++i) {
+    assert_ModuleImportDescriptor(imports[i], expected[i]);
+  }
+}
+
 let emptyModuleBinary;
 setup(() => {
   emptyModuleBinary = new WasmModuleBuilder().toBuffer();
 });
 
 test(() => {
   assert_throws(new TypeError(), () => WebAssembly.Module.imports());
 }, "Missing arguments");
 
 test(() => {
-  assert_throws(new TypeError(), () => WebAssembly.Module.imports({}));
-  assert_throws(new TypeError(), () => WebAssembly.Module.imports(""));
-  assert_throws(new TypeError(), () => WebAssembly.Module.imports(undefined));
-  assert_throws(new TypeError(), () => WebAssembly.Module.imports(null));
+  const invalidArguments = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Module,
+    WebAssembly.Module.prototype,
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => WebAssembly.Module.imports(argument),
+                  `imports(${format_value(argument)})`);
+  }
 }, "Non-Module arguments");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const fn = WebAssembly.Module.imports;
   const thisValues = [
     undefined,
     null,
@@ -41,65 +86,35 @@ test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const imports = WebAssembly.Module.imports(module);
   assert_true(Array.isArray(imports));
 }, "Return type");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   const imports = WebAssembly.Module.imports(module);
-  assert_true(Array.isArray(imports), "Should be array");
-  assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype");
-  assert_array_equals(imports, []);
+  assert_imports(imports, []);
 }, "Empty module");
 
 test(() => {
   const module = new WebAssembly.Module(emptyModuleBinary);
   assert_not_equals(WebAssembly.Module.imports(module), WebAssembly.Module.imports(module));
 }, "Empty module: array caching");
 
-function assert_ModuleImportDescriptor(import_, expected) {
-  assert_equals(Object.getPrototypeOf(import_), Object.prototype, "Prototype");
-
-  const module = Object.getOwnPropertyDescriptor(import_, "module");
-  assert_true(module.writable, "module: writable");
-  assert_true(module.enumerable, "module: enumerable");
-  assert_true(module.configurable, "module: configurable");
-  assert_equals(module.value, expected.module);
-
-  const name = Object.getOwnPropertyDescriptor(import_, "name");
-  assert_true(name.writable, "name: writable");
-  assert_true(name.enumerable, "name: enumerable");
-  assert_true(name.configurable, "name: configurable");
-  assert_equals(name.value, expected.name);
-
-  const kind = Object.getOwnPropertyDescriptor(import_, "kind");
-  assert_true(kind.writable, "kind: writable");
-  assert_true(kind.enumerable, "kind: enumerable");
-  assert_true(kind.configurable, "kind: configurable");
-  assert_equals(kind.value, expected.kind);
-}
-
 test(() => {
   const builder = new WasmModuleBuilder();
 
   builder.addImport("module", "fn", kSig_v_v);
   builder.addImportedGlobal("module", "global", kWasmI32);
   builder.addImportedMemory("module", "memory", 0, 128);
   builder.addImportedTable("module", "table", 0, 128);
 
   const buffer = builder.toBuffer()
   const module = new WebAssembly.Module(buffer);
   const imports = WebAssembly.Module.imports(module);
-  assert_true(Array.isArray(imports), "Should be array");
-  assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype");
-
   const expected = [
     { "module": "module", "kind": "function", "name": "fn" },
     { "module": "module", "kind": "global", "name": "global" },
     { "module": "module", "kind": "memory", "name": "memory" },
     { "module": "module", "kind": "table", "name": "table" },
   ];
-  assert_equals(imports.length, expected.length);
-  for (let i = 0; i < expected.length; ++i) {
-    assert_ModuleImportDescriptor(imports[i], expected[i]);
-  }
+  assert_imports(imports, expected);
 }, "imports");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/toString.any.js
@@ -0,0 +1,9 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/wasm-constants.js
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+test(() => {
+  const emptyModuleBinary = new WasmModuleBuilder().toBuffer();
+  const module = new WebAssembly.Module(emptyModuleBinary);
+  assert_class_string(module, "WebAssembly.Module");
+}, "Object.prototype.toString on an Module");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/assertions.js
@@ -0,0 +1,11 @@
+function assert_equal_to_array(table, expected, message) {
+  assert_equals(table.length, expected.length, `${message}: length`);
+  assert_throws(new RangeError(), () => table.get(-1), `${message}: table.get(-1)`);
+  for (let i = 0; i < expected.length; ++i) {
+    assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`);
+  }
+  assert_throws(new RangeError(), () => table.get(expected.length),
+                `${message}: table.get(${expected.length} of ${expected.length})`);
+  assert_throws(new RangeError(), () => table.get(expected.length + 1),
+                `${message}: table.get(${expected.length + 1} of ${expected.length})`);
+}
--- a/testing/web-platform/tests/wasm/jsapi/table/constructor.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/table/constructor.any.js
@@ -1,40 +1,64 @@
 // META: global=jsshell
-// META: script=/wasm/jsapi/wasm-constants.js
-// META: script=/wasm/jsapi/wasm-module-builder.js
 // META: script=/wasm/jsapi/assertions.js
 
-let emptyModuleBinary;
-setup(() => {
-  emptyModuleBinary = new WasmModuleBuilder().toBuffer();
-});
+function assert_Table(actual, expected) {
+  assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
+                "prototype");
+  assert_true(Object.isExtensible(actual), "extensible");
+
+  assert_equals(actual.length, expected.length, "length");
+  for (let i = 0; i < expected.length; ++i) {
+    assert_equals(actual.get(i), null, `actual.get(${i})`);
+  }
+}
 
 test(() => {
   assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table");
 }, "name");
 
 test(() => {
   assert_function_length(WebAssembly.Table, 1, "WebAssembly.Table");
 }, "length");
 
 test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Table());
 }, "No arguments");
 
 test(() => {
-  const argument = { "initial": 0 };
+  const argument = { "element": "anyfunc", "initial": 0 };
   assert_throws(new TypeError(), () => WebAssembly.Table(argument));
 }, "Calling");
 
 test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Table({}));
 }, "Empty descriptor");
 
 test(() => {
+  const invalidArguments = [
+    undefined,
+    null,
+    false,
+    true,
+    "",
+    "test",
+    Symbol(),
+    1,
+    NaN,
+    {},
+  ];
+  for (const invalidArgument of invalidArguments) {
+    assert_throws(new TypeError(),
+                  () => new WebAssembly.Table(invalidArgument),
+                  `new Table(${format_value(invalidArgument)})`);
+  }
+}, "Invalid descriptor argument");
+
+test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": undefined }));
 }, "Undefined initial value in descriptor");
 
 test(() => {
   assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": undefined, "initial": 0 }));
 }, "Undefined element value in descriptor");
 
 const outOfRangeValues = [
@@ -52,32 +76,98 @@ for (const value of outOfRangeValues) {
   }, `Out-of-range initial value in descriptor: ${format_value(value)}`);
 
   test(() => {
     assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": 0, "maximum": value }));
   }, `Out-of-range maximum value in descriptor: ${format_value(value)}`);
 }
 
 test(() => {
+  assert_throws(new RangeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": 10, "maximum": 9 }));
+}, "Initial value exceeds maximum");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 0 };
+  const table = new WebAssembly.Table(argument);
+  assert_Table(table, { "length": 0 });
+}, "Basic (zero)");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_Table(table, { "length": 5 });
+}, "Basic (non-zero)");
+
+test(() => {
   const proxy = new Proxy({}, {
     has(o, x) {
       assert_unreached(`Should not call [[HasProperty]] with ${x}`);
     },
     get(o, x) {
       switch (x) {
       case "element":
         return "anyfunc";
       case "initial":
       case "maximum":
         return 0;
       default:
         return undefined;
       }
     },
   });
-  new WebAssembly.Table(proxy);
+  const table = new WebAssembly.Table(proxy);
+  assert_Table(table, { "length": 0 });
 }, "Proxy descriptor");
 
 test(() => {
-  const argument = { "element": "anyfunc", "initial": 0 };
-  const table = new WebAssembly.Table(argument);
-  assert_equals(Object.getPrototypeOf(table), WebAssembly.Table.prototype);
-}, "Prototype");
+  const table = new WebAssembly.Table({
+    "element": {
+      toString() { return "anyfunc"; },
+    },
+    "initial": 1,
+  });
+  assert_Table(table, { "length": 1 });
+}, "Type conversion for descriptor.element");
+
+test(() => {
+  const order = [];
+
+  new WebAssembly.Table({
+    get maximum() {
+      order.push("maximum");
+      return {
+        valueOf() {
+          order.push("maximum valueOf");
+          return 1;
+        },
+      };
+    },
+
+    get initial() {
+      order.push("initial");
+      return {
+        valueOf() {
+          order.push("initial valueOf");
+          return 1;
+        },
+      };
+    },
+
+    get element() {
+      order.push("element");
+      return {
+        toString() {
+          order.push("element toString");
+          return "anyfunc";
+        },
+      };
+    },
+  });
+
+  assert_array_equals(order, [
+    "element",
+    "element toString",
+    "initial",
+    "initial valueOf",
+    "maximum",
+    "maximum valueOf",
+  ]);
+}, "Order of evaluation for descriptor");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
@@ -0,0 +1,220 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/wasm-constants.js
+// META: script=/wasm/jsapi/wasm-module-builder.js
+// META: script=assertions.js
+
+let functions;
+setup(() => {
+  const builder = new WasmModuleBuilder();
+
+  builder
+    .addFunction("fn", kSig_v_d)
+    .addBody([
+        kExprEnd
+    ])
+    .exportFunc();
+  builder
+    .addFunction("fn2", kSig_v_v)
+    .addBody([
+        kExprEnd
+    ])
+    .exportFunc();
+
+  const buffer = builder.toBuffer()
+  const module = new WebAssembly.Module(buffer);
+  const instance = new WebAssembly.Instance(module, {});
+  functions = instance.exports;
+});
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_throws(new TypeError(), () => table.get());
+}, "Missing arguments: get");
+
+test(t => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Table,
+    WebAssembly.Table.prototype,
+  ];
+
+  const argument = {
+    valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
+    toString: t.unreached_func("Should not touch the argument (toString)"),
+  };
+
+  const fn = WebAssembly.Table.prototype.get;
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
+  }
+}, "Branding: get");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_throws(new TypeError(), () => table.set());
+  assert_throws(new TypeError(), () => table.set(0));
+}, "Missing arguments: set");
+
+test(t => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Table,
+    WebAssembly.Table.prototype,
+  ];
+
+  const argument = {
+    valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
+    toString: t.unreached_func("Should not touch the argument (toString)"),
+  };
+
+  const fn = WebAssembly.Table.prototype.set;
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => fn.call(thisValue, argument, null), `this=${format_value(thisValue)}`);
+  }
+}, "Branding: set");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null, null, null, null, null]);
+
+  const {fn, fn2} = functions;
+
+  assert_equals(table.set(0, fn), undefined, "set() returns undefined.");
+  table.set(2, fn2);
+  table.set(4, fn);
+
+  assert_equal_to_array(table, [fn, null, fn2, null, fn]);
+
+  table.set(0, null);
+  assert_equal_to_array(table, [null, null, fn2, null, fn]);
+}, "Basic");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null, null, null, null, null]);
+
+  const {fn, fn2} = functions;
+
+  table.set(0, fn);
+  table.set(2, fn2);
+  table.set(4, fn);
+
+  assert_equal_to_array(table, [fn, null, fn2, null, fn]);
+
+  table.grow(4);
+
+  assert_equal_to_array(table, [fn, null, fn2, null, fn, null, null, null, null]);
+}, "Growing");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null, null, null, null, null]);
+
+  const {fn} = functions;
+
+  assert_throws(new RangeError(), () => table.set(-1, fn));
+  assert_throws(new RangeError(), () => table.set(5, fn));
+  assert_equal_to_array(table, [null, null, null, null, null]);
+}, "Setting out-of-bounds");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 1 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null]);
+
+  const invalidArguments = [
+    undefined,
+    true,
+    false,
+    "test",
+    Symbol(),
+    7,
+    NaN,
+    {},
+  ];
+  for (const argument of invalidArguments) {
+    assert_throws(new TypeError(), () => table.set(0, argument),
+                  `set(${format_value(argument)})`);
+  }
+  assert_equal_to_array(table, [null]);
+}, "Setting non-function");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 1 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null]);
+
+  const fn = function() {};
+  assert_throws(new TypeError(), () => table.set(0, fn));
+  assert_equal_to_array(table, [null]);
+}, "Setting non-wasm function");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 1 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, [null]);
+
+  const fn = () => {};
+  assert_throws(new TypeError(), () => table.set(0, fn));
+  assert_equal_to_array(table, [null]);
+}, "Setting non-wasm arrow function");
+
+const outOfRangeValues = [
+  undefined,
+  NaN,
+  Infinity,
+  -Infinity,
+  -1,
+  0x100000000,
+  0x1000000000,
+  "0x100000000",
+  { valueOf() { return 0x100000000; } },
+];
+
+for (const value of outOfRangeValues) {
+  test(() => {
+    const argument = { "element": "anyfunc", "initial": 1 };
+    const table = new WebAssembly.Table(argument);
+    assert_throws(new TypeError(), () => table.get(value));
+  }, `Getting out-of-range argument: ${format_value(value)}`);
+
+  test(() => {
+    const argument = { "element": "anyfunc", "initial": 1 };
+    const table = new WebAssembly.Table(argument);
+    assert_throws(new TypeError(), () => table.set(value, null));
+  }, `Setting out-of-range argument: ${format_value(value)}`);
+}
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 1 };
+  const table = new WebAssembly.Table(argument);
+  let called = 0;
+  const value = {
+    valueOf() {
+      called++;
+      return 0;
+    },
+  };
+  assert_throws(new TypeError(), () => table.set(value, {}));
+  assert_equals(called, 1);
+}, "Order of argument conversion");
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/grow.any.js
@@ -0,0 +1,86 @@
+// META: global=jsshell
+// META: script=assertions.js
+
+function nulls(n) {
+  return Array(n).fill(null);
+}
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_throws(new TypeError(), () => table.grow());
+}, "Missing arguments");
+
+test(t => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Table,
+    WebAssembly.Table.prototype,
+  ];
+
+  const argument = {
+    valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
+    toString: t.unreached_func("Should not touch the argument (toString)"),
+  };
+
+  const fn = WebAssembly.Table.prototype.grow;
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, nulls(5), "before");
+
+  const result = table.grow(3);
+  assert_equals(result, 5);
+  assert_equal_to_array(table, nulls(8), "after");
+}, "Basic");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 3, "maximum": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, nulls(3), "before");
+
+  const result = table.grow(2);
+  assert_equals(result, 3);
+  assert_equal_to_array(table, nulls(5), "after");
+}, "Reached maximum");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 2, "maximum": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_equal_to_array(table, nulls(2), "before");
+
+  assert_throws(new RangeError(), () => table.grow(4));
+  assert_equal_to_array(table, nulls(2), "after");
+}, "Exceeded maximum");
+
+const outOfRangeValues = [
+  undefined,
+  NaN,
+  Infinity,
+  -Infinity,
+  -1,
+  0x100000000,
+  0x1000000000,
+  "0x100000000",
+  { valueOf() { return 0x100000000; } },
+];
+
+for (const value of outOfRangeValues) {
+  test(() => {
+    const argument = { "element": "anyfunc", "initial": 1 };
+    const table = new WebAssembly.Table(argument);
+    assert_throws(new TypeError(), () => table.grow(value));
+  }, `Out-of-range argument: ${format_value(value)}`);
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/length.any.js
@@ -0,0 +1,46 @@
+// META: global=jsshell
+
+test(() => {
+  const thisValues = [
+    undefined,
+    null,
+    true,
+    "",
+    Symbol(),
+    1,
+    {},
+    WebAssembly.Table,
+    WebAssembly.Table.prototype,
+  ];
+
+  const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length");
+  assert_equals(typeof desc, "object");
+
+  const getter = desc.get;
+  assert_equals(typeof getter, "function");
+
+  assert_equals(typeof desc.set, "undefined");
+
+  for (const thisValue of thisValues) {
+    assert_throws(new TypeError(), () => getter.call(thisValue), `this=${format_value(thisValue)}`);
+  }
+}, "Branding");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 2 };
+  const table = new WebAssembly.Table(argument);
+  assert_equals(table.length, 2, "Initial length");
+  table.length = 4;
+  assert_equals(table.length, 2, "Should not change the length");
+}, "Setting (sloppy mode)");
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 2 };
+  const table = new WebAssembly.Table(argument);
+  assert_equals(table.length, 2, "Initial length");
+  assert_throws(new TypeError(), () => {
+    "use strict";
+    table.length = 4;
+  });
+  assert_equals(table.length, 2, "Should not change the length");
+}, "Setting (strict mode)");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/toString.any.js
@@ -0,0 +1,7 @@
+// META: global=jsshell
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 0 };
+  const table = new WebAssembly.Table(argument);
+  assert_class_string(table, "WebAssembly.Table");
+}, "Object.prototype.toString on an Table");
--- a/toolkit/content/tests/chrome/test_notificationbox.xul
+++ b/toolkit/content/tests/chrome/test_notificationbox.xul
@@ -176,46 +176,30 @@ var tests =
       testtag_notificationbox_State(nb, "called dismiss()", null, 0);
       testtag_notification_eventCallback(["dismissed", "removed"], ntf,
                                          "dismiss()");
       return ntf;
     }
   },
   {
     test: function(nb, ntf) {
-      // Create a popup to be used by a menu-button.
-      var doc = nb.ownerDocument;
-      var menuPopup = doc.createElementNS(NSXUL, "menupopup");
-      var menuItem = menuPopup.appendChild(doc.createElementNS(NSXUL, "menuitem"));
-      menuItem.setAttribute("label", "Menu Item");
-      // Append a notification with a button of type 'menu-button'.
       ntf = nb.appendNotification(
         "Notification", "note", "happy.png",
         nb.PRIORITY_WARNING_LOW,
         [{
           label: "Button",
-          type: "menu-button",
-          popup: menuPopup
         }]
       );
 
       return ntf;
     },
     result: function(nb, ntf) {
       testtag_notificationbox_State(nb, "append", ntf, 1);
       testtag_notification_State(nb, ntf, "append", "Notification", "note",
                                  "happy.png", nb.PRIORITY_WARNING_LOW);
-      var button = ntf.querySelector(".notification-button");
-      SimpleTest.is(button.type, "menu-button", "Button type should be set");
-      var menuPopup = button.getElementsByTagNameNS(NSXUL, "menupopup");
-      SimpleTest.is(menuPopup.length, 1, "There should be a menu attached");
-      var menuItem = menuPopup[0].firstChild;
-      SimpleTest.is(menuItem.localName, "menuitem", "There should be a menu item");
-      SimpleTest.is(menuItem.getAttribute("label"), "Menu Item", "Label should match");
-      // Clean up.
       nb.removeNotification(ntf);
 
       return [1, null];
     }
   },
   {
     repeat: true,
     test: function(nb, arr) {
--- a/toolkit/content/widgets/button.xml
+++ b/toolkit/content/widgets/button.xml
@@ -228,123 +228,9 @@
       <![CDATA[
         this.open = true;
         // Prevent page from scrolling on the space key.
         event.preventDefault();
       ]]>
       </handler>
     </handlers>
   </binding>
-
-  <binding id="menu-button-base"
-           extends="chrome://global/content/bindings/button.xml#button-base">
-    <implementation>
-      <constructor>
-        this.init();
-      </constructor>
-
-      <method name="init">
-        <body>
-        <![CDATA[
-          var btn = document.getAnonymousElementByAttribute(this, "anonid", "button");
-          if (!btn)
-            throw "XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\"";
-
-          var menubuttonParent = this;
-          btn.addEventListener("mouseover", function() {
-            if (!this.disabled)
-              menubuttonParent.buttonover = true;
-          }, true);
-          btn.addEventListener("mouseout", function() {
-            menubuttonParent.buttonover = false;
-          }, true);
-          btn.addEventListener("mousedown", function() {
-            if (!this.disabled) {
-              menubuttonParent.buttondown = true;
-              document.addEventListener("mouseup", menubuttonParent, true);
-            }
-          }, true);
-        ]]>
-        </body>
-      </method>
-
-      <property name="buttonover" onget="return this.getAttribute('buttonover');">
-        <setter>
-        <![CDATA[
-          var v = val || val == "true";
-          if (!v && this.buttondown) {
-            this.buttondown = false;
-            this._pendingActive = true;
-          } else if (this._pendingActive) {
-            this.buttondown = true;
-            this._pendingActive = false;
-          }
-
-          if (v)
-            this.setAttribute("buttonover", "true");
-          else
-            this.removeAttribute("buttonover");
-          return val;
-        ]]>
-        </setter>
-      </property>
-
-      <property name="buttondown" onget="return this.getAttribute('buttondown') == 'true';">
-        <setter>
-        <![CDATA[
-          if (val || val == "true")
-            this.setAttribute("buttondown", "true");
-          else
-            this.removeAttribute("buttondown");
-          return val;
-        ]]>
-        </setter>
-      </property>
-
-      <field name="_pendingActive">false</field>
-
-      <method name="handleEvent">
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          this._pendingActive = false;
-          this.buttondown = false;
-          document.removeEventListener("mouseup", this, true);
-        ]]>
-        </body>
-      </method>
-
-    </implementation>
-
-    <handlers>
-      <handler event="keypress" keycode="VK_RETURN">
-        if (event.originalTarget == this)
-          this.open = true;
-      </handler>
-      <handler event="keypress" key=" ">
-        if (event.originalTarget == this) {
-          this.open = true;
-          // Prevent page from scrolling on the space key.
-          event.preventDefault();
-        }
-      </handler>
-    </handlers>
-  </binding>
-
-  <binding id="menu-button" display="xul:menu"
-           extends="chrome://global/content/bindings/button.xml#menu-button-base">
-    <resources>
-      <stylesheet src="chrome://global/skin/button.css"/>
-    </resources>
-
-    <content>
-      <children includes="observes|template|menupopup|panel|tooltip"/>
-      <xul:button class="box-inherit button-menubutton-button"
-                  anonid="button" flex="1" allowevents="true"
-                  xbl:inherits="disabled,crop,image,label,accesskey,command,
-                                buttonover,buttondown,align,dir,pack,orient">
-        <children/>
-      </xul:button>
-      <xul:dropmarker class="button-menubutton-dropmarker" xbl:inherits="open,disabled,label"/>
-    </content>
-  </binding>
-
 </bindings>
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -131,26 +131,16 @@
               var defaultElem;
 
               for (var b = 0; b < aButtons.length; b++) {
                 var button = aButtons[b];
                 var buttonElem = document.createXULElement("button");
                 buttonElem.setAttribute("label", button.label);
                 if (typeof button.accessKey == "string")
                   buttonElem.setAttribute("accesskey", button.accessKey);
-                if (typeof button.type == "string") {
-                  buttonElem.setAttribute("type", button.type);
-                  if ((button.type == "menu-button" || button.type == "menu") &&
-                      "popup" in button) {
-                    buttonElem.appendChild(button.popup);
-                    delete button.popup;
-                  }
-                  if (typeof button.anchor == "string")
-                    buttonElem.setAttribute("anchor", button.anchor);
-                }
                 buttonElem.classList.add("notification-button");
 
                 if (button.isDefault ||
                     b == 0 && !("isDefault" in button))
                   defaultElem = buttonElem;
 
                 newitem.appendChild(buttonElem);
                 buttonElem.buttonInfo = button;
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -103,20 +103,16 @@ vbox {
 button {
   -moz-binding: url("chrome://global/content/bindings/button.xml#button");
 }
 
 button[type="menu"] {
   -moz-binding: url("chrome://global/content/bindings/button.xml#menu");
 }
 
-button[type="menu-button"] {
-  -moz-binding: url("chrome://global/content/bindings/button.xml#menu-button");
-}
-
 /********** toolbarbutton **********/
 
 toolbarbutton {
   -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton");
 }
 
 toolbarbutton.badged-button > toolbarbutton,
 toolbarbutton.badged-button {
--- a/toolkit/recordreplay/Lock.cpp
+++ b/toolkit/recordreplay/Lock.cpp
@@ -146,17 +146,17 @@ Lock::Find(void* aNativeLock)
       return iter->second;
     }
   }
 
   return nullptr;
 }
 
 void
-Lock::Enter(const std::function<void()>& aCallback)
+Lock::Enter()
 {
   MOZ_RELEASE_ASSERT(!AreThreadEventsPassedThrough() && !HasDivergedFromRecording());
   MOZ_RELEASE_ASSERT(!AreThreadEventsDisallowed());
 
   RecordReplayAssert("Lock %d", (int) mId);
 
   // Include an event in each thread's record when a lock acquire begins. This
   // is not required by the replay but is used to check that lock acquire order
@@ -169,19 +169,26 @@ Lock::Enter(const std::function<void()>&
   LockAcquires* acquires = gLockAcquires.Get(mId);
   if (IsRecording()) {
     acquires->mAcquires->WriteScalar(thread->Id());
   } else {
     // Wait until this thread is next in line to acquire the lock.
     while (thread->Id() != acquires->mNextOwner) {
       Thread::Wait();
     }
-    // Acquire the lock before updating the next owner.
-    aCallback();
-    acquires->ReadAndNotifyNextOwner(thread);
+  }
+}
+
+void
+Lock::Exit()
+{
+  if (IsReplaying()) {
+    // Notify the next owner before releasing the lock.
+    LockAcquires* acquires = gLockAcquires.Get(mId);
+    acquires->ReadAndNotifyNextOwner(Thread::Current());
   }
 }
 
 struct AtomicLock : public detail::MutexImpl
 {
   using detail::MutexImpl::lock;
   using detail::MutexImpl::unlock;
 };
--- a/toolkit/recordreplay/Lock.h
+++ b/toolkit/recordreplay/Lock.h
@@ -37,19 +37,22 @@ public:
     MOZ_ASSERT(aId);
   }
 
   size_t Id() { return mId; }
 
   // When recording, this is called after the lock has been acquired, and
   // records the acquire in the lock's acquire order stream. When replaying,
   // this is called before the lock has been acquired, and blocks the thread
-  // until it is next in line to acquire the lock before acquiring it via
-  // aCallback.
-  void Enter(const std::function<void()>& aCallback);
+  // until it is next in line to acquire the lock.
+  void Enter();
+
+  // This is called before releasing the lock, allowing the next owner to
+  // acquire it while replaying.
+  void Exit();
 
   // Create a new Lock corresponding to a native lock, with a fresh ID.
   static void New(void* aNativeLock);
 
   // Destroy any Lock associated with a native lock.
   static void Destroy(void* aNativeLock);
 
   // Get the recorded Lock for a native lock if there is one, otherwise null.
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -1175,24 +1175,26 @@ WaitForCvar(pthread_mutex_t* aMutex, boo
   Lock* lock = Lock::Find(aMutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEvents pt;
     return aCallback();
   }
   RecordReplayAssert("WaitForCvar %d", (int) lock->Id());
   ssize_t rv = 0;
   if (IsRecording()) {
-    {
-      AutoPassThroughThreadEvents pt;
-      rv = aCallback();
-    }
+    AutoPassThroughThreadEvents pt;
+    rv = aCallback();
   } else {
     DirectUnlockMutex(aMutex);
   }
-  lock->Enter([=]() { DirectLockMutex(aMutex); });
+  lock->Exit();
+  lock->Enter();
+  if (IsReplaying()) {
+    DirectLockMutex(aMutex);
+  }
   if (aRecordReturnValue) {
     return RecordReplayValue(rv);
   }
   MOZ_RELEASE_ASSERT(rv == 0);
   return 0;
 }
 
 static ssize_t
@@ -1268,21 +1270,30 @@ RR_pthread_mutex_destroy(pthread_mutex_t
 static ssize_t
 RR_pthread_mutex_lock(pthread_mutex_t* aMutex)
 {
   Lock* lock = Lock::Find(aMutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEventsUseStackPointer pt;
     return OriginalCall(pthread_mutex_lock, ssize_t, aMutex);
   }
+  ssize_t rv = 0;
   if (IsRecording()) {
-    DirectLockMutex(aMutex);
+    AutoPassThroughThreadEvents pt;
+    rv = OriginalCall(pthread_mutex_lock, ssize_t, aMutex);
   }
-  lock->Enter([=]() { DirectLockMutex(aMutex); });
-  return 0;
+  rv = RecordReplayValue(rv);
+  MOZ_RELEASE_ASSERT(rv == 0 || rv == EDEADLK);
+  if (rv == 0) {
+    lock->Enter();
+    if (IsReplaying()) {
+      DirectLockMutex(aMutex);
+    }
+  }
+  return rv;
 }
 
 static ssize_t
 RR_pthread_mutex_trylock(pthread_mutex_t* aMutex)
 {
   Lock* lock = Lock::Find(aMutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEvents pt;
@@ -1291,26 +1302,35 @@ RR_pthread_mutex_trylock(pthread_mutex_t
   ssize_t rv = 0;
   if (IsRecording()) {
     AutoPassThroughThreadEvents pt;
     rv = OriginalCall(pthread_mutex_trylock, ssize_t, aMutex);
   }
   rv = RecordReplayValue(rv);
   MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY);
   if (rv == 0) {
-    lock->Enter([=]() { DirectLockMutex(aMutex); });
+    lock->Enter();
+    if (IsReplaying()) {
+      DirectLockMutex(aMutex);
+    }
   }
   return rv;
 }
 
 static ssize_t
 RR_pthread_mutex_unlock(pthread_mutex_t* aMutex)
 {
-  AutoEnsurePassThroughThreadEventsUseStackPointer pt;
-  return OriginalCall(pthread_mutex_unlock, ssize_t, aMutex);
+  Lock* lock = Lock::Find(aMutex);
+  if (!lock) {
+    AutoEnsurePassThroughThreadEventsUseStackPointer pt;
+    return OriginalCall(pthread_mutex_unlock, ssize_t, aMutex);
+  }
+  lock->Exit();
+  DirectUnlockMutex(aMutex);
+  return 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // stdlib redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 static ssize_t
 RR_dlclose(void* aHandle)
--- a/toolkit/themes/linux/global/button.css
+++ b/toolkit/themes/linux/global/button.css
@@ -38,35 +38,22 @@ button:hover:not(:-moz-any(:active,[disa
 }
 
 /* .......... disabled state .......... */
 
 button[disabled="true"] {
   color: GrayText;
 }
 
-/* ::::: menu/menu-button buttons ::::: */
-
-button[type="menu-button"] {
-  -moz-appearance: dualbutton;
-}
+/* ::::: menu buttons ::::: */
 
-.button-menubutton-button {
-  margin: 0;
-}
-
-.button-menu-dropmarker,
-.button-menubutton-dropmarker {
+.button-menu-dropmarker {
   -moz-appearance: toolbarbutton-dropdown !important;
 }
 
-.button-menubutton-dropmarker {
-  margin-inline-end: 3px;
-}
-
 /* ::::: plain buttons ::::: */
 
 button.plain {
   margin: 0 !important;
   padding: 0 !important;
 }
 
 button[type="disclosure"] {
--- a/toolkit/themes/linux/global/menu.css
+++ b/toolkit/themes/linux/global/menu.css
@@ -75,17 +75,17 @@ menuitem[default="true"],
 menuitem.spell-suggestion,
 menucaption {
   font-weight: bold;
 }
 
 /* ::::: menu/menuitems in menulist popups ::::: */
 
 menulist > menupopup {
-  font: message-box;
+  font: inherit;
 }
 
 menulist > menupopup > menuitem,
 menulist > menupopup > menucaption,
 menulist > menupopup > menu {
   padding: 1px 5px;
   max-width: none;
 }
--- a/toolkit/themes/osx/global/button.css
+++ b/toolkit/themes/osx/global/button.css
@@ -46,35 +46,26 @@ button[type="default"] {
 }
 
 /* .......... disabled state .......... */
 
 button[disabled="true"] {
   color: GrayText;
 }
 
-/* ::::: menu/menu-button buttons ::::: */
+/* ::::: menu buttons ::::: */
 
-button[type="menu-button"] {
-  margin: 0;
-  border: none;
-}
-
-.button-menu-dropmarker,
-.button-menubutton-dropmarker {
+.button-menu-dropmarker {
+  display: none;
   -moz-appearance: none !important;
   border: none;
   background-color: transparent !important;
   margin: 1px;
 }
 
-.button-menu-dropmarker {
-  display: none;
-}
-
 /* ::::: plain buttons ::::: */
 
 button.plain {
   margin: 0 !important;
   padding: 0 !important;
 }
 
 /* ::::: help button ::::: */
--- a/toolkit/themes/windows/global/button.css
+++ b/toolkit/themes/windows/global/button.css
@@ -72,37 +72,25 @@ button[disabled="true"] {
 
 @media (-moz-windows-classic) {
   button[disabled="true"] {
     color: ThreeDShadow;
     text-shadow: 1px 1px ThreeDHighlight;
   }
 }
 
-/* ::::: menu/menu-button buttons ::::: */
+/* ::::: menu buttons ::::: */
 
-button[type="menu-button"] {
-  margin: 0;
-}
-
-.button-menu-dropmarker,
-.button-menubutton-dropmarker {
+.button-menu-dropmarker {
   -moz-appearance: none !important;
   margin: 1px;
   width: 11px;
   height: 11px;
 }
 
-.button-menubutton-dropmarker[open="true"] {
-  margin-top: 2px;
-  margin-bottom: 0px;
-  margin-inline-start: 2px;
-  margin-inline-end: 0px;
-}
-
 /* ::::: plain buttons ::::: */
 
 button.plain {
   margin: 0 !important;
   padding: 0 !important;
 }
 
 button[type="disclosure"] {
--- a/toolkit/themes/windows/global/menu.css
+++ b/toolkit/themes/windows/global/menu.css
@@ -165,17 +165,17 @@ menu[_moz-menuactive="true"],
 menuitem[_moz-menuactive="true"] {
   background-color: -moz-menuhover;
   color: -moz-menuhovertext;
 }
 
 /* ::::: menu/menuitems in menulist popups ::::: */
 
 menulist > menupopup {
-  font: message-box;
+  font: inherit;
 }
 
 menulist > menupopup > menuitem,
 menulist > menupopup > menucaption,
 menulist > menupopup > menu {
   -moz-appearance: none !important;
   border: 1px solid transparent;
   padding-inline-start: 5px;
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -585,17 +585,16 @@ STATIC_ATOMS = [
     Atom("maxlength", "maxlength"),
     Atom("maxpos", "maxpos"),
     Atom("maxwidth", "maxwidth"),
     Atom("media", "media"),
     Atom("mediaType", "media-type"),
     Atom("menu", "menu"),
     Atom("menubar", "menubar"),
     Atom("menubutton", "menubutton"),
-    Atom("menuButton", "menu-button"),
     Atom("menucaption", "menucaption"),
     Atom("menugroup", "menugroup"),
     Atom("menuitem", "menuitem"),
     Atom("menulist", "menulist"),
     Atom("menupopup", "menupopup"),
     Atom("menuseparator", "menuseparator"),
     Atom("message", "message"),
     Atom("meta", "meta"),