Merge inbound to mozilla-central. a=merge
authorTiberius Oros <toros@mozilla.com>
Thu, 06 Sep 2018 01:25:01 +0300
changeset 490693 c4c125ee2556
parent 490542 626cdd5bd9d3 (current diff)
parent 490692 b23f0be9191b (diff)
child 490715 a26c0dfbd636
child 490754 fbb3a843672a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
c4c125ee2556 / 64.0a1 / 20180905223809 / files
nightly linux64
c4c125ee2556 / 64.0a1 / 20180905223809 / files
nightly mac
c4c125ee2556 / 64.0a1 / 20180905223809 / files
nightly win32
c4c125ee2556 / 64.0a1 / 20180905223809 / files
nightly win64
c4c125ee2556 / 64.0a1 / 20180905223809 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
security/nss/gtests/google_test/gtest/README
security/nss/gtests/google_test/gtest/build-aux/.keep
security/nss/gtests/google_test/gtest/msvc/gtest-md.sln
security/nss/gtests/google_test/gtest/msvc/gtest-md.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest.sln
security/nss/gtests/google_test/gtest/msvc/gtest.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_main-md.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_main.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_prod_test-md.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_prod_test.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_unittest-md.vcproj
security/nss/gtests/google_test/gtest/msvc/gtest_unittest.vcproj
security/nss/gtests/google_test/gtest/test/gtest-death-test_ex_test.cc
security/nss/gtests/google_test/gtest/test/gtest-death-test_test.cc
security/nss/gtests/google_test/gtest/test/gtest-filepath_test.cc
security/nss/gtests/google_test/gtest/test/gtest-linked_ptr_test.cc
security/nss/gtests/google_test/gtest/test/gtest-listener_test.cc
security/nss/gtests/google_test/gtest/test/gtest-message_test.cc
security/nss/gtests/google_test/gtest/test/gtest-options_test.cc
security/nss/gtests/google_test/gtest/test/gtest-param-test2_test.cc
security/nss/gtests/google_test/gtest/test/gtest-param-test_test.cc
security/nss/gtests/google_test/gtest/test/gtest-param-test_test.h
security/nss/gtests/google_test/gtest/test/gtest-port_test.cc
security/nss/gtests/google_test/gtest/test/gtest-printers_test.cc
security/nss/gtests/google_test/gtest/test/gtest-test-part_test.cc
security/nss/gtests/google_test/gtest/test/gtest-tuple_test.cc
security/nss/gtests/google_test/gtest/test/gtest_break_on_failure_unittest.py
security/nss/gtests/google_test/gtest/test/gtest_break_on_failure_unittest_.cc
security/nss/gtests/google_test/gtest/test/gtest_catch_exceptions_test.py
security/nss/gtests/google_test/gtest/test/gtest_catch_exceptions_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_color_test.py
security/nss/gtests/google_test/gtest/test/gtest_color_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_env_var_test.py
security/nss/gtests/google_test/gtest/test/gtest_env_var_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_filter_unittest.py
security/nss/gtests/google_test/gtest/test/gtest_filter_unittest_.cc
security/nss/gtests/google_test/gtest/test/gtest_list_tests_unittest.py
security/nss/gtests/google_test/gtest/test/gtest_list_tests_unittest_.cc
security/nss/gtests/google_test/gtest/test/gtest_output_test.py
security/nss/gtests/google_test/gtest/test/gtest_output_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_output_test_golden_lin.txt
security/nss/gtests/google_test/gtest/test/gtest_shuffle_test.py
security/nss/gtests/google_test/gtest/test/gtest_shuffle_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_throw_on_failure_test.py
security/nss/gtests/google_test/gtest/test/gtest_throw_on_failure_test_.cc
security/nss/gtests/google_test/gtest/test/gtest_uninitialized_test.py
security/nss/gtests/google_test/gtest/test/gtest_uninitialized_test_.cc
security/nss/gtests/google_test/gtest/xcode/Scripts/versiongenerate.py
taskcluster/ci/test/test-sets.yml
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/accelerometer/idlharness.https.html.ini
testing/web-platform/meta/ambient-light/idlharness.https.html.ini
testing/web-platform/meta/animation-worklet/interfaces.any.js.ini
testing/web-platform/meta/async-local-storage/storage-smoke-test.https.tentative.html.ini
testing/web-platform/meta/background-fetch/credentials-in-url.https.window.js.ini
testing/web-platform/meta/background-fetch/idlharness.any.js.ini
testing/web-platform/meta/background-fetch/interfaces.https.any.js.ini
testing/web-platform/meta/battery-status/battery-interface-idlharness.https.html.ini
testing/web-platform/meta/budget-api/idlharness.https.any.js.ini
testing/web-platform/meta/budget-api/interfaces.any.js.ini
testing/web-platform/meta/compat/interfaces.any.js.ini
testing/web-platform/meta/cookies/prefix/__host.document-cookie.non-secure.html.ini
testing/web-platform/meta/cookies/prefix/__host.http.non-secure.html.ini
testing/web-platform/meta/cookies/prefix/__secure.document-cookie.non-secure.html.ini
testing/web-platform/meta/cookies/prefix/__secure.http.non-secure.html.ini
testing/web-platform/meta/cookies/prefix/__secure.http.secure.html.ini
testing/web-platform/meta/cookies/secure/cookie-forcing.html.ini
testing/web-platform/meta/cookies/secure/create-cookie-http.html.ini
testing/web-platform/meta/css/css-contain/contain-style-counters.html.ini
testing/web-platform/meta/css/css-pseudo/interfaces.html.ini
testing/web-platform/meta/css/css-text/overflow-wrap/overflow-wrap-break-word-004.html.ini
testing/web-platform/meta/css/css-text/word-break/word-break-break-all-010.html.ini
testing/web-platform/meta/css/css-text/word-break/word-break-break-all-011.html.ini
testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/images3/object-position-svg-001o.html.ini
testing/web-platform/meta/feature-policy/interfaces.any.js.ini
testing/web-platform/meta/fetch/cross-origin-resource-policy/fetch-in-service-worker.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html.ini
testing/web-platform/meta/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html.ini
testing/web-platform/meta/fullscreen/interfaces.html.ini
testing/web-platform/meta/gamepad/idlharness.html.ini
testing/web-platform/meta/geolocation-sensor/idlharness.https.html.ini
testing/web-platform/meta/gyroscope/idlharness.https.html.ini
testing/web-platform/meta/html-imports/document/document-method-changes.html.ini
testing/web-platform/meta/html-imports/fetching/already-in-import-map.html.ini
testing/web-platform/meta/html-imports/fetching/loading-attempt.html.ini
testing/web-platform/meta/html-imports/html-link-element/import-attribute.html.ini
testing/web-platform/meta/html-media-capture/idlharness.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-in-shadow-dom.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-inlines.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-label-focus.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-node-is-uneditable.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-node-is-unfocusable.html.ini
testing/web-platform/meta/html/editing/focus/inert/inert-node-is-unselectable.html.ini
testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html.ini
testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-default-style.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-display.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-list-item.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-transform-translatez.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-align.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-block-formatting-context.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-display.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-grid-flex-multicol.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend.html.ini
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/min-inline-size.html.ini
testing/web-platform/meta/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/009.https.html.ini
testing/web-platform/meta/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/010.html.ini
testing/web-platform/meta/html/webappapis/scripting/events/body-exposed-window-event-handlers.html.ini
testing/web-platform/meta/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.html.ini
testing/web-platform/meta/input-device-capabilities/interfaces.html.ini
testing/web-platform/meta/input-events/idlharness.html.ini
testing/web-platform/meta/keyboard-lock/idlharness.https.html.ini
testing/web-platform/meta/keyboard-map/idlharness.https.html.ini
testing/web-platform/meta/magnetometer/idlharness.https.html.ini
testing/web-platform/meta/media-source/interfaces.html.ini
testing/web-platform/meta/mediacapture-image/idlharness.html.ini
testing/web-platform/meta/mediacapture-record/idlharness.html.ini
testing/web-platform/meta/mediasession/idlharness.html.ini
testing/web-platform/meta/notifications/idlharness.any.js.ini
testing/web-platform/meta/notifications/interfaces.html.ini
testing/web-platform/meta/orientation-sensor/idlharness.https.html.ini
testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
testing/web-platform/meta/pointerevents/idlharness.html.ini
testing/web-platform/meta/proximity/idlharness.https.html.ini
testing/web-platform/meta/push-api/idlharness.any.js.ini
testing/web-platform/meta/resize-observer/idlharness.html.ini
testing/web-platform/meta/scroll-animations/idlharness.html.ini
testing/web-platform/meta/selection/interfaces.html.ini
testing/web-platform/meta/service-workers/service-worker/navigationpreload.https.html.ini
testing/web-platform/meta/speech-api/SpeechSynthesis-speak-without-activation-fails.html.ini
testing/web-platform/meta/storage/opaque-origin.https.html.ini
testing/web-platform/meta/storage/storagemanager-persist.https.html.ini
testing/web-platform/meta/svg/interfaces.html.ini
testing/web-platform/meta/trusted-types/DOMParser-requiresTrustedTypes.tentative.html.ini
testing/web-platform/meta/trusted-types/DOMParser.tentative.html.ini
testing/web-platform/meta/trusted-types/Element-innerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLAnchorElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLAreaElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLBaseElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLIFrameElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLIFrameElement-srcdoc.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLImageElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLLinkElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLMediaElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLObjectElement.tentative.html.ini
testing/web-platform/meta/trusted-types/HTMLSourceElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/TrustedHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/TrustedScript.tentative.html.ini
testing/web-platform/meta/trusted-types/TrustedScriptURL.tentative.html.ini
testing/web-platform/meta/trusted-types/TrustedURL.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-Element-innerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLAnchorElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLAreaElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-embed-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-frame-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-innerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-input-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-insertAdjacentHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-outerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-script-src.tentative.html.ini
testing/web-platform/meta/trusted-types/block-string-assignment-to-track-src.tentative.html.ini
testing/web-platform/meta/trusted-types/createContextualFragment.tentative.html.ini
testing/web-platform/meta/trusted-types/embed-src.tentative.html.ini
testing/web-platform/meta/trusted-types/frame-src.tentative.html.ini
testing/web-platform/meta/trusted-types/innerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/input-src.tentative.html.ini
testing/web-platform/meta/trusted-types/insertAdjacentHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/outerHTML.tentative.html.ini
testing/web-platform/meta/trusted-types/script-src.tentative.html.ini
testing/web-platform/meta/trusted-types/srcDoc-requiresTrustedTypes.tentative.html.ini
testing/web-platform/meta/trusted-types/srcDoc.tentative.html.ini
testing/web-platform/meta/trusted-types/track-src.tentative.html.ini
testing/web-platform/meta/uievents/interfaces.html.ini
testing/web-platform/meta/url/interfaces.any.js.ini
testing/web-platform/meta/url/urlsearchparams-constructor.html.ini
testing/web-platform/meta/wasm/wasm_service_worker_test.https.html.ini
testing/web-platform/meta/web-locks/acquire.tentative.https.html.ini
testing/web-platform/meta/web-locks/held.tentative.https.html.ini
testing/web-platform/meta/web-locks/ifAvailable.tentative.https.html.ini
testing/web-platform/meta/web-locks/lock-attributes.tentative.https.html.ini
testing/web-platform/meta/web-locks/mode-exclusive.tentative.https.html.ini
testing/web-platform/meta/web-locks/mode-mixed.tentative.https.html.ini
testing/web-platform/meta/web-locks/mode-shared.tentative.https.html.ini
testing/web-platform/meta/web-locks/query-empty.tentative.https.html.ini
testing/web-platform/meta/web-locks/query-order.tentative.https.html.ini
testing/web-platform/meta/web-locks/query.tentative.https.html.ini
testing/web-platform/meta/web-locks/resource-names.tentative.https.html.ini
testing/web-platform/meta/web-locks/secure-context.tentative.https.html.ini
testing/web-platform/meta/web-locks/signal.tentative.https.html.ini
testing/web-platform/meta/web-locks/steal.tentative.https.html.ini
testing/web-platform/meta/web-share/idlharness.https.html.ini
testing/web-platform/meta/webaudio/idlharness.https.html.ini
testing/web-platform/meta/webauthn/idlharness.https.any.js.ini
testing/web-platform/meta/webmessaging/with-options/null-arg-two.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/two-arg.tentative.html.ini
testing/web-platform/meta/webrtc/RTCPeerConnection-addTransceiver.html.ini
testing/web-platform/meta/webrtc/interfaces.https.html.ini
testing/web-platform/meta/webusb/idlharness.any.js.ini
testing/web-platform/meta/webxr/interfaces.https.html.ini
testing/web-platform/meta/webxr/xrSession_exclusive_requestAnimationFrame.https.html.ini
testing/web-platform/meta/workers/WorkerGlobalScope_requestAnimationFrame.htm.ini
testing/web-platform/tests/2dcontext/drawing-text-to-the-canvas/2d.text.draw.baseline.alphabetic.html
testing/web-platform/tests/cookies/secure/create-cookie-http.html
testing/web-platform/tests/dom/inert/inert-does-not-match-disabled-selector.html
testing/web-platform/tests/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html
testing/web-platform/tests/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html
testing/web-platform/tests/html/editing/focus/inert/inert-does-not-match-disabled-selector.html
testing/web-platform/tests/html/editing/focus/inert/inert-in-shadow-dom.html
testing/web-platform/tests/html/editing/focus/inert/inert-inlines.html
testing/web-platform/tests/html/editing/focus/inert/inert-label-focus.html
testing/web-platform/tests/html/editing/focus/inert/inert-node-is-uneditable.html
testing/web-platform/tests/html/editing/focus/inert/inert-node-is-unfocusable.html
testing/web-platform/tests/html/editing/focus/inert/inert-node-is-unselectable.html
testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/010-1.html
testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/010-2.html
testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/010.html
testing/web-platform/tests/quirks/unitless-length/excluded-properties.html
testing/web-platform/tests/storage/estimate-indexeddb-worker.https.html
testing/web-platform/tests/storage/estimate-indexeddb.https.html
testing/web-platform/tests/storage/estimate-parallel.https.html
testing/web-platform/tests/storage/opaque-origin.https.html
testing/web-platform/tests/storage/persisted-worker.https.html
testing/web-platform/tests/storage/persisted.https.html
testing/web-platform/tests/storage/resources/storagemanager-persist-worker.js
testing/web-platform/tests/storage/resources/storagemanager-persisted-worker.js
testing/web-platform/tests/storage/storage-estimate-indexeddb.js
testing/web-platform/tests/storage/storage-persisted.js
testing/web-platform/tests/storage/storagemanager-estimate.https.html
testing/web-platform/tests/storage/storagemanager-persist-worker.https.html
testing/web-platform/tests/storage/storagemanager-persist.https.html
testing/web-platform/tests/storage/storagemanager-persisted-worker.https.html
testing/web-platform/tests/storage/storagemanager-persisted.https.html
testing/web-platform/tests/wasm/resources/blank.html
testing/web-platform/tests/wasm/resources/frame.html
testing/web-platform/tests/wasm/resources/incrementer.wasm
testing/web-platform/tests/wasm/resources/service-worker.js
testing/web-platform/tests/wasm/wasm_local_iframe_test.html
testing/web-platform/tests/wasm/wasm_serialization_tests.html
testing/web-platform/tests/wasm/wasm_serialization_tests.js
testing/web-platform/tests/wasm/wasm_serialization_worker.js
testing/web-platform/tests/wasm/wasm_service_worker_test.https.html
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 
 support-files =
   events.js
   head.js
   shared-head.js
 
 [browser_shutdown_acc_reference.js]
+skip-if = (os == 'linux' && debug && bits == 64) #Bug 1421307
 [browser_shutdown_doc_acc_reference.js]
 [browser_shutdown_multi_acc_reference_obj.js]
 [browser_shutdown_multi_acc_reference_doc.js]
 [browser_shutdown_multi_reference.js]
 [browser_shutdown_parent_own_reference.js]
 skip-if = !e10s || (verify && debug && (os == 'win')) # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_pref.js]
 [browser_shutdown_proxy_acc_reference.js]
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -71,16 +71,18 @@
     <command id="Browser:Stop"    oncommand="BrowserStop();" disabled="true"/>
     <command id="Browser:Reload"  oncommand="if (event.shiftKey) BrowserReloadSkipCache(); else BrowserReload()" disabled="true"/>
     <command id="Browser:ReloadOrDuplicate" oncommand="BrowserReloadOrDuplicate(event)" disabled="true">
       <observes element="Browser:Reload" attribute="disabled"/>
     </command>
     <command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
       <observes element="Browser:Reload" attribute="disabled"/>
     </command>
+    <command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);"/>
+    <command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
     <command id="Browser:ShowAllTabs" oncommand="gTabsPanel.showAllTabsPanel();"/>
     <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="cmd_gestureRotateLeft" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
     <command id="cmd_gestureRotateRight" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
     <command id="cmd_gestureRotateEnd" oncommand="gGestureSupport.rotateEnd()"/>
--- a/browser/base/content/test/general/browser_bug578534.js
+++ b/browser/base/content/test/general/browser_bug578534.js
@@ -1,23 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 add_task(async function test() {
   let uriString = "http://example.com/";
   let cookieBehavior = "network.cookie.cookieBehavior";
   let uriObj = Services.io.newURI(uriString);
-  let cp = Cc["@mozilla.org/cookie/permission;1"]
-             .getService(Ci.nsICookiePermission);
 
   await SpecialPowers.pushPrefEnv({ set: [[ cookieBehavior, 2 ]] });
-  cp.setAccess(uriObj, cp.ACCESS_ALLOW);
+  Services.perms.add(uriObj, "cookie", Services.perms.ALLOW_ACTION);
 
   await BrowserTestUtils.withNewTab({ gBrowser, url: uriString }, async function(browser) {
     await ContentTask.spawn(browser, null, function() {
       is(content.navigator.cookieEnabled, true,
          "navigator.cookieEnabled should be true");
     });
   });
 
-  cp.setAccess(uriObj, cp.ACCESS_DEFAULT);
+  Services.perms.add(uriObj, "cookie", Services.perms.UNKNOWN_ACTION);
 });
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -80,17 +80,18 @@
             // The element we are dragging over
             let elt = aEvent.target;
             if (elt.localName == "menupopup")
               elt = elt.parentNode;
 
             // Calculate positions taking care of arrowscrollbox
             let scrollbox = this._scrollBox;
             let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
-            let scrollboxOffset = this.boxObject.y;
+            let scrollboxOffset = scrollbox.scrollBoxObject.y -
+                                  (scrollbox.boxObject.y - this.boxObject.y);
             let eltY = elt.boxObject.y - scrollboxOffset;
             let eltHeight = elt.boxObject.height;
 
             if (!elt._placesNode) {
               // If we are dragging over a non places node drop at the end.
               dropPoint.ip = new PlacesInsertionPoint({
                 parentId: PlacesUtils.getConcreteItemId(resultNode),
                 parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
@@ -428,17 +429,17 @@
         if (dropPoint.folderElt || this._hideDropIndicator(event)) {
           this._indicatorBar.hidden = true;
           event.preventDefault();
           event.stopPropagation();
           return;
         }
 
         // We should display the drop indicator relative to the arrowscrollbox.
-        let scrollbox = this._scrollBox.boxObject;
+        let scrollbox = this._scrollBox.scrollBoxObject;
         let newMarginTop = 0;
         if (scrollDir == 0) {
           let elt = this.firstElementChild;
           while (elt && event.screenY > elt.boxObject.screenY +
                                         elt.boxObject.height / 2)
             elt = elt.nextElementSibling;
           newMarginTop = elt ? elt.boxObject.screenY - scrollbox.screenY :
                                scrollbox.height;
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -395,25 +395,20 @@ var gPrivacyPane = {
       document.l10n.setAttributes(checkbox, "permissions-notification-pause");
       if (AlertsServiceDND.manualDoNotDisturb) {
         let notificationsDoNotDisturb =
           document.getElementById("notificationsDoNotDisturb");
         notificationsDoNotDisturb.setAttribute("checked", true);
       }
     }
 
-    Services.obs.addObserver(this, "sitedatamanager:sites-updated");
-    Services.obs.addObserver(this, "sitedatamanager:updating-sites");
-    let unload = () => {
-      window.removeEventListener("unload", unload);
-      Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
-      Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
-    };
-    window.addEventListener("unload", unload);
-    SiteDataManager.updateSites();
+    if (!contentBlockingUiEnabled) {
+      // If content blocking UI is enabled, this has been handled by initContentBlocking.
+      this.initSiteDataControls();
+    }
     setEventListener("clearSiteDataButton", "command",
       gPrivacyPane.clearSiteData);
     setEventListener("siteDataSettings", "command",
       gPrivacyPane.showSiteDataSettings);
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
     document.getElementById("siteDataLearnMoreLink").setAttribute("href", url);
 
     let notificationInfoURL =
@@ -461,16 +456,28 @@ var gPrivacyPane = {
       document.getElementById("privateBrowsingAutoStart").hidden = true;
       document.querySelector("menuitem[value='dontremember']").hidden = true;
     }
 
     // Notify observers that the UI is now ready
     Services.obs.notifyObservers(window, "privacy-pane-loaded");
   },
 
+  initSiteDataControls() {
+    Services.obs.addObserver(this, "sitedatamanager:sites-updated");
+    Services.obs.addObserver(this, "sitedatamanager:updating-sites");
+    let unload = () => {
+      window.removeEventListener("unload", unload);
+      Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
+      Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
+    };
+    window.addEventListener("unload", unload);
+    SiteDataManager.updateSites();
+  },
+
   // CONTENT BLOCKING
 
   /**
    * Initializes the content blocking section.
    */
   initContentBlocking() {
     let contentBlockingCheckbox = document.getElementById("contentBlockingCheckbox");
     setEventListener("contentBlockingToggle", "command",
@@ -533,16 +540,22 @@ var gPrivacyPane = {
     // If we do this without a timeout, trackingProtectionReadPrefs will set the checked
     // attribute on our checkbox element before the XBL binding has had a chance to have
     // been re-applied to it.
     setTimeout(() => {
       browserPrivacyCategory.parentNode.insertBefore(siteDataGroup,
                                                      browserPrivacyCategory.nextSibling);
       browserPrivacyCategory.parentNode.insertBefore(trackingGroup,
                                                      browserPrivacyCategory.nextSibling);
+
+      // We do this after having moved the elements in the DOM above, in order to avoid
+      // a race condition with this timeout handler reapplying the XBL bindings to the
+      // elements in this subtree and the observe() method attempting to set the disabled
+      // attribute on the site data controls.
+      this.initSiteDataControls();
     }, 0);
   },
 
   /**
    * Resets all user-exposed content blocking preferences to their default values.
    */
   async restoreContentBlockingPrefs() {
     function clearIfNotLocked(pref) {
--- a/browser/locales/en-US/chrome/browser/sitePermissions.properties
+++ b/browser/locales/en-US/chrome/browser/sitePermissions.properties
@@ -25,17 +25,17 @@ state.current.hide = Hide Prompt
 # Used to label permission state checkboxes in the page info dialog.
 state.multichoice.alwaysAsk = Always Ask
 state.multichoice.allow = Allow
 state.multichoice.allowForSession = Allow for Session
 state.multichoice.block = Block
 
 permission.autoplay-media.label = Automatically Play Media with Sound
 permission.cookie.label = Set Cookies
-permission.desktop-notification2.label = Receive Notifications
+permission.desktop-notification3.label = Send Notifications
 permission.image.label = Load Images
 permission.camera.label = Use the Camera
 permission.microphone.label = Use the Microphone
 permission.screen.label = Share the Screen
 permission.install.label = Install Add-ons
 permission.popup.label = Open Pop-up Windows
 permission.geo.label = Access Your Location
 permission.shortcuts.label = Override Keyboard Shortcuts
--- a/browser/modules/ReaderParent.jsm
+++ b/browser/modules/ReaderParent.jsm
@@ -52,39 +52,51 @@ var ReaderParent = {
 
   updateReaderButton(browser) {
     let win = browser.ownerGlobal;
     if (browser != win.gBrowser.selectedBrowser) {
       return;
     }
 
     let button = win.document.getElementById("reader-mode-button");
-    let command = win.document.getElementById("View:ReaderView");
+    let menuitem = win.document.getElementById("menu_readerModeItem");
     let key = win.document.getElementById("key_toggleReaderMode");
     // aria-reader is not a real ARIA attribute. However, this will cause
     // Gecko accessibility to expose the "reader" object attribute. We do this
     // so that the reader state is easy for accessibility clients to access
     // programmatically.
     if (browser.currentURI.spec.startsWith("about:reader")) {
+      let closeText = gStringBundle.GetStringFromName("readerView.close");
+
       button.setAttribute("readeractive", true);
       button.hidden = false;
-      let closeText = gStringBundle.GetStringFromName("readerView.close");
-      command.setAttribute("label", closeText);
-      command.setAttribute("hidden", false);
-      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.close.accesskey"));
+      button.setAttribute("aria-label", closeText);
+
+      menuitem.setAttribute("label", closeText);
+      menuitem.setAttribute("hidden", false);
+      menuitem.setAttribute("accesskey",
+        gStringBundle.GetStringFromName("readerView.close.accesskey"));
+
       key.setAttribute("disabled", false);
+
       browser.setAttribute("aria-reader", "active");
     } else {
+      let enterText = gStringBundle.GetStringFromName("readerView.enter");
+
       button.removeAttribute("readeractive");
       button.hidden = !browser.isArticle;
-      let enterText = gStringBundle.GetStringFromName("readerView.enter");
-      command.setAttribute("label", enterText);
-      command.setAttribute("hidden", !browser.isArticle);
-      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.enter.accesskey"));
+      button.setAttribute("aria-label", enterText);
+
+      menuitem.setAttribute("label", enterText);
+      menuitem.setAttribute("hidden", !browser.isArticle);
+      menuitem.setAttribute("accesskey",
+        gStringBundle.GetStringFromName("readerView.enter.accesskey"));
+
       key.setAttribute("disabled", !browser.isArticle);
+
       if (browser.isArticle) {
         browser.setAttribute("aria-reader", "available");
       } else {
         browser.removeAttribute("aria-reader");
       }
     }
   },
 
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -749,17 +749,17 @@ var gPermissionObject = {
         return SitePermissions.ALLOW_COOKIES_FOR_SESSION;
 
       return SitePermissions.ALLOW;
     },
   },
 
   "desktop-notification": {
     exactHostMatch: true,
-    labelID: "desktop-notification2",
+    labelID: "desktop-notification3",
   },
 
   "camera": {
     exactHostMatch: true,
   },
 
   "microphone": {
     exactHostMatch: true,
--- a/devtools/.eslintrc.mochitests.js
+++ b/devtools/.eslintrc.mochitests.js
@@ -6,16 +6,17 @@ module.exports = {
   // All globals made available in the test environment.
   "globals": {
     "DevToolsUtils": true,
     "gDevTools": true,
     "once": true,
     "synthesizeKeyFromKeyTag": true,
     "TargetFactory": true,
     "waitForTick": true,
+    "waitUntilState": true,
   },
 
   "parserOptions": {
     "ecmaFeatures": {
       "jsx": true,
     }
   },
 
--- a/devtools/client/framework/test/browser_toolbox_toolbar_reorder_by_dnd.js
+++ b/devtools/client/framework/test/browser_toolbox_toolbar_reorder_by_dnd.js
@@ -54,25 +54,17 @@ const TEST_DATA = [
   },
   {
     description: "DragAndDrop the target component over the ending of the tab",
     dragTarget: "webconsole",
     passedTargets: ["jsdebugger", "styleeditor", "performance",
                     "memory", "netmonitor", "storage"],
     dropTarget: "#toolbox-buttons-end",
     expectedOrder: ["inspector", "jsdebugger", "styleeditor", "performance",
-                    "memory", "netmonitor", "storage", "webconsole", "accessibility"],
-  },
-  {
-    description: "Mouse was out from the document while dragging",
-    dragTarget: "webconsole",
-    passedTargets: ["inspector"],
-    dropTarget: null,
-    expectedOrder: ["webconsole", "inspector", "jsdebugger", "styleeditor",
-                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+                    "memory", "netmonitor", "storage", "accessibility", "webconsole"],
   },
 ];
 
 add_task(async function() {
   const tab = await addTab("about:blank");
   const toolbox = await openToolboxForTab(tab, "inspector", Toolbox.HostType.BOTTOM);
 
   const originalPreference = Services.prefs.getCharPref("devtools.toolbox.tabsOrder");
--- a/devtools/client/framework/toolbox-tabs-order-manager.js
+++ b/devtools/client/framework/toolbox-tabs-order-manager.js
@@ -14,17 +14,16 @@ const PREFERENCE_NAME = "devtools.toolbo
  */
 class ToolboxTabsOrderManager {
   constructor(onOrderUpdated) {
     this.onOrderUpdated = onOrderUpdated;
     this.currentPanelDefinitions = [];
 
     this.onMouseDown = this.onMouseDown.bind(this);
     this.onMouseMove = this.onMouseMove.bind(this);
-    this.onMouseOut = this.onMouseOut.bind(this);
     this.onMouseUp = this.onMouseUp.bind(this);
 
     Services.prefs.addObserver(PREFERENCE_NAME, this.onOrderUpdated);
 
     this.telemetry = new Telemetry();
   }
 
   destroy() {
@@ -34,96 +33,100 @@ class ToolboxTabsOrderManager {
     const ids =
       this.currentPanelDefinitions.map(definition => definition.extensionId || definition.id);
     const pref = ids.join(",");
     Services.prefs.setCharPref(PREFERENCE_NAME, pref);
 
     this.onMouseUp();
   }
 
+  insertBefore(target) {
+    const xBefore = this.dragTarget.offsetLeft;
+    this.toolboxTabsElement.insertBefore(this.dragTarget, target);
+    const xAfter = this.dragTarget.offsetLeft;
+    this.dragStartX += xAfter - xBefore;
+    this.isOrderUpdated = true;
+  }
+
+  isFirstTab(tabElement) {
+    return !tabElement.previousSibling;
+  }
+
+  isLastTab(tabElement) {
+    return !tabElement.nextSibling ||
+           tabElement.nextSibling.id === "tools-chevron-menu-button";
+  }
+
   setCurrentPanelDefinitions(currentPanelDefinitions) {
     this.currentPanelDefinitions = currentPanelDefinitions;
   }
 
   onMouseDown(e) {
     if (!e.target.classList.contains("devtools-tab")) {
       return;
     }
 
     this.dragStartX = e.pageX;
     this.dragTarget = e.target;
     this.previousPageX = e.pageX;
     this.toolboxContainerElement = this.dragTarget.closest("#toolbox-container");
     this.toolboxTabsElement = this.dragTarget.closest(".toolbox-tabs");
     this.isOrderUpdated = false;
+    this.eventTarget = this.dragTarget.ownerGlobal.top;
 
-    this.dragTarget.ownerDocument.addEventListener("mousemove", this.onMouseMove);
-    this.dragTarget.ownerDocument.addEventListener("mouseout", this.onMouseOut);
-    this.dragTarget.ownerDocument.addEventListener("mouseup", this.onMouseUp);
+    this.eventTarget.addEventListener("mousemove", this.onMouseMove);
+    this.eventTarget.addEventListener("mouseup", this.onMouseUp);
 
     this.toolboxContainerElement.classList.add("tabs-reordering");
   }
 
   onMouseMove(e) {
-    const tabsElement = this.toolboxTabsElement;
     const diffPageX = e.pageX - this.previousPageX;
     const dragTargetCenterX =
       this.dragTarget.offsetLeft + diffPageX + this.dragTarget.clientWidth / 2;
     let isDragTargetPreviousSibling = false;
 
-    for (const tabElement of tabsElement.querySelectorAll(".devtools-tab")) {
+    for (const tabElement of this.toolboxTabsElement.querySelectorAll(".devtools-tab")) {
       if (tabElement === this.dragTarget) {
         isDragTargetPreviousSibling = true;
         continue;
       }
 
-      const anotherElementCenterX =
-        tabElement.offsetLeft + tabElement.clientWidth / 2;
-
-      if (Math.abs(dragTargetCenterX - anotherElementCenterX) <
-          tabElement.clientWidth / 3) {
-        const xBefore = this.dragTarget.offsetLeft;
+      const anotherCenterX = tabElement.offsetLeft + tabElement.clientWidth / 2;
+      const isReplaceable =
+        // Is the dragTarget near the center of the other tab?
+        Math.abs(dragTargetCenterX - anotherCenterX) < tabElement.clientWidth / 3 ||
+        // Has the dragTarget moved before the first tab
+        // (mouse moved too fast between two events)
+        (this.isFirstTab(tabElement) && dragTargetCenterX < anotherCenterX) ||
+        // Has the dragTarget moved after the last tab
+        // (mouse moved too fast between two events)
+        (this.isLastTab(tabElement) && anotherCenterX < dragTargetCenterX);
 
-        if (isDragTargetPreviousSibling) {
-          tabsElement.insertBefore(this.dragTarget, tabElement.nextSibling);
-        } else {
-          tabsElement.insertBefore(this.dragTarget, tabElement);
-        }
-
-        const xAfter = this.dragTarget.offsetLeft;
-        this.dragStartX += xAfter - xBefore;
-
-        this.isOrderUpdated = true;
+      if (isReplaceable) {
+        const replaceableElement =
+          isDragTargetPreviousSibling ? tabElement.nextSibling : tabElement;
+        this.insertBefore(replaceableElement);
         break;
       }
     }
 
     let distance = e.pageX - this.dragStartX;
 
-    if ((!this.dragTarget.previousSibling && distance < 0) ||
-        ((!this.dragTarget.nextSibling ||
-          this.dragTarget.nextSibling.id === "tools-chevron-menu-button") &&
-          distance > 0)) {
+    if ((this.isFirstTab(this.dragTarget) && distance < 0) ||
+        (this.isLastTab(this.dragTarget) && distance > 0)) {
       // If the drag target is already edge of the tabs and the mouse will make the
       // element to move to same direction more, keep the position.
       distance = 0;
     }
 
     this.dragTarget.style.left = `${ distance }px`;
     this.previousPageX = e.pageX;
   }
 
-  onMouseOut(e) {
-    const documentElement = this.dragTarget.ownerDocument.documentElement;
-    if (e.pageX <= 0 || documentElement.clientWidth <= e.pageX ||
-        e.pageY <= 0 || documentElement.clientHeight <= e.pageY) {
-      this.onMouseUp();
-    }
-  }
-
   onMouseUp() {
     if (!this.dragTarget) {
       // The case in here has two type:
       // 1. Although destroy method was called, it was not during reordering.
       // 2. Although mouse event occur, destroy method was called during reordering.
       return;
     }
 
@@ -141,25 +144,25 @@ class ToolboxTabsOrderManager {
       Services.prefs.setCharPref(PREFERENCE_NAME, pref);
 
       // Log which tabs reordered. The question we want to answer is:
       // "How frequently are the tabs re-ordered, also which tabs get re-ordered?"
       const toolId = this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
       this.telemetry.keyedScalarAdd(TABS_REORDERED_SCALAR, toolId, 1);
     }
 
-    this.dragTarget.ownerDocument.removeEventListener("mousemove", this.onMouseMove);
-    this.dragTarget.ownerDocument.removeEventListener("mouseout", this.onMouseOut);
-    this.dragTarget.ownerDocument.removeEventListener("mouseup", this.onMouseUp);
+    this.eventTarget.removeEventListener("mousemove", this.onMouseMove);
+    this.eventTarget.removeEventListener("mouseup", this.onMouseUp);
 
     this.toolboxContainerElement.classList.remove("tabs-reordering");
     this.dragTarget.style.left = null;
     this.dragTarget = null;
     this.toolboxContainerElement = null;
     this.toolboxTabsElement = null;
+    this.eventTarget = null;
   }
 }
 
 function sortPanelDefinitions(definitions) {
   const pref = Services.prefs.getCharPref(PREFERENCE_NAME, "");
   const toolIds = pref.split(",");
 
   return definitions.sort((a, b) => {
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -1,20 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const promise = require("promise");
+const flags = require("devtools/shared/flags");
 const ToolDefinitions = require("devtools/client/definitions").Tools;
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const promise = require("promise");
 const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
@@ -178,24 +179,30 @@ function CssComputedView(inspector, docu
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.styleDocument.addEventListener("copy", this._onCopy);
   this.styleDocument.addEventListener("mousedown", this.focusWindow);
   this.element.addEventListener("click", this._onClick);
   this.element.addEventListener("contextmenu", this._onContextMenu);
-  this.element.addEventListener("mousemove", () => {
-    this.addHighlightersToView();
-  }, { once: true });
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.includeBrowserStylesCheckbox.addEventListener("input",
     this._onIncludeBrowserStyles);
 
+  if (flags.testing) {
+    // In tests, we start listening immediately to avoid having to simulate a mousemove.
+    this.highlighters.addToView(this);
+  } else {
+    this.element.addEventListener("mousemove", () => {
+      this.highlighters.addToView(this);
+    }, { once: true });
+  }
+
   this.searchClearButton.hidden = true;
 
   // No results text.
   this.noResults = this.styleDocument.getElementById("computed-no-results");
 
   // Refresh panel when color unit changed or pref for showing
   // original sources changes.
   this._handlePrefChange = this._handlePrefChange.bind(this);
@@ -728,24 +735,16 @@ CssComputedView.prototype = {
 
       clipboardHelper.copyString(text);
     } catch (e) {
       console.error(e);
     }
   },
 
   /**
-   * Adds the highlighters overlay to the computed view. This is called by the "mousemove"
-   * event handler and in shared-head.js when opening and selecting the computed view.
-   */
-  addHighlightersToView() {
-    this.highlighters.addToView(this);
-  },
-
-  /**
    * Destructor for CssComputedView.
    */
   destroy: function() {
     this._viewedElement = null;
     this._outputParser = null;
 
     this._prefObserver.off("devtools.defaultColorUnit", this._handlePrefChange);
     this._prefObserver.destroy();
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { throttle } = require("devtools/client/inspector/shared/utils");
+const flags = require("devtools/shared/flags");
 
 const {
   clearFlexbox,
   toggleFlexItemShown,
   updateFlexbox,
   updateFlexboxColor,
   updateFlexboxHighlighted,
 } = require("./actions/flexbox");
@@ -57,20 +58,26 @@ class FlexboxInspector {
         "getCurrentFlexbox");
       this.layoutInspector = await this.walker.getLayoutInspector();
     } catch (e) {
       // These calls might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
-    this.document.addEventListener("mousemove", () => {
+    if (flags.testing) {
+      // In tests, we start listening immediately to avoid having to simulate a mousemove.
       this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
       this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
-    }, { once: true });
+    } else {
+      this.document.addEventListener("mousemove", () => {
+        this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
+        this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
+      }, { once: true });
+    }
 
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
     this.onSidebarSelect();
   }
 
   destroy() {
     if (this._highlighters) {
@@ -299,105 +306,83 @@ class FlexboxInspector {
    * with new flexbox data.
    *
    * @param  {FlexboxFront|Null} flexboxFront
    *         The FlexboxFront of the flex container for the current node selection.
    */
   async update(flexboxFront) {
     // Stop refreshing if the inspector or store is already destroyed or no node is
     // selected.
-    if (!this.inspector || !this.store || !this.inspector.selection.nodeFront) {
-      return;
-    }
-
-    // Fetch the current flexbox if no flexbox front was passed into this update.
-    if (!flexboxFront) {
-      try {
-        if (!this.hasGetCurrentFlexbox) {
-          return;
-        }
-
-        flexboxFront = await this.layoutInspector.getCurrentFlexbox(
-          this.inspector.selection.nodeFront);
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
-        return;
-      }
-    }
-
-    // Clear the flexbox panel if there is no flex container for the current node
-    // selection.
-    if (!flexboxFront) {
-      try {
-        this.store.dispatch(clearFlexbox());
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
-      }
+    if (!this.inspector ||
+        !this.store ||
+        !this.inspector.selection.nodeFront ||
+        !this.hasGetCurrentFlexbox) {
       return;
     }
 
-    let containerNodeFront = flexboxFront.containerNodeFront;
+    try {
+      // Fetch the current flexbox if no flexbox front was passed into this update.
+      if (!flexboxFront) {
+        flexboxFront = await this.layoutInspector.getCurrentFlexbox(
+          this.inspector.selection.nodeFront);
+      }
 
-    // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
-    // then get it from the walker. This happens when the walker hasn't seen this
-    // particular DOM Node in the tree yet or when we are connected to an older server.
-    if (!containerNodeFront) {
-      try {
-        containerNodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID,
-          ["containerEl"]);
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
+      // Clear the flexbox panel if there is no flex container for the current node
+      // selection.
+      if (!flexboxFront) {
+        this.store.dispatch(clearFlexbox());
         return;
       }
-    }
-
-    const highlighted = this._highlighters &&
-      containerNodeFront == this.highlighters.flexboxHighlighterShown;
 
-    // Fetch the flex items for the given flex container and the flex item NodeFronts.
-    const flexItems = [];
-    const flexItemFronts = await flexboxFront.getFlexItems();
+      // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
+      // then get it from the walker. This happens when the walker hasn't seen this
+      // particular DOM Node in the tree yet or when we are connected to an older server.
+      let containerNodeFront = flexboxFront.containerNodeFront;
+      if (!containerNodeFront) {
+        containerNodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID,
+          ["containerEl"]);
+      }
 
-    for (const flexItemFront of flexItemFronts) {
-      let itemNodeFront = flexItemFront.nodeFront;
+      // Fetch the flex items for the given flex container and the flex item NodeFronts.
+      const flexItems = [];
+      const flexItemFronts = await flexboxFront.getFlexItems();
 
-      if (!itemNodeFront) {
-        try {
+      for (const flexItemFront of flexItemFronts) {
+        let itemNodeFront = flexItemFront.nodeFront;
+        if (!itemNodeFront) {
           itemNodeFront = await this.walker.getNodeFromActor(flexItemFront.actorID,
             ["element"]);
-        } catch (e) {
-          // This call might fail if called asynchrously after the toolbox is finished
-          // closing.
-          return;
         }
+
+        flexItems.push({
+          actorID: flexItemFront.actorID,
+          shown: false,
+          flexItemSizing: flexItemFront.flexItemSizing,
+          nodeFront: itemNodeFront,
+          properties: flexItemFront.properties,
+        });
       }
 
-      flexItems.push({
-        actorID: flexItemFront.actorID,
-        shown: false,
-        flexItemSizing: flexItemFront.flexItemSizing,
-        nodeFront: itemNodeFront,
-        properties: flexItemFront.properties,
-      });
-    }
+      const highlighted = this._highlighters &&
+        containerNodeFront == this.highlighters.flexboxHighlighterShown;
+      const currentUrl = this.inspector.target.url;
+      // Get the hostname, if there is no hostname, fall back on protocol
+      // ex: `data:` uri, and `about:` pages
+      const hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
+      const customColors = await this.getCustomFlexboxColors();
+      const color = customColors[hostname] ? customColors[hostname] : FLEXBOX_COLOR;
 
-    const currentUrl = this.inspector.target.url;
-    // Get the hostname, if there is no hostname, fall back on protocol
-    // ex: `data:` uri, and `about:` pages
-    const hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
-    const customColors = await this.getCustomFlexboxColors();
-    const color = customColors[hostname] ? customColors[hostname] : FLEXBOX_COLOR;
-
-    this.store.dispatch(updateFlexbox({
-      actorID: flexboxFront.actorID,
-      color,
-      flexItems,
-      highlighted,
-      nodeFront: containerNodeFront,
-      properties: flexboxFront.properties,
-    }));
+      this.store.dispatch(updateFlexbox({
+        actorID: flexboxFront.actorID,
+        color,
+        flexItems,
+        highlighted,
+        nodeFront: containerNodeFront,
+        properties: flexboxFront.properties,
+      }));
+    } catch (e) {
+      // This call might fail if called asynchrously after the toolbox is finished
+      // closing.
+    }
   }
 }
 
 module.exports = FlexboxInspector;
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { throttle } = require("devtools/client/inspector/shared/utils");
+const flags = require("devtools/shared/flags");
 
 const {
   updateGridColor,
   updateGridHighlighted,
   updateGrids,
 } = require("./actions/grids");
 const {
   updateShowGridAreas,
@@ -92,20 +93,26 @@ class GridInspector {
     try {
       this.layoutInspector = await this.inspector.walker.getLayoutInspector();
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
-    this.document.addEventListener("mousemove", () => {
+    if (flags.testing) {
+      // In tests, we start listening immediately to avoid having to simulate a mousemove.
       this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
       this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
-    }, { once: true });
+    } else {
+      this.document.addEventListener("mousemove", () => {
+        this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
+        this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
+      }, { once: true });
+    }
 
     this.inspector.sidebar.on("select", this.onSidebarSelect);
     this.inspector.on("new-root", this.onNavigate);
 
     this.onSidebarSelect();
   }
 
   /**
--- a/devtools/client/inspector/grids/test/.eslintrc.js
+++ b/devtools/client/inspector/grids/test/.eslintrc.js
@@ -1,9 +1,6 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   "extends": "../../../../.eslintrc.mochitests.js",
-  "globals": {
-    "waitUntilState": true
-  }
 };
--- a/devtools/client/inspector/markup/test/.eslintrc.js
+++ b/devtools/client/inspector/markup/test/.eslintrc.js
@@ -1,6 +1,6 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../../.eslintrc.mochitests.js"
+  "extends": "../../../../.eslintrc.mochitests.js",
 };
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -68,16 +68,17 @@ support-files =
   lib_react_dom_16.2.0_min.js
   lib_react_with_addons_15.3.1_min.js
   lib_react_with_addons_15.4.1.js
   react_external_listeners.js
   !/devtools/client/debugger/new/test/mochitest/helpers.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
+  !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_markup_accessibility_focus_blur.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
@@ -125,16 +126,18 @@ skip-if = true # Bug 1177550
 [browser_markup_events_react_development_15.4.1.js]
 [browser_markup_events_react_development_15.4.1_jsx.js]
 [browser_markup_events_react_production_15.3.1.js]
 [browser_markup_events_react_production_15.3.1_jsx.js]
 [browser_markup_events_react_production_16.2.0.js]
 [browser_markup_events_react_production_16.2.0_jsx.js]
 [browser_markup_events_source_map.js]
 [browser_markup_events-windowed-host.js]
+[browser_markup_flex_display_badge.js]
+[browser_markup_grid_display_badge.js]
 [browser_markup_links_01.js]
 [browser_markup_links_02.js]
 [browser_markup_links_03.js]
 [browser_markup_links_04.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_markup_links_05.js]
 [browser_markup_links_06.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the flex display badge toggles on the flexbox highlighter.
+
+const TEST_URI = `
+  <style type="text/css">
+    #flex {
+      display: flex;
+    }
+  </style>
+  <div id="flex"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "FlexboxHighlighter";
+
+add_task(async function() {
+  await pushPref("devtools.inspector.flexboxHighlighter.enabled", true);
+  await pushPref("devtools.flexboxinspector.enabled", true);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters, store } = inspector;
+
+  info("Check the flex display badge is shown and not active.");
+  await selectNode("#flex", inspector);
+  const flexContainer = await getContainerForSelector("#flex", inspector);
+  const flexDisplayBadge = flexContainer.elt.querySelector(".markup-badge[data-display]");
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+
+  info("Check the initial state of the flex highlighter.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No flexbox highlighter exists in the highlighters overlay.");
+  ok(!highlighters.flexboxHighlighterShown, "No flexbox highlighter is shown.");
+
+  info("Toggling ON the flexbox highlighter from the flex display badge.");
+  const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state => state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check the flexbox highlighter is created and flex display badge state.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "Flexbox highlighter is created in the highlighters overlay.");
+  ok(highlighters.flexboxHighlighterShown, "Flexbox highlighter is shown.");
+  ok(flexDisplayBadge.classList.contains("active"), "flex display badge is active.");
+
+  info("Toggling OFF the flexbox highlighter from the flex display badge.");
+  const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state => !state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid display badge toggles on the grid highlighter.
+
+const TEST_URI = `
+  <style type="text/css">
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "CssGridHighlighter";
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters, store } = inspector;
+
+  info("Check the grid display badge is shown and not active.");
+  await selectNode("#grid", inspector);
+  const gridContainer = await getContainerForSelector("#grid", inspector);
+  const gridDisplayBadge = gridContainer.elt.querySelector(".markup-badge[data-display]");
+  ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+
+  info("Check the initial state of the grid highlighter.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the grid display badge.");
+  const onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length === 1 &&
+    state.grids[0].highlighted);
+  gridDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check the CSS grid highlighter is created and grid display badge state.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+  ok(gridDisplayBadge.classList.contains("active"), "grid display badge is active.");
+
+  info("Toggling OFF the CSS grid highlighter from the grid display badge.");
+  const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    !state.grids[0].highlighted);
+  gridDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+});
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -1,21 +1,25 @@
-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../test/head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
+// Load the shared Redux helpers into this compartment.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js",
+  this);
+
 var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
 var clipboard = require("devtools/shared/platform/clipboard");
 
 // If a test times out we want to see the complete log and not just the last few
 // lines.
 SimpleTest.requestCompleteLog();
 
 // Toggle this pref on to see all DevTools event communication. This is hugely
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
+const flags = require("devtools/shared/flags");
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
 const Rule = require("devtools/client/inspector/rules/models/rule");
 const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
@@ -146,28 +147,34 @@ function CssRuleView(inspector, document
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.shortcuts.on("Return", event => this._onShortcut("Return", event));
   this.shortcuts.on("Space", event => this._onShortcut("Space", event));
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
-  this.element.addEventListener("mousemove", () => {
-    this.addHighlightersToView();
-  }, { once: true });
   this.addRuleButton.addEventListener("click", this._onAddRule);
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
   this.classToggle.addEventListener("click", this._onToggleClassPanel);
   this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
 
+  if (flags.testing) {
+    // In tests, we start listening immediately to avoid having to simulate a mousemove.
+    this.highlighters.addToView(this);
+  } else {
+    this.element.addEventListener("mousemove", () => {
+      this.highlighters.addToView(this);
+    }, { once: true });
+  }
+
   this._handlePrefChange = this._handlePrefChange.bind(this);
   this._handleUAStylePrefChange = this._handleUAStylePrefChange.bind(this);
   this._handleDefaultColorUnitPrefChange =
     this._handleDefaultColorUnitPrefChange.bind(this);
 
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_UA_STYLES, this._handleUAStylePrefChange);
   this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handleDefaultColorUnitPrefChange);
@@ -1632,24 +1639,16 @@ CssRuleView.prototype = {
                event.target === this.searchField &&
                this._onClearSearch()) {
       // Handle the search box's keypress event. If the escape key is pressed,
       // clear the search box field.
       event.preventDefault();
       event.stopPropagation();
     }
   },
-
-  /**
-   * Adds the highlighters overlay to the rule view. This is called by the "mousemove"
-   * event handler and in shared-head.js when opening and selecting the rule view.
-   */
-  addHighlightersToView() {
-    this.highlighters.addToView(this);
-  },
 };
 
 /**
  * Helper functions
  */
 
 /**
  * Walk up the DOM from a given node until a parent property holder is found.
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -86,19 +86,16 @@ var openInspectorSidebarTab = async func
 function openRuleView() {
   return openInspector().then(data => {
     const view = data.inspector.getPanel("ruleview").view;
 
     // Replace the view to use a custom debounce function that can be triggered manually
     // through an additional ".flush()" property.
     view.debounce = manualDebounce();
 
-    // Adds the highlighters overlay in the rule view.
-    view.addHighlightersToView();
-
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
       view,
     };
   });
 }
@@ -108,18 +105,16 @@ function openRuleView() {
  * sidebar tab selected.
  *
  * @return a promise that resolves when the inspector is ready and the computed
  * view is visible and ready
  */
 function openComputedView() {
   return openInspectorSidebarTab("computedview").then(data => {
     const view = data.inspector.getPanel("computedview").computedView;
-    // Adds the highlighters overlay in the computed view.
-    view.addHighlightersToView();
 
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
       view,
     };
   });
@@ -160,33 +155,29 @@ function openLayoutView() {
 /**
  * Select the rule view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssRuleView} the rule view
  */
 function selectRuleView(inspector) {
-  const view = inspector.getPanel("ruleview").view;
-  view.addHighlightersToView();
-  return view;
+  return inspector.getPanel("ruleview").view;
 }
 
 /**
  * Select the computed view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssComputedView} the computed view
  */
 function selectComputedView(inspector) {
   inspector.sidebar.select("computedview");
-  const view = inspector.getPanel("computedview").computedView;
-  view.addHighlightersToView();
-  return view;
+  return inspector.getPanel("computedview").computedView;
 }
 
 /**
  * Select the layout view sidebar tab on an already opened inspector panel.
  *
  * @param  {InspectorPanel} inspector
  * @return {BoxModel} the box model
  */
--- a/devtools/client/shared/redux/middleware/test/.eslintrc.js
+++ b/devtools/client/shared/redux/middleware/test/.eslintrc.js
@@ -3,15 +3,14 @@
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   "extends": "../../../../../.eslintrc.mochitests.js",
   "globals": {
     "run_test": true,
     "run_next_test": true,
     "equal": true,
     "do_print": true,
-    "waitUntilState": true
   },
   "rules": {
     // Stop giving errors for run_test
     "camelcase": "off"
   }
 };
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -41,18 +41,16 @@ struct DevTools : public ::testing::Test
 
   virtual void SetUp() {
     MOZ_ASSERT(!_initialized);
 
     cx = getContext();
     if (!cx)
       return;
 
-    JS_BeginRequest(cx);
-
     global.init(cx, createGlobal());
     if (!global)
       return;
     JS::EnterRealm(cx, global);
 
     compartment = js::GetContextCompartment(cx);
     zone = js::GetContextZone(cx);
 
@@ -106,18 +104,16 @@ struct DevTools : public ::testing::Test
 
   virtual void TearDown() {
     _initialized = false;
 
     if (global) {
       JS::LeaveRealm(cx, nullptr);
       global = nullptr;
     }
-    if (cx)
-      JS_EndRequest(cx);
   }
 };
 
 
 // Helper to define a test and ensure that the fixture is initialized properly.
 #define DEF_TEST(name, body)                    \
   TEST_F(DevTools, name) {                      \
     ASSERT_TRUE(_initialized);                  \
--- a/dom/base/WindowDestroyedEvent.cpp
+++ b/dom/base/WindowDestroyedEvent.cpp
@@ -1,12 +1,12 @@
 #include "WindowDestroyedEvent.h"
 
 #include "nsJSUtils.h"
-#include "jsapi.h"              // for JSAutoRequest
+#include "jsapi.h"
 #include "js/Wrapper.h"
 #include "nsIPrincipal.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIAppStartup.h"
 #include "nsToolkitCompsCID.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9565,16 +9565,31 @@ nsIDocument::CreateStaticClone(nsIDocShe
                                  "Cloning a stylesheet didn't work!");
             if (clonedSheet) {
               clonedDoc->AddStyleSheet(clonedSheet);
             }
           }
         }
       }
 
+      for (int t = 0; t < AdditionalSheetTypeCount; ++t) {
+        auto& sheets = mAdditionalSheets[additionalSheetType(t)];
+        for (StyleSheet* sheet : sheets) {
+          if (sheet->IsApplicable()) {
+            RefPtr<StyleSheet> clonedSheet =
+              sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
+            NS_WARNING_ASSERTION(clonedSheet,
+                                 "Cloning a stylesheet didn't work!");
+            if (clonedSheet) {
+              clonedDoc->AddAdditionalStyleSheet(additionalSheetType(t), clonedSheet);
+            }
+          }
+        }
+      }
+
       // Font faces created with the JS API will not be reflected in the
       // stylesheets and need to be copied over to the cloned document.
       if (const FontFaceSet* set = GetFonts()) {
         set->CopyNonRuleFacesTo(clonedDoc->Fonts());
       }
 
     }
   }
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -64,17 +64,17 @@
 #include "nsNetUtil.h"
 #include "nsVariant.h"
 #include "nsPrintfCString.h"
 #include "mozilla/intl/LocaleService.h"
 #include "WindowDestroyedEvent.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
-#include "jsapi.h"              // for JSAutoRequest
+#include "jsapi.h"
 #include "js/Wrapper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsReadableUtils.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -61,17 +61,17 @@
 #include "nsVariant.h"
 #include "nsPrintfCString.h"
 #include "mozilla/intl/LocaleService.h"
 #include "WindowDestroyedEvent.h"
 #include "nsDocShellLoadInfo.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
-#include "jsapi.h"              // for JSAutoRequest
+#include "jsapi.h"
 #include "js/Wrapper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsReadableUtils.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1145,18 +1145,16 @@ IDBObjectStore::DeserializeValue(JSConte
 
   if (!aCloneReadInfo.mData.Size()) {
     aValue.setUndefined();
     return true;
   }
 
   MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
 
-  JSAutoRequest ar(aCx);
-
   static const JSStructuredCloneCallbacks callbacks = {
     CommonStructuredCloneReadCallback,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -317,21 +317,16 @@ AutoJSAPI::~AutoJSAPI()
   }
 
   ReportException();
 
   if (mOldWarningReporter.isSome()) {
     JS::SetWarningReporter(cx(), mOldWarningReporter.value());
   }
 
-  // Leave the request before popping.
-  if (mIsMainThread) {
-    mAutoRequest.reset();
-  }
-
   ScriptSettingsStack::Pop(this);
 }
 
 void
 WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep);
 
 void
 AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
@@ -344,22 +339,16 @@ AutoJSAPI::InitInternal(nsIGlobalObject*
   MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal);
 #ifdef DEBUG
   bool haveException = JS_IsExceptionPending(aCx);
 #endif // DEBUG
 
   mCx = aCx;
   mIsMainThread = aIsMainThread;
   mGlobalObject = aGlobalObject;
-  if (aIsMainThread) {
-    // We _could_ just unconditionally emplace mAutoRequest here.  It's just not
-    // needed on worker threads, and we're hoping to kill it on the main thread
-    // too.
-    mAutoRequest.emplace(mCx);
-  }
   if (aGlobal) {
     JS::ExposeObjectToActiveJS(aGlobal);
   }
   mAutoNullableRealm.emplace(mCx, aGlobal);
 
   ScriptSettingsStack::Push(this);
 
   mOldWarningReporter.emplace(JS::GetWarningReporter(aCx));
--- a/dom/script/ScriptSettings.h
+++ b/dom/script/ScriptSettings.h
@@ -181,17 +181,16 @@ private:
  * Its current duties are as-follows (see individual Init comments for details):
  *
  * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto
  *   the JSContext stack.
  * * Entering an initial (possibly null) compartment, to ensure that the
  *   previously entered compartment for that JSContext is not used by mistake.
  * * Reporting any exceptions left on the JSRuntime, unless the caller steals
  *   or silences them.
- * * On main thread, entering a JSAutoRequest.
  *
  * Additionally, the following duties are planned, but not yet implemented:
  *
  * * De-poisoning the JSRuntime to allow manipulation of JSAPI. This requires
  *   implementing the poisoning first.  For now, this de-poisoning
  *   effectively corresponds to having a non-null cx on the stack.
  *
  * In situations where the consumer expects to run script, AutoEntryScript
@@ -290,17 +289,16 @@ public:
     JS_ClearPendingException(cx());
   }
 
 protected:
   // Protected constructor for subclasses.  This constructor initialises the
   // AutoJSAPI, so Init must NOT be called on subclasses that use this.
   AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
 
-  mozilla::Maybe<JSAutoRequest> mAutoRequest;
   mozilla::Maybe<JSAutoNullableRealm> mAutoNullableRealm;
   JSContext *mCx;
 
   // Whether we're mainthread or not; set when we're initialized.
   bool mIsMainThread;
   Maybe<JS::WarningReporter> mOldWarningReporter;
 
 private:
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2751,22 +2751,19 @@ WorkerThreadPrimaryRunnable::Run()
       WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(mWorkerPrivate);
       return NS_ERROR_FAILURE;
     }
 
     {
       PROFILER_SET_JS_CONTEXT(cx);
 
       {
-        JSAutoRequest ar(cx);
-
         mWorkerPrivate->DoRunLoop(cx);
         // The AutoJSAPI in DoRunLoop should have reported any exceptions left
-        // on cx.  Note that we still need the JSAutoRequest above because
-        // AutoJSAPI on workers does NOT enter a request!
+        // on cx.
         MOZ_ASSERT(!JS_IsExceptionPending(cx));
       }
 
       BackgroundChild::CloseForCurrentThread();
 
       PROFILER_CLEAR_JS_CONTEXT();
     }
 
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -377,17 +377,16 @@ void
 ExecutionRunnable::RunOnWorkletThread()
 {
   WorkletThread::AssertIsOnWorkletThread();
 
   WorkletThread* workletThread = WorkletThread::Get();
   MOZ_ASSERT(workletThread);
 
   JSContext* cx = workletThread->GetJSContext();
-  JSAutoRequest areq(cx);
 
   AutoJSAPI jsapi;
   jsapi.Init();
 
   RefPtr<WorkletGlobalScope> globalScope =
     Worklet::CreateGlobalScope(jsapi.cx(), mWorkletType);
   MOZ_ASSERT(globalScope);
 
--- a/dom/xbl/nsXBLProtoImpl.cpp
+++ b/dom/xbl/nsXBLProtoImpl.cpp
@@ -329,17 +329,16 @@ nsXBLProtoImpl::ResolveAllFields(JSConte
   }
 
   return true;
 }
 
 void
 nsXBLProtoImpl::UndefineFields(JSContext *cx, JS::Handle<JSObject*> obj) const
 {
-  JSAutoRequest ar(cx);
   for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
     nsDependentString name(f->GetName());
 
     const char16_t* s = name.get();
     bool hasProp;
     if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) &&
         hasProp) {
       JS::ObjectOpResult ignored;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -997,17 +997,16 @@ Proxy::HandleEvent(Event* aEvent)
   if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
     if (mXHR->ReadyState() == 1) {
       mInnerEventStreamId++;
     }
   }
 
   {
     AutoSafeJSContext cx;
-    JSAutoRequest ar(cx);
 
     JS::Rooted<JS::Value> value(cx);
     if (!GetOrCreateDOMReflectorNoWrap(cx, mXHR, &value)) {
       return NS_ERROR_FAILURE;
     }
 
     JS::Rooted<JSObject*> scope(cx, &value.toObject());
 
@@ -1441,17 +1440,16 @@ OpenRunnable::MainThreadRunInternal()
 
 void
 SendRunnable::RunOnMainThread(ErrorResult& aRv)
 {
   Nullable<DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString> payload;
 
   if (HasData()) {
     AutoSafeJSContext cx;
-    JSAutoRequest ar(cx);
 
     JS::Rooted<JSObject*> globalObject(cx, JS::CurrentGlobalOrNull(cx));
     if (NS_WARN_IF(!globalObject)) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
 
     nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject);
--- a/extensions/cookie/moz.build
+++ b/extensions/cookie/moz.build
@@ -1,16 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
+EXPORTS += [
+    'nsCookiePermission.h',
+]
+
 UNIFIED_SOURCES += [
     'nsCookieModule.cpp',
     'nsCookiePermission.cpp',
     'nsPermission.cpp',
     'nsPermissionManager.cpp',
 ]
 
 LOCAL_INCLUDES += [
--- a/extensions/cookie/nsCookieModule.cpp
+++ b/extensions/cookie/nsCookieModule.cpp
@@ -9,33 +9,38 @@
 #include "nsPermissionManager.h"
 #include "nsICategoryManager.h"
 #include "nsCookiePermission.h"
 #include "nsString.h"
 
 // Define the constructor function for the objects
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPermissionManager,
   nsPermissionManager::GetXPCOMSingleton)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookiePermission)
 
 NS_DEFINE_NAMED_CID(NS_PERMISSIONMANAGER_CID);
-NS_DEFINE_NAMED_CID(NS_COOKIEPERMISSION_CID);
 
 
 static const mozilla::Module::CIDEntry kCookieCIDs[] = {
     { &kNS_PERMISSIONMANAGER_CID, false, nullptr, nsIPermissionManagerConstructor },
-    { &kNS_COOKIEPERMISSION_CID, false, nullptr, nsCookiePermissionConstructor },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kCookieContracts[] = {
     { NS_PERMISSIONMANAGER_CONTRACTID, &kNS_PERMISSIONMANAGER_CID },
-    { NS_COOKIEPERMISSION_CONTRACTID, &kNS_COOKIEPERMISSION_CID },
     { nullptr }
 };
 
+static void CookieModuleDtor()
+{
+  nsCookiePermission::Shutdown();
+}
+
 static const mozilla::Module kCookieModule = {
     mozilla::Module::kVersion,
     kCookieCIDs,
-    kCookieContracts
+    kCookieContracts,
+    nullptr,
+    nullptr,
+    nullptr,
+    CookieModuleDtor
 };
 
 NSMODULE_DEFN(nsCookieModule) = &kCookieModule;
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -21,16 +21,17 @@
 #include "nsIDOMWindow.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "nsILoadContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsNetCID.h"
 #include "prtime.h"
+#include "mozilla/StaticPtr.h"
 
 /****************************************************************
  ************************ nsCookiePermission ********************
  ****************************************************************/
 
 // values for mCookiesLifetimePolicy
 // 0 == accept normally
 // 1 == ask before accepting, no more supported, treated like ACCEPT_NORMALLY (Bug 606655).
@@ -39,20 +40,41 @@
 static const uint32_t ACCEPT_NORMALLY = 0;
 static const uint32_t ACCEPT_SESSION = 2;
 
 static const bool kDefaultPolicy = true;
 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
 
 static const char kPermissionType[] = "cookie";
 
+namespace {
+mozilla::StaticRefPtr<nsCookiePermission> gSingleton;
+}
+
 NS_IMPL_ISUPPORTS(nsCookiePermission,
                   nsICookiePermission,
                   nsIObserver)
 
+// static
+already_AddRefed<nsICookiePermission>
+nsCookiePermission::GetOrCreate()
+{
+  if (!gSingleton) {
+    gSingleton = new nsCookiePermission();
+  }
+  return do_AddRef(gSingleton);
+}
+
+// static
+void
+nsCookiePermission::Shutdown()
+{
+  gSingleton = nullptr;
+}
+
 bool
 nsCookiePermission::Init()
 {
   // Initialize nsIPermissionManager and fetch relevant prefs. This is only
   // required for some methods on nsICookiePermission, so it should be done
   // lazily.
   nsresult rv;
   mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
--- a/extensions/cookie/nsCookiePermission.h
+++ b/extensions/cookie/nsCookiePermission.h
@@ -8,39 +8,38 @@
 #include "nsICookiePermission.h"
 #include "nsIPermissionManager.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "mozIThirdPartyUtil.h"
 
 class nsIPrefBranch;
 
-class nsCookiePermission : public nsICookiePermission
-                         , public nsIObserver
+class nsCookiePermission final : public nsICookiePermission
+                               , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICOOKIEPERMISSION
   NS_DECL_NSIOBSERVER
 
-  nsCookiePermission()
-    : mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY
-    {}
+  // Singleton accessor
+  static already_AddRefed<nsICookiePermission> GetOrCreate();
+  static void Shutdown();
 
   bool Init();
   void PrefChanged(nsIPrefBranch *, const char *);
 
 private:
+  nsCookiePermission()
+    : mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY
+    {}
   virtual ~nsCookiePermission() {}
 
   bool EnsureInitialized() { return (mPermMgr != nullptr && mThirdPartyUtil != nullptr) || Init(); };
 
   nsCOMPtr<nsIPermissionManager> mPermMgr;
   nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
 
   uint8_t      mCookiesLifetimePolicy;         // pref for how long cookies are stored
 };
 
-// {EF565D0A-AB9A-4A13-9160-0644CDFD859A}
-#define NS_COOKIEPERMISSION_CID \
- {0xEF565D0A, 0xAB9A, 0x4A13, {0x91, 0x60, 0x06, 0x44, 0xcd, 0xfd, 0x85, 0x9a }}
-
 #endif
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -270,17 +270,19 @@ CompositorBridgeChild::InitForWidget(uin
   mProcessToken = aProcessToken;
   mLayerManager = aLayerManager;
   mIdNamespace = aNamespace;
 }
 
 /*static*/ CompositorBridgeChild*
 CompositorBridgeChild::Get()
 {
-  // This is only expected to be used in child processes.
+  // This is only expected to be used in child processes. While the parent
+  // process does have CompositorBridgeChild instances, it has _multiple_ (one
+  // per window), and therefore there is no global singleton available.
   MOZ_ASSERT(!XRE_IsParentProcess());
   return sCompositorBridge;
 }
 
 // static
 bool
 CompositorBridgeChild::ChildProcessHasCompositorBridge()
 {
--- a/gfx/webrender/res/cs_border_segment.glsl
+++ b/gfx/webrender/res/cs_border_segment.glsl
@@ -97,32 +97,51 @@ vec2 get_outer_corner_scale(int segment)
             // Should never get hit
             p = vec2(0.0);
             break;
     }
 
     return p;
 }
 
-vec4 mod_color(vec4 color, float f) {
-    return vec4(clamp(color.rgb * f, vec3(0.0), vec3(color.a)), color.a);
+// NOTE(emilio): If you change this algorithm, do the same change
+// in border.rs
+vec4 mod_color(vec4 color, bool is_black, bool lighter) {
+    const float light_black = 0.7;
+    const float dark_black = 0.3;
+
+    const float dark_scale = 0.66666666;
+    const float light_scale = 1.0;
+
+    if (is_black) {
+        if (lighter) {
+            return vec4(vec3(light_black), color.a);
+        }
+        return vec4(vec3(dark_black), color.a);
+    }
+
+    if (lighter) {
+        return vec4(color.rgb * light_scale, color.a);
+    }
+    return vec4(color.rgb * dark_scale, color.a);
 }
 
 vec4[2] get_colors_for_side(vec4 color, int style) {
     vec4 result[2];
-    const vec2 f = vec2(1.3, 0.7);
+
+    bool is_black = color.rgb == vec3(0.0, 0.0, 0.0);
 
     switch (style) {
         case BORDER_STYLE_GROOVE:
-            result[0] = mod_color(color, f.x);
-            result[1] = mod_color(color, f.y);
+            result[0] = mod_color(color, is_black, true);
+            result[1] = mod_color(color, is_black, false);
             break;
         case BORDER_STYLE_RIDGE:
-            result[0] = mod_color(color, f.y);
-            result[1] = mod_color(color, f.x);
+            result[0] = mod_color(color, is_black, false);
+            result[1] = mod_color(color, is_black, true);
             break;
         default:
             result[0] = color;
             result[1] = color;
             break;
     }
 
     return result;
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -485,16 +485,19 @@ impl AlphaBatchBuilder {
             let pic = ctx.prim_store.get_pic(prim_index);
             let batch = self.batch_list
                             .get_suitable_batch(
                                 key,
                                 &pic_metadata.clipped_world_rect.as_ref().expect("bug"),
                             );
 
             let source_task_id = pic
+                .raster_config
+                .as_ref()
+                .expect("BUG: no raster config")
                 .surface
                 .as_ref()
                 .expect("BUG: unexpected surface in splitting")
                 .resolve_render_task_id();
             let source_task_address = render_tasks.get_task_address(source_task_id);
             let gpu_address = gpu_cache.get_address(&gpu_handle);
 
             let instance = SplitCompositeInstance::new(
@@ -631,17 +634,17 @@ impl AlphaBatchBuilder {
             PrimitiveDetails::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Picture(ref picture) => {
                         // If this picture is participating in a 3D rendering context,
                         // then don't add it to any batches here. Instead, create a polygon
                         // for it and add it to the current plane splitter.
                         if picture.is_in_3d_context {
                             // Push into parent plane splitter.
-                            debug_assert!(picture.surface.is_some());
+                            debug_assert!(picture.raster_config.is_some());
                             let transform = &ctx.transforms
                                 .get_transform_by_id(transform_id);
 
                             match transform.transform_kind {
                                 TransformedRectKind::AxisAligned => {
                                     let polygon = Polygon::from_transformed_rect(
                                         prim_metadata.local_rect.cast(),
                                         transform.m.cast(),
@@ -661,23 +664,26 @@ impl AlphaBatchBuilder {
                                         splitter.add(poly);
                                     }
                                 }
                             }
 
                             return;
                         }
 
-                        let add_to_parent_pic = match picture.composite_mode {
-                            Some(PictureCompositeMode::Filter(filter)) => {
-                                assert!(filter.is_visible());
-                                match filter {
-                                    FilterOp::Blur(..) => {
-                                        match picture.surface {
-                                            Some(ref surface) => {
+                        match picture.raster_config {
+                            Some(ref raster_config) => {
+                                let surface = raster_config.surface
+                                                           .as_ref()
+                                                           .expect("bug: surface must be allocated by now");
+                                match raster_config.composite_mode {
+                                    PictureCompositeMode::Filter(filter) => {
+                                        assert!(filter.is_visible());
+                                        match filter {
+                                            FilterOp::Blur(..) => {
                                                 let kind = BatchKind::Brush(
                                                     BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
                                                 );
                                                 let (uv_rect_address, textures) = surface
                                                     .resolve(
                                                         render_tasks,
                                                         ctx.resource_cache,
                                                         gpu_cache,
@@ -698,118 +704,106 @@ impl AlphaBatchBuilder {
                                                 let instance = BrushInstance {
                                                     prim_header_index,
                                                     segment_index: 0,
                                                     edge_flags: EdgeAaSegmentMask::empty(),
                                                     brush_flags: BrushFlags::empty(),
                                                     clip_task_address,
                                                 };
                                                 batch.push(PrimitiveInstance::from(instance));
-                                                false
                                             }
-                                            None => {
-                                                true
-                                            }
-                                        }
-                                    }
-                                    FilterOp::DropShadow(offset, ..) => {
-                                        // Draw an instance of the shadow first, following by the content.
+                                            FilterOp::DropShadow(offset, ..) => {
+                                                // Draw an instance of the shadow first, following by the content.
 
-                                        // Both the shadow and the content get drawn as a brush image.
-                                        if let Some(ref surface) = picture.surface {
-                                            let kind = BatchKind::Brush(
-                                                BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
-                                            );
+                                                // Both the shadow and the content get drawn as a brush image.
+                                                let kind = BatchKind::Brush(
+                                                    BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
+                                                );
 
-                                            // Gets the saved render task ID of the content, which is
-                                            // deeper in the render task tree than the direct child.
-                                            let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
-                                            let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
-                                            debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
+                                                // Gets the saved render task ID of the content, which is
+                                                // deeper in the render task tree than the direct child.
+                                                let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
+                                                let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
+                                                debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
 
-                                            // Build BatchTextures for shadow/content
-                                            let shadow_textures = BatchTextures::render_target_cache();
-                                            let content_textures = BatchTextures {
-                                                colors: [
-                                                    SourceTexture::RenderTaskCache(saved_index),
-                                                    SourceTexture::Invalid,
-                                                    SourceTexture::Invalid,
-                                                ],
-                                            };
+                                                // Build BatchTextures for shadow/content
+                                                let shadow_textures = BatchTextures::render_target_cache();
+                                                let content_textures = BatchTextures {
+                                                    colors: [
+                                                        SourceTexture::RenderTaskCache(saved_index),
+                                                        SourceTexture::Invalid,
+                                                        SourceTexture::Invalid,
+                                                    ],
+                                                };
 
-                                            // Build batch keys for shadow/content
-                                            let shadow_key = BatchKey::new(kind, non_segmented_blend_mode, shadow_textures);
-                                            let content_key = BatchKey::new(kind, non_segmented_blend_mode, content_textures);
+                                                // Build batch keys for shadow/content
+                                                let shadow_key = BatchKey::new(kind, non_segmented_blend_mode, shadow_textures);
+                                                let content_key = BatchKey::new(kind, non_segmented_blend_mode, content_textures);
 
-                                            // Retrieve the UV rect addresses for shadow/content.
-                                            let cache_task_id = surface.resolve_render_task_id();
-                                            let shadow_uv_rect_address = render_tasks[cache_task_id]
-                                                .get_texture_address(gpu_cache)
-                                                .as_int();
-                                            let content_uv_rect_address = render_tasks[secondary_id]
-                                                .get_texture_address(gpu_cache)
-                                                .as_int();
+                                                // Retrieve the UV rect addresses for shadow/content.
+                                                let cache_task_id = surface.resolve_render_task_id();
+                                                let shadow_uv_rect_address = render_tasks[cache_task_id]
+                                                    .get_texture_address(gpu_cache)
+                                                    .as_int();
+                                                let content_uv_rect_address = render_tasks[secondary_id]
+                                                    .get_texture_address(gpu_cache)
+                                                    .as_int();
 
-                                            // Get the GPU cache address of the extra data handle.
-                                            let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
+                                                // Get the GPU cache address of the extra data handle.
+                                                let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
 
-                                            let content_prim_header_index = prim_headers.push(&prim_header, [
-                                                content_uv_rect_address,
-                                                (ShaderColorMode::Image as i32) << 16 |
-                                                RasterizationSpace::Screen as i32,
-                                                0,
-                                            ]);
+                                                let content_prim_header_index = prim_headers.push(&prim_header, [
+                                                    content_uv_rect_address,
+                                                    (ShaderColorMode::Image as i32) << 16 |
+                                                    RasterizationSpace::Screen as i32,
+                                                    0,
+                                                ]);
 
-                                            let shadow_rect = prim_metadata.local_rect.translate(&offset);
-                                            let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
+                                                let shadow_rect = prim_metadata.local_rect.translate(&offset);
+                                                let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
 
-                                            let shadow_prim_header = PrimitiveHeader {
-                                                local_rect: shadow_rect,
-                                                local_clip_rect: shadow_clip_rect,
-                                                specific_prim_address: shadow_prim_address,
-                                                ..prim_header
-                                            };
+                                                let shadow_prim_header = PrimitiveHeader {
+                                                    local_rect: shadow_rect,
+                                                    local_clip_rect: shadow_clip_rect,
+                                                    specific_prim_address: shadow_prim_address,
+                                                    ..prim_header
+                                                };
 
-                                            let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, [
-                                                shadow_uv_rect_address,
-                                                (ShaderColorMode::Alpha as i32) << 16 |
-                                                RasterizationSpace::Screen as i32,
-                                                0,
-                                            ]);
+                                                let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, [
+                                                    shadow_uv_rect_address,
+                                                    (ShaderColorMode::Alpha as i32) << 16 |
+                                                    RasterizationSpace::Screen as i32,
+                                                    0,
+                                                ]);
 
-                                            let shadow_instance = BrushInstance {
-                                                prim_header_index: shadow_prim_header_index,
-                                                clip_task_address,
-                                                segment_index: 0,
-                                                edge_flags: EdgeAaSegmentMask::empty(),
-                                                brush_flags: BrushFlags::empty(),
-                                            };
+                                                let shadow_instance = BrushInstance {
+                                                    prim_header_index: shadow_prim_header_index,
+                                                    clip_task_address,
+                                                    segment_index: 0,
+                                                    edge_flags: EdgeAaSegmentMask::empty(),
+                                                    brush_flags: BrushFlags::empty(),
+                                                };
 
-                                            let content_instance = BrushInstance {
-                                                prim_header_index: content_prim_header_index,
-                                                clip_task_address,
-                                                segment_index: 0,
-                                                edge_flags: EdgeAaSegmentMask::empty(),
-                                                brush_flags: BrushFlags::empty(),
-                                            };
+                                                let content_instance = BrushInstance {
+                                                    prim_header_index: content_prim_header_index,
+                                                    clip_task_address,
+                                                    segment_index: 0,
+                                                    edge_flags: EdgeAaSegmentMask::empty(),
+                                                    brush_flags: BrushFlags::empty(),
+                                                };
 
-                                            self.batch_list
-                                                .get_suitable_batch(shadow_key, bounding_rect)
-                                                .push(PrimitiveInstance::from(shadow_instance));
+                                                self.batch_list
+                                                    .get_suitable_batch(shadow_key, bounding_rect)
+                                                    .push(PrimitiveInstance::from(shadow_instance));
 
-                                            self.batch_list
-                                                .get_suitable_batch(content_key, bounding_rect)
-                                                .push(PrimitiveInstance::from(content_instance));
-                                        }
-
-                                        false
-                                    }
-                                    _ => {
-                                        match picture.surface {
-                                            Some(ref surface) => {
+                                                self.batch_list
+                                                    .get_suitable_batch(content_key, bounding_rect)
+                                                    .push(PrimitiveInstance::from(content_instance));
+                                            }
+                                            _ => {
                                                 let key = BatchKey::new(
                                                     BatchKind::Brush(BrushBatchKind::Blend),
                                                     BlendMode::PremultipliedAlpha,
                                                     BatchTextures::render_target_cache(),
                                                 );
 
                                                 let filter_mode = match filter {
                                                     FilterOp::Identity => 1, // matches `Contrast(1)`
@@ -863,120 +857,102 @@ impl AlphaBatchBuilder {
                                                     clip_task_address,
                                                     segment_index: 0,
                                                     edge_flags: EdgeAaSegmentMask::empty(),
                                                     brush_flags: BrushFlags::empty(),
                                                 };
 
                                                 let batch = self.batch_list.get_suitable_batch(key, bounding_rect);
                                                 batch.push(PrimitiveInstance::from(instance));
-                                                false
-                                            }
-                                            None => {
-                                                true
                                             }
                                         }
                                     }
+                                    PictureCompositeMode::MixBlend(mode) => {
+                                        let cache_task_id = surface.resolve_render_task_id();
+                                        let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
+
+                                        let key = BatchKey::new(
+                                            BatchKind::Brush(
+                                                BrushBatchKind::MixBlend {
+                                                    task_id,
+                                                    source_id: cache_task_id,
+                                                    backdrop_id,
+                                                },
+                                            ),
+                                            BlendMode::PremultipliedAlpha,
+                                            BatchTextures::no_texture(),
+                                        );
+                                        let batch = self.batch_list.get_suitable_batch(key, bounding_rect);
+                                        let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
+                                        let source_task_address = render_tasks.get_task_address(cache_task_id);
+                                        let prim_header_index = prim_headers.push(&prim_header, [
+                                            mode as u32 as i32,
+                                            backdrop_task_address.0 as i32,
+                                            source_task_address.0 as i32,
+                                        ]);
+
+                                        let instance = BrushInstance {
+                                            prim_header_index,
+                                            clip_task_address,
+                                            segment_index: 0,
+                                            edge_flags: EdgeAaSegmentMask::empty(),
+                                            brush_flags: BrushFlags::empty(),
+                                        };
+
+                                        batch.push(PrimitiveInstance::from(instance));
+                                    }
+                                    PictureCompositeMode::Blit => {
+                                        let cache_task_id = surface.resolve_render_task_id();
+                                        let kind = BatchKind::Brush(
+                                            BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
+                                        );
+                                        let key = BatchKey::new(
+                                            kind,
+                                            non_segmented_blend_mode,
+                                            BatchTextures::render_target_cache(),
+                                        );
+                                        let batch = self.batch_list.get_suitable_batch(
+                                            key,
+                                            bounding_rect,
+                                        );
+
+                                        let uv_rect_address = render_tasks[cache_task_id]
+                                            .get_texture_address(gpu_cache)
+                                            .as_int();
+                                        let prim_header_index = prim_headers.push(&prim_header, [
+                                            uv_rect_address,
+                                            (ShaderColorMode::Image as i32) << 16 |
+                                            RasterizationSpace::Screen as i32,
+                                            0,
+                                        ]);
+
+                                        let instance = BrushInstance {
+                                            prim_header_index,
+                                            clip_task_address,
+                                            segment_index: 0,
+                                            edge_flags: EdgeAaSegmentMask::empty(),
+                                            brush_flags: BrushFlags::empty(),
+                                        };
+                                        batch.push(PrimitiveInstance::from(instance));
+                                    }
                                 }
                             }
-                            Some(PictureCompositeMode::MixBlend(mode)) => {
-                                let cache_task_id = picture
-                                    .surface
-                                    .as_ref()
-                                    .expect("bug: no surface allocated")
-                                    .resolve_render_task_id();
-                                let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
-
-                                let key = BatchKey::new(
-                                    BatchKind::Brush(
-                                        BrushBatchKind::MixBlend {
-                                            task_id,
-                                            source_id: cache_task_id,
-                                            backdrop_id,
-                                        },
-                                    ),
-                                    BlendMode::PremultipliedAlpha,
-                                    BatchTextures::no_texture(),
-                                );
-                                let batch = self.batch_list.get_suitable_batch(key, bounding_rect);
-                                let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
-                                let source_task_address = render_tasks.get_task_address(cache_task_id);
-                                let prim_header_index = prim_headers.push(&prim_header, [
-                                    mode as u32 as i32,
-                                    backdrop_task_address.0 as i32,
-                                    source_task_address.0 as i32,
-                                ]);
-
-                                let instance = BrushInstance {
-                                    prim_header_index,
-                                    clip_task_address,
-                                    segment_index: 0,
-                                    edge_flags: EdgeAaSegmentMask::empty(),
-                                    brush_flags: BrushFlags::empty(),
-                                };
-
-                                batch.push(PrimitiveInstance::from(instance));
-                                false
-                            }
-                            Some(PictureCompositeMode::Blit) => {
-                                let cache_task_id = picture
-                                    .surface
-                                    .as_ref()
-                                    .expect("bug: no surface allocated")
-                                    .resolve_render_task_id();
-                                let kind = BatchKind::Brush(
-                                    BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
+                            None => {
+                                // If this picture is being drawn into an existing target (i.e. with
+                                // no composition operation), recurse and add to the current batch list.
+                                self.add_pic_to_batch(
+                                    picture,
+                                    task_id,
+                                    ctx,
+                                    gpu_cache,
+                                    render_tasks,
+                                    deferred_resolves,
+                                    prim_headers,
                                 );
-                                let key = BatchKey::new(
-                                    kind,
-                                    non_segmented_blend_mode,
-                                    BatchTextures::render_target_cache(),
-                                );
-                                let batch = self.batch_list.get_suitable_batch(
-                                    key,
-                                    bounding_rect,
-                                );
-
-                                let uv_rect_address = render_tasks[cache_task_id]
-                                    .get_texture_address(gpu_cache)
-                                    .as_int();
-                                let prim_header_index = prim_headers.push(&prim_header, [
-                                    uv_rect_address,
-                                    (ShaderColorMode::Image as i32) << 16 |
-                                    RasterizationSpace::Screen as i32,
-                                    0,
-                                ]);
-
-                                let instance = BrushInstance {
-                                    prim_header_index,
-                                    clip_task_address,
-                                    segment_index: 0,
-                                    edge_flags: EdgeAaSegmentMask::empty(),
-                                    brush_flags: BrushFlags::empty(),
-                                };
-                                batch.push(PrimitiveInstance::from(instance));
-                                false
                             }
-                            None => {
-                                true
-                            }
-                        };
-
-                        // If this picture is being drawn into an existing target (i.e. with
-                        // no composition operation), recurse and add to the current batch list.
-                        if add_to_parent_pic {
-                            self.add_pic_to_batch(
-                                picture,
-                                task_id,
-                                ctx,
-                                gpu_cache,
-                                render_tasks,
-                                deferred_resolves,
-                                prim_headers,
-                            );
                         }
                     }
                     BrushKind::Image { request, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
                         for tile in visible_tiles {
                             if let Some((batch_kind, textures, user_data)) = get_image_tile_params(
                                     ctx.resource_cache,
                                     gpu_cache,
                                     deferred_resolves,
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -196,50 +196,44 @@ impl<'a> DisplayListFlattener<'a> {
             info,
             Vec::new(),
             PrimitiveContainer::Brush(prim),
         );
     }
 }
 
 pub trait BorderSideHelpers {
-    fn border_color(
-        &self,
-        scale_factor_0: f32,
-        scale_factor_1: f32,
-        black_color_0: f32,
-        black_color_1: f32,
-    ) -> ColorF;
+    fn border_color(&self, is_inner_border: bool) -> ColorF;
 }
 
 impl BorderSideHelpers for BorderSide {
-    fn border_color(
-        &self,
-        scale_factor_0: f32,
-        scale_factor_1: f32,
-        black_color_0: f32,
-        black_color_1: f32,
-    ) -> ColorF {
-        match self.style {
-            BorderStyle::Inset => {
-                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
-                    self.color.scale_rgb(scale_factor_1)
-                } else {
-                    ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a)
-                }
-            }
-            BorderStyle::Outset => {
-                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
-                    self.color.scale_rgb(scale_factor_0)
-                } else {
-                    ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a)
-                }
-            }
-            _ => self.color,
+    fn border_color(&self, is_inner_border: bool) -> ColorF {
+        let lighter = match self.style {
+            BorderStyle::Inset => is_inner_border,
+            BorderStyle::Outset => !is_inner_border,
+            _ => return self.color,
+        };
+
+        // The modulate colors below are not part of the specification. They are
+        // derived from the Gecko source code and experimentation, and used to
+        // modulate the colors in order to generate colors for the inset/outset
+        // and groove/ridge border styles.
+        //
+        // NOTE(emilio): Gecko at least takes the background color into
+        // account, should we do the same? Looks a bit annoying for this.
+        //
+        // NOTE(emilio): If you change this algorithm, do the same change on
+        // get_colors_for_side in cs_border_segment.glsl.
+        if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
+            let scale = if lighter { 1.0 } else { 2.0 / 3.0 };
+            return self.color.scale_rgb(scale)
         }
+
+        let black = if lighter { 0.7 } else { 0.3 };
+        ColorF::new(black, black, black, self.color.a)
     }
 }
 
 /// The kind of border corner clip.
 #[repr(C)]
 #[derive(Copy, Debug, Clone, PartialEq)]
 pub enum BorderCornerClipKind {
     Dash = 1,
@@ -907,31 +901,18 @@ impl BorderRenderTaskInfo {
                 side0.style
             };
             let style1 = if side1.style.is_hidden() {
                 side0.style
             } else {
                 side1.style
             };
 
-            // These modulate colors are not part of the specification. They
-            // are derived from the Gecko source code and experimentation, and
-            // used to modulate the colors in order to generate colors for
-            // the inset/outset and groove/ridge border styles.
-            let color0 = if flip0 {
-                side0.border_color(2.0 / 3.0, 1.0, 0.7, 0.3)
-            } else {
-                side0.border_color(1.0, 2.0 / 3.0, 0.3, 0.7)
-            };
-
-            let color1 = if flip1 {
-                side1.border_color(2.0 / 3.0, 1.0, 0.7, 0.3)
-            } else {
-                side1.border_color(1.0, 2.0 / 3.0, 0.3, 0.7)
-            };
+            let color0 = side0.border_color(flip0);
+            let color1 = side1.border_color(flip1);
 
             add_segment(
                 info.task_rect,
                 style0,
                 style1,
                 color0,
                 color1,
                 info.segment,
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -420,16 +420,17 @@ impl ClipStore {
         local_prim_clip_rect: LayoutRect,
         spatial_node_index: SpatialNodeIndex,
         prim_to_pic_mapper: &SpaceMapper<LayoutPixel, PicturePixel>,
         pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
         clip_scroll_tree: &ClipScrollTree,
         gpu_cache: &mut GpuCache,
         resource_cache: &mut ResourceCache,
         device_pixel_scale: DevicePixelScale,
+        world_rect: &WorldRect,
     ) -> Option<ClipChainInstance> {
         let mut local_clip_rect = local_prim_clip_rect;
         let spatial_nodes = &clip_scroll_tree.spatial_nodes;
 
         // Walk the clip chain to build local rects, and collect the
         // smallest possible local/device clip area.
 
         self.clip_node_info.clear();
@@ -467,27 +468,21 @@ impl ClipStore {
                 };
 
                 // If we can convert spaces, try to reduce the size of the region
                 // requested, and cache the conversion information for the next step.
                 if let Some(conversion) = conversion {
                     if let Some(clip_rect) = clip_node.item.get_local_clip_rect() {
                         match conversion {
                             ClipSpaceConversion::Local => {
-                                local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
-                                    Some(local_clip_rect) => local_clip_rect,
-                                    None => return None,
-                                };
+                                local_clip_rect = local_clip_rect.intersection(&clip_rect)?;
                             }
                             ClipSpaceConversion::Offset(ref offset) => {
                                 let clip_rect = clip_rect.translate(offset);
-                                local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
-                                    Some(local_clip_rect) => local_clip_rect,
-                                    None => return None,
-                                };
+                                local_clip_rect = local_clip_rect.intersection(&clip_rect)?;
                             }
                             ClipSpaceConversion::Transform(..) => {
                                 // TODO(gw): In the future, we can reduce the size
                                 //           of the pic_clip_rect here. To do this,
                                 //           we can use project_rect or the
                                 //           inverse_rect_footprint method, depending
                                 //           on the relationship of the clip, pic
                                 //           and primitive spatial nodes.
@@ -503,30 +498,21 @@ impl ClipStore {
                         has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(),
                     })
                 }
             }
 
             current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
         }
 
-        let local_bounding_rect = match local_prim_rect.intersection(&local_clip_rect) {
-            Some(rect) => rect,
-            None => return None,
-        };
+        let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?;
 
-        let pic_clip_rect = match prim_to_pic_mapper.map(&local_bounding_rect) {
-            Some(pic_bounding_rect) => pic_bounding_rect,
-            None => return None,
-        };
+        let pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?;
 
-        let world_clip_rect = match pic_to_world_mapper.map(&pic_clip_rect) {
-            Some(world_clip_rect) => world_clip_rect,
-            None => return None,
-        };
+        let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?;
 
         // Now, we've collected all the clip nodes that *potentially* affect this
         // primitive region, and reduced the size of the prim region as much as possible.
 
         // Run through the clip nodes, and see which ones affect this prim region.
 
         let first_clip_node_index = self.clip_node_indices.len() as u32;
         let mut has_non_root_coord_system = false;
@@ -545,16 +531,17 @@ impl ClipStore {
                     has_non_local_clips = true;
                     node.item.get_clip_result(&local_bounding_rect.translate(&-offset))
                 }
                 ClipSpaceConversion::Transform(ref transform) => {
                     has_non_local_clips = true;
                     node.item.get_clip_result_complex(
                         transform,
                         &world_clip_rect,
+                        world_rect,
                     )
                 }
             };
 
             match clip_result {
                 ClipResult::Accept => {
                     // Doesn't affect the primitive at all, so skip adding to list
                 }
@@ -864,16 +851,17 @@ impl ClipItem {
             ClipItem::LineDecoration(..) => None,
         }
     }
 
     fn get_clip_result_complex(
         &self,
         transform: &LayoutToWorldTransform,
         prim_world_rect: &WorldRect,
+        world_rect: &WorldRect,
     ) -> ClipResult {
         let (clip_rect, inner_rect) = match *self {
             ClipItem::Rectangle(clip_rect, ClipMode::Clip) => {
                 (clip_rect, Some(clip_rect))
             }
             ClipItem::RoundedRectangle(ref clip_rect, ref radius, ClipMode::Clip) => {
                 let inner_clip_rect = extract_inner_rect_safe(clip_rect, radius);
                 (*clip_rect, inner_clip_rect)
@@ -892,17 +880,21 @@ impl ClipItem {
         });
 
         if let Some(inner_clip_rect) = inner_clip_rect {
             if inner_clip_rect.contains_rect(prim_world_rect) {
                 return ClipResult::Accept;
             }
         }
 
-        let outer_clip_rect = match project_rect(transform, &clip_rect) {
+        let outer_clip_rect = match project_rect(
+            transform,
+            &clip_rect,
+            world_rect,
+        ) {
             Some(outer_clip_rect) => outer_clip_rect,
             None => return ClipResult::Partial,
         };
 
         match outer_clip_rect.intersection(prim_world_rect) {
             Some(..) => {
                 ClipResult::Partial
             }
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -958,18 +958,18 @@ impl<'a> DisplayListFlattener<'a> {
             // TODO(gw): The way we detect not being the primary framebuffer (len > 2)
             //           is hacky and depends on how we create a root stacking context
             //           during flattening.
             let parent_prim_index = *self.picture_stack.last().unwrap();
             let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
 
             // If not already isolated for some other reason,
             // make this picture as isolated.
-            if parent_pic.composite_mode.is_none() {
-                parent_pic.composite_mode = Some(PictureCompositeMode::Blit);
+            if parent_pic.requested_composite_mode.is_none() {
+                parent_pic.requested_composite_mode = Some(PictureCompositeMode::Blit);
             }
         }
 
         // Get the transform-style of the parent stacking context,
         // which determines if we *might* need to draw this on
         // an intermediate surface for plane splitting purposes.
         let parent_transform_style = match self.sc_stack.last() {
             Some(sc) => sc.transform_style,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -7,29 +7,29 @@ use api::{DeviceUintPoint, DeviceUintRec
 use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint, WorldRect, WorldPixel};
 use clip::{ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap};
-use picture::PictureSurface;
+use picture::{PictureCompositeMode, PictureSurface, RasterConfig};
 use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore, Transform, SpaceMapper};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_backend::FrameId;
 use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::{ResourceCache};
 use scene::{ScenePipeline, SceneProperties};
 use spatial_node::SpatialNode;
 use std::f32;
 use std::sync::Arc;
 use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
 use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
-use util;
+use util::{self, MaxRect};
 
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ChasePrimitive {
     Nothing,
     LocalRect(LayoutRect),
@@ -103,24 +103,34 @@ pub struct PictureState {
     pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
     pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
 }
 
 impl PictureState {
     pub fn new(
         ref_spatial_node_index: SpatialNodeIndex,
         clip_scroll_tree: &ClipScrollTree,
+        world_rect: WorldRect,
     ) -> Self {
-        let map_local_to_pic = SpaceMapper::new(ref_spatial_node_index);
-
-        let mut map_pic_to_world = SpaceMapper::new(SpatialNodeIndex(0));
+        let mut map_pic_to_world = SpaceMapper::new(
+            SpatialNodeIndex(0),
+            world_rect,
+        );
         map_pic_to_world.set_target_spatial_node(
             ref_spatial_node_index,
             clip_scroll_tree,
         );
+        let pic_bounds = map_pic_to_world.unmap(
+            &world_rect,
+        ).unwrap_or(PictureRect::max_rect());
+
+        let map_local_to_pic = SpaceMapper::new(
+            ref_spatial_node_index,
+            pic_bounds,
+        );
 
         PictureState {
             tasks: Vec::new(),
             has_non_root_coord_system: false,
             local_rect_changed: false,
             map_local_to_pic,
             map_pic_to_world,
         }
@@ -241,16 +251,17 @@ impl FrameBuilder {
             resource_cache,
             gpu_cache,
             special_render_passes,
         };
 
         let mut pic_state = PictureState::new(
             root_spatial_node_index,
             &frame_context.clip_scroll_tree,
+            frame_context.world_rect,
         );
 
         let pic_context = self
             .prim_store
             .get_pic_mut(root_prim_index)
             .take_context(
                 true,
                 scene_properties,
@@ -285,17 +296,20 @@ impl FrameBuilder {
             self.screen_rect.size.to_f32(),
             root_prim_index,
             DeviceIntPoint::zero(),
             pic_state.tasks,
             UvRectKind::Rect,
         );
 
         let render_task_id = frame_state.render_tasks.add(root_render_task);
-        pic.surface = Some(PictureSurface::RenderTask(render_task_id));
+        pic.raster_config = Some(RasterConfig {
+            composite_mode: PictureCompositeMode::Blit,
+            surface: Some(PictureSurface::RenderTask(render_task_id)),
+        });
         Some(render_task_id)
     }
 
     fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
         static SCROLLBAR_PADDING: f32 = 8.0;
 
         for scrollbar_prim in &self.scrollbar_prims {
             let metadata = &mut self.prim_store.primitives[scrollbar_prim.prim_index.0].metadata;
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -24,16 +24,25 @@ use util::{TransformedRectKind, world_re
 
  * A number of primitives that are drawn onto the picture.
  * A composite operation describing how to composite this
    picture into its parent.
  * A configuration describing how to draw the primitives on
    this picture (e.g. in screen space or local space).
  */
 
+#[derive(Debug)]
+pub struct RasterConfig {
+    pub composite_mode: PictureCompositeMode,
+
+    // If this picture is drawn to an intermediate surface,
+    // the associated target information.
+    pub surface: Option<PictureSurface>,
+}
+
 /// Specifies how this Picture should be composited
 /// onto the target it belongs to.
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum PictureCompositeMode {
     /// Apply CSS mix-blend-mode effect.
     MixBlend(MixBlendMode),
     /// Apply a CSS filter.
     Filter(FilterOp),
@@ -111,20 +120,16 @@ pub struct PictureCacheKey {
     // changes, the cache key will not match. This can
     // happen, for example, during zooming or changes
     // in device-pixel-ratio.
     unclipped_size: DeviceIntSize,
 }
 
 #[derive(Debug)]
 pub struct PicturePrimitive {
-    // If this picture is drawn to an intermediate surface,
-    // the associated target information.
-    pub surface: Option<PictureSurface>,
-
     // List of primitive runs that make up this picture.
     pub runs: Vec<PrimitiveRun>,
     pub state: Option<PictureState>,
 
     // The pipeline that the primitives on this picture belong to.
     pub pipeline_id: PipelineId,
 
     // If true, apply the local clip rect to primitive drawn
@@ -135,17 +140,18 @@ pub struct PicturePrimitive {
     // the readback of the framebuffer that we use to sample
     // from in the mix-blend-mode shader.
     // For drop-shadow filter, this will store the original
     // picture task which would be rendered on screen after
     // blur pass.
     pub secondary_render_task_id: Option<RenderTaskId>,
     /// How this picture should be composited.
     /// If None, don't composite - just draw directly on parent surface.
-    pub composite_mode: Option<PictureCompositeMode>,
+    pub requested_composite_mode: Option<PictureCompositeMode>,
+    pub raster_config: Option<RasterConfig>,
     // If true, this picture is part of a 3D context.
     pub is_in_3d_context: bool,
     // If requested as a frame output (for rendering
     // pages to a texture), this is the pipeline this
     // picture is the root of.
     pub frame_output_pipeline_id: Option<PipelineId>,
     // An optional cache handle for storing extra data
     // in the GPU cache, depending on the type of
@@ -153,106 +159,103 @@ pub struct PicturePrimitive {
     pub extra_gpu_data_handle: GpuCacheHandle,
 
     // Unique identifier for this picture.
     pub id: PictureId,
 }
 
 impl PicturePrimitive {
     fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
-        match self.composite_mode {
+        match self.requested_composite_mode {
             Some(PictureCompositeMode::Filter(ref mut filter)) => {
                 match *filter {
                     FilterOp::Opacity(ref binding, ref mut value) => {
                         *value = properties.resolve_float(binding);
                     }
                     _ => {}
                 }
 
                 filter.is_visible()
             }
             _ => true,
         }
     }
 
     pub fn new_image(
         id: PictureId,
-        composite_mode: Option<PictureCompositeMode>,
+        requested_composite_mode: Option<PictureCompositeMode>,
         is_in_3d_context: bool,
         pipeline_id: PipelineId,
         frame_output_pipeline_id: Option<PipelineId>,
         apply_local_clip_rect: bool,
     ) -> Self {
         PicturePrimitive {
             runs: Vec::new(),
             state: None,
-            surface: None,
             secondary_render_task_id: None,
-            composite_mode,
+            requested_composite_mode,
+            raster_config: None,
             is_in_3d_context,
             frame_output_pipeline_id,
             extra_gpu_data_handle: GpuCacheHandle::new(),
             apply_local_clip_rect,
             pipeline_id,
             id,
         }
     }
 
-    pub fn can_draw_directly_to_parent_surface(&self) -> bool {
-        match self.composite_mode {
-            Some(PictureCompositeMode::Filter(filter)) => {
-                filter.is_noop()
-            }
-            Some(PictureCompositeMode::Blit) |
-            Some(PictureCompositeMode::MixBlend(..)) => {
-                false
-            }
-            None => {
-                true
-            }
-        }
-    }
-
     pub fn take_context(
         &mut self,
         parent_allows_subpixel_aa: bool,
         scene_properties: &SceneProperties,
         is_chased: bool,
     ) -> Option<PictureContext> {
         if !self.resolve_scene_properties(scene_properties) {
             if cfg!(debug_assertions) && is_chased {
                 println!("\tculled for carrying an invisible composite filter");
             }
 
             return None;
         }
 
+        let actual_composite_mode = match self.requested_composite_mode {
+            Some(PictureCompositeMode::Filter(filter)) if filter.is_noop() => None,
+            mode => mode,
+        };
+
+        self.raster_config = actual_composite_mode.map(|composite_mode| {
+            RasterConfig {
+                composite_mode,
+                surface: None,
+            }
+        });
+
         // Disallow subpixel AA if an intermediate surface is needed.
         // TODO(lsalzman): allow overriding parent if intermediate surface is opaque
         let allow_subpixel_aa = parent_allows_subpixel_aa &&
-            self.can_draw_directly_to_parent_surface();
+            self.raster_config.is_none();
 
-        let inflation_factor = match self.composite_mode {
-            Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
+        let inflation_factor = match self.raster_config {
+            Some(RasterConfig { composite_mode: PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)), .. }) => {
                 // The amount of extra space needed for primitives inside
                 // this picture to ensure the visibility check is correct.
                 BLUR_SAMPLE_SCALE * blur_radius
             }
             _ => {
                 0.0
             }
         };
 
         Some(PictureContext {
             pipeline_id: self.pipeline_id,
             prim_runs: mem::replace(&mut self.runs, Vec::new()),
             apply_local_clip_rect: self.apply_local_clip_rect,
             inflation_factor,
             allow_subpixel_aa,
-            has_surface: !self.can_draw_directly_to_parent_surface(),
+            has_surface: self.raster_config.is_some(),
         })
     }
 
     pub fn add_primitive(
         &mut self,
         prim_index: PrimitiveIndex,
     ) {
         if let Some(ref mut run) = self.runs.last_mut() {
@@ -276,22 +279,22 @@ impl PicturePrimitive {
     ) -> LayoutRect {
         self.runs = context.prim_runs;
         self.state = Some(state);
 
         match local_rect {
             Some(local_rect) => {
                 let local_content_rect = LayoutRect::from_untyped(&local_rect.to_untyped());
 
-                match self.composite_mode {
-                    Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
+                match self.raster_config {
+                    Some(RasterConfig { composite_mode: PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)), .. }) => {
                         let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
                         local_content_rect.inflate(inflate_size, inflate_size)
                     }
-                    Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => {
+                    Some(RasterConfig { composite_mode: PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)), .. }) => {
                         let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
                         local_content_rect.inflate(inflate_size, inflate_size)
 
                         // TODO(gw): When we support culling rect being separate from
                         //           the task/screen rect, we should include both the
                         //           content and shadow rect here, which will prevent
                         //           drop-shadows from disappearing if the main content
                         //           rect is not visible. Something like:
@@ -301,17 +304,17 @@ impl PicturePrimitive {
                         // shadow_rect.union(&local_content_rect)
                     }
                     _ => {
                         local_content_rect
                     }
                 }
             }
             None => {
-                assert!(self.can_draw_directly_to_parent_surface());
+                assert!(self.raster_config.is_none());
                 LayoutRect::zero()
             }
         }
     }
 
     pub fn take_state(&mut self) -> PictureState {
         self.state.take().expect("bug: no state present!")
     }
@@ -322,334 +325,336 @@ impl PicturePrimitive {
         prim_metadata: &mut PrimitiveMetadata,
         prim_context: &PrimitiveContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
     ) {
         let mut pic_state_for_children = self.take_state();
 
-        if self.can_draw_directly_to_parent_surface() {
-            pic_state.tasks.extend(pic_state_for_children.tasks);
-            self.surface = None;
-            return;
-        }
-
-        let clipped_world_rect = prim_metadata
-                                    .clipped_world_rect
-                                    .as_ref()
-                                    .expect("bug: trying to draw an off-screen picture!?");
-
-        let clipped = world_rect_to_device_pixels(
-            *clipped_world_rect,
-            frame_context.device_pixel_scale,
-        ).to_i32();
-
-        let pic_rect = pic_state.map_local_to_pic
-                                .map(&prim_metadata.local_rect)
-                                .unwrap();
-        let world_rect = pic_state.map_pic_to_world
-                                  .map(&pic_rect)
-                                  .unwrap();
-
-        let unclipped = world_rect_to_device_pixels(
-            world_rect,
-            frame_context.device_pixel_scale,
-        );
+        match self.raster_config {
+            Some(ref mut raster_config) => {
+                let clipped_world_rect = prim_metadata
+                                            .clipped_world_rect
+                                            .as_ref()
+                                            .expect("bug: trying to draw an off-screen picture!?");
 
-        // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
-        //           to store the same type of data. The exception is the filter
-        //           with a ColorMatrix, which stores the color matrix here. It's
-        //           probably worth tidying this code up to be a bit more consistent.
-        //           Perhaps store the color matrix after the common data, even though
-        //           it's not used by that shader.
-        match self.composite_mode {
-            Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
-                let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
-                let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+                let clipped = world_rect_to_device_pixels(
+                    *clipped_world_rect,
+                    frame_context.device_pixel_scale,
+                ).to_i32();
 
-                // The clipped field is the part of the picture that is visible
-                // on screen. The unclipped field is the screen-space rect of
-                // the complete picture, if no screen / clip-chain was applied
-                // (this includes the extra space for blur region). To ensure
-                // that we draw a large enough part of the picture to get correct
-                // blur results, inflate that clipped area by the blur range, and
-                // then intersect with the total screen rect, to minimize the
-                // allocation size.
-                let device_rect = clipped
-                    .inflate(blur_range, blur_range)
-                    .intersection(&unclipped.to_i32())
-                    .unwrap();
+                let pic_rect = pic_state.map_local_to_pic
+                                        .map(&prim_metadata.local_rect)
+                                        .unwrap();
+                let world_rect = pic_state.map_pic_to_world
+                                          .map(&pic_rect)
+                                          .unwrap();
 
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &prim_metadata.local_rect,
-                    &prim_context.transform,
-                    &device_rect,
+                let unclipped = world_rect_to_device_pixels(
+                    world_rect,
                     frame_context.device_pixel_scale,
                 );
 
-                // If we are drawing a blur that has primitives or clips that contain
-                // a complex coordinate system, don't bother caching them (for now).
-                // It's likely that they are animating and caching may not help here
-                // anyway. In the future we should relax this a bit, so that we can
-                // cache tasks with complex coordinate systems if we detect the
-                // relevant transforms haven't changed from frame to frame.
-                let surface = if pic_state_for_children.has_non_root_coord_system {
-                    let picture_task = RenderTask::new_picture(
-                        RenderTaskLocation::Dynamic(None, device_rect.size),
-                        unclipped.size,
-                        prim_index,
-                        device_rect.origin,
-                        pic_state_for_children.tasks,
-                        uv_rect_kind,
-                    );
+                // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
+                //           to store the same type of data. The exception is the filter
+                //           with a ColorMatrix, which stores the color matrix here. It's
+                //           probably worth tidying this code up to be a bit more consistent.
+                //           Perhaps store the color matrix after the common data, even though
+                //           it's not used by that shader.
 
-                    let picture_task_id = frame_state.render_tasks.add(picture_task);
-
-                    let blur_render_task = RenderTask::new_blur(
-                        blur_std_deviation,
-                        picture_task_id,
-                        frame_state.render_tasks,
-                        RenderTargetKind::Color,
-                        ClearMode::Transparent,
-                    );
-
-                    let render_task_id = frame_state.render_tasks.add(blur_render_task);
-
-                    pic_state.tasks.push(render_task_id);
+                match raster_config.composite_mode {
+                    PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
+                        let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
+                        let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
 
-                    PictureSurface::RenderTask(render_task_id)
-                } else {
-                    // Get the relative clipped rect within the overall prim rect, that
-                    // forms part of the cache key.
-                    let pic_relative_render_rect = PictureIntRect::new(
-                        PictureIntPoint::new(
-                            device_rect.origin.x - unclipped.origin.x as i32,
-                            device_rect.origin.y - unclipped.origin.y as i32,
-                        ),
-                        PictureIntSize::new(
-                            device_rect.size.width,
-                            device_rect.size.height,
-                        ),
-                    );
+                        // The clipped field is the part of the picture that is visible
+                        // on screen. The unclipped field is the screen-space rect of
+                        // the complete picture, if no screen / clip-chain was applied
+                        // (this includes the extra space for blur region). To ensure
+                        // that we draw a large enough part of the picture to get correct
+                        // blur results, inflate that clipped area by the blur range, and
+                        // then intersect with the total screen rect, to minimize the
+                        // allocation size.
+                        let device_rect = clipped
+                            .inflate(blur_range, blur_range)
+                            .intersection(&unclipped.to_i32())
+                            .unwrap();
 
-                    // Request a render task that will cache the output in the
-                    // texture cache.
-                    let cache_item = frame_state.resource_cache.request_render_task(
-                        RenderTaskCacheKey {
-                            size: device_rect.size,
-                            kind: RenderTaskCacheKeyKind::Picture(PictureCacheKey {
-                                scene_id: frame_context.scene_id,
-                                picture_id: self.id,
-                                unclipped_size: unclipped.size.to_i32(),
-                                pic_relative_render_rect,
-                            }),
-                        },
-                        frame_state.gpu_cache,
-                        frame_state.render_tasks,
-                        None,
-                        false,
-                        |render_tasks| {
-                            let child_tasks = mem::replace(&mut pic_state_for_children.tasks, Vec::new());
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &prim_metadata.local_rect,
+                            &prim_context.transform,
+                            &device_rect,
+                            frame_context.device_pixel_scale,
+                        );
 
+                        // If we are drawing a blur that has primitives or clips that contain
+                        // a complex coordinate system, don't bother caching them (for now).
+                        // It's likely that they are animating and caching may not help here
+                        // anyway. In the future we should relax this a bit, so that we can
+                        // cache tasks with complex coordinate systems if we detect the
+                        // relevant transforms haven't changed from frame to frame.
+                        let surface = if pic_state_for_children.has_non_root_coord_system {
                             let picture_task = RenderTask::new_picture(
                                 RenderTaskLocation::Dynamic(None, device_rect.size),
                                 unclipped.size,
                                 prim_index,
                                 device_rect.origin,
-                                child_tasks,
+                                pic_state_for_children.tasks,
                                 uv_rect_kind,
                             );
 
-                            let picture_task_id = render_tasks.add(picture_task);
+                            let picture_task_id = frame_state.render_tasks.add(picture_task);
 
                             let blur_render_task = RenderTask::new_blur(
                                 blur_std_deviation,
                                 picture_task_id,
-                                render_tasks,
+                                frame_state.render_tasks,
                                 RenderTargetKind::Color,
                                 ClearMode::Transparent,
                             );
 
-                            let render_task_id = render_tasks.add(blur_render_task);
+                            let render_task_id = frame_state.render_tasks.add(blur_render_task);
 
                             pic_state.tasks.push(render_task_id);
 
-                            render_task_id
-                        }
-                    );
+                            PictureSurface::RenderTask(render_task_id)
+                        } else {
+                            // Get the relative clipped rect within the overall prim rect, that
+                            // forms part of the cache key.
+                            let pic_relative_render_rect = PictureIntRect::new(
+                                PictureIntPoint::new(
+                                    device_rect.origin.x - unclipped.origin.x as i32,
+                                    device_rect.origin.y - unclipped.origin.y as i32,
+                                ),
+                                PictureIntSize::new(
+                                    device_rect.size.width,
+                                    device_rect.size.height,
+                                ),
+                            );
 
-                    PictureSurface::TextureCache(cache_item)
-                };
+                            // Request a render task that will cache the output in the
+                            // texture cache.
+                            let cache_item = frame_state.resource_cache.request_render_task(
+                                RenderTaskCacheKey {
+                                    size: device_rect.size,
+                                    kind: RenderTaskCacheKeyKind::Picture(PictureCacheKey {
+                                        scene_id: frame_context.scene_id,
+                                        picture_id: self.id,
+                                        unclipped_size: unclipped.size.to_i32(),
+                                        pic_relative_render_rect,
+                                    }),
+                                },
+                                frame_state.gpu_cache,
+                                frame_state.render_tasks,
+                                None,
+                                false,
+                                |render_tasks| {
+                                    let child_tasks = mem::replace(&mut pic_state_for_children.tasks, Vec::new());
 
-                self.surface = Some(surface);
-            }
-            Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
-                let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
-                let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+                                    let picture_task = RenderTask::new_picture(
+                                        RenderTaskLocation::Dynamic(None, device_rect.size),
+                                        unclipped.size,
+                                        prim_index,
+                                        device_rect.origin,
+                                        child_tasks,
+                                        uv_rect_kind,
+                                    );
+
+                                    let picture_task_id = render_tasks.add(picture_task);
+
+                                    let blur_render_task = RenderTask::new_blur(
+                                        blur_std_deviation,
+                                        picture_task_id,
+                                        render_tasks,
+                                        RenderTargetKind::Color,
+                                        ClearMode::Transparent,
+                                    );
+
+                                    let render_task_id = render_tasks.add(blur_render_task);
 
-                // The clipped field is the part of the picture that is visible
-                // on screen. The unclipped field is the screen-space rect of
-                // the complete picture, if no screen / clip-chain was applied
-                // (this includes the extra space for blur region). To ensure
-                // that we draw a large enough part of the picture to get correct
-                // blur results, inflate that clipped area by the blur range, and
-                // then intersect with the total screen rect, to minimize the
-                // allocation size.
-                let device_rect = clipped
-                    .inflate(blur_range, blur_range)
-                    .intersection(&unclipped.to_i32())
-                    .unwrap();
+                                    pic_state.tasks.push(render_task_id);
+
+                                    render_task_id
+                                }
+                            );
+
+                            PictureSurface::TextureCache(cache_item)
+                        };
+
+                        raster_config.surface = Some(surface);
+                    }
+                    PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color)) => {
+                        let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
+                        let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+
+                        // The clipped field is the part of the picture that is visible
+                        // on screen. The unclipped field is the screen-space rect of
+                        // the complete picture, if no screen / clip-chain was applied
+                        // (this includes the extra space for blur region). To ensure
+                        // that we draw a large enough part of the picture to get correct
+                        // blur results, inflate that clipped area by the blur range, and
+                        // then intersect with the total screen rect, to minimize the
+                        // allocation size.
+                        let device_rect = clipped
+                            .inflate(blur_range, blur_range)
+                            .intersection(&unclipped.to_i32())
+                            .unwrap();
 
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &prim_metadata.local_rect,
-                    &prim_context.transform,
-                    &device_rect,
-                    frame_context.device_pixel_scale,
-                );
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &prim_metadata.local_rect,
+                            &prim_context.transform,
+                            &device_rect,
+                            frame_context.device_pixel_scale,
+                        );
 
-                let mut picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, device_rect.size),
-                    unclipped.size,
-                    prim_index,
-                    device_rect.origin,
-                    pic_state_for_children.tasks,
-                    uv_rect_kind,
-                );
-                picture_task.mark_for_saving();
+                        let mut picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, device_rect.size),
+                            unclipped.size,
+                            prim_index,
+                            device_rect.origin,
+                            pic_state_for_children.tasks,
+                            uv_rect_kind,
+                        );
+                        picture_task.mark_for_saving();
 
-                let picture_task_id = frame_state.render_tasks.add(picture_task);
+                        let picture_task_id = frame_state.render_tasks.add(picture_task);
+
+                        let blur_render_task = RenderTask::new_blur(
+                            blur_std_deviation.round(),
+                            picture_task_id,
+                            frame_state.render_tasks,
+                            RenderTargetKind::Color,
+                            ClearMode::Transparent,
+                        );
+
+                        self.secondary_render_task_id = Some(picture_task_id);
 
-                let blur_render_task = RenderTask::new_blur(
-                    blur_std_deviation.round(),
-                    picture_task_id,
-                    frame_state.render_tasks,
-                    RenderTargetKind::Color,
-                    ClearMode::Transparent,
-                );
+                        let render_task_id = frame_state.render_tasks.add(blur_render_task);
+                        pic_state.tasks.push(render_task_id);
+                        raster_config.surface = Some(PictureSurface::RenderTask(render_task_id));
+
+                        // If the local rect of the contents changed, force the cache handle
+                        // to be invalidated so that the primitive data below will get
+                        // uploaded to the GPU this frame. This can occur during property
+                        // animation.
+                        if pic_state.local_rect_changed {
+                            frame_state.gpu_cache.invalidate(&mut self.extra_gpu_data_handle);
+                        }
 
-                self.secondary_render_task_id = Some(picture_task_id);
+                        if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
+                            // TODO(gw): This is very hacky code below! It stores an extra
+                            //           brush primitive below for the special case of a
+                            //           drop-shadow where we need a different local
+                            //           rect for the shadow. To tidy this up in future,
+                            //           we could consider abstracting the code in prim_store.rs
+                            //           that writes a brush primitive header.
+
+                            // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
+                            //  [brush specific data]
+                            //  [segment_rect, segment data]
+                            let shadow_rect = prim_metadata.local_rect.translate(&offset);
 
-                let render_task_id = frame_state.render_tasks.add(blur_render_task);
-                pic_state.tasks.push(render_task_id);
-                self.surface = Some(PictureSurface::RenderTask(render_task_id));
+                            // ImageBrush colors
+                            request.push(color.premultiplied());
+                            request.push(PremultipliedColorF::WHITE);
+                            request.push([
+                                prim_metadata.local_rect.size.width,
+                                prim_metadata.local_rect.size.height,
+                                0.0,
+                                0.0,
+                            ]);
 
-                // If the local rect of the contents changed, force the cache handle
-                // to be invalidated so that the primitive data below will get
-                // uploaded to the GPU this frame. This can occur during property
-                // animation.
-                if pic_state.local_rect_changed {
-                    frame_state.gpu_cache.invalidate(&mut self.extra_gpu_data_handle);
-                }
+                            // segment rect / extra data
+                            request.push(shadow_rect);
+                            request.push([0.0, 0.0, 0.0, 0.0]);
+                        }
+                    }
+                    PictureCompositeMode::MixBlend(..) => {
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &prim_metadata.local_rect,
+                            &prim_context.transform,
+                            &clipped,
+                            frame_context.device_pixel_scale,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            prim_index,
+                            clipped.origin,
+                            pic_state_for_children.tasks,
+                            uv_rect_kind,
+                        );
 
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
-                    // TODO(gw): This is very hacky code below! It stores an extra
-                    //           brush primitive below for the special case of a
-                    //           drop-shadow where we need a different local
-                    //           rect for the shadow. To tidy this up in future,
-                    //           we could consider abstracting the code in prim_store.rs
-                    //           that writes a brush primitive header.
+                        let readback_task_id = frame_state.render_tasks.add(
+                            RenderTask::new_readback(clipped)
+                        );
+
+                        self.secondary_render_task_id = Some(readback_task_id);
+                        pic_state.tasks.push(readback_task_id);
 
-                    // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
-                    //  [brush specific data]
-                    //  [segment_rect, segment data]
-                    let shadow_rect = prim_metadata.local_rect.translate(&offset);
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+                        pic_state.tasks.push(render_task_id);
+                        raster_config.surface = Some(PictureSurface::RenderTask(render_task_id));
+                    }
+                    PictureCompositeMode::Filter(filter) => {
+                        if let FilterOp::ColorMatrix(m) = filter {
+                            if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
+                                for i in 0..5 {
+                                    request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
+                                }
+                            }
+                        }
+
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &prim_metadata.local_rect,
+                            &prim_context.transform,
+                            &clipped,
+                            frame_context.device_pixel_scale,
+                        );
 
-                    // ImageBrush colors
-                    request.push(color.premultiplied());
-                    request.push(PremultipliedColorF::WHITE);
-                    request.push([
-                        prim_metadata.local_rect.size.width,
-                        prim_metadata.local_rect.size.height,
-                        0.0,
-                        0.0,
-                    ]);
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            prim_index,
+                            clipped.origin,
+                            pic_state_for_children.tasks,
+                            uv_rect_kind,
+                        );
 
-                    // segment rect / extra data
-                    request.push(shadow_rect);
-                    request.push([0.0, 0.0, 0.0, 0.0]);
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+                        pic_state.tasks.push(render_task_id);
+                        raster_config.surface = Some(PictureSurface::RenderTask(render_task_id));
+                    }
+                    PictureCompositeMode::Blit => {
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &prim_metadata.local_rect,
+                            &prim_context.transform,
+                            &clipped,
+                            frame_context.device_pixel_scale,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            prim_index,
+                            clipped.origin,
+                            pic_state_for_children.tasks,
+                            uv_rect_kind,
+                        );
+
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+                        pic_state.tasks.push(render_task_id);
+                        raster_config.surface = Some(PictureSurface::RenderTask(render_task_id));
+                    }
                 }
             }
-            Some(PictureCompositeMode::MixBlend(..)) => {
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &prim_metadata.local_rect,
-                    &prim_context.transform,
-                    &clipped,
-                    frame_context.device_pixel_scale,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    prim_index,
-                    clipped.origin,
-                    pic_state_for_children.tasks,
-                    uv_rect_kind,
-                );
-
-                let readback_task_id = frame_state.render_tasks.add(
-                    RenderTask::new_readback(clipped)
-                );
-
-                self.secondary_render_task_id = Some(readback_task_id);
-                pic_state.tasks.push(readback_task_id);
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                pic_state.tasks.push(render_task_id);
-                self.surface = Some(PictureSurface::RenderTask(render_task_id));
-            }
-            Some(PictureCompositeMode::Filter(filter)) => {
-                if let FilterOp::ColorMatrix(m) = filter {
-                    if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
-                        for i in 0..5 {
-                            request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
-                        }
-                    }
-                }
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &prim_metadata.local_rect,
-                    &prim_context.transform,
-                    &clipped,
-                    frame_context.device_pixel_scale,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    prim_index,
-                    clipped.origin,
-                    pic_state_for_children.tasks,
-                    uv_rect_kind,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                pic_state.tasks.push(render_task_id);
-                self.surface = Some(PictureSurface::RenderTask(render_task_id));
-            }
-            Some(PictureCompositeMode::Blit) | None => {
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &prim_metadata.local_rect,
-                    &prim_context.transform,
-                    &clipped,
-                    frame_context.device_pixel_scale,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    prim_index,
-                    clipped.origin,
-                    pic_state_for_children.tasks,
-                    uv_rect_kind,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                pic_state.tasks.push(render_task_id);
-                self.surface = Some(PictureSurface::RenderTask(render_task_id));
+            None => {
+                pic_state.tasks.extend(pic_state_for_children.tasks);
             }
         }
     }
 }
 
 // Calculate a single screen-space UV for a picture.
 fn calculate_screen_uv(
     local_pos: &LayoutPoint,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -111,24 +111,29 @@ pub enum CoordinateSpaceMapping<F, T> {
     Transform(TypedTransform3D<f32, F, T>),
 }
 
 #[derive(Debug)]
 pub struct SpaceMapper<F, T> {
     kind: CoordinateSpaceMapping<F, T>,
     pub ref_spatial_node_index: SpatialNodeIndex,
     current_target_spatial_node_index: SpatialNodeIndex,
+    bounds: TypedRect<f32, T>,
 }
 
 impl<F, T> SpaceMapper<F, T> where F: fmt::Debug {
-    pub fn new(ref_spatial_node_index: SpatialNodeIndex) -> Self {
+    pub fn new(
+        ref_spatial_node_index: SpatialNodeIndex,
+        bounds: TypedRect<f32, T>,
+    ) -> Self {
         SpaceMapper {
             kind: CoordinateSpaceMapping::Local,
             ref_spatial_node_index,
             current_target_spatial_node_index: ref_spatial_node_index,
+            bounds,
         }
     }
 
     pub fn set_target_spatial_node(
         &mut self,
         target_node_index: SpatialNodeIndex,
         clip_scroll_tree: &ClipScrollTree,
     ) {
@@ -156,26 +161,41 @@ impl<F, T> SpaceMapper<F, T> where F: fm
 
                 CoordinateSpaceMapping::Transform(
                     transform.with_source::<F>().with_destination::<T>()
                 )
             };
         }
     }
 
+    pub fn unmap(&self, rect: &TypedRect<f32, T>) -> Option<TypedRect<f32, F>> {
+        match self.kind {
+            CoordinateSpaceMapping::Local => {
+                Some(TypedRect::from_untyped(&rect.to_untyped()))
+            }
+            CoordinateSpaceMapping::Offset(ref offset) => {
+                let offset = TypedVector2D::new(-offset.x, -offset.y);
+                Some(TypedRect::from_untyped(&rect.translate(&offset).to_untyped()))
+            }
+            CoordinateSpaceMapping::Transform(ref transform) => {
+                transform.inverse_rect_footprint(rect)
+            }
+        }
+    }
+
     pub fn map(&self, rect: &TypedRect<f32, F>) -> Option<TypedRect<f32, T>> {
         match self.kind {
             CoordinateSpaceMapping::Local => {
                 Some(TypedRect::from_untyped(&rect.to_untyped()))
             }
             CoordinateSpaceMapping::Offset(ref offset) => {
                 Some(TypedRect::from_untyped(&rect.translate(offset).to_untyped()))
             }
             CoordinateSpaceMapping::Transform(ref transform) => {
-                match project_rect(transform, rect) {
+                match project_rect(transform, rect, &self.bounds) {
                     Some(bounds) => {
                         Some(bounds)
                     }
                     None => {
                         warn!("parent relative transform can't transform the primitive rect for {:?}", rect);
                         None
                     }
                 }
@@ -403,17 +423,20 @@ impl BrushKind {
         BrushKind::Solid {
             color,
             opacity_binding: OpacityBinding::new(),
         }
     }
 
     // Construct a brush that is a border with `border` style and `widths`
     // dimensions.
-    pub fn new_border(border: NormalBorder, widths: BorderWidths) -> BrushKind {
+    pub fn new_border(mut border: NormalBorder, widths: BorderWidths) -> BrushKind {
+        // FIXME(emilio): Is this the best place to do this?
+        border.normalize(&widths);
+
         let cache_key = BorderCacheKey::new(&border, &widths);
         BrushKind::Border {
             source: BorderSource::Border {
                 border,
                 widths,
                 cache_key,
                 task_info: None,
                 handle: None,
@@ -522,17 +545,17 @@ impl BrushPrimitive {
             kind,
             segment_desc,
         }
     }
 
     pub fn may_need_clip_mask(&self) -> bool {
         match self.kind {
             BrushKind::Picture(ref pic) => {
-                pic.composite_mode.is_some()
+                pic.raster_config.is_some()
             }
             _ => {
                 true
             }
         }
     }
 
     pub fn new_picture(prim: PicturePrimitive) -> Self {
@@ -1460,17 +1483,17 @@ impl PrimitiveStore {
         // this to other primitives, such as text runs and gradients.
         match prim.details {
             PrimitiveDetails::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Picture(ref pic) => {
                         // If we encounter a picture that is a pass-through
                         // (i.e. no composite mode), then we can recurse into
                         // that to try and find a primitive to collapse to.
-                        if pic.composite_mode.is_none() {
+                        if pic.requested_composite_mode.is_none() {
                             return self.get_opacity_collapse_prim(run.base_prim_index);
                         }
                     }
                     // If we find a single rect or image, we can use that
                     // as the primitive to collapse the opacity into.
                     BrushKind::Solid { .. } | BrushKind::Image { .. } => {
                         return Some(run.base_prim_index)
                     }
@@ -1491,17 +1514,17 @@ impl PrimitiveStore {
     // we just support collapsing pictures with an opacity filter
     // by pushing that opacity value into the color of a primitive
     // if that picture contains one compatible primitive.
     pub fn optimize_picture_if_possible(
         &mut self,
         pic_prim_index: PrimitiveIndex,
     ) {
         // Only handle opacity filters for now.
-        let binding = match self.get_pic(pic_prim_index).composite_mode {
+        let binding = match self.get_pic(pic_prim_index).requested_composite_mode {
             Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) => {
                 binding
             }
             _ => {
                 return;
             }
         };
 
@@ -1539,17 +1562,17 @@ impl PrimitiveStore {
             }
         }
 
         // The opacity filter has been collapsed, so mark this picture
         // as a pass though. This means it will no longer allocate an
         // intermediate surface or incur an extra blend / blit. Instead,
         // the collapsed primitive will be drawn directly into the
         // parent picture.
-        self.get_pic_mut(pic_prim_index).composite_mode = None;
+        self.get_pic_mut(pic_prim_index).requested_composite_mode = None;
     }
 
     pub fn prim_count(&self) -> usize {
         self.primitives.len()
     }
 
     pub fn prepare_prim_for_render(
         &mut self,
@@ -1596,16 +1619,17 @@ impl PrimitiveStore {
                     prim_context.spatial_node_index
                 } else {
                     root_spatial_node_index
                 };
 
                 let mut pic_state_for_children = PictureState::new(
                     root_spatial_node_index,
                     frame_context.clip_scroll_tree,
+                    frame_context.world_rect,
                 );
 
                 // Mark whether this picture has a complex coordinate system.
                 pic_state_for_children.has_non_root_coord_system |=
                     prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root();
 
                 let mut pic_rect = PictureRect::zero();
                 self.prepare_prim_runs(
@@ -1688,16 +1712,17 @@ impl PrimitiveStore {
                     prim.metadata.local_clip_rect,
                     prim_context.spatial_node_index,
                     &pic_state.map_local_to_pic,
                     &pic_state.map_pic_to_world,
                     &frame_context.clip_scroll_tree,
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                     frame_context.device_pixel_scale,
+                    &frame_context.world_rect,
                 );
 
             let clip_chain = match clip_chain {
                 Some(clip_chain) => clip_chain,
                 None => {
                     prim.metadata.clipped_world_rect = None;
                     return false;
                 }
@@ -2205,16 +2230,17 @@ impl Primitive {
                     self.metadata.local_clip_rect,
                     prim_context.spatial_node_index,
                     &pic_state.map_local_to_pic,
                     &pic_state.map_pic_to_world,
                     &frame_context.clip_scroll_tree,
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                     frame_context.device_pixel_scale,
+                    &frame_context.world_rect,
                 );
 
             match segment_clip_chain {
                 Some(segment_clip_chain) => {
                     if segment_clip_chain.clips_range.count == 0 ||
                        (!segment.may_need_clip_mask && !segment_clip_chain.has_non_local_clips) {
                         segment.clip_task_id = BrushSegmentTaskId::Opaque;
                         continue;
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AddFont, BlobImageResources, AsyncBlobImageRasterizer, ResourceUpdate};
 use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest};
 use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
 use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams};
-use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
+use api::{FontInstanceData, FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize, TileRange, NormalizedRect, BlobImageData};
 use app_units::Au;
 #[cfg(feature = "capture")]
 use capture::ExternalCaptureImage;
 #[cfg(feature = "replay")]
 use capture::PlainExternalImage;
@@ -345,16 +345,33 @@ struct Resources {
     font_instances: FontInstanceMap,
     image_templates: ImageTemplates,
 }
 
 impl BlobImageResources for Resources {
     fn get_font_data(&self, key: FontKey) -> &FontTemplate {
         self.font_templates.get(&key).unwrap()
     }
+    fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
+        match self.font_instances.read().unwrap().get(&key) {
+            Some(instance) => Some(FontInstanceData {
+                font_key: instance.font_key,
+                size: instance.size,
+                options: Some(FontInstanceOptions {
+                  render_mode: instance.render_mode,
+                  flags: instance.flags,
+                  bg_color: instance.bg_color,
+                  synthetic_italics: instance.synthetic_italics,
+                }),
+                platform_options: instance.platform_options,
+                variations: instance.variations.clone(),
+            }),
+            None => None,
+        }
+    }
     fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> {
         self.image_templates
             .get(key)
             .map(|resource| (&resource.data, &resource.descriptor))
     }
 }
 
 pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option<GlyphDimensions>>;
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -17,17 +17,17 @@ const NEARLY_ZERO: f32 = 1.0 / 4096.0;
 
 // TODO: Implement these in euclid!
 pub trait MatrixHelpers<Src, Dst> {
     fn preserves_2d_axis_alignment(&self) -> bool;
     fn has_perspective_component(&self) -> bool;
     fn has_2d_inverse(&self) -> bool;
     fn exceeds_2d_scale(&self, limit: f64) -> bool;
     fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
-    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
+    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> Option<TypedRect<f32, Src>>;
     fn transform_kind(&self) -> TransformedRectKind;
     fn is_simple_translation(&self) -> bool;
     fn is_simple_2d_translation(&self) -> bool;
 }
 
 impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
     // A port of the preserves2dAxisAlignment function in Skia.
     // Defined in the SkMatrix44 class.
@@ -85,23 +85,23 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
             self.m41 - target.x * self.m44,
             self.m12 - target.y * self.m14,
             self.m22 - target.y * self.m24,
             self.m42 - target.y * self.m44,
         );
         m.inverse().map(|inv| TypedPoint2D::new(inv.m31, inv.m32))
     }
 
-    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src> {
-        TypedRect::from_points(&[
-            self.inverse_project(&rect.origin).unwrap_or(TypedPoint2D::zero()),
-            self.inverse_project(&rect.top_right()).unwrap_or(TypedPoint2D::zero()),
-            self.inverse_project(&rect.bottom_left()).unwrap_or(TypedPoint2D::zero()),
-            self.inverse_project(&rect.bottom_right()).unwrap_or(TypedPoint2D::zero()),
-        ])
+    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> Option<TypedRect<f32, Src>> {
+        Some(TypedRect::from_points(&[
+            self.inverse_project(&rect.origin)?,
+            self.inverse_project(&rect.top_right())?,
+            self.inverse_project(&rect.bottom_left())?,
+            self.inverse_project(&rect.bottom_right())?,
+        ]))
     }
 
     fn transform_kind(&self) -> TransformedRectKind {
         if self.preserves_2d_axis_alignment() {
             TransformedRectKind::AxisAligned
         } else {
             TransformedRectKind::Complex
         }
@@ -388,17 +388,17 @@ impl<Src, Dst> FastTransform<Src, Dst> {
 
     pub fn unapply(&self, rect: &TypedRect<f32, Dst>) -> Option<TypedRect<f32, Src>> {
         match *self {
             FastTransform::Offset(offset) =>
                 Some(TypedRect::from_untyped(&rect.to_untyped().translate(&-offset.to_untyped()))),
             FastTransform::Transform { inverse: Some(ref inverse), is_2d: true, .. }  =>
                 inverse.transform_rect(rect),
             FastTransform::Transform { ref transform, is_2d: false, .. } =>
-                Some(transform.inverse_rect_footprint(rect)),
+                transform.inverse_rect_footprint(rect),
             FastTransform::Transform { inverse: None, .. }  => None,
         }
     }
 
     pub fn post_translate(&self, new_offset: TypedVector2D<f32, Dst>) -> Self {
         match *self {
             FastTransform::Offset(offset) => {
                 let offset = offset.to_untyped() + new_offset.to_untyped();
@@ -441,33 +441,34 @@ impl<Src, Dst> From<TypedVector2D<f32, S
 }
 
 pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>;
 pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>;
 
 pub fn project_rect<F, T>(
     transform: &TypedTransform3D<f32, F, T>,
     rect: &TypedRect<f32, F>,
+    bounds: &TypedRect<f32, T>,
 ) -> Option<TypedRect<f32, T>>
  where F: fmt::Debug
 {
     let homogens = [
         transform.transform_point2d_homogeneous(&rect.origin),
         transform.transform_point2d_homogeneous(&rect.top_right()),
         transform.transform_point2d_homogeneous(&rect.bottom_left()),
         transform.transform_point2d_homogeneous(&rect.bottom_right()),
     ];
 
     // Note: we only do the full frustum collision when the polygon approaches the camera plane.
     // Otherwise, it will be clamped to the screen bounds anyway.
     if homogens.iter().any(|h| h.w <= 0.0) {
         let mut clipper = Clipper::new();
         clipper.add_frustum(
             transform,
-            None,
+            Some(*bounds),
         );
 
         let polygon = Polygon::from_rect(*rect, 1);
         let results = clipper.clip(polygon);
         if results.is_empty() {
             return None
         }
 
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -268,16 +268,43 @@ impl NormalBorder {
     pub fn with_color(&self, color: ColorF) -> Self {
         let mut b = *self;
         b.left.color = color;
         b.right.color = color;
         b.top.color = color;
         b.bottom.color = color;
         b
     }
+
+    /// Normalizes a border so that we don't render disallowed stuff, like inset
+    /// borders that are less than two pixels wide.
+    #[inline]
+    pub fn normalize(&mut self, widths: &BorderWidths) {
+        #[inline]
+        fn renders_small_border_solid(style: BorderStyle) -> bool {
+            match style {
+                BorderStyle::Groove |
+                BorderStyle::Ridge |
+                BorderStyle::Inset |
+                BorderStyle::Outset => true,
+                _ => false,
+            }
+        }
+
+        let normalize_side = |side: &mut BorderSide, width: f32| {
+            if renders_small_border_solid(side.style) && width < 2. {
+                side.style = BorderStyle::Solid;
+            }
+        };
+
+        normalize_side(&mut self.left, widths.left);
+        normalize_side(&mut self.right, widths.right);
+        normalize_side(&mut self.top, widths.top);
+        normalize_side(&mut self.bottom, widths.bottom);
+    }
 }
 
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
 pub enum RepeatMode {
     Stretch,
     Repeat,
     Round,
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use app_units::Au;
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 #[cfg(target_os = "windows")]
 pub use dwrote::FontDescriptor as NativeFontHandle;
 #[cfg(target_os = "macos")]
 use serde::de::{self, Deserialize, Deserializer};
@@ -342,16 +343,25 @@ impl Default for FontInstancePlatformOpt
 pub struct FontInstanceKey(pub IdNamespace, pub u32);
 
 impl FontInstanceKey {
     pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
         FontInstanceKey(namespace, key)
     }
 }
 
+#[derive(Clone)]
+pub struct FontInstanceData {
+    pub font_key: FontKey,
+    pub size: Au,
+    pub options: Option<FontInstanceOptions>,
+    pub platform_options: Option<FontInstancePlatformOptions>,
+    pub variations: Vec<FontVariation>,
+}
+
 pub type GlyphIndex = u32;
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct GlyphInstance {
     pub index: GlyphIndex,
     pub point: LayoutPoint,
 }
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate serde_bytes;
 
-use font::{FontInstanceKey, FontKey, FontTemplate};
+use font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate};
 use std::sync::Arc;
 use {DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use {IdNamespace, TileOffset, TileSize};
 use euclid::size2;
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ImageKey(pub IdNamespace, pub u32);
@@ -172,16 +172,17 @@ impl ImageData {
             ImageData::Raw(_) => true,
         }
     }
 }
 
 /// The resources exposed by the resource cache available for use by the blob rasterizer.
 pub trait BlobImageResources {
     fn get_font_data(&self, key: FontKey) -> &FontTemplate;
+    fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData>;
     fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
 }
 
 /// A handler on the render backend that can create rasterizer objects which will
 /// be sent to the scene builder thread to execute the rasterization.
 ///
 /// The handler is responsible for collecting resources, managing/updating blob commands
 /// and creating the rasterizer objects, but isn't expected to do any rasterization itself.
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-3fa5eb8aaa0172306bfdc5e87d1d0c9af39d103a
+d89e290c57aab76c45d8016975240cf762354e39
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -302,31 +302,29 @@ const WHITELIST_FUNCTIONS: &'static [&'s
     "JS_HasProperty",
     "JS_HasPropertyById",
     "JS::HeapObjectPostBarrier",
     "JS::HeapValuePostBarrier",
     "JS_InitializePropertiesFromCompatibleNativeObject",
     "JS::InitSelfHostedCode",
     "JS::IsConstructor",
     "JS::IsPromiseObject",
-    "JS_BeginRequest",
     "JS_ClearPendingException",
     "JS_DefineElement",
     "JS_DefineFunction",
     "JS_DefineFunctions",
     "JS_DefineProperties",
     "JS_DefineProperty",
     "JS_DefinePropertyById",
     "JS_DefineUCProperty",
     "JS::detail::InitWithFailureDiagnostic",
     "JS_DestroyContext",
     "JS::DisableIncrementalGC",
     "js::Dump.*",
     "JS_EncodeStringToUTF8",
-    "JS_EndRequest",
     "JS::EnterRealm",
     "JS_EnumerateStandardClasses",
     "JS_ErrorFromException",
     "JS_FireOnNewGlobalObject",
     "JS_free",
     "JS_GC",
     "JS_GetArrayBufferData",
     "JS_GetArrayBufferViewType",
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -191,18 +191,16 @@ impl Runtime {
             if use_internal_job_queue {
                 assert!(js::UseInternalJobQueues(js_context));
             }
 
             JS::InitSelfHostedCode(js_context);
 
             JS::SetWarningReporter(js_context, Some(report_warning));
 
-            JS_BeginRequest(js_context);
-
             Ok(Runtime {
                 cx: js_context,
             })
         }
     }
 
     /// Returns the underlying `JSContext` object.
     pub fn cx(&self) -> *mut JSContext {
@@ -251,17 +249,16 @@ impl Runtime {
             }
         }
     }
 }
 
 impl Drop for Runtime {
     fn drop(&mut self) {
         unsafe {
-            JS_EndRequest(self.cx);
             JS_DestroyContext(self.cx);
 
             CONTEXT.with(|context| {
                 assert_eq!(context.get(), self.cx);
                 context.set(ptr::null_mut());
             });
 
             if OUTSTANDING_RUNTIMES.fetch_sub(1, Ordering::SeqCst) == 1 {
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -437,17 +437,17 @@ bool
 js::IsAnyBuiltinEval(JSFunction* fun)
 {
     return fun->maybeNative() == IndirectEval;
 }
 
 static bool
 ExecuteInExtensibleLexicalEnvironment(JSContext* cx, HandleScript scriptArg, HandleObject env)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(env);
     MOZ_ASSERT(IsExtensibleLexicalEnvironment(env));
     MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
 
     RootedScript script(cx, scriptArg);
     if (script->realm() != cx->realm()) {
         script = CloneGlobalScript(cx, ScopeKind::NonSyntactic, script);
         if (!script)
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -1688,33 +1688,33 @@ SetObject::clear(JSContext* cx, unsigned
     return CallNonGenericMethod(cx, is, clear_impl, args);
 }
 
 /*** JS static utility functions *********************************************/
 
 static bool
 forEach(const char* funcName, JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisArg)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     RootedId forEachId(cx, NameToId(cx->names().forEach));
     RootedFunction forEachFunc(cx, JS::GetSelfHostedFunction(cx, funcName, forEachId, 2));
     if (!forEachFunc)
         return false;
 
     RootedValue fval(cx, ObjectValue(*forEachFunc));
     return Call(cx, fval, obj, callbackFn, thisArg, &fval);
 }
 
 // Handles Clear/Size for public jsapi map/set access
 template<typename RetT>
 RetT
 CallObjFunc(RetT(*ObjFunc)(JSContext*, HandleObject), JSContext* cx, HandleObject obj)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     // Always unwrap, in case this is an xray or cross-compartment wrapper.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
 
     // Enter the realm of the backing object before calling functions on
     // it.
@@ -1722,17 +1722,17 @@ CallObjFunc(RetT(*ObjFunc)(JSContext*, H
     return ObjFunc(cx, unwrappedObj);
 }
 
 // Handles Has/Delete for public jsapi map/set access
 bool
 CallObjFunc(bool(*ObjFunc)(JSContext *cx, HandleObject obj, HandleValue key, bool *rval),
             JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, key);
 
     // Always unwrap, in case this is an xray or cross-compartment wrapper.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
     JSAutoRealm ar(cx, unwrappedObj);
 
     // If we're working with a wrapped map/set, rewrap the key into the
@@ -1747,17 +1747,17 @@ CallObjFunc(bool(*ObjFunc)(JSContext *cx
 
 // Handles iterator generation for public jsapi map/set access
 template<typename Iter>
 bool
 CallObjFunc(bool(*ObjFunc)(JSContext* cx, Iter kind,
                            HandleObject obj, MutableHandleValue iter),
             JSContext *cx, Iter iterType, HandleObject obj, MutableHandleValue rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     // Always unwrap, in case this is an xray or cross-compartment wrapper.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
     {
         // Retrieve the iterator while in the unwrapped map/set's compartment,
         // otherwise we'll crash on a compartment assert.
@@ -1787,17 +1787,17 @@ JS_PUBLIC_API(uint32_t)
 JS::MapSize(JSContext* cx, HandleObject obj)
 {
     return CallObjFunc<uint32_t>(&MapObject::size, cx, obj);
 }
 
 JS_PUBLIC_API(bool)
 JS::MapGet(JSContext* cx, HandleObject obj, HandleValue key, MutableHandleValue rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, key, rval);
 
     // Unwrap the object, and enter its realm. If object isn't wrapped,
     // this is essentially a noop.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
     {
         JSAutoRealm ar(cx, unwrappedObj);
@@ -1818,17 +1818,17 @@ JS::MapGet(JSContext* cx, HandleObject o
             return false;
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::MapSet(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, key, val);
 
     // Unwrap the object, and enter its compartment. If object isn't wrapped,
     // this is essentially a noop.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
     {
         JSAutoRealm ar(cx, unwrappedObj);
@@ -1899,17 +1899,17 @@ JS_PUBLIC_API(uint32_t)
 JS::SetSize(JSContext *cx, HandleObject obj)
 {
     return CallObjFunc<uint32_t>(&SetObject::size, cx, obj);
 }
 
 JS_PUBLIC_API(bool)
 JS::SetAdd(JSContext *cx, HandleObject obj, HandleValue key)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, key);
 
     // Unwrap the object, and enter its compartment. If object isn't wrapped,
     // this is essentially a noop.
     RootedObject unwrappedObj(cx);
     unwrappedObj = UncheckedUnwrap(obj);
     {
         JSAutoRealm ar(cx, unwrappedObj);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4948,27 +4948,16 @@ IsConstructor(JSContext* cx, unsigned ar
     if (args.length() < 1)
         args.rval().setBoolean(false);
     else
         args.rval().setBoolean(IsConstructor(args[0]));
     return true;
 }
 
 static bool
-IsLegacyIterator(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() < 1)
-        args.rval().setBoolean(false);
-    else
-        args.rval().setBoolean(IsPropertyIterator(args[0]));
-    return true;
-}
-
-static bool
 SetTimeResolution(JSContext* cx, unsigned argc, Value* vp)
 {
    CallArgs args = CallArgsFromVp(argc, vp);
    RootedObject callee(cx, &args.callee());
 
    if (!args.requireAtLeast(cx, "setTimeResolution", 2))
         return false;
 
@@ -5820,20 +5809,16 @@ gc::ZealModeHelpText),
 "TimeSinceCreation()",
 "  Returns the time in milliseconds since process creation.\n"
 "  This uses a clock compatible with the profiler.\n"),
 
     JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
 "isConstructor(value)",
 "  Returns whether the value is considered IsConstructor.\n"),
 
-    JS_FN_HELP("isLegacyIterator", IsLegacyIterator, 1, 0,
-"isLegacyIterator(value)",
-"  Returns whether the value is considered is a legacy iterator.\n"),
-
     JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
 "getTimeZone()",
 "  Get the current time zone.\n"),
 
     JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
 "getDefaultLocale()",
 "  Get the current default locale.\n"),
 
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -198,17 +198,17 @@ JS::IsWeakMapObject(JSObject* obj)
 {
     return obj->is<WeakMapObject>();
 }
 
 JS_PUBLIC_API(bool)
 JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
                     MutableHandleValue rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(key);
     rval.setUndefined();
     ObjectValueMap* map = mapObj->as<WeakMapObject>().getMap();
     if (!map)
         return true;
     if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
         // Read barrier to prevent an incorrectly gray value from escaping the
         // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
@@ -217,17 +217,17 @@ JS::GetWeakMapEntry(JSContext* cx, Handl
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
                     HandleValue val)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(key, val);
     Handle<WeakMapObject*> rootedMap = mapObj.as<WeakMapObject>();
     return WeakCollectionPutEntryInternal(cx, rootedMap, key, val);
 }
 
 static bool
 WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/fuzz-tests/tests.cpp
+++ b/js/src/fuzz-tests/tests.cpp
@@ -71,30 +71,28 @@ jsfuzz_init(JSContext** cx, JS::Persiste
 
     const size_t MAX_STACK_SIZE = 500000;
 
     JS_SetNativeStackQuota(*cx, MAX_STACK_SIZE);
 
     js::UseInternalJobQueues(*cx);
     if (!JS::InitSelfHostedCode(*cx))
         return false;
-    JS_BeginRequest(*cx);
     global->init(*cx);
     *global = jsfuzz_createGlobal(*cx, nullptr);
     if (!*global)
         return false;
     JS::EnterRealm(*cx, *global);
     return true;
 }
 
 static void
 jsfuzz_uninit(JSContext* cx)
 {
     if (cx) {
-        JS_EndRequest(cx);
         JS_DestroyContext(cx);
         cx = nullptr;
     }
 }
 
 int
 main(int argc, char* argv[])
 {
--- a/js/src/gc/GCMarker.h
+++ b/js/src/gc/GCMarker.h
@@ -246,16 +246,25 @@ class GCMarker : public JSTracer
 
     // Mark the given GC thing and traverse its children at some point.
     template <typename T> void traverse(T thing);
 
     // Calls traverse on target after making additional assertions.
     template <typename S, typename T> void traverseEdge(S source, T* target);
     template <typename S, typename T> void traverseEdge(S source, const T& target);
 
+    // Helper methods that coerce their second argument to the base pointer
+    // type.
+    template <typename S> void traverseObjectEdge(S source, JSObject* target) {
+        traverseEdge(source, target);
+    }
+    template <typename S> void traverseStringEdge(S source, JSString* target) {
+        traverseEdge(source, target);
+    }
+
     // Notes a weak graph edge for later sweeping.
     template <typename T> void noteWeakEdge(T* edge);
 
     /*
      * Care must be taken changing the mark color from gray to black. The cycle
      * collector depends on the invariant that there are no black to gray edges
      * in the GC heap. This invariant lets the CC not trace through black
      * objects. If this invariant is violated, the cycle collector may free
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1232,137 +1232,137 @@ WasmFunctionScope::Data::trace(JSTracer*
 }
 void
 Scope::traceChildren(JSTracer* trc)
 {
     TraceNullableEdge(trc, &enclosing_, "scope enclosing");
     TraceNullableEdge(trc, &environmentShape_, "scope env shape");
     switch (kind_) {
       case ScopeKind::Function:
-        static_cast<FunctionScope::Data*>(data_)->trace(trc);
+        as<FunctionScope>().data().trace(trc);
         break;
       case ScopeKind::FunctionBodyVar:
       case ScopeKind::ParameterExpressionVar:
-        static_cast<VarScope::Data*>(data_)->trace(trc);
+        as<VarScope>().data().trace(trc);
         break;
       case ScopeKind::Lexical:
       case ScopeKind::SimpleCatch:
       case ScopeKind::Catch:
       case ScopeKind::NamedLambda:
       case ScopeKind::StrictNamedLambda:
-        static_cast<LexicalScope::Data*>(data_)->trace(trc);
+        as<LexicalScope>().data().trace(trc);
         break;
       case ScopeKind::Global:
       case ScopeKind::NonSyntactic:
-        static_cast<GlobalScope::Data*>(data_)->trace(trc);
+        as<GlobalScope>().data().trace(trc);
         break;
       case ScopeKind::Eval:
       case ScopeKind::StrictEval:
-        static_cast<EvalScope::Data*>(data_)->trace(trc);
+        as<EvalScope>().data().trace(trc);
         break;
       case ScopeKind::Module:
-        static_cast<ModuleScope::Data*>(data_)->trace(trc);
+        as<ModuleScope>().data().trace(trc);
         break;
       case ScopeKind::With:
         break;
       case ScopeKind::WasmInstance:
-        static_cast<WasmInstanceScope::Data*>(data_)->trace(trc);
+        as<WasmInstanceScope>().data().trace(trc);
         break;
       case ScopeKind::WasmFunction:
-        static_cast<WasmFunctionScope::Data*>(data_)->trace(trc);
+        as<WasmFunctionScope>().data().trace(trc);
         break;
     }
 }
 inline void
 js::GCMarker::eagerlyMarkChildren(Scope* scope)
 {
     if (scope->enclosing_)
-        traverseEdge(scope, static_cast<Scope*>(scope->enclosing_));
+        traverseEdge(scope, scope->enclosing_.get());
     if (scope->environmentShape_)
-        traverseEdge(scope, static_cast<Shape*>(scope->environmentShape_));
+        traverseEdge(scope, scope->environmentShape_.get());
     TrailingNamesArray* names = nullptr;
     uint32_t length = 0;
-    switch (scope->kind_) {
+    switch (scope->kind()) {
       case ScopeKind::Function: {
-        FunctionScope::Data* data = static_cast<FunctionScope::Data*>(scope->data_);
-        traverseEdge(scope, static_cast<JSObject*>(data->canonicalFunction));
-        names = &data->trailingNames;
-        length = data->length;
+        FunctionScope::Data& data = scope->as<FunctionScope>().data();
+        traverseObjectEdge(scope, data.canonicalFunction);
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::FunctionBodyVar:
       case ScopeKind::ParameterExpressionVar: {
-        VarScope::Data* data = static_cast<VarScope::Data*>(scope->data_);
-        names = &data->trailingNames;
-        length = data->length;
+        VarScope::Data& data = scope->as<VarScope>().data();
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::Lexical:
       case ScopeKind::SimpleCatch:
       case ScopeKind::Catch:
       case ScopeKind::NamedLambda:
       case ScopeKind::StrictNamedLambda: {
-        LexicalScope::Data* data = static_cast<LexicalScope::Data*>(scope->data_);
-        names = &data->trailingNames;
-        length = data->length;
+        LexicalScope::Data& data = scope->as<LexicalScope>().data();
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::Global:
       case ScopeKind::NonSyntactic: {
-        GlobalScope::Data* data = static_cast<GlobalScope::Data*>(scope->data_);
-        names = &data->trailingNames;
-        length = data->length;
+        GlobalScope::Data& data = scope->as<GlobalScope>().data();
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::Eval:
       case ScopeKind::StrictEval: {
-        EvalScope::Data* data = static_cast<EvalScope::Data*>(scope->data_);
-        names = &data->trailingNames;
-        length = data->length;
+        EvalScope::Data& data = scope->as<EvalScope>().data();
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::Module: {
-        ModuleScope::Data* data = static_cast<ModuleScope::Data*>(scope->data_);
-        traverseEdge(scope, static_cast<JSObject*>(data->module));
-        names = &data->trailingNames;
-        length = data->length;
+        ModuleScope::Data& data = scope->as<ModuleScope>().data();
+        traverseObjectEdge(scope, data.module);
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::With:
         break;
 
       case ScopeKind::WasmInstance: {
-        WasmInstanceScope::Data* data = static_cast<WasmInstanceScope::Data*>(scope->data_);
-        traverseEdge(scope, static_cast<JSObject*>(data->instance));
-        names = &data->trailingNames;
-        length = data->length;
+        WasmInstanceScope::Data& data = scope->as<WasmInstanceScope>().data();
+        traverseObjectEdge(scope, data.instance);
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
 
       case ScopeKind::WasmFunction: {
-        WasmFunctionScope::Data* data = static_cast<WasmFunctionScope::Data*>(scope->data_);
-        names = &data->trailingNames;
-        length = data->length;
+        WasmFunctionScope::Data& data = scope->as<WasmFunctionScope>().data();
+        names = &data.trailingNames;
+        length = data.length;
         break;
       }
     }
     if (scope->kind_ == ScopeKind::Function) {
         for (uint32_t i = 0; i < length; i++) {
-            if (JSAtom* name = names->operator[](i).name())
-                traverseEdge(scope, static_cast<JSString*>(name));
+            if (JSAtom* name = names->get(i).name())
+                traverseStringEdge(scope, name);
         }
     } else {
         for (uint32_t i = 0; i < length; i++)
-            traverseEdge(scope, static_cast<JSString*>(names->operator[](i).name()));
+            traverseStringEdge(scope, names->get(i).name());
     }
 }
 
 void
 js::ObjectGroup::traceChildren(JSTracer* trc)
 {
     AutoSweepObjectGroup sweep(this);
 
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -77,18 +77,16 @@ main(int argc, const char** argv)
     JSContext* cx = checkPtr(JS_NewContext(1024 * 1024));
 
     JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
     JS_SetNativeStackQuota(cx, 5000000);
 
     checkBool(JS::InitSelfHostedCode(cx));
     JS::SetWarningReporter(cx, reportWarning);
 
-    JSAutoRequest areq(cx);
-
     /* Create the global object. */
     JS::RealmOptions options;
     RootedObject global(cx, checkPtr(JS_NewGlobalObject(cx, &global_class,
                         nullptr, JS::FireOnNewGlobalHook, options)));
     JSAutoRealm ar(cx, global);
 
     /* Populate the global object with the standard globals,
        like Object and Array. */
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -19,16 +19,17 @@ const functionId       = 3;
 const tableId          = 4;
 const memoryId         = 5;
 const globalId         = 6;
 const exportId         = 7;
 const startId          = 8;
 const elemId           = 9;
 const codeId           = 10;
 const dataId           = 11;
+const gcFeatureOptInId = 42;
 
 // User-defined section names
 const nameName         = "name";
 
 // Name section name types
 const nameTypeModule   = 0;
 const nameTypeFunction = 1;
 const nameTypeLocal    = 2;
@@ -172,16 +173,20 @@ function moduleWithSections(sectionArray
     for (let section of sectionArray) {
         bytes.push(section.name);
         bytes.push(...varU32(section.body.length));
         bytes.push(...section.body);
     }
     return toU8(bytes);
 }
 
+function gcFeatureOptInSection(version) {
+    return { name: gcFeatureOptInId, body: [ version & 0x7F ] }
+}
+
 function sigSection(sigs) {
     var body = [];
     body.push(...varU32(sigs.length));
     for (let sig of sigs) {
         body.push(...varU32(FuncCode));
         body.push(...varU32(sig.args.length));
         for (let arg of sig.args)
             body.push(...varU32(arg));
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -42,19 +42,19 @@ assertErrorMessage(() => wasmEval(toU8(m
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(globalId))), CompileError, sectionError("global"));
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(exportId))), CompileError, sectionError("export"));
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(startId))), CompileError, sectionError("start"));
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(elemId))), CompileError, sectionError("elem"));
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(codeId))), CompileError, sectionError("code"));
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(dataId))), CompileError, sectionError("data"));
 
 // unknown sections are unconditionally rejected
-assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(42))), CompileError, unknownSection);
-assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(42, 0))), CompileError, unknownSection);
-assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(42, 1, 0))), CompileError, unknownSection);
+assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(37))), CompileError, unknownSection);
+assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(37, 0))), CompileError, unknownSection);
+assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(37, 1, 0))), CompileError, unknownSection);
 
 // user sections have special rules
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0))), CompileError, sectionError("custom"));  // no length
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 0))), CompileError, sectionError("custom"));  // no id
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 0, 0))), CompileError, sectionError("custom"));  // payload too small to have id length
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 1, 1))), CompileError, sectionError("custom"));  // id not present
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 1, 1, 65))), CompileError, sectionError("custom"));  // id length doesn't fit in section
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 1, 0, 0))), CompileError, sectionError("custom"));  // second, unfinished custom section
--- a/js/src/jit-test/tests/wasm/gc/anyref-global-postbarrier.js
+++ b/js/src/jit-test/tests/wasm/gc/anyref-global-postbarrier.js
@@ -7,34 +7,37 @@ const { startProfiling, endProfiling, as
 // Dummy constructor.
 function Baguette(calories) {
     this.calories = calories;
 }
 
 // Ensure the baseline compiler sync's before the postbarrier.
 (function() {
     wasmEvalText(`(module
+        (gc_feature_opt_in 1)
         (global (mut anyref) (ref.null anyref))
         (func (export "f")
             get_global 0
             ref.null anyref
             set_global 0
             set_global 0
         )
     )`).exports.f();
 })();
 
 let exportsPlain = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (global i32 (i32.const 42))
     (global $g (mut anyref) (ref.null anyref))
     (func (export "set") (param anyref) get_local 0 set_global $g)
     (func (export "get") (result anyref) get_global $g)
 )`).exports;
 
 let exportsObj = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (global $g (export "g") (mut anyref) (ref.null anyref))
     (func (export "set") (param anyref) get_local 0 set_global $g)
     (func (export "get") (result anyref) get_global $g)
 )`).exports;
 
 // 7 => Generational GC zeal.
 gczeal(7, 1);
 
--- a/js/src/jit-test/tests/wasm/gc/anyref-global-prebarrier.js
+++ b/js/src/jit-test/tests/wasm/gc/anyref-global-prebarrier.js
@@ -1,15 +1,16 @@
 if (!wasmGcEnabled()) {
     quit(0);
 }
 
 const { startProfiling, endProfiling, assertEqPreciseStacks, isSingleStepProfilingEnabled } = WasmHelpers;
 
 let e = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (global $g (mut anyref) (ref.null anyref))
     (func (export "set") (param anyref) get_local 0 set_global $g)
 )`).exports;
 
 let obj = { field: null };
 
 // GCZeal mode 4 implies that prebarriers are being verified at many
 // locations in the interpreter, during interrupt checks, etc. It can be ultra
--- a/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js
+++ b/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js
@@ -1,14 +1,15 @@
 if (!wasmGcEnabled()) {
     quit(0);
 }
 
 gczeal(14, 1);
 let { exports } = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (global $anyref (import "glob" "anyref") anyref)
     (func (export "get") (result anyref) get_global $anyref)
 )`, {
     glob: {
         anyref: { sentinel: "lol" },
     }
 });
 assertEq(exports.get().sentinel, "lol");
--- a/js/src/jit-test/tests/wasm/gc/anyref.js
+++ b/js/src/jit-test/tests/wasm/gc/anyref.js
@@ -9,63 +9,67 @@ function Baguette(calories) {
     this.calories = calories;
 }
 
 // Type checking.
 
 const { validate, CompileError } = WebAssembly;
 
 assertErrorMessage(() => wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (result anyref)
         i32.const 42
     )
 )`), CompileError, mismatchError('i32', 'anyref'));
 
 assertErrorMessage(() => wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (result anyref)
         i32.const 0
         ref.null anyref
         i32.const 42
         select
     )
 )`), CompileError, /select operand types/);
 
 assertErrorMessage(() => wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (result i32)
         ref.null anyref
         if
             i32.const 42
         end
     )
 )`), CompileError, mismatchError('anyref', 'i32'));
 
 
 // Basic compilation tests.
 
 let simpleTests = [
-    "(module (func (drop (ref.null anyref))))",
-    "(module (func $test (local anyref)))",
-    "(module (func $test (param anyref)))",
-    "(module (func $test (result anyref) (ref.null anyref)))",
-    "(module (func $test (block anyref (unreachable)) unreachable))",
-    "(module (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
-    `(module (import "a" "b" (param anyref)))`,
-    `(module (import "a" "b" (result anyref)))`,
-    `(module (global anyref (ref.null anyref)))`,
-    `(module (global (mut anyref) (ref.null anyref)))`,
+    "(module (gc_feature_opt_in 1) (func (drop (ref.null anyref))))",
+    "(module (gc_feature_opt_in 1) (func $test (local anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (param anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (result anyref) (ref.null anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (block anyref (unreachable)) unreachable))",
+    "(module (gc_feature_opt_in 1) (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
+    `(module (gc_feature_opt_in 1) (import "a" "b" (param anyref)))`,
+    `(module (gc_feature_opt_in 1) (import "a" "b" (result anyref)))`,
+    `(module (gc_feature_opt_in 1) (global anyref (ref.null anyref)))`,
+    `(module (gc_feature_opt_in 1) (global (mut anyref) (ref.null anyref)))`,
 ];
 
 for (let src of simpleTests) {
     wasmEvalText(src, {a:{b(){}}});
     assertEq(validate(wasmTextToBinary(src)), true);
 }
 
 // Basic behavioral tests.
 
 let { exports } = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (export "is_null") (result i32)
         ref.null anyref
         ref.is_null
     )
 
     (func $sum (result i32) (param i32)
         get_local 0
         i32.const 42
@@ -93,16 +97,17 @@ let { exports } = wasmEvalText(`(module
 
 assertEq(exports.is_null(), 1);
 assertEq(exports.is_null_spill(), 1);
 assertEq(exports.is_null_local(), 1);
 
 // Anyref param and result in wasm functions.
 
 exports = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (export "is_null") (result i32) (param $ref anyref)
         get_local $ref
         ref.is_null
     )
 
     (func (export "ref_or_null") (result anyref) (param $ref anyref) (param $selector i32)
         get_local $ref
         ref.null anyref
@@ -150,31 +155,33 @@ assertEq(ref.calories, baguette.calories
 
 ref = exports.nested(baguette, 0);
 assertEq(ref, baguette);
 assertEq(ref.calories, baguette.calories);
 
 // Make sure grow-memory isn't blocked by the lack of gc.
 (function() {
     assertEq(wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (memory 0 64)
     (func (export "f") (param anyref) (result i32)
         i32.const 10
         grow_memory
         drop
         current_memory
     )
 )`).exports.f({}), 10);
 })();
 
 // More interesting use cases about control flow joins.
 
 function assertJoin(body) {
     let val = { i: -1 };
     assertEq(wasmEvalText(`(module
+        (gc_feature_opt_in 1)
         (func (export "test") (param $ref anyref) (param $i i32) (result anyref)
             ${body}
         )
     )`).exports.test(val), val);
     assertEq(val.i, -1);
 }
 
 assertJoin("(block anyref get_local $ref)");
@@ -231,16 +238,17 @@ assertJoin(`(block $out anyref (block $u
     i32.add
     tee_local $i
     br_table $unreachable $out
     ) unreachable))
 `);
 
 let x = { i: 42 }, y = { f: 53 };
 exports = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (func (export "test") (param $lhs anyref) (param $rhs anyref) (param $i i32) (result anyref)
         get_local $lhs
         get_local $rhs
         get_local $i
         select
     )
 )`).exports;
 
@@ -285,16 +293,17 @@ let imports = {
         },
         ret() {
             return imports.myBaguette;
         }
     }
 };
 
 exports = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (import $ret "funcs" "ret" (result anyref))
     (import $param "funcs" "param" (param anyref))
 
     (func (export "param") (param $x anyref) (param $y anyref)
         get_local $y
         get_local $x
         call $param
         call $param
@@ -313,16 +322,17 @@ imports.myBaguette = null;
 assertEq(exports.ret(), null);
 
 imports.myBaguette = new Baguette(1337);
 assertEq(exports.ret(), imports.myBaguette);
 
 // Check lazy stubs generation.
 
 exports = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (import $mirror "funcs" "mirror" (param anyref) (result anyref))
     (import $augment "funcs" "augment" (param anyref) (result anyref))
 
     (global $count_f (mut i32) (i32.const 0))
     (global $count_g (mut i32) (i32.const 0))
 
     (func $f (param $param anyref) (result anyref)
         i32.const 1
@@ -396,38 +406,39 @@ assertEq(x.i, 24);
 assertEq(x.newProp, "hello");
 assertEq(exports.count_f(), 1);
 assertEq(exports.count_g(), 1);
 
 // Globals.
 
 // Anyref globals in wasm modules.
 
-assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "anyref") anyref))`, { glob: { anyref: 42 } }),
+assertErrorMessage(() => wasmEvalText(`(module (gc_feature_opt_in 1) (global (import "glob" "anyref") anyref))`, { glob: { anyref: 42 } }),
     WebAssembly.LinkError,
     /import object field 'anyref' is not a Object-or-null/);
 
-assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "anyref") anyref))`, { glob: { anyref: new WebAssembly.Global({ value: 'i32' }, 42) } }),
+assertErrorMessage(() => wasmEvalText(`(module (gc_feature_opt_in 1) (global (import "glob" "anyref") anyref))`, { glob: { anyref: new WebAssembly.Global({ value: 'i32' }, 42) } }),
     WebAssembly.LinkError,
     /imported global type mismatch/);
 
-assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "i32") i32))`, { glob: { i32: {} } }),
+assertErrorMessage(() => wasmEvalText(`(module (gc_feature_opt_in 1) (global (import "glob" "i32") i32))`, { glob: { i32: {} } }),
     WebAssembly.LinkError,
     /import object field 'i32' is not a Number/);
 
 imports = {
     constants: {
         imm_null: null,
         imm_bread: new Baguette(321),
         mut_null: new WebAssembly.Global({ value: "anyref", mutable: true }, null),
         mut_bread: new WebAssembly.Global({ value: "anyref", mutable: true }, new Baguette(123))
     }
 };
 
 exports = wasmEvalText(`(module
+    (gc_feature_opt_in 1)
     (global $g_imp_imm_null  (import "constants" "imm_null") anyref)
     (global $g_imp_imm_bread (import "constants" "imm_bread") anyref)
 
     (global $g_imp_mut_null   (import "constants" "mut_null") (mut anyref))
     (global $g_imp_mut_bread  (import "constants" "mut_bread") (mut anyref))
 
     (global $g_imm_null     anyref (ref.null anyref))
     (global $g_imm_getglob  anyref (get_global $g_imp_imm_bread))
--- a/js/src/jit-test/tests/wasm/gc/binary.js
+++ b/js/src/jit-test/tests/wasm/gc/binary.js
@@ -3,17 +3,20 @@ if (!wasmGcEnabled()) {
 }
 
 load(libdir + "wasm-binary.js");
 
 const v2vSig = {args:[], ret:VoidCode};
 const v2vSigSection = sigSection([v2vSig]);
 
 function checkInvalid(body, errorMessage) {
-    assertErrorMessage(() => new WebAssembly.Module(moduleWithSections([v2vSigSection, declSection([0]), bodySection([body])])), WebAssembly.CompileError, errorMessage);
+    assertErrorMessage(() => new WebAssembly.Module(
+        moduleWithSections([gcFeatureOptInSection(1), v2vSigSection, declSection([0]), bodySection([body])])),
+                       WebAssembly.CompileError,
+                       errorMessage);
 }
 
 const invalidRefNullBody = funcBody({locals:[], body:[
     RefNull,
     RefCode,
     0x42,
 
     RefNull,
--- a/js/src/jit-test/tests/wasm/gc/debugger.js
+++ b/js/src/jit-test/tests/wasm/gc/debugger.js
@@ -1,24 +1,25 @@
 if (!wasmGcEnabled() || !wasmDebuggingIsSupported()) {
     quit(0);
 }
 
 (function() {
     let g = newGlobal();
     let dbg = new Debugger(g);
-    g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func (result anyref) (param anyref) get_local 0) (export "" 0))')));`);
+    g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (gc_feature_opt_in 1) (func (result anyref) (param anyref) get_local 0) (export "" 0))')));`);
 })();
 
 (function() {
     var g = newGlobal();
     g.parent = this;
 
     let src = `
       (module
+        (gc_feature_opt_in 1)
         (func (export "func") (result anyref) (param $ref anyref)
             get_local $ref
         )
       )
     `;
 
     g.eval(`
         var obj = { somekey: 'somevalue' };
--- a/js/src/jit-test/tests/wasm/gc/disabled.js
+++ b/js/src/jit-test/tests/wasm/gc/disabled.js
@@ -1,28 +1,49 @@
 if (wasmGcEnabled()) {
     quit();
 }
 
 const { CompileError, validate } = WebAssembly;
 
-const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /(unrecognized opcode|bad type|invalid inline block type)/;
+const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|(Structure|reference) types not enabled|invalid inline block type/;
 
 function assertValidateError(text) {
     assertEq(validate(wasmTextToBinary(text)), false);
 }
 
 let simpleTests = [
-    "(module (func (drop (ref.null anyref))))",
-    "(module (func $test (local anyref)))",
-    "(module (func $test (param anyref)))",
-    "(module (func $test (result anyref) (ref.null anyref)))",
-    "(module (func $test (block anyref (unreachable)) unreachable))",
-    "(module (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
-    `(module (import "a" "b" (param anyref)))`,
-    `(module (import "a" "b" (result anyref)))`,
+    "(module (gc_feature_opt_in 1) (func (drop (ref.null anyref))))",
+    "(module (gc_feature_opt_in 1) (func $test (local anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (param anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (result anyref) (ref.null anyref)))",
+    "(module (gc_feature_opt_in 1) (func $test (block anyref (unreachable)) unreachable))",
+    "(module (gc_feature_opt_in 1) (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
+    `(module (gc_feature_opt_in 1) (import "a" "b" (param anyref)))`,
+    `(module (gc_feature_opt_in 1) (import "a" "b" (result anyref)))`,
+    `(module (gc_feature_opt_in 1) (type $s (struct)))`,
 ];
 
+// Two distinct failure modes:
+//
+// - if we have no compiled-in support for wasm-gc we'll get a syntax error when
+//   parsing the test programs that use ref types and structures.
+//
+// - if we have compiled-in support for wasm-gc, but wasm-gc is not currently
+//   enabled, we will succeed parsing but fail compilation and validation.
+//
+// But it should always be all of one or all of the other.
+
+var fail_syntax = 0;
+var fail_compile = 0;
 for (let src of simpleTests) {
-    print(src)
+    try {
+        wasmTextToBinary(src);
+    } catch (e) {
+        assertEq(e instanceof SyntaxError, true);
+        fail_syntax++;
+        continue;
+    }
     assertErrorMessage(() => wasmEvalText(src), CompileError, UNRECOGNIZED_OPCODE_OR_BAD_TYPE);
     assertValidateError(src);
+    fail_compile++;
 }
+assertEq((fail_syntax == 0) != (fail_compile == 0), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/gc-feature-opt-in.js
@@ -0,0 +1,120 @@
+if (!wasmGcEnabled()) {
+    quit(0);
+}
+
+// Encoding.  If the section is present it must be first.
+
+var bad_order =
+    new Uint8Array([0x00, 0x61, 0x73, 0x6d,
+                    0x01, 0x00, 0x00, 0x00,
+
+                    0x01,                   // Type section
+                    0x01,                   // Section size
+                    0x00,                   // Zero types
+
+                    0x2a,                   // GcFeatureOptIn section
+                    0x01,                   // Section size
+                    0x01]);                 // Version
+
+assertErrorMessage(() => new WebAssembly.Module(bad_order),
+                   WebAssembly.CompileError,
+                   /expected custom section/);
+
+// Version numbers.  Version 1 is good, version 2 is bad.
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 2))`)),
+                   WebAssembly.CompileError,
+                   /unsupported version of the gc feature/);
+
+// Struct types are only available if we opt in.
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+      (type (struct (field i32))))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (type (struct (field i32))))`)),
+                   WebAssembly.CompileError,
+                   /Structure types not enabled/);
+
+// Parameters of ref type are only available if we opt in.
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+      (type (func (param anyref))))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (type (func (param anyref))))`)),
+                   WebAssembly.CompileError,
+                   /reference types not enabled/);
+
+// Ditto returns
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+      (type (func (result anyref))))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (type (func (result anyref))))`)),
+                   WebAssembly.CompileError,
+                   /reference types not enabled/);
+
+// Ditto locals
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+      (func (result i32)
+       (local anyref)
+       (i32.const 0)))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (func (result i32)
+       (local anyref)
+       (i32.const 0)))`)),
+                   WebAssembly.CompileError,
+                   /reference types not enabled/);
+
+// Ditto globals
+
+new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+      (global (mut anyref) (ref.null anyref)))`));
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (global (mut anyref) (ref.null anyref)))`)),
+                   WebAssembly.CompileError,
+                   /reference types not enabled/);
+
+// Ref instructions are only available if we opt in.
+//
+// When testing these we need to avoid struct types or parameters, locals,
+// returns, or globals of ref type, or guards on those will preempt the guards
+// on the instructions.
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (func (ref.null anyref)))`)),
+                   WebAssembly.CompileError,
+                   /unrecognized opcode/);
+
+assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (func ref.is_null))`)),
+                   WebAssembly.CompileError,
+                   /unrecognized opcode/);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/ion-and-baseline.js
@@ -0,0 +1,94 @@
+// Attempt to test intercalls from ion to baseline and back.
+//
+// We get into this situation when the modules are compiled with different
+// tiering policies, or one tiers up before the other, or (for now) one opts
+// into gc and is baseline-compiled and the other does not and is ion-compiled.
+// There are lots of variables here.  Generally, a small module will be
+// ion-compiled unless there's reason to baseline-compile it, so we're likely
+// actually testing something here.
+//
+// Some logging with printf confirms that refmod is baseline-compiled and
+// nonrefmod is ion-compiled at present, with --wasm-gc enabled.
+
+if (!wasmGcEnabled())
+    quit(0);
+
+// Ion can't talk about references yet *but* we can call indirect from Ion to a
+// baseline module that has exported a function that accepts or returns anyref,
+// without the caller knowing this or having to declare it.  All such calls
+// should fail in an orderly manner with a type mismatch, at the point of the
+// call.
+
+var refmod = new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (gc_feature_opt_in 1)
+
+      (import $tbl "" "tbl" (table 4 anyfunc))
+      (import $print "" "print" (func (param i32)))
+
+      (type $htype (func (param anyref)))
+      (type $itype (func (result anyref)))
+
+      (elem (i32.const 0) $f $g)
+
+      (func $f (param anyref)
+       (call $print (i32.const 1)))
+
+      (func $g (result anyref)
+       (call $print (i32.const 2))
+       (ref.null anyref))
+
+      (func (export "test_h")
+       (call_indirect $htype (ref.null anyref) (i32.const 2)))
+
+      (func (export "test_i")
+       (drop (call_indirect $itype (i32.const 3))))
+
+     )`));
+
+var nonrefmod = new WebAssembly.Module(wasmTextToBinary(
+    `(module
+      (import $tbl "" "tbl" (table 4 anyfunc))
+      (import $print "" "print" (func (param i32)))
+
+      (type $ftype (func (param i32)))
+      (type $gtype (func (result i32)))
+
+      (elem (i32.const 2) $h $i)
+
+      ;; Should fail because of the signature mismatch: parameter
+      (func (export "test_f")
+       (call_indirect $ftype (i32.const 37) (i32.const 0)))
+
+      ;; Should fail because of the signature mismatch: return value
+      (func (export "test_g")
+       (drop (call_indirect $gtype (i32.const 1))))
+
+      (func $h (param i32)
+       (call $print (i32.const 2)))
+
+      (func $i (result i32)
+       (call $print (i32.const 3))
+       (i32.const 37))
+     )`));
+
+var tbl = new WebAssembly.Table({initial:4, element:"anyfunc"});
+var refins = new WebAssembly.Instance(refmod, {"":{print, tbl}}).exports;
+var nonrefins = new WebAssembly.Instance(nonrefmod, {"":{print, tbl}}).exports;
+
+assertErrorMessage(() => nonrefins.test_f(),
+                   WebAssembly.RuntimeError,
+                   /indirect call signature mismatch/);
+
+assertErrorMessage(() => nonrefins.test_g(),
+                   WebAssembly.RuntimeError,
+                   /indirect call signature mismatch/);
+
+assertErrorMessage(() => refins.test_h(),
+                   WebAssembly.RuntimeError,
+                   /indirect call signature mismatch/);
+
+assertErrorMessage(() => refins.test_i(),
+                   WebAssembly.RuntimeError,
+                   /indirect call signature mismatch/);
+
--- a/js/src/jit-test/tests/wasm/gc/ref-global.js
+++ b/js/src/jit-test/tests/wasm/gc/ref-global.js
@@ -2,16 +2,17 @@ if (!wasmGcEnabled())
     quit(0);
 
 // Basic private-to-module functionality.  At the moment all we have is null
 // pointers, not very exciting.
 
 {
     let bin = wasmTextToBinary(
         `(module
+          (gc_feature_opt_in 1)
 
           (type $point (struct
                         (field $x f64)
                         (field $y f64)))
 
           (global $g1 (mut (ref $point)) (ref.null (ref $point)))
           (global $g2 (mut (ref $point)) (ref.null (ref $point)))
           (global $g3 (ref $point) (ref.null (ref $point)))
@@ -37,28 +38,30 @@ if (!wasmGcEnabled())
     ins.clear();                // Should not crash
 }
 
 // We can't import a global of a reference type because we don't have a good
 // notion of structural type compatibility yet.
 {
     let bin = wasmTextToBinary(
         `(module
+          (gc_feature_opt_in 1)
           (type $box (struct (field $val i32)))
           (import "m" "g" (global (mut (ref $box)))))`);
 
     assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError,
                        /cannot expose reference type/);
 }
 
 // We can't export a global of a reference type because we can't later import
 // it.  (Once we can export it, the value setter must also perform the necessary
 // subtype check, which implies we have some notion of exporting types, and we
 // don't have that yet.)
 {
     let bin = wasmTextToBinary(
         `(module
+          (gc_feature_opt_in 1)
           (type $box (struct (field $val i32)))
           (global $boxg (export "box") (mut (ref $box)) (ref.null (ref $box))))`);
 
     assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError,
                        /cannot expose reference type/);
 }
--- a/js/src/jit-test/tests/wasm/gc/ref-restrict.js
+++ b/js/src/jit-test/tests/wasm/gc/ref-restrict.js
@@ -47,269 +47,302 @@ if (!wasmGcEnabled())
 function wasmCompile(text) {
     return new WebAssembly.Module(wasmTextToBinary(text));
 }
 
 // Exported function can't take ref type parameter, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $x i32)))
       (func (export "f") (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (func (export "f") (param anyref) (unreachable)))`),
          "object");
 
 // Exported function can't return ref result, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $x i32)))
       (func (export "f") (result (ref $box)) (ref.null (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (func (export "f") (result anyref) (ref.null anyref)))`),
          "object");
 
 // Imported function can't take ref parameter, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $x i32)))
       (import "m" "f" (param (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "f" (param anyref)))`),
          "object");
 
 // Imported function can't return ref type, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $x i32)))
       (import "m" "f" (param i32) (result (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "f" (param i32) (result anyref)))`),
          "object");
 
 // Imported global can't be of Ref type (irrespective of mutability), though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (import "m" "g" (global (mut (ref $box)))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (import "m" "g" (global (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "g" (global (mut anyref))))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "g" (global anyref)))`),
          "object");
 
 // Exported global can't be of Ref type (irrespective of mutability), though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (global $boxg (export "box") (mut (ref $box)) (ref.null (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (global $boxg (export "box") (ref $box) (ref.null (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (global $boxg (export "box") (mut anyref) (ref.null anyref)))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (global $boxg (export "box") anyref (ref.null anyref)))`),
          "object");
 
 // Exported table cannot reference functions that are exposed for Ref, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (table (export "tbl") 1 anyfunc)
       (elem (i32.const 0) $f1)
       (func $f1 (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (table (export "tbl") 1 anyfunc)
       (elem (i32.const 0) $f1)
       (func $f1 (result (ref $box)) (ref.null (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (table (export "tbl") 1 anyfunc)
       (elem (i32.const 0) $f1)
       (func $f1 (param anyref) (unreachable)))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (table (export "tbl") 1 anyfunc)
       (elem (i32.const 0) $f1)
       (func $f1 (result anyref) (ref.null anyref)))`),
          "object");
 
 // Imported table cannot reference functions that are exposed for Ref, though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (import "m" "tbl" (table 1 anyfunc))
       (elem (i32.const 0) $f1)
       (func $f1 (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (import "m" "tbl" (table 1 anyfunc))
       (elem (i32.const 0) $f1)
       (func $f1 (result (ref $box)) (ref.null (ref $box))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "tbl" (table 1 anyfunc))
       (elem (i32.const 0) $f1)
       (func $f1 (param anyref) (unreachable)))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (import "m" "tbl" (table 1 anyfunc))
       (elem (i32.const 0) $f1)
       (func $f1 (result anyref) (ref.null anyref)))`),
          "object");
 
 // Can't call via exported table with type that is exposed for Ref, though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (type $fn (func (param (ref $box))))
       (table (export "tbl") 1 anyfunc)
       (func (param i32)
        (call_indirect $fn (ref.null (ref $box)) (get_local 0))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (type $fn (func (result (ref $box))))
       (table (export "tbl") 1 anyfunc)
       (func (param i32) (result (ref $box))
        (call_indirect $fn (get_local 0))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $fn (func (param anyref)))
       (table (export "tbl") 1 anyfunc)
       (func (param i32)
        (call_indirect $fn (ref.null anyref) (get_local 0))))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $fn (func (result anyref)))
       (table (export "tbl") 1 anyfunc)
       (func (param i32) (result anyref)
        (call_indirect $fn (get_local 0))))`),
          "object");
 
 // Can't call via imported table with type that is exposed for Ref, though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (type $fn (func (param (ref $box))))
       (import "m" "tbl" (table 1 anyfunc))
       (func (param i32)
        (call_indirect $fn (ref.null (ref $box)) (get_local 0))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $box (struct (field $val i32)))
       (type $fn (func (result (ref $box))))
       (import "m" "tbl" (table 1 anyfunc))
       (func (param i32) (result (ref $box))
        (call_indirect $fn (get_local 0))))`),
                    WebAssembly.CompileError,
                    /cannot expose reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $fn (func (param anyref)))
       (import "m" "tbl" (table 1 anyfunc))
       (func (param i32)
        (call_indirect $fn (ref.null anyref) (get_local 0))))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
+      (gc_feature_opt_in 1)
       (type $fn (func (result anyref)))
       (import "m" "tbl" (table 1 anyfunc))
       (func (param i32) (result anyref)
        (call_indirect $fn (get_local 0))))`),
          "object");
 
 // We can call via a private table with a type that is exposed for Ref.
 
 {
     let m = wasmCompile(
         `(module
+          (gc_feature_opt_in 1)
           (type $box (struct (field $val i32)))
           (type $fn (func (param (ref $box)) (result i32)))
           (table 1 anyfunc)
           (elem (i32.const 0) $f1)
           (func $f1 (param (ref $box)) (result i32) (i32.const 37))
           (func (export "f") (param i32) (result i32)
            (call_indirect $fn (ref.null (ref $box)) (get_local 0))))`);
     let i = new WebAssembly.Instance(m).exports;
--- a/js/src/jit-test/tests/wasm/gc/ref.js
+++ b/js/src/jit-test/tests/wasm/gc/ref.js
@@ -1,18 +1,19 @@
 if (!wasmGcEnabled()) {
     assertErrorMessage(() => wasmEvalText(`(module (func (param (ref 0)) (unreachable)))`),
-                       WebAssembly.CompileError, /bad type/);
+                       WebAssembly.CompileError, /reference types not enabled/);
     quit(0);
 }
 
 // Parsing and resolving.
 
 var bin = wasmTextToBinary(
     `(module
+      (gc_feature_opt_in 1)
       (type $cons (struct
                    (field $car i32)
                    (field $cdr (ref $cons))))
 
       (type $odd (struct
                   (field $x i32)
                   (field $to_even (ref $even))))
 
@@ -62,137 +63,152 @@ var bin = wasmTextToBinary(
 // Validation
 
 assertEq(WebAssembly.validate(bin), true);
 
 // ref.is_null should work on any reference type
 
 new WebAssembly.Module(wasmTextToBinary(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct))
  (func $null (param (ref $s)) (result i32)
    (ref.is_null (get_local 0))))
 `))
 
 // Automatic upcast to anyref
 
 new WebAssembly.Module(wasmTextToBinary(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (func $f (param (ref $s)) (call $g (get_local 0)))
  (func $g (param anyref) (unreachable)))
 `));
 
 // Misc failure modes
 
 assertErrorMessage(() => wasmEvalText(`
 (module
-  (func (param (ref $odd)) (unreachable)))
+ (gc_feature_opt_in 1)
+ (func (param (ref $odd)) (unreachable)))
 `),
 SyntaxError, /Type label.*not found/);
 
 // Ref type mismatch in parameter is allowed through the prefix rule
 // but not if the structs are incompatible.
 
 wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field i32)))
  (func $f (param (ref $s)) (unreachable))
  (func $g (param (ref $t)) (call $f (get_local 0)))
 )`);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field f32))) ;; Incompatible type
  (func $f (param (ref $s)) (unreachable))
  (func $g (param (ref $t)) (call $f (get_local 0)))
 )`),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field (mut i32)))) ;; Incompatible mutability
  (func $f (param (ref $s)) (unreachable))
  (func $g (param (ref $t)) (call $f (get_local 0)))
 )`),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 // Ref type mismatch in assignment to local but the prefix rule allows
 // the assignment to succeed if the structs are the same.
 
 wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field i32)))
  (func $f (param (ref $s)) (local (ref $t)) (set_local 1 (get_local 0))))
 `)
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field f32)))
  (func $f (param (ref $s)) (local (ref $t)) (set_local 1 (get_local 0))))
 `),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field (mut i32))))
  (func $f (param (ref $s)) (unreachable))
  (func $g (param (ref $t)) (call $f (get_local 0)))
 )`),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 // Ref type mismatch in return but the prefix rule allows the return
 // to succeed if the structs are the same.
 
 wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field i32)))
  (func $f (param (ref $s)) (result (ref $t)) (get_local 0)))
 `);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field f32)))
  (func $f (param (ref $s)) (result (ref $t)) (get_local 0)))
 `),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (type $t (struct (field (mut i32))))
  (func $f (param (ref $s)) (result (ref $t)) (get_local 0)))
 `),
 WebAssembly.CompileError, /expression has type ref.*but expected ref/);
 
 // Ref type can't reference a function type
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $x (func (param i32)))
  (func $f (param (ref $x)) (unreachable)))
 `),
 SyntaxError, /Type label.*not found/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type (func (param i32)))
  (func $f (param (ref 0)) (unreachable)))
 `),
 WebAssembly.CompileError, /does not reference a struct type/);
 
 // No automatic downcast from anyref
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32)))
  (func $f (param anyref) (call $g (get_local 0)))
  (func $g (param (ref $s)) (unreachable)))
 `),
 WebAssembly.CompileError, /expression has type anyref but expected ref/);
--- a/js/src/jit-test/tests/wasm/gc/structs.js
+++ b/js/src/jit-test/tests/wasm/gc/structs.js
@@ -1,16 +1,14 @@
-if (!wasmGcEnabled()) {
-    assertErrorMessage(() => wasmEvalText(`(module (type $s (struct)))`),
-                       WebAssembly.CompileError, /Structure types not enabled/);
+if (!wasmGcEnabled())
     quit();
-}
 
 var bin = wasmTextToBinary(
     `(module
+      (gc_feature_opt_in 1)
 
       (table 2 anyfunc)
       (elem (i32.const 0) $doit $doitagain)
 
       ;; Type array has a mix of types
 
       (type $f1 (func (param i32) (result i32)))
 
@@ -68,82 +66,95 @@ assertEq(ins.hello(4.0, 1), 16.0)
 
 assertEq(ins.x1(12), 36)
 assertEq(ins.x2(8), Math.PI)
 
 // The field name is optional, so this should work.
 
 wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field i32))))
 `)
 
 // Empty structs are OK.
 
 wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct)))
 `)
 
 // Multiply defined structures.
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field $x i32)))
  (type $s (struct (field $y i32))))
 `),
 SyntaxError, /duplicate type name/);
 
 // Bogus type definition syntax.
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s))
 `),
 SyntaxError, /parsing wasm text/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (field $x i32)))
 `),
 SyntaxError, /bad type definition/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (field $x i31))))
 `),
 SyntaxError, /parsing wasm text/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct (fjeld $x i32))))
 `),
 SyntaxError, /parsing wasm text/);
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct abracadabra)))
 `),
 SyntaxError, /parsing wasm text/);
 
 // Function should not reference struct type: syntactic test
 
 assertErrorMessage(() => wasmEvalText(`
 (module
+ (gc_feature_opt_in 1)
  (type $s (struct))
  (type $f (func (param i32) (result i32)))
  (func (type 0) (param i32) (result i32) (unreachable)))
 `),
 WebAssembly.CompileError, /signature index references non-signature/);
 
 // Function should not reference struct type: binary test
 
 var bad = new Uint8Array([0x00, 0x61, 0x73, 0x6d,
                           0x01, 0x00, 0x00, 0x00,
 
+                          0x2a,                   // GcFeatureOptIn section
+                          0x01,                   // Section size
+                          0x01,                   // Version
+
                           0x01,                   // Type section
                           0x03,                   // Section size
                           0x01,                   // One type
                           0x50,                   // Struct
                           0x00,                   // Zero fields
 
                           0x03,                   // Function section
                           0x02,                   // Section size
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -17,33 +17,31 @@ JSAPITest* JSAPITest::list;
 bool JSAPITest::init()
 {
     cx = createContext();
     if (!cx)
         return false;
     js::UseInternalJobQueues(cx);
     if (!JS::InitSelfHostedCode(cx))
         return false;
-    JS_BeginRequest(cx);
     global.init(cx);
     createGlobal();
     if (!global)
         return false;
     JS::EnterRealm(cx, global);
     return true;
 }
 
 void JSAPITest::uninit()
 {
     if (global) {
         JS::LeaveRealm(cx, nullptr);
         global = nullptr;
     }
     if (cx) {
-        JS_EndRequest(cx);
         destroyContext();
         cx = nullptr;
     }
     msgs.clear();
 }
 
 bool JSAPITest::exec(const char* bytes, const char* filename, int lineno)
 {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -359,96 +359,96 @@ AssertHeapIsIdleOrStringIsFlat(JSString*
      */
     MOZ_ASSERT_IF(JS::RuntimeHeapIsBusy(), str->isFlat());
 }
 
 JS_PUBLIC_API(bool)
 JS_ValueToObject(JSContext* cx, HandleValue value, MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     if (value.isNullOrUndefined()) {
         objp.set(nullptr);
         return true;
     }
     JSObject* obj = ToObject(cx, value);
     if (!obj)
         return false;
     objp.set(obj);
     return true;
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_ValueToFunction(JSContext* cx, HandleValue value)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     return ReportIfNotFunction(cx, value);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_ValueToConstructor(JSContext* cx, HandleValue value)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     return ReportIfNotFunction(cx, value);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_ValueToSource(JSContext* cx, HandleValue value)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     return ValueToSource(cx, value);
 }
 
 JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t* ip)
 {
     return mozilla::NumberIsInt32(d, ip);
 }
 
 JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext* cx, HandleValue value)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     return TypeOfValue(value);
 }
 
 JS_PUBLIC_API(bool)
 JS_StrictlyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value1, value2);
     MOZ_ASSERT(equal);
     return StrictlyEqual(cx, value1, value2, equal);
 }
 
 JS_PUBLIC_API(bool)
 JS_LooselyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value1, value2);
     MOZ_ASSERT(equal);
     return LooselyEqual(cx, value1, value2, equal);
 }
 
 JS_PUBLIC_API(bool)
 JS_SameValue(JSContext* cx, HandleValue value1, HandleValue value2, bool* same)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value1, value2);
     MOZ_ASSERT(same);
     return SameValue(cx, value1, value2, same);
 }
 
 JS_PUBLIC_API(bool)
 JS_IsBuiltinEvalFunction(JSFunction* fun)
 {
@@ -526,59 +526,16 @@ JS_SetContextPrivate(JSContext* cx, void
 }
 
 JS_PUBLIC_API(void)
 JS_SetFutexCanWait(JSContext* cx)
 {
     cx->fx.setCanWait(true);
 }
 
-static void
-StartRequest(JSContext* cx)
-{
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
-
-    if (cx->requestDepth) {
-        cx->requestDepth++;
-    } else {
-        /* Indicate that a request is running. */
-        cx->requestDepth = 1;
-        cx->triggerActivityCallback(true);
-    }
-}
-
-static void
-StopRequest(JSContext* cx)
-{
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
-
-    MOZ_ASSERT(cx->requestDepth != 0);
-    if (cx->requestDepth != 1) {
-        cx->requestDepth--;
-    } else {
-        cx->requestDepth = 0;
-        cx->triggerActivityCallback(false);
-    }
-}
-
-JS_PUBLIC_API(void)
-JS_BeginRequest(JSContext* cx)
-{
-    cx->outstandingRequests++;
-    StartRequest(cx);
-}
-
-JS_PUBLIC_API(void)
-JS_EndRequest(JSContext* cx)
-{
-    MOZ_ASSERT(cx->outstandingRequests != 0);
-    cx->outstandingRequests--;
-    StopRequest(cx);
-}
-
 JS_PUBLIC_API(JSRuntime*)
 JS_GetParentRuntime(JSContext* cx)
 {
     return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime : cx->runtime();
 }
 
 JS_PUBLIC_API(JSRuntime*)
 JS_GetRuntime(JSContext* cx)
@@ -597,17 +554,16 @@ JS::InitSelfHostedCode(JSContext* cx)
 {
     MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
                        "JS::InitSelfHostedCode() called more than once");
 
     AutoNoteSingleThreadedRegion anstr;
 
     JSRuntime* rt = cx->runtime();
 
-    JSAutoRequest ar(cx);
     if (!rt->initializeAtoms(cx))
         return false;
 
 #ifndef JS_CODEGEN_NONE
     if (!rt->getJitRuntime(cx))
         return false;
 #endif
 
@@ -684,30 +640,30 @@ JS_SetExternalStringSizeofCallback(JSCon
 {
     cx->runtime()->externalStringSizeofCallback = callback;
 }
 
 JS_PUBLIC_API(Realm*)
 JS::EnterRealm(JSContext* cx, JSObject* target)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(target));
 
     Realm* oldRealm = cx->realm();
     cx->enterRealmOf(target);
     return oldRealm;
 }
 
 JS_PUBLIC_API(void)
 JS::LeaveRealm(JSContext* cx, JS::Realm* oldRealm)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->leaveRealm(oldRealm);
 }
 
 JSAutoRealm::JSAutoRealm(JSContext* cx, JSObject* target
                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : cx_(cx),
     oldRealm_(cx->realm())
 {
@@ -788,27 +744,27 @@ JS_GetZoneUserData(JS::Zone* zone)
 {
     return zone->data;
 }
 
 JS_PUBLIC_API(bool)
 JS_WrapObject(JSContext* cx, MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (objp)
         JS::ExposeObjectToActiveJS(objp);
     return cx->compartment()->wrap(cx, objp);
 }
 
 JS_PUBLIC_API(bool)
 JS_WrapValue(JSContext* cx, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JS::ExposeValueToActiveJS(vp);
     return cx->compartment()->wrap(cx, vp);
 }
 
 static void
 ReleaseAssertObjectHasNoWrappers(JSContext* cx, HandleObject target)
 {
     RootedValue origv(cx, ObjectValue(*target));
@@ -1029,17 +985,17 @@ static const JSStdName builtin_property_
 };
 
 JS_PUBLIC_API(bool)
 JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* resolved)
 {
     const JSStdName* stdnm;
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     *resolved = false;
 
     if (!JSID_IS_ATOM(id))
         return true;
 
@@ -1107,17 +1063,17 @@ JS_MayResolveStandardClass(const JSAtomS
            LookupStdName(names, atom, standard_class_names) ||
            LookupStdName(names, atom, builtin_property_names);
 }
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     return GlobalObject::initStandardClasses(cx, global);
 }
 
 static bool
 EnumerateUnresolvedStandardClasses(JSContext* cx, Handle<GlobalObject*> global,
                                    AutoIdVector& properties, const JSStdName* table)
@@ -1174,29 +1130,29 @@ JS_NewEnumerateStandardClasses(JSContext
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_GetClassObject(JSContext* cx, JSProtoKey key, MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSObject* obj = GlobalObject::getOrCreateConstructor(cx, key);
     if (!obj)
         return false;
     objp.set(obj);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_GetClassPrototype(JSContext* cx, JSProtoKey key, MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSObject* proto = GlobalObject::getOrCreatePrototype(cx, key);
     if (!proto)
         return false;
     objp.set(proto);
     return true;
 }
 
 namespace JS {
@@ -1208,17 +1164,17 @@ ProtoKeyToId(JSContext* cx, JSProtoKey k
 }
 
 } /* namespace JS */
 
 JS_PUBLIC_API(JSProtoKey)
 JS_IdToProtoKey(JSContext* cx, HandleId id)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(id);
 
     if (!JSID_IS_ATOM(id))
         return JSProto_Null;
 
     JSAtom* atom = JSID_TO_ATOM(id);
     const JSStdName* stdnm = LookupStdName(cx->names(), atom, standard_class_names);
     if (!stdnm)
@@ -1260,17 +1216,17 @@ JS_ExtensibleLexicalEnvironment(JSObject
     MOZ_ASSERT(lexical);
     return lexical;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::CurrentGlobalOrNull(JSContext* cx)
 {
     AssertHeapIsIdleOrIterating();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (!cx->realm())
         return nullptr;
     return cx->global();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetNonCCWObjectGlobal(JSObject* obj)
 {
@@ -1315,25 +1271,25 @@ JS::IsProfileTimelineRecordingEnabled()
 {
     return gProfileTimelineRecordingEnabled;
 }
 
 JS_PUBLIC_API(void*)
 JS_malloc(JSContext* cx, size_t nbytes)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return static_cast<void*>(cx->maybe_pod_malloc<uint8_t>(nbytes));
 }
 
 JS_PUBLIC_API(void*)
 JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return static_cast<void*>(cx->maybe_pod_realloc<uint8_t>(static_cast<uint8_t*>(p),
                                                              oldBytes, newBytes));
 }
 
 JS_PUBLIC_API(void)
 JS_free(JSContext* cx, void* p)
 {
     return js_free(p);
@@ -1537,27 +1493,27 @@ JS_SetGCParametersBasedOnAvailableMemory
 }
 
 
 JS_PUBLIC_API(JSString*)
 JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length,
                      const JSStringFinalizer* fin)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSString* s = JSExternalString::new_(cx, chars, length, fin);
     return s;
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewMaybeExternalString(JSContext* cx, const char16_t* chars, size_t length,
                           const JSStringFinalizer* fin, bool* allocatedExternal)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewMaybeExternalString(cx, chars, length, fin, allocatedExternal);
 }
 
 extern JS_PUBLIC_API(bool)
 JS_IsExternalString(JSString* str)
 {
     return str->isExternal();
 }
@@ -1589,17 +1545,17 @@ SetNativeStackQuotaAndLimit(JSContext* c
     }
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSContext* cx, size_t systemCodeStackSize, size_t trustedScriptStackSize,
                        size_t untrustedScriptStackSize)
 {
-    MOZ_ASSERT(cx->requestDepth == 0);
+    MOZ_ASSERT(!cx->activation());
 
     if (!trustedScriptStackSize)
         trustedScriptStackSize = systemCodeStackSize;
     else
         MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
 
     if (!untrustedScriptStackSize)
         untrustedScriptStackSize = trustedScriptStackSize;
@@ -1615,47 +1571,47 @@ JS_SetNativeStackQuota(JSContext* cx, si
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_ValueToId(JSContext* cx, HandleValue value, MutableHandleId idp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     return ValueToId<CanGC>(cx, value, idp);
 }
 
 JS_PUBLIC_API(bool)
 JS_StringToId(JSContext* cx, HandleString string, MutableHandleId idp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(string);
     RootedValue value(cx, StringValue(string));
     return ValueToId<CanGC>(cx, value, idp);
 }
 
 JS_PUBLIC_API(bool)
 JS_IdToValue(JSContext* cx, jsid id, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(id);
     vp.set(IdToValue(id));
     cx->check(vp);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     MOZ_ASSERT(obj != nullptr);
     MOZ_ASSERT(hint == JSTYPE_UNDEFINED || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
     vp.setObject(*obj);
     return ToPrimitiveSlow(cx, hint, vp);
 }
 
 JS_PUBLIC_API(bool)
@@ -1708,17 +1664,17 @@ JS::GetFirstArgumentAsTypeHint(JSContext
 
 JS_PUBLIC_API(JSObject*)
 JS_InitClass(JSContext* cx, HandleObject obj, HandleObject parent_proto,
              const JSClass* clasp, JSNative constructor, unsigned nargs,
              const JSPropertySpec* ps, const JSFunctionSpec* fs,
              const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, parent_proto);
     return InitClass(cx, obj, parent_proto, Valueify(clasp), constructor,
                      nargs, ps, fs, static_ps, static_fs);
 }
 
 JS_PUBLIC_API(bool)
 JS_LinkConstructorAndPrototype(JSContext* cx, HandleObject ctor, HandleObject proto)
 {
@@ -1730,17 +1686,17 @@ JS_GetClass(JSObject* obj)
 {
     return obj->getJSClass();
 }
 
 JS_PUBLIC_API(bool)
 JS_InstanceOf(JSContext* cx, HandleObject obj, const JSClass* clasp, CallArgs* args)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 #ifdef DEBUG
     if (args) {
         cx->check(obj);
         cx->check(args->thisv(), args->calleev());
     }
 #endif
     if (!obj || obj->getJSClass() != clasp) {
         if (args)
@@ -1779,17 +1735,17 @@ JS_GetInstancePrivate(JSContext* cx, Han
         return nullptr;
     return obj->as<NativeObject>().getPrivate();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_GetConstructor(JSContext* cx, HandleObject proto)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(proto);
 
     RootedValue cval(cx);
     if (!GetProperty(cx, proto, proto, cx->names().constructor, &cval))
         return nullptr;
     if (!IsFunctionObject(cval)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                                   proto->getClass()->name);
@@ -1883,17 +1839,17 @@ JS_PUBLIC_API(JSObject*)
 JS_NewGlobalObject(JSContext* cx, const JSClass* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::RealmOptions& options)
 {
     MOZ_RELEASE_ASSERT(cx->runtime()->hasInitializedSelfHosting(),
                        "Must call JS::InitSelfHostedCode() before creating a global");
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return GlobalObject::new_(cx, Valueify(clasp), principals, hookOption, options);
 }
 
 JS_PUBLIC_API(void)
 JS_GlobalObjectTraceHook(JSTracer* trc, JSObject* global)
 {
     GlobalObject* globalObj = &global->as<GlobalObject>();
@@ -1930,34 +1886,34 @@ JS_FireOnNewGlobalObject(JSContext* cx, 
     Debugger::onNewGlobalObject(cx, globalObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObject(JSContext* cx, const JSClass* jsclasp)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     return NewBuiltinClassInstance(cx, clasp);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectWithGivenProto(JSContext* cx, const JSClass* jsclasp, HandleObject proto)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(proto);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
@@ -1965,26 +1921,26 @@ JS_NewObjectWithGivenProto(JSContext* cx
     return NewObjectWithGivenProto(cx, clasp, proto);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewPlainObject(JSContext* cx)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return NewBuiltinClassInstance<PlainObject>(cx);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const CallArgs& args)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     Value callee = args.calleev();
     cx->check(callee);
     RootedObject obj(cx, &callee.toObject());
     return CreateThis(cx, Valueify(clasp), obj);
 }
 
 JS_PUBLIC_API(bool)
@@ -2009,17 +1965,17 @@ JS_GetPrototype(JSContext* cx, HandleObj
     cx->check(obj);
     return GetPrototype(cx, obj, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, proto);
 
     return SetPrototype(cx, obj, proto);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
                           MutableHandleObject result)
@@ -2049,17 +2005,17 @@ JS_SetImmutablePrototype(JSContext *cx, 
     return SetImmutablePrototype(cx, obj, succeeded);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
                                 MutableHandle<PropertyDescriptor> desc)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     return GetOwnPropertyDescriptor(cx, obj, id, desc);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
                             MutableHandle<PropertyDescriptor> desc)
@@ -2101,17 +2057,17 @@ JS_GetPropertyDescriptor(JSContext* cx, 
     return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc);
 }
 
 static bool
 DefinePropertyByDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                            Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, desc);
     return DefineProperty(cx, obj, id, desc, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
                       Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
@@ -2137,17 +2093,17 @@ DefineAccessorPropertyById(JSContext* cx
     // JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
     // throw if this happens, but we've accepted it for long enough that it's
     // not worth trying to make callers change their ways. Just flip it off on
     // its way through the API layer so that we can enforce this internally.
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         attrs &= ~JSPROP_READONLY;
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, getter, setter);
 
     return js::DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
 }
 
 static bool
 DefineAccessorPropertyById(JSContext* cx, HandleObject obj, HandleId id,
                            const JSNativeWrapper& get, const JSNativeWrapper& set,
@@ -2193,17 +2149,17 @@ DefineAccessorPropertyById(JSContext* cx
 
 static bool
 DefineDataPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                        unsigned attrs)
 {
     MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, value);
 
     return js::DefineDataProperty(cx, obj, id, value, attrs);
 }
 
 /*
  * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
@@ -2463,17 +2419,17 @@ JS_DefineUCProperty(JSContext* cx, Handl
 }
 
 static bool
 DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                   unsigned attrs)
 {
     cx->check(obj, value);
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return DefineDataPropertyById(cx, obj, id, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
@@ -2531,17 +2487,17 @@ JS_DefineElement(JSContext* cx, HandleOb
     Value value = NumberValue(valueArg);
     return ::DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     return HasProperty(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
 {
@@ -2561,28 +2517,28 @@ JS_HasUCProperty(JSContext* cx, HandleOb
     RootedId id(cx, AtomToId(atom));
     return JS_HasPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return JS_HasPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     return HasOwnProperty(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
 {
@@ -2593,28 +2549,28 @@ JS_HasOwnProperty(JSContext* cx, HandleO
     return JS_HasOwnPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardGetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
                         MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, receiver);
 
     return GetProperty(cx, obj, receiver, id, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardGetElementTo(JSContext* cx, HandleObject obj, uint32_t index, HandleObject receiver,
                        MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     return GetElement(cx, obj, receiver, index, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPropertyById(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
@@ -2649,27 +2605,27 @@ JS_GetElement(JSContext* cx, HandleObjec
     return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardSetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                         HandleValue receiver, ObjectOpResult& result)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, v, receiver);
 
     return SetProperty(cx, obj, id, v, receiver, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id, v);
 
     RootedValue receiver(cx, ObjectValue(*obj));
     ObjectOpResult ignored;
     return SetProperty(cx, obj, id, v, receiver, ignored);
 }
 
 JS_PUBLIC_API(bool)
@@ -2692,17 +2648,17 @@ JS_SetUCProperty(JSContext* cx, HandleOb
     RootedId id(cx, AtomToId(atom));
     return JS_SetPropertyById(cx, obj, id, v);
 }
 
 static bool
 SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, v);
 
     RootedValue receiver(cx, ObjectValue(*obj));
     ObjectOpResult ignored;
     return SetElement(cx, obj, index, v, receiver, ignored);
 }
 
 JS_PUBLIC_API(bool)
@@ -2745,54 +2701,54 @@ JS_SetElement(JSContext* cx, HandleObjec
     RootedValue value(cx, NumberValue(v));
     return SetElement(cx, obj, index, value);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     return DeleteProperty(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name, ObjectOpResult& result)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return DeleteProperty(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeleteUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
                     ObjectOpResult& result)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return DeleteProperty(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     return DeleteElement(cx, obj, index, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id)
 {
@@ -2813,17 +2769,17 @@ JS_DeleteElement(JSContext* cx, HandleOb
     ObjectOpResult ignored;
     return JS_DeleteElement(cx, obj, index, ignored);
 }
 
 JS_PUBLIC_API(bool)
 JS_Enumerate(JSContext* cx, HandleObject obj, JS::MutableHandle<IdVector> props)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, props);
     MOZ_ASSERT(props.empty());
 
     AutoIdVector ids(cx);
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids))
         return false;
 
     return props.append(ids.begin(), ids.end());
@@ -2842,34 +2798,34 @@ JS::IsConstructor(JSObject* obj)
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args,
                      MutableHandleValue rval)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, fval, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
     RootedValue thisv(cx, ObjectOrNullValue(obj));
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
                 MutableHandleValue rval)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, fun, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
     RootedValue fval(cx, ObjectValue(*fun));
     RootedValue thisv(cx, ObjectOrNullValue(obj));
@@ -2877,17 +2833,17 @@ JS_CallFunction(JSContext* cx, HandleObj
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args,
                     MutableHandleValue rval)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, args);
 
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     RootedValue fval(cx);
     RootedId id(cx, AtomToId(atom));
@@ -2902,32 +2858,32 @@ JS_CallFunctionName(JSContext* cx, Handl
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Call(JSContext* cx, HandleValue thisv, HandleValue fval, const JS::HandleValueArray& args,
          MutableHandleValue rval)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(thisv, fval, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Construct(JSContext* cx, HandleValue fval, HandleObject newTarget, const JS::HandleValueArray& args,
               MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(fval, newTarget, args);
 
     if (!IsConstructor(fval)) {
         ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
         return false;
     }
 
     RootedValue newTargetVal(cx, ObjectValue(*newTarget));
@@ -2943,17 +2899,17 @@ JS::Construct(JSContext* cx, HandleValue
     return js::Construct(cx, fval, cargs, newTargetVal, objp);
 }
 
 JS_PUBLIC_API(bool)
 JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args,
               MutableHandleObject objp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(fval, args);
 
     if (!IsConstructor(fval)) {
         ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
         return false;
     }
 
     ConstructArgs cargs(cx);
@@ -2965,17 +2921,17 @@ JS::Construct(JSContext* cx, HandleValue
 
 
 /* * */
 
 JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
 
     if (!obj->isNative())
         return js::HasOwnProperty(cx, obj, id, foundp);
 
     RootedNativeObject nativeObj(cx, &obj->as<NativeObject>());
     Rooted<PropertyResult> prop(cx);
     NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
@@ -3003,28 +2959,28 @@ JS_AlreadyHasOwnUCProperty(JSContext* cx
     RootedId id(cx, AtomToId(atom));
     return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_FreezeObject(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     return FreezeObject(cx, obj);
 }
 
 static bool
 DeepFreezeSlot(JSContext* cx, const Value& v)
 {
     if (v.isPrimitive())
@@ -3032,17 +2988,17 @@ DeepFreezeSlot(JSContext* cx, const Valu
     RootedObject obj(cx, &v.toObject());
     return JS_DeepFreezeObject(cx, obj);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeepFreezeObject(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
     bool extensible;
     if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return true;
@@ -3109,17 +3065,17 @@ DefineSelfHostedProperty(JSContext* cx, 
     return DefineAccessorPropertyById(cx, obj, id, getterFunc, setterFunc, attrs);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_DefineObject(JSContext* cx, HandleObject obj, const char* name, const JSClass* jsclasp,
                 unsigned attrs)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
     RootedObject nobj(cx, NewBuiltinClassInstance(cx, clasp));
     if (!nobj)
@@ -3143,17 +3099,17 @@ ValueFromScalar(int32_t x)
     return Int32Value(x);
 }
 
 template<typename T>
 static bool
 DefineConstScalar(JSContext* cx, HandleObject obj, const JSConstScalarSpec<T>* cds)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
     for (; cds->name; cds++) {
         RootedValue value(cx, ValueFromScalar(cds->val));
         if (!DefineDataProperty(cx, obj, cds->name, value, attrs))
             return false;
     }
     return true;
 }
@@ -3277,17 +3233,17 @@ JS::ObjectToCompletePropertyDescriptor(J
     return true;
 }
 
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext* cx, JSObject* objArg)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     if (!obj->isNative())
         return;
 
     const Class* clasp = obj->getClass();
     unsigned numReserved = JSCLASS_RESERVED_SLOTS(clasp);
     unsigned numSlots = obj->as<NativeObject>().slotSpan();
@@ -3307,28 +3263,28 @@ JS_SetReservedSlot(JSObject* obj, uint32
     obj->as<NativeObject>().setReservedSlot(index, value);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     cx->check(contents);
     return NewDenseCopiedArray(cx, contents.length(), contents.begin());
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, size_t length)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return NewDenseFullyAllocatedArray(cx, length);
 }
 
 inline bool
 IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType)
 {
     cx->check(obj);
@@ -3358,26 +3314,26 @@ JS_IsArrayObject(JSContext* cx, JS::Hand
     RootedObject obj(cx, &value.toObject());
     return JS_IsArrayObject(cx, obj, isArray);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext* cx, HandleObject obj, uint32_t* lengthp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     return GetLengthProperty(cx, obj, lengthp);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext* cx, HandleObject obj, uint32_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     return SetLengthProperty(cx, obj, length);
 }
 
 JS_PUBLIC_API(bool)
 JS::IsMapObject(JSContext* cx, JS::HandleObject obj, bool* isMap)
 {
     return IsGivenTypeObject(cx, obj, ESClass::Map, isMap);
@@ -3444,17 +3400,17 @@ JS_InitReadPrincipalsCallback(JSContext*
 
 JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                const char* name)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     RootedAtom atom(cx);
     if (name) {
         atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return nullptr;
     }
 
@@ -3463,17 +3419,17 @@ JS_NewFunction(JSContext* cx, JSNative n
            : NewNativeFunction(cx, native, nargs, atom);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(id);
 
     RootedAtom name(cx, IdToFunctionName(cx, id));
     if (!name)
         return nullptr;
 
     JSAtom* shAtom = Atomize(cx, selfHostedName, strlen(selfHostedName));
     if (!shAtom)
@@ -3590,17 +3546,17 @@ IsFunctionCloneable(HandleFunction fun)
 
     return true;
 }
 
 static JSObject*
 CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, HandleScope scope)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(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));
@@ -3727,60 +3683,60 @@ JS_IsConstructor(JSFunction* fun)
     return fun->isConstructor();
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     return DefineFunctions(cx, obj, fs, NotIntrinsic);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineUCFunction(JSContext* cx, HandleObject obj,
                     const char16_t* name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
     JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction*)
 JS_DefineFunctionById(JSContext* cx, HandleObject obj, HandleId id, JSNative call,
                       unsigned nargs, unsigned attrs)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj, id);
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 /* Use the fastest available getc. */
 #if defined(HAVE_GETC_UNLOCKED)
 # define fast_getc getc_unlocked
 #elif defined(HAVE__GETC_NOLOCK)
@@ -4021,17 +3977,17 @@ JS::CompileOptions::CompileOptions(JSCon
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         SourceBufferHolder& srcBuf, MutableHandleScript script)
 {
     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
 
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf));
     return !!script;
 }
 
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         const char* bytes, size_t length, MutableHandleScript script)
@@ -4138,17 +4094,17 @@ JS::CompileForNonSyntacticScope(JSContex
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
                  const uint8_t* buf, size_t length)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options, buf, length);
 }
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file)
 {
     FileContents fileContents(cx);
@@ -4367,17 +4323,17 @@ JS_CompileUCScript(JSContext* cx, JS::So
 {
     return ::Compile(cx, options, srcBuf, script);
 }
 
 JS_PUBLIC_API(bool)
 JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(obj);
 
     cx->clearPendingException();
 
     UniqueTwoByteChars chars
         { JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(utf8, length), &length).get() };
     if (!chars)
         return true;
@@ -4462,17 +4418,17 @@ static bool
 CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
                 HandleAtom name, bool isInvalidName,
                 SourceBufferHolder& srcBuf, uint32_t parameterListEnd,
                 HandleObject enclosingEnv, HandleScope enclosingScope,
                 MutableHandleFunction fun)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(enclosingEnv);
     RootedAtom funAtom(cx);
 
     fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
                                 isInvalidName ? nullptr : name,
                                 /* proto = */ nullptr,
                                 gc::AllocKind::FUNCTION, TenuredObject,
                                 enclosingEnv));
@@ -4622,44 +4578,44 @@ JS::ExposeScriptToDebugger(JSContext* cx
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileScript(JSContext* cx, HandleScript script)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     script->ensureNonLazyCanonicalFunction();
     RootedFunction fun(cx, script->functionNonDelazifying());
     if (fun)
         return JS_DecompileFunction(cx, fun);
     bool haveSource = script->scriptSource()->hasSourceData();
     if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
         return nullptr;
     return haveSource ? JSScript::sourceData(cx, script)
                       : NewStringCopyZ<CanGC>(cx, "[no source]");
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileFunction(JSContext* cx, HandleFunction fun)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(fun);
     return FunctionToString(cx, fun, /* isToSource = */ false);
 }
 
 MOZ_NEVER_INLINE static bool
 ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(scope, script);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(scope), script->hasNonSyntacticScope());
     return Execute(cx, script, *scope, rval);
 }
 
 static bool
 ExecuteScript(JSContext* cx, AutoObjectVector& envChain, HandleScript scriptArg, Value* rval)
 {
@@ -4705,17 +4661,17 @@ JS_ExecuteScript(JSContext* cx, AutoObje
 {
     return ExecuteScript(cx, envChain, scriptArg, nullptr);
 }
 
 JS_PUBLIC_API(bool)
 JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg,
                           JS::MutableHandleValue rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     RootedScript script(cx, scriptArg);
     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
     if (script->realm() != cx->realm()) {
         script = CloneGlobalScript(cx, ScopeKind::Global, script);
         if (!script)
             return false;
 
         js::Debugger::onNewScript(cx, script);
@@ -4723,17 +4679,17 @@ JS::CloneAndExecuteScript(JSContext* cx,
     return ExecuteScript(cx, globalLexical, script, rval.address());
 }
 
 JS_PUBLIC_API(bool)
 JS::CloneAndExecuteScript(JSContext* cx, JS::AutoObjectVector& envChain,
                           HandleScript scriptArg,
                           JS::MutableHandleValue rval)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     RootedScript script(cx, scriptArg);
     if (script->realm() != cx->realm()) {
         script = CloneGlobalScript(cx, ScopeKind::NonSyntactic, script);
         if (!script)
             return false;
 
         js::Debugger::onNewScript(cx, script);
     }
@@ -4743,17 +4699,17 @@ JS::CloneAndExecuteScript(JSContext* cx,
 static bool
 Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env,
          const ReadOnlyCompileOptions& optionsArg,
          SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     CompileOptions options(cx, optionsArg);
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(env);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic);
 
     options.setIsRunOnce(true);
     RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(),
                                                           scopeKind, options, srcBuf));
     if (!script)
         return false;
@@ -4860,17 +4816,17 @@ JS::SetModuleMetadataHook(JSRuntime* rt,
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf, JS::MutableHandleScript script)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     script.set(frontend::CompileModule(cx, options, srcBuf));
     return !!script;
 }
 
 JS_PUBLIC_API(void)
 JS::SetTopLevelScriptPrivate(JSScript* script, void* value)
 {
@@ -4884,74 +4840,74 @@ JS::GetTopLevelScriptPrivate(JSScript* s
     MOZ_ASSERT(script);
     return script->maybeTopLevelPrivate();
 }
 
 JS_PUBLIC_API(bool)
 JS::ModuleInstantiate(JSContext* cx, JS::HandleScript script)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(script);
     RootedModuleObject module(cx, script->module());
     MOZ_ASSERT(module);
     return ModuleObject::Instantiate(cx, module);
 }
 
 JS_PUBLIC_API(bool)
 JS::ModuleEvaluate(JSContext* cx, JS::HandleScript script)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(script);
     RootedModuleObject module(cx, script->module());
     MOZ_ASSERT(module);
     return ModuleObject::Evaluate(cx, module);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetRequestedModules(JSContext* cx, JS::HandleScript script)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(script);
     RootedModuleObject module(cx, script->module());
     MOZ_ASSERT(module);
     return &module->requestedModules();
 }
 
 JS_PUBLIC_API(JSString*)
 JS::GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue value)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     JSObject* obj = &value.toObject();
     return obj->as<RequestedModuleObject>().moduleSpecifier();
 }
 
 JS_PUBLIC_API(void)
 JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value,
                                 uint32_t* lineNumber, uint32_t* columnNumber)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(value);
     MOZ_ASSERT(lineNumber);
     MOZ_ASSERT(columnNumber);
     auto& requested = value.toObject().as<RequestedModuleObject>();
     *lineNumber = requested.lineNumber();
     *columnNumber = requested.columnNumber();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(ctor, inputArgs);
 
     RootedValue ctorVal(cx, ObjectValue(*ctor));
     if (!IsConstructor(ctorVal)) {
         ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, ctorVal, nullptr);
         return nullptr;
     }
 
@@ -5031,17 +4987,17 @@ JS::JobQueueMayNotBeEmpty(JSContext* cx)
     cx->canSkipEnqueuingJobs = false;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(executor, proto);
 
     if (!executor)
         return PromiseObject::createSkippingExecutor(cx);
 
     MOZ_ASSERT(IsCallable(executor));
     return PromiseObject::create(cx, executor, proto);
 }
@@ -5050,25 +5006,25 @@ JS_PUBLIC_API(bool)
 JS::IsPromiseObject(JS::HandleObject obj)
 {
     return obj->is<PromiseObject>();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetPromiseConstructor(JSContext* cx)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     Rooted<GlobalObject*> global(cx, cx->global());
     return GlobalObject::getOrCreatePromiseConstructor(cx, global);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetPromisePrototype(JSContext* cx)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     Rooted<GlobalObject*> global(cx, cx->global());
     return GlobalObject::getOrCreatePromisePrototype(cx, global);
 }
 
 JS_PUBLIC_API(JS::PromiseState)
 JS::GetPromiseState(JS::HandleObject promiseObj_)
 {
     JSObject* promiseObj = CheckedUnwrap(promiseObj_);
@@ -5127,42 +5083,42 @@ JS::DumpPromiseResolutionSite(JSContext*
         fputs(stackStr.get(), stderr);
 }
 #endif
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(resolutionValue);
 
     RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, resolutionValue));
     MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>());
     return promise;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(rejectionValue);
 
     RootedObject promise(cx, PromiseObject::unforgeableReject(cx, rejectionValue));
     MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>());
     return promise;
 }
 
 static bool
 ResolveOrRejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue resultOrReason_,
                        bool reject)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(promiseObj, resultOrReason_);
 
     mozilla::Maybe<AutoRealm> ar;
     Rooted<PromiseObject*> promise(cx);
     RootedValue resultOrReason(cx, resultOrReason_);
     if (IsWrapper(promiseObj)) {
         JSObject* unwrappedPromiseObj = CheckedUnwrap(promiseObj);
         if (!unwrappedPromiseObj) {
@@ -5196,17 +5152,17 @@ JS::RejectPromise(JSContext* cx, JS::Han
 
 static bool
 CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj,
                             JS::HandleObject onResolvedObj_, JS::HandleObject onRejectedObj_,
                             JS::MutableHandleObject resultObj,
                             CreateDependentPromise createDependent)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(promiseObj, onResolvedObj_, onRejectedObj_);
 
     MOZ_ASSERT_IF(onResolvedObj_, IsCallable(onResolvedObj_));
     MOZ_ASSERT_IF(onRejectedObj_, IsCallable(onRejectedObj_));
 
     {
         mozilla::Maybe<AutoRealm> ar;
         Rooted<PromiseObject*> promise(cx);
@@ -5275,31 +5231,31 @@ JS::AddPromiseReactions(JSContext* cx, J
  * promise.
  *
  * Asserts that the array is dense and all entries are Promise objects.
  */
 JS_PUBLIC_API(JSObject*)
 JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return js::GetWaitForAllPromise(cx, promises);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableDefaultStreamObject(JSContext* cx,
                                    JS::HandleObject underlyingSource /* = nullptr */,
                                    JS::HandleFunction size /* = nullptr */,
                                    double highWaterMark /* = 1 */,
                                    JS::HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
     }
     RootedValue sourceVal(cx, ObjectValue(*source));
@@ -5311,17 +5267,17 @@ JS::NewReadableDefaultStreamObject(JSCon
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableByteStreamObject(JSContext* cx,
                                 JS::HandleObject underlyingSource /* = nullptr */,
                                 double highWaterMark /* = 1 */,
                                 JS::HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
     }
     RootedValue sourceVal(cx, ObjectValue(*source));
@@ -5370,17 +5326,17 @@ JS::HasReadableStreamCallbacks(JSContext
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
                                           uint8_t flags /* = 0 */,
                                           HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
 #ifdef DEBUG
     JSRuntime* rt = cx->runtime();
     MOZ_ASSERT(rt->readableStreamDataRequestCallback);
     MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
     MOZ_ASSERT(rt->readableStreamCancelCallback);
     MOZ_ASSERT(rt->readableStreamClosedCallback);
     MOZ_ASSERT(rt->readableStreamErroredCallback);
@@ -5437,17 +5393,17 @@ JS::ReadableStreamIsDisturbed(const JSOb
 {
     return stream->as<ReadableStream>().disturbed();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::ReadableStreamCancel(JSContext* cx, HandleObject streamObj, HandleValue reason)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
     cx->check(reason);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return ReadableStream::cancel(cx, stream, reason);
 }
 
 JS_PUBLIC_API(JS::ReadableStreamMode)
@@ -5455,28 +5411,28 @@ JS::ReadableStreamGetMode(const JSObject
 {
     return stream->as<ReadableStream>().mode();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::ReadableStreamGetReader(JSContext* cx, HandleObject streamObj, ReadableStreamReaderMode mode)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return ReadableStream::getReader(cx, stream, mode);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return ReadableStream::getExternalSource(cx, stream, source);
 }
 
 JS_PUBLIC_API(void)
 JS::ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream)
@@ -5484,29 +5440,29 @@ JS::ReadableStreamReleaseExternalUnderly
     stream->as<ReadableStream>().releaseExternalSource();
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, JS::HandleObject streamObj,
                                                 uint32_t availableData)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return ReadableStream::updateDataAvailableFromSource(cx, stream, availableData);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamTee(JSContext* cx, HandleObject streamObj,
                       MutableHandleObject branch1Obj, MutableHandleObject branch2Obj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     Rooted<ReadableStream*> branch1Stream(cx);
     Rooted<ReadableStream*> branch2Stream(cx);
 
     if (!ReadableStream::tee(cx, stream, false, &branch1Stream, &branch2Stream))
         return false;
@@ -5522,28 +5478,28 @@ JS::ReadableStreamGetDesiredSize(JSObjec
 {
     streamObj->as<ReadableStream>().desiredSize(hasValue, value);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamClose(JSContext* cx, HandleObject streamObj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return ReadableStream::close(cx, stream);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamEnqueue(JSContext* cx, HandleObject streamObj, HandleValue chunk)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
     cx->check(chunk);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     if (stream->mode() != JS::ReadableStreamMode::Default) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
                                   "JS::ReadableStreamEnqueue");
@@ -5551,17 +5507,17 @@ JS::ReadableStreamEnqueue(JSContext* cx,
     }
     return ReadableStream::enqueue(cx, stream, chunk);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject streamObj, HandleObject chunkObj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
     cx->check(chunkObj);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     if (stream->mode() != JS::ReadableStreamMode::Byte) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER,
                                   "JS::ReadableByteStreamEnqueueBuffer");
@@ -5583,17 +5539,17 @@ JS::ReadableByteStreamEnqueueBuffer(JSCo
 
     return ReadableStream::enqueueBuffer(cx, stream, buffer);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamError(JSContext* cx, HandleObject streamObj, HandleValue error)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(streamObj);
     cx->check(error);
 
     Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
     return js::ReadableStream::error(cx, stream, error);
 }
 
 JS_PUBLIC_API(bool)
@@ -5601,49 +5557,49 @@ JS::ReadableStreamReaderIsClosed(const J
 {
     return js::ReadableStreamReaderIsClosed(reader);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(reader);
     cx->check(reason);
 
     return js::ReadableStreamReaderCancel(cx, reader, reason);
 }
 
 JS_PUBLIC_API(bool)
 JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(reader);
 
     return js::ReadableStreamReaderReleaseLock(cx, reader);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(readerObj);
 
     Rooted<ReadableStreamDefaultReader*> reader(cx, &readerObj->as<ReadableStreamDefaultReader>());
     return js::ReadableStreamDefaultReader::read(cx, reader);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject readerObj, HandleObject viewObj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(readerObj);
     cx->check(viewObj);
 
     Rooted<ReadableStreamBYOBReader*> reader(cx, &readerObj->as<ReadableStreamBYOBReader>());
     Rooted<ArrayBufferViewObject*> view(cx, &viewObj->as<ArrayBufferViewObject>());
     return js::ReadableStreamBYOBReader::read(cx, reader, view);
 }
 
@@ -5680,17 +5636,17 @@ JS_RequestInterruptCallbackCanWait(JSCon
 JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
   JSContext* cx, HandleObject stack, const char* asyncCause,
   JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind)
   : cx(cx),
     oldAsyncStack(cx, cx->asyncStackForNewActivations()),
     oldAsyncCause(cx->asyncCauseForNewActivations),
     oldAsyncCallIsExplicit(cx->asyncCallIsExplicit)
 {
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     // The option determines whether we actually use the new values at this
     // point. It will not affect restoring the previous values when the object
     // is destroyed, so if the option changes it won't cause consistency issues.
     if (!cx->options().asyncStack())
         return;
 
     SavedFrame* asyncStack = &stack->as<SavedFrame>();
@@ -5708,51 +5664,51 @@ JS::AutoSetAsyncStackForNewCalls::~AutoS
     cx->asyncCallIsExplicit = oldAsyncCallIsExplicit;
 }
 
 /************************************************************************/
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyN(JSContext* cx, const char* s, size_t n)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewStringCopyN<CanGC>(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyZ(JSContext* cx, const char* s)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (!s)
         return cx->runtime()->emptyString;
     return NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ s)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewStringCopyUTF8Z<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars s)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewStringCopyUTF8N<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(bool)
 JS_StringHasBeenPinned(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     if (!str->isAtom())
         return false;
 
     return str->asAtom().isPinned();
 }
 
 JS_PUBLIC_API(jsid)
@@ -5763,115 +5719,115 @@ INTERNED_STRING_TO_JSID(JSContext* cx, J
     MOZ_ASSERT_IF(cx, JS_StringHasBeenPinned(cx, str));
     return AtomToId(&str->asAtom());
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeAndPinJSString(JSContext* cx, HandleString str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSAtom* atom = AtomizeString(cx, str, PinAtom);
     MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeString(JSContext* cx, const char* s)
 {
     return JS_AtomizeStringN(cx, s, strlen(s));
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeStringN(JSContext* cx, const char* s, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return Atomize(cx, s, length, DoNotPinAtom);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeAndPinString(JSContext* cx, const char* s)
 {
     return JS_AtomizeAndPinStringN(cx, s, strlen(s));
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSAtom* atom = Atomize(cx, s, length, PinAtom);
     MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewLatin1String(JSContext* cx, JS::Latin1Char* chars, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewString(cx, chars, length);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewUCString(JSContext* cx, char16_t* chars, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewString(cx, chars, length);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewUCStringDontDeflate(JSContext* cx, char16_t* chars, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewStringDontDeflate(cx, chars, length);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewUCStringCopyN(JSContext* cx, const char16_t* s, size_t n)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (!n)
         return cx->names().empty;
     return NewStringCopyN<CanGC>(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewUCStringCopyZ(JSContext* cx, const char16_t* s)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (!s)
         return cx->runtime()->emptyString;
     return NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeUCString(JSContext* cx, const char16_t* s)
 {
     return JS_AtomizeUCStringN(cx, s, js_strlen(s));
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeUCStringN(JSContext* cx, const char16_t* s, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return AtomizeChars(cx, s, length, DoNotPinAtom);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     JSAtom* atom = AtomizeChars(cx, s, length, PinAtom);
     MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
 JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s)
 {
@@ -5897,32 +5853,32 @@ JS_StringHasLatin1Chars(JSString* str)
 }
 
 JS_PUBLIC_API(const JS::Latin1Char*)
 JS_GetLatin1StringCharsAndLength(JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
                                  size_t* plength)
 {
     MOZ_ASSERT(plength);
     AssertHeapIsIdleOrStringIsFlat(str);
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return nullptr;
     *plength = linear->length();
     return linear->latin1Chars(nogc);
 }
 
 JS_PUBLIC_API(const char16_t*)
 JS_GetTwoByteStringCharsAndLength(JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
                                   size_t* plength)
 {
     MOZ_ASSERT(plength);
     AssertHeapIsIdleOrStringIsFlat(str);
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return nullptr;
     *plength = linear->length();
     return linear->twoByteChars(nogc);
 }
 
@@ -5931,17 +5887,17 @@ JS_GetTwoByteExternalStringChars(JSStrin
 {
     return str->asExternal().twoByteChars();
 }
 
 JS_PUBLIC_API(bool)
 JS_GetStringCharAt(JSContext* cx, JSString* str, size_t index, char16_t* res)
 {
     AssertHeapIsIdleOrStringIsFlat(str);
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
     *res = linear->latin1OrTwoByteChar(index);
     return true;
@@ -5952,17 +5908,17 @@ JS_GetFlatStringCharAt(JSFlatString* str
 {
     return str->latin1OrTwoByteChar(index);
 }
 
 JS_PUBLIC_API(bool)
 JS_CopyStringChars(JSContext* cx, mozilla::Range<char16_t> dest, JSString* str)
 {
     AssertHeapIsIdleOrStringIsFlat(str);
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
     MOZ_ASSERT(linear->length() <= dest.length());
     CopyChars(dest.begin().get(), *linear);
@@ -5988,17 +5944,17 @@ JS_GetTwoByteInternedStringChars(const J
         return nullptr;
     return flat->twoByteChars(nogc);
 }
 
 extern JS_PUBLIC_API(JSFlatString*)
 JS_FlattenString(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
     JSFlatString* flat = str->ensureFlat(cx);
     if (!flat)
         return nullptr;
     return flat;
 }
 
 extern JS_PUBLIC_API(const Latin1Char*)
@@ -6012,26 +5968,26 @@ JS_GetTwoByteFlatStringChars(const JS::A
 {
     return str->twoByteChars(nogc);
 }
 
 JS_PUBLIC_API(bool)
 JS_CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return CompareStrings(cx, str1, str2, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_StringEqualsAscii(JSContext* cx, JSString* str, const char* asciiBytes, bool* match)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     JSLinearString* linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
     *match = StringEqualsAscii(linearStr, asciiBytes);
     return true;
 }
 
@@ -6056,33 +6012,33 @@ JS_PutEscapedString(JSContext* cx, char*
         return size_t(-1);
     return PutEscapedString(buffer, size, linearStr, quote);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewDependentString(JSContext* cx, HandleString str, size_t start, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewDependentString(cx, str, start, length);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_ConcatStrings(JSContext* cx, HandleString left, HandleString right)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return ConcatStrings<CanGC>(cx, left, right);
 }
 
 JS_PUBLIC_API(bool)
 JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst, size_t* dstlenp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     if (!dst) {
         *dstlenp = srclen;
         return true;
     }
 
     size_t dstlen = *dstlenp;
 
@@ -6098,46 +6054,46 @@ JS_DecodeBytes(JSContext* cx, const char
     *dstlenp = srclen;
     return true;
 }
 
 JS_PUBLIC_API(char*)
 JS_EncodeString(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return js::EncodeLatin1(cx, str).release();
 }
 
 JS_PUBLIC_API(char*)
 JS_EncodeStringToUTF8(JSContext* cx, HandleString str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return StringToNewUTF8CharsZ(cx, *str).release();
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringEncodingLength(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     if (!str->ensureLinear(cx))
         return size_t(-1);
     return str->length();
 }
 
 JS_PUBLIC_API(bool)
 JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t length)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
     JS::AutoCheckCannotGC nogc;
     size_t writeLength = Min(linear->length(), length);
     if (linear->hasLatin1Chars()) {
@@ -6150,28 +6106,28 @@ JS_EncodeStringToBuffer(JSContext* cx, J
     }
     return true;
 }
 
 JS_PUBLIC_API(JS::Symbol*)
 JS::NewSymbol(JSContext* cx, HandleString description)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     if (description)
         cx->check(description);
 
     return Symbol::new_(cx, SymbolCode::UniqueSymbol, description);
 }
 
 JS_PUBLIC_API(JS::Symbol*)
 JS::GetSymbolFor(JSContext* cx, HandleString key)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(key);
 
     return Symbol::for_(cx, key);
 }
 
 JS_PUBLIC_API(JSString*)
 JS::GetSymbolDescription(HandleSymbol symbol)
 {
@@ -6219,34 +6175,34 @@ JS::PropertySpecNameEqualsId(const char*
     return JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_ATOM(id), name);
 }
 
 JS_PUBLIC_API(bool)
 JS_Stringify(JSContext* cx, MutableHandleValue vp, HandleObject replacer,
              HandleValue space, JSONWriteCallback callback, void* data)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(replacer, space);
     StringBuffer sb(cx);
     if (!sb.ensureTwoByteChars())
         return false;
     if (!Stringify(cx, vp, replacer, space, sb, StringifyBehavior::Normal))
         return false;
     if (sb.empty() && !sb.append(cx->names().null))
         return false;
     return callback(sb.rawTwoByteBegin(), sb.length(), data);
 }
 
 JS_PUBLIC_API(bool)
 JS::ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input,
                       JSONWriteCallback callback, void* data)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(input);
 
     StringBuffer sb(cx);
     if (!sb.ensureTwoByteChars())
         return false;
 
     RootedValue inputValue(cx, ObjectValue(*input));
     if (!Stringify(cx, &inputValue, nullptr, NullHandleValue, sb,
@@ -6258,39 +6214,39 @@ JS::ToJSONMaybeSafely(JSContext* cx, JS:
 
     return callback(sb.rawTwoByteBegin(), sb.length(), data);
 }
 
 JS_PUBLIC_API(bool)
 JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return ParseJSONWithReviver(cx, mozilla::Range<const char16_t>(chars, len), NullHandleValue, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ParseJSON(JSContext* cx, HandleString str, MutableHandleValue vp)
 {
     return JS_ParseJSONWithReviver(cx, str, NullHandleValue, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, HandleValue reviver, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return ParseJSONWithReviver(cx, mozilla::Range<const char16_t>(chars, len), reviver, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ParseJSONWithReviver(JSContext* cx, HandleString str, HandleValue reviver, MutableHandleValue vp)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(str);
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.init(cx, str))
         return false;
 
     return stableChars.isLatin1()
            ? ParseJSONWithReviver(cx, stableChars.latin1Range(), reviver, vp)
@@ -6548,25 +6504,25 @@ JS::SetWarningReporter(JSContext* cx, JS
 
 /*
  * Dates.
  */
 JS_PUBLIC_API(JSObject*)
 JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewDateObject(cx, year, mon, mday, hour, min, sec);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewDateObject(JSContext* cx, JS::ClippedTime time)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     return NewDateObjectMsec(cx, time);
 }
 
 JS_PUBLIC_API(bool)
 JS_ObjectIsDate(JSContext* cx, HandleObject obj, bool* isDate)
 {
     cx->check(obj);
 
@@ -6582,74 +6538,74 @@ JS_ObjectIsDate(JSContext* cx, HandleObj
 
 /*
  * Regular Expressions.
  */
 JS_PUBLIC_API(JSObject*)
 JS_NewRegExpObject(JSContext* cx, const char* bytes, size_t length, unsigned flags)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     UniqueTwoByteChars chars(InflateString(cx, bytes, length));
     if (!chars)
         return nullptr;
 
     return RegExpObject::create(cx, chars.get(), length, RegExpFlag(flags), cx->tempLifoAlloc(),
                                 GenericObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewUCRegExpObject(JSContext* cx, const char16_t* chars, size_t length, unsigned flags)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     return RegExpObject::create(cx, chars, length, RegExpFlag(flags), cx->tempLifoAlloc(),
                                 GenericObject);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     cx->check(input);
 
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
     if (!res)
         return false;
 
     res->reset(input);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearRegExpStatics(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
     MOZ_ASSERT(obj);
 
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
     if (!res)
         return false;
 
     res->clear();
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t* chars,
                  size_t length, size_t* indexp, bool test, MutableHandleValue rval)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
+    CHECK_THREAD(cx);
 
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
     if (!res)
         return false;
 
     RootedLinearString input(cx, NewStringCopyN<CanGC>(cx, chars, length));
     if (!input)
@@ -6658,17 +6614,17 @@ JS_ExecuteRegExp(JSContext* cx, HandleOb
     return ExecuteRegExpLegacy(cx, res, reobj.as<RegExpObject>(), input, indexp, test, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj, char16_t* chars, size_t length,
                           size_t* indexp, bool test, MutableHandleValue rval)
 {
     AssertHeapIsIdle();
-    CHECK_REQUEST(c