diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index bd8a281859..770e983249 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -606,7 +606,6 @@ var settingsToObserve = { 'dom.mozApps.signed_apps_installable_from': 'https://marketplace.firefox.com', 'dom.presentation.discovery.enabled': false, 'dom.presentation.discoverable': false, - 'dom.serviceWorkers.interception.enabled': true, 'dom.serviceWorkers.testing.enabled': false, 'gfx.layerscope.enabled': false, 'layers.draw-borders': false, diff --git a/b2g/components/test/mochitest/test_aboutserviceworkers.html b/b2g/components/test/mochitest/test_aboutserviceworkers.html index b239bf41c8..d30e58f4f6 100644 --- a/b2g/components/test/mochitest/test_aboutserviceworkers.html +++ b/b2g/components/test/mochitest/test_aboutserviceworkers.html @@ -210,7 +210,6 @@ function setup() { ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true] ]}, () => { SpecialPowers.pushPermissions([ { "type": "webapps-manage", "allow": 1, "context": document }, diff --git a/browser/app/profile/palemoon.js b/browser/app/profile/palemoon.js index 477f3feb74..ddb5fd69db 100644 --- a/browser/app/profile/palemoon.js +++ b/browser/app/profile/palemoon.js @@ -1269,9 +1269,7 @@ pref("browser.display.standalone_images.background_color", "#2E3B41"); pref("view_source.tab", false); // Enable ServiceWorkers for Push API consumers. -// Interception is still disabled. pref("dom.serviceWorkers.enabled", true); - pref("dom.serviceWorkers.openWindow.enabled", true); // Enable Push API. diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 3c4c7c4f85..c2e9c1a6e7 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -14196,11 +14196,6 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceReques bool* aShouldIntercept) { *aShouldIntercept = false; - // Preffed off. - if (!nsContentUtils::ServiceWorkerInterceptionEnabled()) { - return NS_OK; - } - // No in private browsing if (mInPrivateBrowsing) { return NS_OK; diff --git a/dom/apps/tests/test_packaged_app_update.html b/dom/apps/tests/test_packaged_app_update.html index 14eceb9122..00483519ae 100644 --- a/dom/apps/tests/test_packaged_app_update.html +++ b/dom/apps/tests/test_packaged_app_update.html @@ -150,7 +150,11 @@ var steps = [ function() { SpecialPowers.pushPrefEnv( {"set": [["dom.mozPermissionSettings.enabled", true], - ["dom.webapps.useCurrentProfile", true]]}, + ["dom.webapps.useCurrentProfile", true], + // This test resets a conection to simulate network error, + // so we need to set the following pref to 0 that we do not + // retry it in necko. + ["network.http.request.max-attempts", 0]]}, PackagedTestHelper.next); } ); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index d79c2ab071..384a092666 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -271,7 +271,6 @@ bool nsContentUtils::sEncodeDecodeURLHash = false; bool nsContentUtils::sGettersDecodeURLHash = false; bool nsContentUtils::sPrivacyResistFingerprinting = false; bool nsContentUtils::sSendPerformanceTimingNotifications = false; -bool nsContentUtils::sSWInterceptionEnabled = false; uint32_t nsContentUtils::sHandlingInputTimeout = 1000; @@ -572,10 +571,6 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting, "privacy.resistFingerprinting", false); - Preferences::AddBoolVarCache(&sSWInterceptionEnabled, - "dom.serviceWorkers.interception.enabled", - false); - Preferences::AddUintVarCache(&sHandlingInputTimeout, "dom.event.handling-user-input-time-limit", 1000); @@ -1766,8 +1761,7 @@ nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) bool nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument) { - if (!ServiceWorkerInterceptionEnabled() || - nsContentUtils::IsInPrivateBrowsing(aDocument)) { + if (nsContentUtils::IsInPrivateBrowsing(aDocument)) { return false; } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 98ad7ccaa0..4ee7ea452e 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2003,14 +2003,6 @@ public: return sSendPerformanceTimingNotifications; } - /* - * Returns true if ServiceWorker Interception is enabled by pref. - */ - static bool ServiceWorkerInterceptionEnabled() - { - return sSWInterceptionEnabled; - } - /* * Returns true if the frame timing APIs are enabled. */ @@ -2696,7 +2688,6 @@ private: static bool sGettersDecodeURLHash; static bool sPrivacyResistFingerprinting; static bool sSendPerformanceTimingNotifications; - static bool sSWInterceptionEnabled; static uint32_t sCookiesLifetimePolicy; static uint32_t sCookiesBehavior; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 14fe171dff..b832b01bfc 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -936,7 +936,7 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther, nsCOMPtr browserDOMWindow = mRemoteBrowser->GetBrowserDOMWindow(); - if (!otherBrowserDOMWindow || !browserDOMWindow) { + if (!!otherBrowserDOMWindow != !!browserDOMWindow) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 78a69b2bab..ff1824c2fc 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2565,6 +2565,8 @@ CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW, return; } + EnsureTarget(); + mTarget->ClearRect(gfx::Rect(aX, aY, aW, aH)); RedrawUser(gfxRect(aX, aY, aW, aH)); @@ -4952,8 +4954,15 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, nsCOMPtr shell = presContext->PresShell(); Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes); + // If this canvas was contained in the drawn window, the pre-transaction callback + // may have returned its DT. If so, we must reacquire it here. + EnsureTarget(); if (drawDT) { RefPtr snapshot = drawDT->Snapshot(); + if (NS_WARN_IF(!snapshot)) { + aError.Throw(NS_ERROR_FAILURE); + return; + } RefPtr data = snapshot->GetDataSurface(); DataSourceSurface::MappedSurface rawData; @@ -5367,7 +5376,7 @@ void CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx, double aDy, ErrorResult& aError) { - dom::Uint8ClampedArray arr; + RootedTypedArray arr(nsContentUtils::RootingCxForThread()); DebugOnly inited = arr.Init(aImageData.GetDataObject()); MOZ_ASSERT(inited); @@ -5383,7 +5392,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx, double aDirtyHeight, ErrorResult& aError) { - dom::Uint8ClampedArray arr; + RootedTypedArray arr(nsContentUtils::RootingCxForThread()); DebugOnly inited = arr.Init(aImageData.GetDataObject()); MOZ_ASSERT(inited); diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index b0a019eb2c..8fc8e4380c 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -208,7 +208,8 @@ WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarge GLint yOffset, GLint zOffset, GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData) { - dom::Uint8ClampedArray scopedArr; + dom::RootedTypedArray scopedArr( + nsContentUtils::RootingCxForThread()); UniquePtr blob; blob = UnpackBlobFromImageData(mContext, funcName, unpackType, imageData, &scopedArr); diff --git a/dom/canvas/crashtests/1246775-1.html b/dom/canvas/crashtests/1246775-1.html new file mode 100644 index 0000000000..8c4ddabdf8 --- /dev/null +++ b/dom/canvas/crashtests/1246775-1.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/dom/canvas/crashtests/crashtests.list b/dom/canvas/crashtests/crashtests.list index 9ed7de4c16..0864f1a6cf 100644 --- a/dom/canvas/crashtests/crashtests.list +++ b/dom/canvas/crashtests/crashtests.list @@ -29,4 +29,5 @@ skip-if(azureCairo) load 1229983-1.html load 1229932-1.html load 1233613.html load 1244850-1.html +load 1246775-1.html load texImage2D.html diff --git a/dom/canvas/test/test_offscreencanvas_serviceworker.html b/dom/canvas/test/test_offscreencanvas_serviceworker.html index c5cfb93db1..aa8d1eb9f4 100644 --- a/dom/canvas/test/test_offscreencanvas_serviceworker.html +++ b/dom/canvas/test/test_offscreencanvas_serviceworker.html @@ -36,7 +36,6 @@ SpecialPowers.pushPrefEnv({'set': [ ['gfx.offscreencanvas.enabled', true], ['webgl.force-enabled', true], ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/icc/Icc.cpp b/dom/icc/Icc.cpp index 94a8d3d322..39c2a4aabb 100644 --- a/dom/icc/Icc.cpp +++ b/dom/icc/Icc.cpp @@ -104,7 +104,7 @@ Icc::NotifyStkEvent(const nsAString& aName, nsIStkProactiveCmd* aStkProactiveCmd cmdFactory->CreateCommandMessage(aStkProactiveCmd, &value); NS_ENSURE_TRUE(value.isObject(), NS_ERROR_UNEXPECTED); - MozStkCommandEventInit init; + RootedDictionary init(cx); init.mBubbles = false; init.mCancelable = false; init.mCommand = value; diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index c76ca7ccea..f7c04c28ce 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -174,8 +174,6 @@ WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPee NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia # LOCALIZATION NOTE: Do not translate "ServiceWorker". %S is a URL. InterceptionFailedWithURL=Failed to load '%S'. A ServiceWorker intercepted the request and encountered an unexpected error. -# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "opaque", or "Response". %S is a URL. -OpaqueInterceptionDisabledWithURL=Failed to load '%S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while opaque interception is disabled. # LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "FetchEvent", "no-cors", "opaque", "Response", or "RequestMode". %1$S is a URL. %2$S is a RequestMode value. BadOpaqueInterceptionRequestModeWithURL=Failed to load '%1$S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while handling a '%2$S' FetchEvent. Opaque Response objects are only valid when the RequestMode is 'no-cors'. # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Error", "Response", "FetchEvent.respondWith()", or "fetch()". %S is a URL. diff --git a/dom/push/test/test_serviceworker_lifetime.html b/dom/push/test/test_serviceworker_lifetime.html index e3c9f052c0..d305cba805 100644 --- a/dom/push/test/test_serviceworker_lifetime.html +++ b/dom/push/test/test_serviceworker_lifetime.html @@ -322,7 +322,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true] ]}, runTest); SpecialPowers.addPermission('desktop-notification', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/security/test/csp/test_child-src_worker.html b/dom/security/test/csp/test_child-src_worker.html index 1216e4e2f0..79dfdbad2d 100644 --- a/dom/security/test/csp/test_child-src_worker.html +++ b/dom/security/test/csp/test_child-src_worker.html @@ -117,7 +117,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true] diff --git a/dom/security/test/csp/test_service_worker.html b/dom/security/test/csp/test_service_worker.html index d1dfac7301..0cff84751a 100644 --- a/dom/security/test/csp/test_service_worker.html +++ b/dom/security/test/csp/test_service_worker.html @@ -36,7 +36,6 @@ function receiveMessage(event) { onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true] diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp index 6ea267b103..9f5123d085 100644 --- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -929,11 +929,8 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle global) return false; } - dom::ConstantSpec umask_cs[] = { - { "umask", JS::NumberValue(gUserUmask) }, - PROP_END - }; - if (!dom::DefineConstants(cx, objSys, umask_cs)) { + if (!JS_DefineProperty(cx, objSys, "umask", gUserUmask, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { return false; } diff --git a/dom/tests/mochitest/fetch/sw_reroute.js b/dom/tests/mochitest/fetch/sw_reroute.js index 0525258c6c..57c46c6c5a 100644 --- a/dom/tests/mochitest/fetch/sw_reroute.js +++ b/dom/tests/mochitest/fetch/sw_reroute.js @@ -12,7 +12,6 @@ function testScript(script) { SpecialPowers.pushPrefEnv({ "set": [["dom.requestcache.enabled", true], ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.interception.opaque.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.serviceWorkers.exemptFromPerDomainMax", true]] }, function() { diff --git a/dom/webidl/ServiceWorkerContainer.webidl b/dom/webidl/ServiceWorkerContainer.webidl index 080896f02a..91232f9f3a 100644 --- a/dom/webidl/ServiceWorkerContainer.webidl +++ b/dom/webidl/ServiceWorkerContainer.webidl @@ -16,21 +16,20 @@ interface ServiceWorkerContainer : EventTarget { // and discussion at https://etherpad.mozilla.org/serviceworker07apr [Unforgeable] readonly attribute ServiceWorker? controller; - [Throws] + [SameObject, Throws] readonly attribute Promise ready; - [Throws] + [NewObject] Promise register(USVString scriptURL, optional RegistrationOptions options); - [Throws] - Promise getRegistration(optional USVString documentURL = ""); + [NewObject] + Promise getRegistration(optional USVString documentURL = ""); - [Throws] + [NewObject] Promise> getRegistrations(); attribute EventHandler oncontrollerchange; - attribute EventHandler onreloadpage; attribute EventHandler onerror; attribute EventHandler onmessage; }; diff --git a/dom/webidl/ServiceWorkerGlobalScope.webidl b/dom/webidl/ServiceWorkerGlobalScope.webidl index 8902c46762..b59bf0aa05 100644 --- a/dom/webidl/ServiceWorkerGlobalScope.webidl +++ b/dom/webidl/ServiceWorkerGlobalScope.webidl @@ -17,12 +17,11 @@ interface ServiceWorkerGlobalScope : WorkerGlobalScope { [SameObject] readonly attribute ServiceWorkerRegistration registration; [Throws, NewObject] - Promise skipWaiting(); + Promise skipWaiting(); attribute EventHandler oninstall; attribute EventHandler onactivate; - [Func="mozilla::dom::workers::ServiceWorkerGlobalScope::InterceptionEnabled"] attribute EventHandler onfetch; // The event.source of these MessageEvents are instances of Client diff --git a/dom/workers/ServiceWorkerContainer.h b/dom/workers/ServiceWorkerContainer.h index b96ac9c991..c9f2fb8f50 100644 --- a/dom/workers/ServiceWorkerContainer.h +++ b/dom/workers/ServiceWorkerContainer.h @@ -29,7 +29,6 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) IMPL_EVENT_HANDLER(controllerchange) - IMPL_EVENT_HANDLER(reloadpage) IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(message) diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 2a41d2be51..a4b2b73db2 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -544,15 +544,6 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); - // Allow opaque response interception to be disabled until we can ensure the - // security implications are not a complete disaster. - if (response->Type() == ResponseType::Opaque && - !worker->OpaqueInterceptionEnabled()) { - autoCancel.SetCancelMessage( - NS_LITERAL_CSTRING("OpaqueInterceptionDisabledWithURL"), mRequestURL); - return; - } - // Section "HTTP Fetch", step 2.2: // If one of the following conditions is true, return a network error: // * response's type is "error". diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 358da36349..3d2e662ac8 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -31,8 +31,6 @@ WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION) WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED) WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED) -WORKER_SIMPLE_PREF("dom.serviceWorkers.interception.enabled", InterceptionEnabled, INTERCEPTION_ENABLED) -WORKER_SIMPLE_PREF("dom.serviceWorkers.interception.opaque.enabled", OpaqueInterceptionEnabled, INTERCEPTION_OPAQUE_ENABLED) WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED) WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) WORKER_SIMPLE_PREF("dom.requestcache.enabled", RequestCacheEnabled, REQUESTCACHE_ENABLED) diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 6b38d488cc..c773afcfcc 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -633,16 +633,6 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv) return promise.forget(); } -// static -bool -ServiceWorkerGlobalScope::InterceptionEnabled(JSContext* aCx, JSObject* aObj) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - return worker->InterceptionEnabled(); -} - bool ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj) { diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index a985e09e68..e6919c54c1 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -239,9 +239,6 @@ public: WrapGlobalObject(JSContext* aCx, JS::MutableHandle aReflector) override; - static bool - InterceptionEnabled(JSContext* aCx, JSObject* aObj); - static bool OpenWindowEnabled(JSContext* aCx, JSObject* aObj); diff --git a/dom/workers/test/serviceworkers/browser_download.js b/dom/workers/test/serviceworkers/browser_download.js index 667b32b26f..dff9b1e5ec 100644 --- a/dom/workers/test/serviceworkers/browser_download.js +++ b/dom/workers/test/serviceworkers/browser_download.js @@ -44,8 +44,7 @@ function test() { SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], ['dom.serviceWorkers.exemptFromPerDomainMax', true], - ['dom.serviceWorkers.testing.enabled', true], - ['dom.serviceWorkers.interception.enabled', true]]}, + ['dom.serviceWorkers.testing.enabled', true]]}, function() { var url = gTestRoot + 'download_window.html'; var tab = gBrowser.addTab(); diff --git a/dom/workers/test/serviceworkers/interception_featuredetect.js b/dom/workers/test/serviceworkers/interception_featuredetect.js deleted file mode 100644 index d720144894..0000000000 --- a/dom/workers/test/serviceworkers/interception_featuredetect.js +++ /dev/null @@ -1,4 +0,0 @@ -// Only succeeds if onfetch is available. -if (!("onfetch" in self)) { - throw new Error("Not capable of interception"); -} diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index 44d2fe9ad9..bc4a5d46ac 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -167,7 +167,6 @@ support-files = strict_mode_warning.js skip_waiting_installed_worker.js skip_waiting_scope/index.html - interception_featuredetect.js thirdparty/iframe1.html thirdparty/iframe2.html thirdparty/register.html @@ -215,7 +214,6 @@ support-files = [test_importscript.html] [test_install_event.html] [test_installation_simple.html] -[test_interception_featuredetect.html] [test_match_all.html] [test_match_all_advanced.html] [test_match_all_client_id.html] diff --git a/dom/workers/test/serviceworkers/test_bug1151916.html b/dom/workers/test/serviceworkers/test_bug1151916.html index abd3d4da2f..67cada3e69 100644 --- a/dom/workers/test/serviceworkers/test_bug1151916.html +++ b/dom/workers/test/serviceworkers/test_bug1151916.html @@ -94,7 +94,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true], diff --git a/dom/workers/test/serviceworkers/test_claim.html b/dom/workers/test/serviceworkers/test_claim.html index 1c0aa97c7c..d7015850fc 100644 --- a/dom/workers/test/serviceworkers/test_claim.html +++ b/dom/workers/test/serviceworkers/test_claim.html @@ -162,7 +162,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_claim_fetch.html b/dom/workers/test/serviceworkers/test_claim_fetch.html index 9c7d925d00..8db6d304e4 100644 --- a/dom/workers/test/serviceworkers/test_claim_fetch.html +++ b/dom/workers/test/serviceworkers/test_claim_fetch.html @@ -88,7 +88,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_claim_oninstall.html b/dom/workers/test/serviceworkers/test_claim_oninstall.html index ac52ed15dc..2779231bad 100644 --- a/dom/workers/test/serviceworkers/test_claim_oninstall.html +++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html @@ -68,7 +68,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_client_focus.html b/dom/workers/test/serviceworkers/test_client_focus.html index cbe669d237..b0bf43bb34 100644 --- a/dom/workers/test/serviceworkers/test_client_focus.html +++ b/dom/workers/test/serviceworkers/test_client_focus.html @@ -86,7 +86,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_close.html b/dom/workers/test/serviceworkers/test_close.html index 1ea6efde0c..d2f72b9ef9 100644 --- a/dom/workers/test/serviceworkers/test_close.html +++ b/dom/workers/test/serviceworkers/test_close.html @@ -54,7 +54,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_controller.html b/dom/workers/test/serviceworkers/test_controller.html index 1840bbd1bf..789d7746da 100644 --- a/dom/workers/test/serviceworkers/test_controller.html +++ b/dom/workers/test/serviceworkers/test_controller.html @@ -74,7 +74,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html index bf9c21d058..e56bb84ca0 100644 --- a/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html +++ b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html @@ -40,7 +40,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html index 45d02a409a..fe4cb991cf 100644 --- a/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html +++ b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html @@ -44,7 +44,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true], // This is needed so that we can test upgrading a non-secure load inside an https iframe. ["security.mixed_content.block_active_content", false], ["security.mixed_content.block_display_content", false], diff --git a/dom/workers/test/serviceworkers/test_empty_serviceworker.html b/dom/workers/test/serviceworkers/test_empty_serviceworker.html index c0c96ccc27..b272287cb9 100644 --- a/dom/workers/test/serviceworkers/test_empty_serviceworker.html +++ b/dom/workers/test/serviceworkers/test_empty_serviceworker.html @@ -36,7 +36,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_eval_allowed.html b/dom/workers/test/serviceworkers/test_eval_allowed.html index 25125d31de..bfe8ac280d 100644 --- a/dom/workers/test/serviceworkers/test_eval_allowed.html +++ b/dom/workers/test/serviceworkers/test_eval_allowed.html @@ -44,7 +44,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_eventsource_intercept.html b/dom/workers/test/serviceworkers/test_eventsource_intercept.html index ab384158fd..55df62bb7b 100644 --- a/dom/workers/test/serviceworkers/test_eventsource_intercept.html +++ b/dom/workers/test/serviceworkers/test_eventsource_intercept.html @@ -95,8 +95,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true], - ["dom.serviceWorkers.interception.opaque.enabled", true], ["dom.caches.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_fetch_event.html b/dom/workers/test/serviceworkers/test_fetch_event.html index 5c21c3183e..764be87b13 100644 --- a/dom/workers/test/serviceworkers/test_fetch_event.html +++ b/dom/workers/test/serviceworkers/test_fetch_event.html @@ -73,8 +73,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], - ["dom.serviceWorkers.interception.opaque.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_file_blob_upload.html b/dom/workers/test/serviceworkers/test_file_blob_upload.html index 5410d65e64..30a31eb7e8 100644 --- a/dom/workers/test/serviceworkers/test_file_blob_upload.html +++ b/dom/workers/test/serviceworkers/test_file_blob_upload.html @@ -135,7 +135,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_force_refresh.html b/dom/workers/test/serviceworkers/test_force_refresh.html index e39330e5a3..05caf0e6ac 100644 --- a/dom/workers/test/serviceworkers/test_force_refresh.html +++ b/dom/workers/test/serviceworkers/test_force_refresh.html @@ -74,7 +74,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true], diff --git a/dom/workers/test/serviceworkers/test_gzip_redirect.html b/dom/workers/test/serviceworkers/test_gzip_redirect.html index f8c2ac082c..7ac92122ce 100644 --- a/dom/workers/test/serviceworkers/test_gzip_redirect.html +++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html @@ -77,7 +77,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html index ea2c1fe8ef..068046ca6f 100644 --- a/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html +++ b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html @@ -54,7 +54,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true], // This is needed so that we can test upgrading a non-secure load inside an https iframe. ["security.mixed_content.block_active_content", false], ["security.mixed_content.block_display_content", false], diff --git a/dom/workers/test/serviceworkers/test_https_fetch.html b/dom/workers/test/serviceworkers/test_https_fetch.html index 01e18b03d2..e990200f85 100644 --- a/dom/workers/test/serviceworkers/test_https_fetch.html +++ b/dom/workers/test/serviceworkers/test_https_fetch.html @@ -51,7 +51,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true] diff --git a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html index 410db7a276..1cf1dbef13 100644 --- a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html +++ b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html @@ -45,7 +45,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true] diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html index b780bc07b3..3878a1df6d 100644 --- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html @@ -48,7 +48,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html index ba2801c1b8..81a1d1da0e 100644 --- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html @@ -48,7 +48,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; diff --git a/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html index 100ec57fea..a968ac1a8e 100644 --- a/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html +++ b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html @@ -58,7 +58,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.caches.enabled", true] diff --git a/dom/workers/test/serviceworkers/test_imagecache.html b/dom/workers/test/serviceworkers/test_imagecache.html index 319b4edcfd..8627b54af7 100644 --- a/dom/workers/test/serviceworkers/test_imagecache.html +++ b/dom/workers/test/serviceworkers/test_imagecache.html @@ -45,7 +45,6 @@ onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_imagecache_max_age.html b/dom/workers/test/serviceworkers/test_imagecache_max_age.html index 63c8e48319..eb3c1f1668 100644 --- a/dom/workers/test/serviceworkers/test_imagecache_max_age.html +++ b/dom/workers/test/serviceworkers/test_imagecache_max_age.html @@ -63,7 +63,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.serviceWorkers.interception.enabled", true], ]}, runTest); }; diff --git a/dom/workers/test/serviceworkers/test_importscript.html b/dom/workers/test/serviceworkers/test_importscript.html index 7a363eed2a..5d2d5b3522 100644 --- a/dom/workers/test/serviceworkers/test_importscript.html +++ b/dom/workers/test/serviceworkers/test_importscript.html @@ -63,7 +63,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, runTest); diff --git a/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html new file mode 100644 index 0000000000..a659af92ba --- /dev/null +++ b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html @@ -0,0 +1,53 @@ + + + + + Bug 1198078 - test that we respect mixed content blocking in importScript() inside service workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_install_event.html b/dom/workers/test/serviceworkers/test_install_event.html
index dc0fcbf07a..0e59510fe7 100644
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -134,7 +134,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_installation_simple.html b/dom/workers/test/serviceworkers/test_installation_simple.html
index 1806b7a408..1b0d6c9471 100644
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -201,7 +201,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.caches.testing.enabled", true],
diff --git a/dom/workers/test/serviceworkers/test_interception_featuredetect.html b/dom/workers/test/serviceworkers/test_interception_featuredetect.html
deleted file mode 100644
index 61dd04366b..0000000000
--- a/dom/workers/test/serviceworkers/test_interception_featuredetect.html
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-  Bug 1173389 - Test fetch interception feature detection.
-  
-  
-
-
-

- -

-
-
-
-
-
diff --git a/dom/workers/test/serviceworkers/test_match_all.html b/dom/workers/test/serviceworkers/test_match_all.html
index 47c195cfb3..f4e65a730e 100644
--- a/dom/workers/test/serviceworkers/test_match_all.html
+++ b/dom/workers/test/serviceworkers/test_match_all.html
@@ -70,7 +70,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_match_all_advanced.html b/dom/workers/test/serviceworkers/test_match_all_advanced.html
index c68e691e3c..a458ed70ba 100644
--- a/dom/workers/test/serviceworkers/test_match_all_advanced.html
+++ b/dom/workers/test/serviceworkers/test_match_all_advanced.html
@@ -90,7 +90,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_id.html b/dom/workers/test/serviceworkers/test_match_all_client_id.html
index 9807820c66..c3cc5756a2 100644
--- a/dom/workers/test/serviceworkers/test_match_all_client_id.html
+++ b/dom/workers/test/serviceworkers/test_match_all_client_id.html
@@ -81,7 +81,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_properties.html b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
index 51f4e3fc5d..14e3445a4b 100644
--- a/dom/workers/test/serviceworkers/test_match_all_client_properties.html
+++ b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
@@ -88,7 +88,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_navigator.html b/dom/workers/test/serviceworkers/test_navigator.html
index 2b286bd936..164f41bcd2 100644
--- a/dom/workers/test/serviceworkers/test_navigator.html
+++ b/dom/workers/test/serviceworkers/test_navigator.html
@@ -28,7 +28,6 @@
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true]
   ]}, function() {
     checkEnabled();
diff --git a/dom/workers/test/serviceworkers/test_not_intercept_plugin.html b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
index 7d0df8780c..a90e068d34 100644
--- a/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
+++ b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
@@ -68,8 +68,6 @@
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.requestcontext.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html
index 02feefb3c9..d5c3ecf8b0 100644
--- a/dom/workers/test/serviceworkers/test_notificationclick.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -55,7 +55,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=916893
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.webnotifications.workers.enabled", true],
     ["dom.webnotifications.serviceworker.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
     ["notification.prompt.testing", true],
   ]}, runTest);
 
diff --git a/dom/workers/test/serviceworkers/test_opaque_intercept.html b/dom/workers/test/serviceworkers/test_opaque_intercept.html
index db86ffacec..5cb12e518c 100644
--- a/dom/workers/test/serviceworkers/test_opaque_intercept.html
+++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html
@@ -77,8 +77,6 @@
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_origin_after_redirect.html
index 58efa61b79..42f8b35caa 100644
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html
@@ -48,7 +48,6 @@
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
index 78d44ca530..2a19e20dec 100644
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
@@ -48,7 +48,6 @@
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
index d6b45a6ef4..dcac11aea1 100644
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
@@ -48,7 +48,6 @@
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
index e63cb7e753..b70d1704e8 100644
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
@@ -48,7 +48,6 @@
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
diff --git a/dom/workers/test/serviceworkers/test_post_message.html b/dom/workers/test/serviceworkers/test_post_message.html
index 1e2fc3c690..7df4bef1fc 100644
--- a/dom/workers/test/serviceworkers/test_post_message.html
+++ b/dom/workers/test/serviceworkers/test_post_message.html
@@ -69,7 +69,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_post_message_advanced.html b/dom/workers/test/serviceworkers/test_post_message_advanced.html
index 2f63dc5c57..ca76958615 100644
--- a/dom/workers/test/serviceworkers/test_post_message_advanced.html
+++ b/dom/workers/test/serviceworkers/test_post_message_advanced.html
@@ -98,7 +98,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_post_message_source.html b/dom/workers/test/serviceworkers/test_post_message_source.html
index aec3820e9e..4353e59b49 100644
--- a/dom/workers/test/serviceworkers/test_post_message_source.html
+++ b/dom/workers/test/serviceworkers/test_post_message_source.html
@@ -57,7 +57,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_request_context.js b/dom/workers/test/serviceworkers/test_request_context.js
index 829a6e3416..3432a34b04 100644
--- a/dom/workers/test/serviceworkers/test_request_context.js
+++ b/dom/workers/test/serviceworkers/test_request_context.js
@@ -70,7 +70,6 @@ onload = function() {
     ["dom.image.srcset.enabled", true],
     ["dom.requestcontext.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_sandbox_intercept.html b/dom/workers/test/serviceworkers/test_sandbox_intercept.html
index 7410ae78bc..f391a3ac93 100644
--- a/dom/workers/test/serviceworkers/test_sandbox_intercept.html
+++ b/dom/workers/test/serviceworkers/test_sandbox_intercept.html
@@ -41,7 +41,6 @@
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_sanitize.html b/dom/workers/test/serviceworkers/test_sanitize.html
index 8168e497ce..732785faa7 100644
--- a/dom/workers/test/serviceworkers/test_sanitize.html
+++ b/dom/workers/test/serviceworkers/test_sanitize.html
@@ -75,7 +75,6 @@
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, function() {
diff --git a/dom/workers/test/serviceworkers/test_sanitize_domain.html b/dom/workers/test/serviceworkers/test_sanitize_domain.html
index d55873203a..70e108b717 100644
--- a/dom/workers/test/serviceworkers/test_sanitize_domain.html
+++ b/dom/workers/test/serviceworkers/test_sanitize_domain.html
@@ -78,7 +78,6 @@
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, function() {
diff --git a/dom/workers/test/serviceworkers/test_scopes.html b/dom/workers/test/serviceworkers/test_scopes.html
index 7bd389390d..2d8116f837 100644
--- a/dom/workers/test/serviceworkers/test_scopes.html
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -111,7 +111,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_service_worker_allowed.html b/dom/workers/test/serviceworkers/test_service_worker_allowed.html
index 98a850d7a3..eca94ebb43 100644
--- a/dom/workers/test/serviceworkers/test_service_worker_allowed.html
+++ b/dom/workers/test/serviceworkers/test_service_worker_allowed.html
@@ -67,7 +67,6 @@
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
   ]}, runTest);
 
 
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
index f998d8f663..60a5dfd1f2 100644
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
@@ -104,7 +104,6 @@
     // force creation of our own Navigator object before our prefs are set.
     var prefs = [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ];
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
index da8c3d0af1..96dd9f1599 100644
--- a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
+++ b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
@@ -56,7 +56,6 @@
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_skip_waiting.html b/dom/workers/test/serviceworkers/test_skip_waiting.html
index f4081ee26f..7707d60350 100644
--- a/dom/workers/test/serviceworkers/test_skip_waiting.html
+++ b/dom/workers/test/serviceworkers/test_skip_waiting.html
@@ -86,7 +86,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_strict_mode_warning.html b/dom/workers/test/serviceworkers/test_strict_mode_warning.html
index 025fc089e0..5b66673b91 100644
--- a/dom/workers/test/serviceworkers/test_strict_mode_warning.html
+++ b/dom/workers/test/serviceworkers/test_strict_mode_warning.html
@@ -32,7 +32,6 @@
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_third_party_iframes.html b/dom/workers/test/serviceworkers/test_third_party_iframes.html
index 7c20a1f185..2a27c18a2d 100644
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -143,7 +143,6 @@ let steps = [() => {
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["browser.dom.window.dump.enabled", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
   ]}, next);
diff --git a/dom/workers/test/serviceworkers/test_unregister.html b/dom/workers/test/serviceworkers/test_unregister.html
index b6263d4752..8366f50c1c 100644
--- a/dom/workers/test/serviceworkers/test_unregister.html
+++ b/dom/workers/test/serviceworkers/test_unregister.html
@@ -128,7 +128,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
index 0116a435a5..5414ab40a4 100644
--- a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
+++ b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
@@ -87,7 +87,6 @@
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true]
     ]}, runTest);
   SimpleTest.waitForExplicitFinish();
 
diff --git a/dom/workers/test/serviceworkers/test_workerUnregister.html b/dom/workers/test/serviceworkers/test_workerUnregister.html
index 631d961a67..947861c176 100644
--- a/dom/workers/test/serviceworkers/test_workerUnregister.html
+++ b/dom/workers/test/serviceworkers/test_workerUnregister.html
@@ -72,7 +72,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_workerUpdate.html b/dom/workers/test/serviceworkers/test_workerUpdate.html
index 7cfbde08f5..5621d6cb89 100644
--- a/dom/workers/test/serviceworkers/test_workerUpdate.html
+++ b/dom/workers/test/serviceworkers/test_workerUpdate.html
@@ -52,7 +52,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
index f1691ee2d7..7f2bcf1195 100644
--- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
@@ -74,7 +74,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_xslt.html b/dom/workers/test/serviceworkers/test_xslt.html
index be56f50865..0cf3ea9035 100644
--- a/dom/workers/test/serviceworkers/test_xslt.html
+++ b/dom/workers/test/serviceworkers/test_xslt.html
@@ -102,7 +102,6 @@
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
diff --git a/js/public/Utility.h b/js/public/Utility.h
index 8c2a7d1b92..510708628c 100644
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -103,15 +103,6 @@ inline uint32_t GetThreadType(void) { return 0; }
 
 # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 
-/*
- * In order to test OOM conditions, when the testing function
- * oomAfterAllocations COUNT is passed, we fail continuously after the NUM'th
- * allocation from now.
- */
-extern JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations; /* set in builtin/TestingFunctions.cpp */
-extern JS_PUBLIC_DATA(uint32_t) OOM_counter; /* data race, who cares. */
-extern JS_PUBLIC_DATA(bool) OOM_failAlways;
-
 #ifdef JS_OOM_BREAKPOINT
 static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
 #define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
@@ -122,7 +113,31 @@ static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
 namespace js {
 namespace oom {
 
+/*
+ * Out of memory testing support.  We provide various testing functions to
+ * simulate OOM conditions and so we can test that they are handled correctly.
+ */
+
 extern JS_PUBLIC_DATA(uint32_t) targetThread;
+extern JS_PUBLIC_DATA(uint64_t) maxAllocations;
+extern JS_PUBLIC_DATA(uint64_t) counter;
+extern JS_PUBLIC_DATA(bool) failAlways;
+
+static inline void
+SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always) {
+    MOZ_ASSERT(counter + allocations > counter);
+    MOZ_ASSERT(thread > js::oom::THREAD_TYPE_NONE && thread < js::oom::THREAD_TYPE_MAX);
+    targetThread = thread;
+    maxAllocations = counter + allocations;
+    failAlways = always;
+}
+
+static inline void
+ResetSimulatedOOM() {
+    targetThread = THREAD_TYPE_NONE;
+    maxAllocations = UINT64_MAX;
+    failAlways = false;
+}
 
 static inline bool
 IsThreadSimulatingOOM()
@@ -133,8 +148,8 @@ IsThreadSimulatingOOM()
 static inline bool
 IsSimulatedOOMAllocation()
 {
-    return IsThreadSimulatingOOM() && (OOM_counter == OOM_maxAllocations ||
-           (OOM_counter > OOM_maxAllocations && OOM_failAlways));
+    return IsThreadSimulatingOOM() &&
+           (counter == maxAllocations || (counter > maxAllocations && failAlways));
 }
 
 static inline bool
@@ -143,7 +158,7 @@ ShouldFailWithOOM()
     if (!IsThreadSimulatingOOM())
         return false;
 
-    OOM_counter++;
+    counter++;
     if (IsSimulatedOOMAllocation()) {
         JS_OOM_CALL_BP_FUNC();
         return true;
@@ -151,6 +166,11 @@ ShouldFailWithOOM()
     return false;
 }
 
+static inline bool
+HadSimulatedOOM() {
+    return counter >= maxAllocations;
+}
+
 } /* namespace oom */
 } /* namespace js */
 
@@ -188,22 +208,22 @@ struct MOZ_RAII AutoEnterOOMUnsafeRegion
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     AutoEnterOOMUnsafeRegion()
-      : oomEnabled_(oom::IsThreadSimulatingOOM() && OOM_maxAllocations != UINT32_MAX),
+      : oomEnabled_(oom::IsThreadSimulatingOOM() && oom::maxAllocations != UINT64_MAX),
         oomAfter_(0)
     {
         if (oomEnabled_) {
-            oomAfter_ = int64_t(OOM_maxAllocations) - OOM_counter;
-            OOM_maxAllocations = UINT32_MAX;
+            oomAfter_ = int64_t(oom::maxAllocations) - int64_t(oom::counter);
+            oom::maxAllocations = UINT64_MAX;
         }
     }
 
     ~AutoEnterOOMUnsafeRegion() {
         if (oomEnabled_) {
-            MOZ_ASSERT(OOM_maxAllocations == UINT32_MAX);
-            int64_t maxAllocations = OOM_counter + oomAfter_;
-            MOZ_ASSERT(maxAllocations >= 0 && maxAllocations < UINT32_MAX,
+            MOZ_ASSERT(oom::maxAllocations == UINT64_MAX);
+            int64_t maxAllocations = int64_t(oom::counter) + oomAfter_;
+            MOZ_ASSERT(maxAllocations >= 0,
                        "alloc count + oom limit exceeds range, your oom limit is probably too large");
-            OOM_maxAllocations = uint32_t(maxAllocations);
+            oom::maxAllocations = uint64_t(maxAllocations);
         }
     }
 
diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp
index db51a1fc49..5ee019d731 100644
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2579,6 +2579,8 @@ SimdToExpr(SimdType type, SimdOperation op)
 #undef B32CASE
 #undef ENUMERATE
 
+typedef Vector NameVector;
+
 // Encapsulates the building of an asm bytecode function from an asm.js function
 // source code, packing the asm.js code into the asm bytecode form that can
 // be decoded and compiled with a FunctionCompiler.
@@ -2605,7 +2607,13 @@ class MOZ_STACK_CLASS FunctionValidator
     Maybe    encoder_;
 
     LocalMap          locals_;
-    LabelMap          labels_;
+
+    // Labels
+    LabelMap          breakLabels_;
+    LabelMap          continueLabels_;
+    Uint32Vector      breakableStack_;
+    Uint32Vector      continuableStack_;
+    uint32_t          blockDepth_;
 
     bool              hasAlreadyReturned_;
     ExprType          ret_;
@@ -2615,16 +2623,28 @@ class MOZ_STACK_CLASS FunctionValidator
       : m_(m),
         fn_(fn),
         locals_(m.cx()),
-        labels_(m.cx()),
+        breakLabels_(m.cx()),
+        continueLabels_(m.cx()),
+        blockDepth_(0),
         hasAlreadyReturned_(false)
     {}
 
+    ~FunctionValidator() {
+        if (m_.hasAlreadyFailed())
+            return;
+        MOZ_ASSERT(!blockDepth_);
+        MOZ_ASSERT(breakableStack_.empty());
+        MOZ_ASSERT(continuableStack_.empty());
+        MOZ_ASSERT(breakLabels_.empty());
+        MOZ_ASSERT(continueLabels_.empty());
+    }
+
     ModuleValidator& m() const        { return m_; }
     ExclusiveContext* cx() const      { return m_.cx(); }
     ParseNode* fn() const             { return fn_; }
 
     bool init(PropertyName* name, unsigned line) {
-        if (!locals_.init() || !labels_.init())
+        if (!locals_.init() || !breakLabels_.init() || !continueLabels_.init())
             return false;
 
         if (!m_.mg().startFuncDef(line, &fg_))
@@ -2680,22 +2700,103 @@ class MOZ_STACK_CLASS FunctionValidator
     }
 
     /**************************************************************** Labels */
-
-    uint32_t lookupLabel(PropertyName* label) const {
-        if (auto p = labels_.lookup(label))
-            return p->value();
-        return -1;
+  private:
+    bool writeBr(uint32_t absolute, Expr expr = Expr::Br) {
+        MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
+        MOZ_ASSERT(absolute < blockDepth_);
+        return encoder().writeExpr(expr) &&
+               encoder().writeVarU32(blockDepth_ - 1 - absolute);
+    }
+    void removeLabel(PropertyName* label, LabelMap* map) {
+        LabelMap::Ptr p = map->lookup(label);
+        MOZ_ASSERT(p);
+        map->remove(p);
     }
 
-    bool addLabel(PropertyName* label, uint32_t* id) {
-        *id = labels_.count();
-        return labels_.putNew(label, *id);
+  public:
+    bool pushBreakableBlock(uint32_t numStmts) {
+        return encoder().writeExpr(Expr::Block) &&
+               encoder().writeVarU32(numStmts) &&
+               breakableStack_.append(blockDepth_++);
+    }
+    void popBreakableBlock() {
+        JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
     }
 
-    void removeLabel(PropertyName* label) {
-        auto p = labels_.lookup(label);
-        MOZ_ASSERT(!!p);
-        labels_.remove(p);
+    bool pushUnbreakableBlock(uint32_t numStmts, const NameVector* labels = nullptr) {
+        if (labels) {
+            for (PropertyName* label : *labels) {
+                if (!breakLabels_.putNew(label, blockDepth_))
+                    return false;
+            }
+        }
+        blockDepth_++;
+        return encoder().writeExpr(Expr::Block) &&
+               encoder().writeVarU32(numStmts);
+    }
+    void popUnbreakableBlock(const NameVector* labels = nullptr) {
+        if (labels) {
+            for (PropertyName* label : *labels)
+                removeLabel(label, &breakLabels_);
+        }
+        --blockDepth_;
+    }
+
+    bool pushContinuableBlock(uint32_t numStmts) {
+        return encoder().writeExpr(Expr::Block) &&
+               encoder().writeVarU32(numStmts) &&
+               continuableStack_.append(blockDepth_++);
+    }
+    void popContinuableBlock() {
+        JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
+    }
+
+    bool pushLoop(uint32_t numStmts) {
+        return encoder().writeExpr(Expr::Loop) &&
+               encoder().writeVarU32(numStmts) &&
+               breakableStack_.append(blockDepth_++) &&
+               continuableStack_.append(blockDepth_++);
+    }
+    void popLoop() {
+        JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
+        JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
+    }
+
+    bool writeBreakIf() {
+        return writeBr(breakableStack_.back(), Expr::BrIf);
+    }
+    bool writeContinueIf() {
+        return writeBr(continuableStack_.back(), Expr::BrIf);
+    }
+    bool writeUnlabeledBreakOrContinue(bool isBreak) {
+        return writeBr(isBreak? breakableStack_.back() : continuableStack_.back());
+    }
+    bool writeContinue() {
+        return writeBr(continuableStack_.back());
+    }
+
+    bool addLabels(const NameVector& labels, uint32_t relativeBreakDepth,
+                   uint32_t relativeContinueDepth)
+    {
+        for (PropertyName* label : labels) {
+            if (!breakLabels_.putNew(label, blockDepth_ + relativeBreakDepth))
+                return false;
+            if (!continueLabels_.putNew(label, blockDepth_ + relativeContinueDepth))
+                return false;
+        }
+        return true;
+    }
+    void removeLabels(const NameVector& labels) {
+        for (PropertyName* label : labels) {
+            removeLabel(label, &breakLabels_);
+            removeLabel(label, &continueLabels_);
+        }
+    }
+    bool writeLabeledBreakOrContinue(PropertyName* label, bool isBreak) {
+        LabelMap& map = isBreak ? breakLabels_ : continueLabels_;
+        if (LabelMap::Ptr p = map.lookup(label))
+            return writeBr(p->value());
+        MOZ_CRASH("nonexistent label");
     }
 
     /*************************************************** Read-only interface */
@@ -5555,6 +5656,8 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
     MOZ_ASSERT(comma->isKind(PNK_COMMA));
     ParseNode* operands = ListHead(comma);
 
+    // The block depth isn't taken into account here, because a comma list can't
+    // contain breaks and continues and nested control flow structures.
     if (!f.encoder().writeExpr(Expr::Block))
         return false;
     if (!f.encoder().writeVarU32(ListLength(comma)))
@@ -6005,19 +6108,25 @@ CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt)
     return CheckAsExprStatement(f, expr);
 }
 
-enum class InterruptCheckPosition {
-    Head,
-    Loop
-};
-
 static bool
-CheckWhile(FunctionValidator& f, ParseNode* whileStmt)
+CheckLoopConditionOnEntry(FunctionValidator& f, ParseNode* cond)
 {
-    MOZ_ASSERT(whileStmt->isKind(PNK_WHILE));
-    ParseNode* cond = BinaryLeft(whileStmt);
-    ParseNode* body = BinaryRight(whileStmt);
+    uint32_t maybeLit;
 
-    if (!f.encoder().writeExpr(Expr::While))
+    // TODO: will not need to generate nop when blocks switch from
+    // number-of-statements immediate to end marker.
+    if (IsLiteralInt(f.m(), cond, &maybeLit) && maybeLit)
+        return f.encoder().writeExpr(Expr::Nop);
+
+    // brIf (i32.eq 0 $f) $out
+    if (!f.writeBreakIf())
+        return false;
+
+    // TODO change this to i32.eqz
+    // i32.eq 0 $f
+    if (!f.encoder().writeExpr(Expr::I32Eq))
+        return false;
+    if (!f.writeInt32Lit(0))
         return false;
 
     Type condType;
@@ -6026,11 +6135,43 @@ CheckWhile(FunctionValidator& f, ParseNode* whileStmt)
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
-    return CheckStatement(f, body);
+    return true;
 }
 
 static bool
-CheckFor(FunctionValidator& f, ParseNode* forStmt)
+CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
+{
+    MOZ_ASSERT(whileStmt->isKind(PNK_WHILE));
+    ParseNode* cond = BinaryLeft(whileStmt);
+    ParseNode* body = BinaryRight(whileStmt);
+
+    // A while loop `while(#cond) #body` is equivalent to:
+    // (loop $after_loop $top
+    //    (brIf $after_loop (i32.eq 0 #cond))
+    //    #body
+    //    (br $top)
+    // )
+    if (labels && !f.addLabels(*labels, 0, 1))
+        return false;
+
+    if (!f.pushLoop(/* numStmts = */ 3))
+        return false;
+
+    if (!CheckLoopConditionOnEntry(f, cond))
+        return false;
+    if (!CheckStatement(f, body))
+        return false;
+    if (!f.writeContinue())
+        return false;
+
+    f.popLoop();
+    if (labels)
+        f.removeLabels(*labels);
+    return true;
+}
+
+static bool
+CheckFor(FunctionValidator& f, ParseNode* forStmt, const NameVector* labels = nullptr)
 {
     MOZ_ASSERT(forStmt->isKind(PNK_FOR));
     ParseNode* forHead = BinaryLeft(forStmt);
@@ -6043,44 +6184,89 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt)
     ParseNode* maybeCond = TernaryKid2(forHead);
     ParseNode* maybeInc = TernaryKid3(forHead);
 
-    Expr stmt = maybeInit ? (maybeInc ? Expr::ForInitInc   : Expr::ForInitNoInc)
-                          : (maybeInc ? Expr::ForNoInitInc : Expr::ForNoInitNoInc);
-    if (!f.encoder().writeExpr(stmt))
+    // A for-loop `for (#init; #cond; #inc) #body` is equivalent to:
+    // (block                                               // depth X
+    //   (#init)
+    //   (loop $after_loop $loop_top                        // depth X+2 (loop)
+    //     (brIf $after (eq 0 #cond))
+    //     (block $after_body #body)                        // depth X+3
+    //     #inc
+    //     (br $loop_top)
+    //   )
+    // )
+    // A break in the body should break out to $after_loop, i.e. depth + 1.
+    // A continue in the body should break out to $after_body, i.e. depth + 3.
+    if (labels && !f.addLabels(*labels, 1, 3))
+        return false;
+
+    if (!f.pushUnbreakableBlock(/* numStmts = */ 1 + !!maybeInit))
         return false;
 
     if (maybeInit && !CheckAsExprStatement(f, maybeInit))
         return false;
 
-    if (maybeCond) {
-        Type condType;
-        if (!CheckExpr(f, maybeCond, &condType))
+    {
+        if (!f.pushLoop(/* numStmts = */ 2 + !!maybeCond + !!maybeInc))
             return false;
-        if (!condType.isInt())
-            return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars());
-    } else if (!f.writeInt32Lit(1)) {
-        return false;
+
+        if (maybeCond && !CheckLoopConditionOnEntry(f, maybeCond))
+            return false;
+
+        {
+            // Continuing in the body should just break out to the increment.
+            if (!f.pushContinuableBlock(1))
+                return false;
+            if (!CheckStatement(f, body))
+                return false;
+            f.popContinuableBlock();
+        }
+
+        if (maybeInc && !CheckAsExprStatement(f, maybeInc))
+            return false;
+
+        if (!f.writeContinue())
+            return false;
+        f.popLoop();
     }
 
-    if (!CheckStatement(f, body))
-        return false;
+    f.popUnbreakableBlock();
 
-    if (maybeInc && !CheckAsExprStatement(f, maybeInc))
-        return false;
+    if (labels)
+        f.removeLabels(*labels);
 
     return true;
 }
 
 static bool
-CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt)
+CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
 {
     MOZ_ASSERT(whileStmt->isKind(PNK_DOWHILE));
     ParseNode* body = BinaryLeft(whileStmt);
     ParseNode* cond = BinaryRight(whileStmt);
 
-    if (!f.encoder().writeExpr(Expr::DoWhile))
+    // A do-while loop `do { #body } while (#cond)` is equivalent to:
+    // (loop $after_loop $top       // depth X
+    //   (block #body)              // depth X+2
+    //   (brIf #cond $top)
+    // )
+    // A break should break out of the entire loop, i.e. at depth 0.
+    // A continue should break out to the condition, i.e. at depth 2.
+    if (labels && !f.addLabels(*labels, 0, 2))
         return false;
 
-    if (!CheckStatement(f, body))
+    if (!f.pushLoop(2 /* numStmts = #body + br_if */))
+        return false;
+
+    {
+        // An unlabeled continue in the body should break out to the condition.
+        if (!f.pushContinuableBlock(1))
+            return false;
+        if (!CheckStatement(f, body))
+            return false;
+        f.popContinuableBlock();
+    }
+
+    if (!f.writeContinueIf())
         return false;
 
     Type condType;
@@ -6089,30 +6275,47 @@ CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt)
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
+    f.popLoop();
+    if (labels)
+        f.removeLabels(*labels);
     return true;
 }
 
+static bool CheckStatementList(FunctionValidator& f, ParseNode*, const NameVector* = nullptr);
+
 static bool
 CheckLabel(FunctionValidator& f, ParseNode* labeledStmt)
 {
     MOZ_ASSERT(labeledStmt->isKind(PNK_LABEL));
-    PropertyName* label = LabeledStatementLabel(labeledStmt);
-    ParseNode* stmt = LabeledStatementStatement(labeledStmt);
 
-    if (!f.encoder().writeExpr(Expr::Label))
+    NameVector labels;
+    ParseNode* innermost = labeledStmt;
+    do {
+        if (!labels.append(LabeledStatementLabel(innermost)))
+            return false;
+        innermost = LabeledStatementStatement(innermost);
+    } while (innermost->getKind() == PNK_LABEL);
+
+    switch (innermost->getKind()) {
+      case PNK_FOR:
+        return CheckFor(f, innermost, &labels);
+      case PNK_DOWHILE:
+        return CheckDoWhile(f, innermost, &labels);
+      case PNK_WHILE:
+        return CheckWhile(f, innermost, &labels);
+      case PNK_STATEMENTLIST:
+        return CheckStatementList(f, innermost, &labels);
+      default:
+        break;
+    }
+
+    if (!f.pushUnbreakableBlock(1, &labels))
         return false;
 
-    uint32_t labelId;
-    if (!f.addLabel(label, &labelId))
+    if (!CheckStatement(f, innermost))
         return false;
 
-    if (!f.encoder().writeVarU32(labelId))
-        return false;
-
-    if (!CheckStatement(f, stmt))
-        return false;
-
-    f.removeLabel(label);
+    f.popUnbreakableBlock(&labels);
     return true;
 }
 
@@ -6195,7 +6398,7 @@ CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt)
 
 static bool
 CheckSwitchRange(FunctionValidator& f, ParseNode* stmt, int32_t* low, int32_t* high,
-                 int32_t* tableLength)
+                 uint32_t* tableLength)
 {
     if (IsDefaultCase(stmt)) {
         *low = 0;
@@ -6221,106 +6424,147 @@ CheckSwitchRange(FunctionValidator& f, ParseNode* stmt, int32_t* low, int32_t* h
     }
 
     int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
-    if (i64 > 4*1024*1024)
+    if (i64 > 4 * 1024 * 1024)
         return f.fail(initialStmt, "all switch statements generate tables; this table would be too big");
 
-    *tableLength = int32_t(i64);
+    *tableLength = uint32_t(i64);
     return true;
 }
 
-void
-PatchSwitch(FunctionValidator& f,
-            size_t hasDefaultAt, bool hasDefault,
-            size_t lowAt, int32_t low,
-            size_t highAt, int32_t high,
-            size_t numCasesAt, uint32_t numCases)
+static bool
+CheckSwitchExpr(FunctionValidator& f, ParseNode* switchExpr)
 {
-    f.encoder().patchU8(hasDefaultAt, uint8_t(hasDefault));
-    f.encoder().patchVarU32(lowAt, low);
-    f.encoder().patchVarU32(highAt, high);
-    f.encoder().patchVarU32(numCasesAt, numCases);
+    Type exprType;
+    if (!CheckExpr(f, switchExpr, &exprType))
+        return false;
+    if (!exprType.isSigned())
+        return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
+    return true;
 }
 
+// A switch will be constructed as:
+// - the default block wrapping all the other blocks, to be able to break
+// out of the switch with an unlabeled break statement. It has two statements
+// (an inner block and the default expr). asm.js rules require default to be at
+// the end, so the default block always encloses all the cases blocks.
+// - one block per case between low and high; undefined cases just jump to the
+// default case. Each of these blocks contain two statements: the next case's
+// block and the possibly empty statement list comprising the case body. The
+// last block pushed is the first case so the (relative) branch target therefore
+// matches the sequential order of cases.
+// - one block for the br_table, so that the first break goes to the first
+// case's block.
 static bool
 CheckSwitch(FunctionValidator& f, ParseNode* switchStmt)
 {
     MOZ_ASSERT(switchStmt->isKind(PNK_SWITCH));
 
-    if (!f.encoder().writeExpr(Expr::TableSwitch))
-        return false;
-
-    // Has default
-    size_t hasDefaultAt;
-    if (!f.encoder().writePatchableU8(&hasDefaultAt))
-        return false;
-
-    // Low / High / Num cases
-    size_t lowAt;
-    if (!f.encoder().writePatchableVarU32(&lowAt))
-        return false;
-    size_t highAt;
-    if (!f.encoder().writePatchableVarU32(&highAt))
-        return false;
-    size_t numCasesAt;
-    if (!f.encoder().writePatchableVarU32(&numCasesAt))
-        return false;
-
     ParseNode* switchExpr = BinaryLeft(switchStmt);
     ParseNode* switchBody = BinaryRight(switchStmt);
 
     if (!switchBody->isKind(PNK_STATEMENTLIST))
         return f.fail(switchBody, "switch body may not contain 'let' declarations");
 
-    Type exprType;
-    if (!CheckExpr(f, switchExpr, &exprType))
-        return false;
-
-    if (!exprType.isSigned())
-        return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
-
     ParseNode* stmt = ListHead(switchBody);
+    if (!stmt)
+        return CheckSwitchExpr(f, switchExpr);
 
     if (!CheckDefaultAtEnd(f, stmt))
         return false;
 
-    if (!stmt) {
-        PatchSwitch(f, hasDefaultAt, false, lowAt, 0, highAt, 0, numCasesAt, 0);
-        return true;
-    }
-
-    int32_t low = 0, high = 0, tableLength = 0;
+    int32_t low = 0, high = 0;
+    uint32_t tableLength = 0;
     if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
         return false;
 
-    Vector cases(f.cx());
-    if (!cases.resize(tableLength))
+    static const uint32_t CASE_NOT_DEFINED = UINT32_MAX;
+
+    Uint32Vector caseDepths;
+    if (!caseDepths.appendN(CASE_NOT_DEFINED, tableLength))
         return false;
 
     uint32_t numCases = 0;
+    for (ParseNode* s = stmt; s && !IsDefaultCase(s); s = NextNode(s)) {
+        int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(s)).toInt32();
+
+        MOZ_ASSERT(caseValue >= low);
+        unsigned i = caseValue - low;
+        if (caseDepths[i] != CASE_NOT_DEFINED)
+            return f.fail(s, "no duplicate case labels");
+
+        MOZ_ASSERT(numCases != CASE_NOT_DEFINED);
+        caseDepths[i] = numCases++;
+    }
+
+    // Open the wrapping breakable default block.
+    if (!f.pushBreakableBlock(2))
+        return false;
+
+    // Open all the case blocks.
+    for (uint32_t i = 0; i < numCases; i++) {
+        if (!f.pushUnbreakableBlock(2))
+            return false;
+    }
+
+    // Open the br_table block.
+    if (!f.pushUnbreakableBlock(1))
+        return false;
+
+    // The default block is the last one.
+    uint32_t defaultDepth = numCases;
+
+    // Start the br_table block.
+    if (!f.encoder().writeExpr(Expr::BrTable))
+        return false;
+
+    // Write default depth and number of cases (tableLength - 1 + 1 (default)).
+    if (!f.encoder().writeVarU32(defaultDepth))
+        return false;
+    if (!f.encoder().writeVarU32(tableLength))
+        return false;
+
+    // Each case value describes the relative depth to the actual block. When
+    // a case is not explicitly defined, it goes to the default.
+    for (size_t i = 0; i < tableLength; i++) {
+        uint32_t target = caseDepths[i] == CASE_NOT_DEFINED ? defaultDepth : caseDepths[i];
+        if (!f.encoder().writeVarU32(target))
+            return false;
+    }
+
+    // Subtract lowest case value, so that all the cases start from 0.
+    if (low) {
+        if (!f.encoder().writeExpr(Expr::I32Sub))
+            return false;
+        if (!CheckSwitchExpr(f, switchExpr))
+            return false;
+        if (!f.writeInt32Lit(low))
+            return false;
+    } else {
+        if (!CheckSwitchExpr(f, switchExpr))
+            return false;
+    }
+
+    // Our br_table is done. Close its block, write the cases down in order.
+    f.popUnbreakableBlock();
+
     for (; stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
-        int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32();
-        unsigned caseIndex = caseValue - low;
-
-        if (cases[caseIndex])
-            return f.fail(stmt, "no duplicate case labels");
-
-        cases[caseIndex] = true;
-        numCases += 1;
-        if (!f.encoder().writeVarU32(caseValue))
-            return false;
-
         if (!CheckStatement(f, CaseBody(stmt)))
             return false;
+        f.popUnbreakableBlock();
     }
 
-    bool hasDefault = false;
+    // Write the default block.
     if (stmt && IsDefaultCase(stmt)) {
-        hasDefault = true;
         if (!CheckStatement(f, CaseBody(stmt)))
             return false;
+    } else {
+        // TODO no need to write this nop once we go postorder.
+        if (!f.encoder().writeExpr(Expr::Nop))
+            return false;
     }
 
-    PatchSwitch(f, hasDefaultAt, hasDefault, lowAt, low, highAt, high, numCasesAt, numCases);
+    // Close the wrapping block.
+    f.popBreakableBlock();
     return true;
 }
 
@@ -6362,13 +6606,12 @@ CheckReturn(FunctionValidator& f, ParseNode* returnStmt)
 }
 
 static bool
-CheckStatementList(FunctionValidator& f, ParseNode* stmtList)
+CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* labels /*= nullptr */)
 {
     MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
 
-    if (!f.encoder().writeExpr(Expr::Block))
-        return false;
-    if (!f.encoder().writeVarU32(ListLength(stmtList)))
+    uint32_t numStmts = ListLength(stmtList);
+    if (!f.pushUnbreakableBlock(numStmts, labels))
         return false;
 
     for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
@@ -6376,23 +6619,16 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList)
             return false;
     }
 
+    f.popUnbreakableBlock(labels);
     return true;
 }
 
 static bool
-CheckBreakOrContinue(FunctionValidator& f, PropertyName* maybeLabel,
-                     Expr withoutLabel, Expr withLabel)
+CheckBreakOrContinue(FunctionValidator& f, bool isBreak, ParseNode* stmt)
 {
-    if (!maybeLabel)
-        return f.encoder().writeExpr(withoutLabel);
-
-    if (!f.encoder().writeExpr(withLabel))
-        return false;
-
-    uint32_t labelId = f.lookupLabel(maybeLabel);
-    MOZ_ASSERT(labelId != uint32_t(-1));
-
-    return f.encoder().writeVarU32(labelId);
+    if (PropertyName* maybeLabel = LoopControlMaybeLabel(stmt))
+        return f.writeLabeledBreakOrContinue(maybeLabel, isBreak);
+    return f.writeUnlabeledBreakOrContinue(isBreak);
 }
 
 static bool
@@ -6410,10 +6646,8 @@ CheckStatement(FunctionValidator& f, ParseNode* stmt)
       case PNK_SWITCH:        return CheckSwitch(f, stmt);
       case PNK_RETURN:        return CheckReturn(f, stmt);
       case PNK_STATEMENTLIST: return CheckStatementList(f, stmt);
-      case PNK_BREAK:         return CheckBreakOrContinue(f, LoopControlMaybeLabel(stmt),
-                                                          Expr::Break, Expr::BreakLabel);
-      case PNK_CONTINUE:      return CheckBreakOrContinue(f, LoopControlMaybeLabel(stmt),
-                                                          Expr::Continue, Expr::ContinueLabel);
+      case PNK_BREAK:         return CheckBreakOrContinue(f, true, stmt);
+      case PNK_CONTINUE:      return CheckBreakOrContinue(f, false, stmt);
       default:;
     }
 
diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp
index 5580a2fa45..c6d04ba6a0 100644
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -427,10 +427,9 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
       case Expr::IfElse:
         return DecodeIfElse(f, /* hasElse */ true, expected);
       case Expr::I32Clz:
+      case Expr::I32Ctz:
       case Expr::I32Popcnt:
         return DecodeUnaryOperator(f, expected, ExprType::I32);
-      case Expr::I32Ctz:
-        return f.fail("NYI: ctz");
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h
index cbb9215586..08789c7f95 100644
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -66,7 +66,7 @@ enum class Expr : uint16_t
     Select,
     Br,
     BrIf,
-    TableSwitch,
+    BrTable,
     Return,
     Unreachable,
 
@@ -239,20 +239,6 @@ enum class Expr : uint16_t
     F64StoreMem,
 
     // asm.js specific
-    While,          // all CFG ops to be deleted in favor of Loop/Br/BrIf
-    DoWhile,
-
-    ForInitInc,
-    ForInitNoInc,
-    ForNoInitNoInc,
-    ForNoInitInc,
-
-    Label,
-    Continue,
-    ContinueLabel,
-    Break,
-    BreakLabel,
-
     Id,
 
     I32Min,
diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp
index 731014391c..b0b2eeb808 100644
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -28,7 +28,6 @@ using namespace js::wasm;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 
-typedef Vector LabelVector;
 typedef Vector BlockVector;
 
 // Encapsulates the compilation of a single function in an asm.js module. The
@@ -37,14 +36,11 @@ typedef Vector BlockVector;
 class FunctionCompiler
 {
   private:
-    typedef HashMap, SystemAllocPolicy> LabeledBlockMap;
-    typedef HashMap, SystemAllocPolicy> UnlabeledBlockMap;
-    typedef Vector PositionStack;
+    typedef Vector BlocksVector;
 
     ModuleGeneratorThreadView& mg_;
     const FuncBytecode&        func_;
     Decoder                    decoder_;
-    size_t                     nextId_;
     size_t                     lastReadCallSite_;
 
     TempAllocator&             alloc_;
@@ -54,12 +50,9 @@ class FunctionCompiler
 
     MBasicBlock*               curBlock_;
 
-    PositionStack              loopStack_;
-    PositionStack              breakableStack_;
-    UnlabeledBlockMap          unlabeledBreaks_;
-    UnlabeledBlockMap          unlabeledContinues_;
-    LabeledBlockMap            labeledBreaks_;
-    LabeledBlockMap            labeledContinues_;
+    uint32_t                   loopDepth_;
+    uint32_t                   blockDepth_;
+    BlocksVector               targets_;
 
     FuncCompileResults&        compileResults_;
 
@@ -69,13 +62,14 @@ class FunctionCompiler
       : mg_(mg),
         func_(func),
         decoder_(func.bytecode()),
-        nextId_(0),
         lastReadCallSite_(0),
         alloc_(mirGen.alloc()),
         graph_(mirGen.graph()),
         info_(mirGen.info()),
         mirGen_(mirGen),
         curBlock_(nullptr),
+        loopDepth_(0),
+        blockDepth_(0),
         compileResults_(compileResults)
     {}
 
@@ -86,21 +80,13 @@ class FunctionCompiler
 
     bool init()
     {
-        if (!unlabeledBreaks_.init() ||
-            !unlabeledContinues_.init() ||
-            !labeledBreaks_.init() ||
-            !labeledContinues_.init())
-        {
-            return false;
-        }
-
         // Prepare the entry block for MIR generation:
 
         const ValTypeVector& args = func_.sig().args();
 
         if (!mirGen_.ensureBallast())
             return false;
-        if (!newBlock(/* pred = */ nullptr, &curBlock_))
+        if (!newBlock(/* prev */ nullptr, &curBlock_))
             return false;
 
         for (ABIArgValTypeIter i(args); !i.done(); i++) {
@@ -153,11 +139,13 @@ class FunctionCompiler
 
     void checkPostconditions()
     {
-        MOZ_ASSERT(loopStack_.empty());
-        MOZ_ASSERT(unlabeledBreaks_.empty());
-        MOZ_ASSERT(unlabeledContinues_.empty());
-        MOZ_ASSERT(labeledBreaks_.empty());
-        MOZ_ASSERT(labeledContinues_.empty());
+        MOZ_ASSERT(loopDepth_ == 0);
+        MOZ_ASSERT(blockDepth_ == 0);
+#ifdef DEBUG
+        for (BlockVector& vec : targets_) {
+            MOZ_ASSERT(vec.empty());
+        }
+#endif
         MOZ_ASSERT(inDeadCode());
         MOZ_ASSERT(decoder_.done(), "all bytecode must be consumed");
         MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
@@ -905,19 +893,24 @@ class FunctionCompiler
     {
         if (inDeadCode() && thenBlocks.empty())
             return true;
-        MBasicBlock* pred = curBlock_ ? curBlock_ : thenBlocks[0];
+
         MBasicBlock* join;
-        if (!newBlock(pred, &join))
-            return false;
-        if (curBlock_)
-            curBlock_->end(MGoto::New(alloc(), join));
-        for (size_t i = 0; i < thenBlocks.length(); i++) {
-            thenBlocks[i]->end(MGoto::New(alloc(), join));
-            if (pred == curBlock_ || i > 0) {
-                if (!join->addPredecessor(alloc(), thenBlocks[i]))
-                    return false;
-            }
+
+        if (curBlock_) {
+            if (!goToNewBlock(curBlock_, &join))
+                return false;
+            if (!thenBlocks.empty() && !goToExistingBlock(thenBlocks[0], join))
+                return false;
+        } else {
+            if (!goToNewBlock(thenBlocks[0], &join))
+                return false;
         }
+
+        for (size_t i = 1; i < thenBlocks.length(); i++) {
+            if (!goToExistingBlock(thenBlocks[i], join))
+                return false;
+        }
+
         curBlock_ = join;
         return true;
     }
@@ -943,57 +936,49 @@ class FunctionCompiler
         return curBlock_->pop();
     }
 
-    bool startPendingLoop(size_t id, MBasicBlock** loopEntry)
+    bool startBlock()
     {
-        if (!loopStack_.append(id) || !breakableStack_.append(id))
-            return false;
-        if (inDeadCode()) {
-            *loopEntry = nullptr;
-            return true;
-        }
-        MOZ_ASSERT(curBlock_->loopDepth() == loopStack_.length() - 1);
-        *loopEntry = MBasicBlock::NewAsmJS(mirGraph(), info(), curBlock_,
-                                           MBasicBlock::PENDING_LOOP_HEADER);
-        if (!*loopEntry)
-            return false;
-        mirGraph().addBlock(*loopEntry);
-        (*loopEntry)->setLoopDepth(loopStack_.length());
-        curBlock_->end(MGoto::New(alloc(), *loopEntry));
-        curBlock_ = *loopEntry;
+        MOZ_ASSERT_IF(blockDepth_ < targets_.length(), targets_[blockDepth_].empty());
+        blockDepth_++;
         return true;
     }
 
-    bool branchAndStartLoopBody(MDefinition* cond, MBasicBlock** afterLoop)
+    bool finishBlock()
     {
-        if (inDeadCode()) {
-            *afterLoop = nullptr;
+        MOZ_ASSERT(blockDepth_);
+        uint32_t topLabel = --blockDepth_;
+        return bindBranches(topLabel);
+    }
+
+    bool startLoop(MBasicBlock** loopHeader)
+    {
+        *loopHeader = nullptr;
+
+        blockDepth_ += 2;
+        loopDepth_++;
+
+        if (inDeadCode())
             return true;
-        }
-        MOZ_ASSERT(curBlock_->loopDepth() > 0);
-        MBasicBlock* body;
-        if (!newBlock(curBlock_, &body))
+
+        // Create the loop header.
+        MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_ - 1);
+        *loopHeader = MBasicBlock::NewAsmJS(mirGraph(), info(), curBlock_,
+                                            MBasicBlock::PENDING_LOOP_HEADER);
+        if (!*loopHeader)
+            return false;
+
+        (*loopHeader)->setLoopDepth(loopDepth_);
+        mirGraph().addBlock(*loopHeader);
+        curBlock_->end(MGoto::New(alloc(), *loopHeader));
+
+        MBasicBlock* body;
+        if (!goToNewBlock(*loopHeader, &body))
             return false;
-        if (cond->isConstant() && cond->toConstant()->valueToBooleanInfallible()) {
-            *afterLoop = nullptr;
-            curBlock_->end(MGoto::New(alloc(), body));
-        } else {
-            if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop))
-                return false;
-            curBlock_->end(MTest::New(alloc(), cond, body, *afterLoop));
-        }
         curBlock_ = body;
         return true;
     }
 
   private:
-    size_t popLoop()
-    {
-        size_t id = loopStack_.popCopy();
-        MOZ_ASSERT(!unlabeledContinues_.has(id));
-        breakableStack_.popBack();
-        return id;
-    }
-
     void fixupRedundantPhis(MBasicBlock* b)
     {
         for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) {
@@ -1002,20 +987,8 @@ class FunctionCompiler
                 b->setSlot(i, def->toPhi()->getOperand(0));
         }
     }
-    template 
-    void fixupRedundantPhis(MBasicBlock* loopEntry, T& map)
-    {
-        if (!map.initialized())
-            return;
-        for (typename T::Enum e(map); !e.empty(); e.popFront()) {
-            BlockVector& blocks = e.front().value();
-            for (size_t i = 0; i < blocks.length(); i++) {
-                if (blocks[i]->loopDepth() >= loopEntry->loopDepth())
-                    fixupRedundantPhis(blocks[i]);
-            }
-        }
-    }
-    bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* backedge, MBasicBlock* afterLoop)
+
+    bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* loopBody, MBasicBlock* backedge)
     {
         if (!loopEntry->setBackedgeAsmJS(backedge))
             return false;
@@ -1028,12 +1001,16 @@ class FunctionCompiler
         }
 
         // Fix up phis stored in the slots Vector of pending blocks.
-        if (afterLoop)
-            fixupRedundantPhis(afterLoop);
-        fixupRedundantPhis(loopEntry, labeledContinues_);
-        fixupRedundantPhis(loopEntry, labeledBreaks_);
-        fixupRedundantPhis(loopEntry, unlabeledContinues_);
-        fixupRedundantPhis(loopEntry, unlabeledBreaks_);
+        for (BlockVector& vec : targets_) {
+            for (MBasicBlock* block : vec) {
+                if (block->loopDepth() >= loopEntry->loopDepth())
+                    fixupRedundantPhis(block);
+            }
+        }
+
+        // The loop body, if any, might be referencing recycled phis too.
+        if (loopBody)
+            fixupRedundantPhis(loopBody);
 
         // Discard redundant phis and add to the free list.
         for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd(); ) {
@@ -1050,105 +1027,72 @@ class FunctionCompiler
     }
 
   public:
-    bool closeLoop(MBasicBlock* loopEntry, MBasicBlock* afterLoop)
+    bool closeLoop(MBasicBlock* loopHeader)
     {
-        size_t id = popLoop();
-        if (!loopEntry) {
-            MOZ_ASSERT(!afterLoop);
+        MOZ_ASSERT(blockDepth_ >= 2);
+        MOZ_ASSERT(loopDepth_);
+
+        uint32_t headerLabel = blockDepth_ - 1;
+        uint32_t afterLabel = blockDepth_ - 2;
+
+        if (!loopHeader) {
             MOZ_ASSERT(inDeadCode());
-            MOZ_ASSERT(!unlabeledBreaks_.has(id));
+            MOZ_ASSERT(afterLabel >= targets_.length() || targets_[afterLabel].empty());
+            MOZ_ASSERT(headerLabel >= targets_.length() || targets_[headerLabel].empty());
+            blockDepth_ -= 2;
+            loopDepth_--;
             return true;
         }
-        MOZ_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1);
-        MOZ_ASSERT_IF(afterLoop, afterLoop->loopDepth() == loopStack_.length());
-        if (curBlock_) {
-            MOZ_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1);
-            curBlock_->end(MGoto::New(alloc(), loopEntry));
-            if (!setLoopBackedge(loopEntry, curBlock_, afterLoop))
-                return false;
-        }
-        curBlock_ = afterLoop;
-        if (curBlock_)
-            mirGraph().moveBlockToEnd(curBlock_);
-        return bindUnlabeledBreaks(id);
-    }
 
-    bool branchAndCloseDoWhileLoop(MDefinition* cond, MBasicBlock* loopEntry)
-    {
-        size_t id = popLoop();
-        if (!loopEntry) {
-            MOZ_ASSERT(inDeadCode());
-            MOZ_ASSERT(!unlabeledBreaks_.has(id));
-            return true;
-        }
-        MOZ_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1);
-        if (curBlock_) {
-            MOZ_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1);
-            if (cond->isConstant()) {
-                if (cond->toConstant()->valueToBooleanInfallible()) {
-                    curBlock_->end(MGoto::New(alloc(), loopEntry));
-                    if (!setLoopBackedge(loopEntry, curBlock_, nullptr))
-                        return false;
-                    curBlock_ = nullptr;
-                } else {
-                    MBasicBlock* afterLoop;
-                    if (!newBlock(curBlock_, &afterLoop))
-                        return false;
-                    curBlock_->end(MGoto::New(alloc(), afterLoop));
-                    curBlock_ = afterLoop;
-                }
-            } else {
-                MBasicBlock* afterLoop;
-                if (!newBlock(curBlock_, &afterLoop))
-                    return false;
-                curBlock_->end(MTest::New(alloc(), cond, loopEntry, afterLoop));
-                if (!setLoopBackedge(loopEntry, curBlock_, afterLoop))
-                    return false;
-                curBlock_ = afterLoop;
-            }
-        }
-        return bindUnlabeledBreaks(id);
-    }
+        // Expr::Loop doesn't have an implicit backedge so temporarily set
+        // aside the end of the loop body to bind backedges.
+        MBasicBlock* loopBody = curBlock_;
+        curBlock_ = nullptr;
 
-    bool bindContinues(size_t id, const LabelVector* maybeLabels)
-    {
-        bool createdJoinBlock = false;
-        if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(id)) {
-            if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock))
-                return false;
-            unlabeledContinues_.remove(p);
-        }
-        return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_, &createdJoinBlock);
-    }
-
-    bool bindLabeledBreaks(const LabelVector* maybeLabels)
-    {
-        bool createdJoinBlock = false;
-        return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_, &createdJoinBlock);
-    }
-
-    bool addBreak(uint32_t* maybeLabelId) {
-        if (maybeLabelId)
-            return addBreakOrContinue(*maybeLabelId, &labeledBreaks_);
-        return addBreakOrContinue(breakableStack_.back(), &unlabeledBreaks_);
-    }
-
-    bool addContinue(uint32_t* maybeLabelId) {
-        if (maybeLabelId)
-            return addBreakOrContinue(*maybeLabelId, &labeledContinues_);
-        return addBreakOrContinue(loopStack_.back(), &unlabeledContinues_);
-    }
-
-    bool startSwitch(size_t id, MDefinition* expr, int32_t low, int32_t high,
-                     MBasicBlock** switchBlock)
-    {
-        if (!breakableStack_.append(id))
+        // TODO (bug 1253544): blocks branching to the top join to a single
+        // backedge block. Could they directly be set as backedges of the loop
+        // instead?
+        if (!bindBranches(headerLabel))
             return false;
+
+        MOZ_ASSERT(loopHeader->loopDepth() == loopDepth_);
+
+        if (curBlock_) {
+            // We're on the loop backedge block, created by bindBranches.
+            MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_);
+            curBlock_->end(MGoto::New(alloc(), loopHeader));
+            if (!setLoopBackedge(loopHeader, loopBody, curBlock_))
+                return false;
+        }
+
+        curBlock_ = loopBody;
+
+        loopDepth_--;
+        if (!bindBranches(afterLabel))
+            return false;
+
+        // If we have not created a new block in bindBranches, we're still on
+        // the inner loop body, which loop depth is incorrect.
+        if (curBlock_ && curBlock_->loopDepth() != loopDepth_) {
+            MBasicBlock* out;
+            if (!goToNewBlock(curBlock_, &out))
+                return false;
+            curBlock_ = out;
+        }
+
+        blockDepth_ -= 2;
+        return true;
+    }
+
+    bool startSwitch(MDefinition* expr, uint32_t numCases, MBasicBlock** switchBlock)
+    {
         if (inDeadCode()) {
             *switchBlock = nullptr;
             return true;
         }
-        curBlock_->end(MTableSwitch::New(alloc(), expr, low, high));
+        MOZ_ASSERT(numCases <= INT32_MAX);
+        MOZ_ASSERT(numCases);
+        curBlock_->end(MTableSwitch::New(alloc(), expr, 0, int32_t(numCases - 1)));
         *switchBlock = curBlock_;
         curBlock_ = nullptr;
         return true;
@@ -1156,65 +1100,89 @@ class FunctionCompiler
 
     bool startSwitchCase(MBasicBlock* switchBlock, MBasicBlock** next)
     {
+        MOZ_ASSERT(inDeadCode());
         if (!switchBlock) {
             *next = nullptr;
             return true;
         }
         if (!newBlock(switchBlock, next))
             return false;
-        if (curBlock_) {
-            curBlock_->end(MGoto::New(alloc(), *next));
-            if (!(*next)->addPredecessor(alloc(), curBlock_))
-                return false;
-        }
         curBlock_ = *next;
         return true;
     }
 
-    bool startSwitchDefault(MBasicBlock* switchBlock, BlockVector* cases, MBasicBlock** defaultBlock)
-    {
-        if (!startSwitchCase(switchBlock, defaultBlock))
-            return false;
-        if (!*defaultBlock)
-            return true;
-        mirGraph().moveBlockToEnd(*defaultBlock);
-        return true;
-    }
-
     bool joinSwitch(MBasicBlock* switchBlock, const BlockVector& cases, MBasicBlock* defaultBlock)
     {
-        size_t id = breakableStack_.popCopy();
+        MOZ_ASSERT(inDeadCode());
         if (!switchBlock)
             return true;
+
         MTableSwitch* mir = switchBlock->lastIns()->toTableSwitch();
         size_t defaultIndex;
         if (!mir->addDefault(defaultBlock, &defaultIndex))
             return false;
-        for (unsigned i = 0; i < cases.length(); i++) {
-            if (!cases[i]) {
+
+        for (MBasicBlock* caseBlock : cases) {
+            if (!caseBlock) {
                 if (!mir->addCase(defaultIndex))
                     return false;
             } else {
                 size_t caseIndex;
-                if (!mir->addSuccessor(cases[i], &caseIndex))
+                if (!mir->addSuccessor(caseBlock, &caseIndex))
                     return false;
                 if (!mir->addCase(caseIndex))
                     return false;
             }
         }
-        if (curBlock_) {
-            MBasicBlock* next;
-            if (!newBlock(curBlock_, &next))
-                return false;
-            curBlock_->end(MGoto::New(alloc(), next));
-            curBlock_ = next;
-        }
-        return bindUnlabeledBreaks(id);
+
+        return true;
     }
 
-    // Provides unique identifiers for internal uses in the control flow stacks;
-    // these ids have to grow monotonically.
-    unsigned nextId() { return nextId_++; }
+    bool br(uint32_t relativeDepth)
+    {
+        if (inDeadCode())
+            return true;
+
+        MOZ_ASSERT(relativeDepth < blockDepth_);
+        uint32_t absolute = blockDepth_ - 1 - relativeDepth;
+        if (absolute >= targets_.length()) {
+            if (!targets_.resize(absolute + 1))
+                return false;
+        }
+
+        if (!targets_[absolute].append(curBlock_))
+            return false;
+
+        curBlock_ = nullptr;
+        return true;
+    }
+
+    bool brIf(uint32_t relativeDepth, MDefinition* condition)
+    {
+        if (inDeadCode())
+            return true;
+
+        // TODO (bug 1253334): we could use MTest with the right jump target,
+        // here. If it's backward, it's trivial; if it's forward, we need to
+        // memorize it, then fix it later when we actually encounter the target.
+        MBasicBlock* thenBlock = nullptr;
+        MBasicBlock* joinBlock = nullptr;
+        if (!newBlock(curBlock_, &thenBlock))
+            return false;
+        if (!newBlock(curBlock_, &joinBlock))
+            return false;
+
+        curBlock_->end(MTest::New(alloc(), condition, thenBlock, joinBlock));
+        curBlock_ = thenBlock;
+        mirGraph().moveBlockToEnd(curBlock_);
+
+        if (!br(relativeDepth))
+            return false;
+
+        MOZ_ASSERT(inDeadCode());
+        curBlock_ = joinBlock;
+        return true;
+    }
 
     /************************************************************ DECODING ***/
 
@@ -1250,93 +1218,54 @@ class FunctionCompiler
 
     /*************************************************************************/
   private:
-    bool newBlockWithDepth(MBasicBlock* pred, unsigned loopDepth, MBasicBlock** block)
+    bool newBlock(MBasicBlock* pred, MBasicBlock** block)
     {
         *block = MBasicBlock::NewAsmJS(mirGraph(), info(), pred, MBasicBlock::NORMAL);
         if (!*block)
             return false;
         mirGraph().addBlock(*block);
-        (*block)->setLoopDepth(loopDepth);
+        (*block)->setLoopDepth(loopDepth_);
         return true;
     }
 
-    bool newBlock(MBasicBlock* pred, MBasicBlock** block)
+    bool goToNewBlock(MBasicBlock* pred, MBasicBlock** block)
     {
-        return newBlockWithDepth(pred, loopStack_.length(), block);
-    }
-
-    bool bindBreaksOrContinues(BlockVector* preds, bool* createdJoinBlock)
-    {
-        for (unsigned i = 0; i < preds->length(); i++) {
-            MBasicBlock* pred = (*preds)[i];
-            if (*createdJoinBlock) {
-                pred->end(MGoto::New(alloc(), curBlock_));
-                if (!curBlock_->addPredecessor(alloc(), pred))
-                    return false;
-            } else {
-                MBasicBlock* next;
-                if (!newBlock(pred, &next))
-                    return false;
-                pred->end(MGoto::New(alloc(), next));
-                if (curBlock_) {
-                    curBlock_->end(MGoto::New(alloc(), next));
-                    if (!next->addPredecessor(alloc(), curBlock_))
-                        return false;
-                }
-                curBlock_ = next;
-                *createdJoinBlock = true;
-            }
-            MOZ_ASSERT(curBlock_->begin() == curBlock_->end());
-            if (!mirGen_.ensureBallast())
-                return false;
-        }
-        preds->clear();
-        return true;
-    }
-
-    bool bindLabeledBreaksOrContinues(const LabelVector* maybeLabels, LabeledBlockMap* map,
-                                      bool* createdJoinBlock)
-    {
-        if (!maybeLabels)
-            return true;
-        const LabelVector& labels = *maybeLabels;
-        for (unsigned i = 0; i < labels.length(); i++) {
-            if (LabeledBlockMap::Ptr p = map->lookup(labels[i])) {
-                if (!bindBreaksOrContinues(&p->value(), createdJoinBlock))
-                    return false;
-                map->remove(p);
-            }
-            if (!mirGen_.ensureBallast())
-                return false;
-        }
-        return true;
-    }
-
-    template 
-    bool addBreakOrContinue(Key key, Map* map)
-    {
-        if (inDeadCode())
-            return true;
-        typename Map::AddPtr p = map->lookupForAdd(key);
-        if (!p) {
-            BlockVector empty;
-            if (!map->add(p, key, Move(empty)))
-                return false;
-        }
-        if (!p->value().append(curBlock_))
+        if (!newBlock(pred, block))
             return false;
-        curBlock_ = nullptr;
+        pred->end(MGoto::New(alloc(), *block));
         return true;
     }
 
-    bool bindUnlabeledBreaks(size_t id)
+    bool goToExistingBlock(MBasicBlock* prev, MBasicBlock* next)
     {
-        bool createdJoinBlock = false;
-        if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(id)) {
-            if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock))
+        MOZ_ASSERT(prev);
+        MOZ_ASSERT(next);
+        prev->end(MGoto::New(alloc(), next));
+        return next->addPredecessor(alloc(), prev);
+    }
+
+    bool bindBranches(uint32_t absolute)
+    {
+        if (absolute >= targets_.length() || targets_[absolute].empty())
+            return true;
+
+        BlockVector& preds = targets_[absolute];
+
+        MBasicBlock* join;
+        if (!goToNewBlock(preds[0], &join))
+            return false;
+        for (size_t i = 1; i < preds.length(); i++) {
+            if (!mirGen_.ensureBallast())
+                return false;
+            if (!goToExistingBlock(preds[i], join))
                 return false;
-            unlabeledBreaks_.remove(p);
         }
+
+        if (curBlock_ && !goToExistingBlock(curBlock_, join))
+            return false;
+        curBlock_ = join;
+
+        preds.clear();
         return true;
     }
 };
@@ -1408,28 +1337,42 @@ EmitLoadGlobal(FunctionCompiler& f, ExprType type, MDefinition** def)
     return true;
 }
 
-static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**, LabelVector* = nullptr);
-static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullptr);
+static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**);
 
 static bool
-EmitLoadStoreAddress(FunctionCompiler& f, uint32_t* offset, uint32_t* align, MDefinition** base)
+EmitLoadStoreAddress(FunctionCompiler& f, Scalar::Type viewType, uint32_t* offset,
+                     uint32_t* align, MDefinition** base)
 {
     *offset = f.readVarU32();
     MOZ_ASSERT(*offset == 0, "Non-zero offsets not supported yet");
 
     *align = f.readVarU32();
 
-    return EmitExpr(f, ExprType::I32, base);
+    if (!EmitExpr(f, ExprType::I32, base))
+        return false;
+
+    // TODO Remove this (and the viewType param) after implementing unaligned
+    // loads/stores.
+    if (f.mg().isAsmJS())
+        return true;
+
+    int32_t maskVal = ~(Scalar::byteSize(viewType) - 1);
+    if (maskVal == -1)
+        return true;
+
+    MDefinition* mask = f.constant(Int32Value(maskVal), MIRType_Int32);
+    *base = f.bitwise(*base, mask, MIRType_Int32);
+    return true;
 }
 
 static bool
-EmitLoad(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
+EmitLoad(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
-    *def = f.loadHeap(scalarType, ptr);
+    *def = f.loadHeap(viewType, ptr);
     return true;
 }
 
@@ -1438,7 +1381,7 @@ EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
 
     MDefinition* rhs = nullptr;
@@ -1471,7 +1414,7 @@ EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type vi
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
 
     MDefinition* rhs = nullptr;
@@ -1544,7 +1487,7 @@ EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def)
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     *def = f.atomicLoadHeap(viewType, index);
@@ -1558,7 +1501,7 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
@@ -1577,7 +1520,7 @@ EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
@@ -1594,7 +1537,7 @@ EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* oldValue;
@@ -1614,7 +1557,7 @@ EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
@@ -2432,123 +2375,22 @@ EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, SimdSign sign,
 }
 
 static bool
-EmitWhile(FunctionCompiler& f, const LabelVector* maybeLabels)
+EmitLoop(FunctionCompiler& f, ExprType expected, MDefinition** def)
 {
-    size_t headId = f.nextId();
-
-    MBasicBlock* loopEntry;
-    if (!f.startPendingLoop(headId, &loopEntry))
+    MBasicBlock* loopHeader;
+    if (!f.startLoop(&loopHeader))
         return false;
-
-    MDefinition* condDef;
-    if (!EmitExpr(f, ExprType::I32, &condDef))
-        return false;
-
-    MBasicBlock* afterLoop;
-    if (!f.branchAndStartLoopBody(condDef, &afterLoop))
-        return false;
-
     f.addInterruptCheck();
-
-    MDefinition* _;
-    if (!EmitExprStmt(f, &_))
-        return false;
-
-    if (!f.bindContinues(headId, maybeLabels))
-        return false;
-
-    return f.closeLoop(loopEntry, afterLoop);
-}
-
-static bool
-EmitFor(FunctionCompiler& f, Expr expr, const LabelVector* maybeLabels)
-{
-    MOZ_ASSERT(expr == Expr::ForInitInc || expr == Expr::ForInitNoInc ||
-               expr == Expr::ForNoInitInc || expr == Expr::ForNoInitNoInc);
-    size_t headId = f.nextId();
-
-    if (expr == Expr::ForInitInc || expr == Expr::ForInitNoInc) {
-        MDefinition* _;
-        if (!EmitExprStmt(f, &_))
+    if (uint32_t numStmts = f.readVarU32()) {
+        for (uint32_t i = 0; i < numStmts - 1; i++) {
+            MDefinition* _;
+            if (!EmitExpr(f, ExprType::Void, &_))
+                return false;
+        }
+        if (!EmitExpr(f, expected, def))
             return false;
     }
-
-    MBasicBlock* loopEntry;
-    if (!f.startPendingLoop(headId, &loopEntry))
-        return false;
-
-    MDefinition* condDef;
-    if (!EmitExpr(f, ExprType::I32, &condDef))
-        return false;
-
-    MBasicBlock* afterLoop;
-    if (!f.branchAndStartLoopBody(condDef, &afterLoop))
-        return false;
-
-    f.addInterruptCheck();
-
-    MDefinition* _;
-    if (!EmitExprStmt(f, &_))
-        return false;
-
-    if (!f.bindContinues(headId, maybeLabels))
-        return false;
-
-    if (expr == Expr::ForInitInc || expr == Expr::ForNoInitInc) {
-        MDefinition* _;
-        if (!EmitExprStmt(f, &_))
-            return false;
-    }
-
-    return f.closeLoop(loopEntry, afterLoop);
-}
-
-static bool
-EmitDoWhile(FunctionCompiler& f, const LabelVector* maybeLabels)
-{
-    size_t headId = f.nextId();
-
-    MBasicBlock* loopEntry;
-    if (!f.startPendingLoop(headId, &loopEntry))
-        return false;
-
-    f.addInterruptCheck();
-
-    MDefinition* _;
-    if (!EmitExprStmt(f, &_))
-        return false;
-
-    if (!f.bindContinues(headId, maybeLabels))
-        return false;
-
-    MDefinition* condDef;
-    if (!EmitExpr(f, ExprType::I32, &condDef))
-        return false;
-
-    return f.branchAndCloseDoWhileLoop(condDef, loopEntry);
-}
-
-static bool
-EmitLabel(FunctionCompiler& f, LabelVector* maybeLabels)
-{
-    uint32_t labelId = f.readVarU32();
-
-    if (maybeLabels) {
-        if (!maybeLabels->append(labelId))
-            return false;
-        MDefinition* _;
-        return EmitExprStmt(f, &_, maybeLabels);
-    }
-
-    LabelVector labels;
-    if (!labels.append(labelId))
-        return false;
-
-    MDefinition* _;
-    if (!EmitExprStmt(f, &_, &labels))
-        return false;
-
-    return f.bindLabeledBreaks(&labels);
+    return f.closeLoop(loopHeader);
 }
 
 typedef bool HasElseBlock;
@@ -2619,48 +2461,57 @@ EmitIfElse(FunctionCompiler& f, bool hasElse, ExprType expected, MDefinition** d
 }
 
 static bool
-EmitTableSwitch(FunctionCompiler& f)
+EmitBrTable(FunctionCompiler& f)
 {
-    bool hasDefault = f.readU8();
-    int32_t low = f.readVarU32();
-    int32_t high = f.readVarU32();
+    uint32_t defaultDepth = f.readVarU32();
     uint32_t numCases = f.readVarU32();
 
-    MDefinition* exprDef;
-    if (!EmitExpr(f, ExprType::I32, &exprDef))
-        return false;
-
-    // Switch with no cases
-    if (!hasDefault && numCases == 0)
-        return true;
+    // Empty table
+    if (!numCases) {
+        MDefinition* _;
+        return EmitExpr(f, ExprType::I32, &_) &&
+               f.br(defaultDepth);
+    }
 
     BlockVector cases;
-    if (!cases.resize(high - low + 1))
+    if (!cases.resize(numCases))
+        return false;
+
+    Uint32Vector depths;
+    if (!depths.resize(numCases))
+        return false;
+
+    for (size_t i = 0; i < numCases; i++)
+        depths[i] = f.readVarU32();
+
+    MDefinition* index;
+    if (!EmitExpr(f, ExprType::I32, &index))
         return false;
 
     MBasicBlock* switchBlock;
-    if (!f.startSwitch(f.nextId(), exprDef, low, high, &switchBlock))
+    if (!f.startSwitch(index, numCases, &switchBlock))
         return false;
 
-    while (numCases--) {
-        int32_t caseValue = f.readVarU32();
-        MOZ_ASSERT(caseValue >= low && caseValue <= high);
-        unsigned caseIndex = caseValue - low;
-        if (!f.startSwitchCase(switchBlock, &cases[caseIndex]))
+    MBasicBlock* defaultBlock = nullptr;
+    if (!f.startSwitchCase(switchBlock, &defaultBlock))
+        return false;
+    if (!f.br(defaultDepth))
+        return false;
+
+    // TODO (bug 1253334): we could avoid one indirection here, by
+    // jump-threading by hand the jump to the right enclosing block.
+    for (uint32_t i = 0; i < numCases; i++) {
+        uint32_t depth = depths[i];
+        // Don't emit blocks for the default case, to reduce the number of
+        // MBasicBlocks created.
+        if (depth == defaultDepth)
+            continue;
+        if (!f.startSwitchCase(switchBlock, &cases[i]))
             return false;
-        MDefinition* _;
-        if (!EmitExprStmt(f, &_))
+        if (!f.br(depth))
             return false;
     }
 
-    MBasicBlock* defaultBlock;
-    if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock))
-        return false;
-
-    MDefinition* _;
-    if (hasDefault && !EmitExprStmt(f, &_))
-        return false;
-
     return f.joinSwitch(switchBlock, cases, defaultBlock);
 }
 
@@ -2684,41 +2535,41 @@ EmitReturn(FunctionCompiler& f)
 static bool
 EmitBlock(FunctionCompiler& f, ExprType type, MDefinition** def)
 {
-    uint32_t numStmts = f.readVarU32();
-    if (numStmts) {
+    if (!f.startBlock())
+        return false;
+    if (uint32_t numStmts = f.readVarU32()) {
         for (uint32_t i = 0; i < numStmts - 1; i++) {
-            // Fine to clobber def, we only want the last use.
-            if (!EmitExprStmt(f, def))
+            MDefinition* _;
+            if (!EmitExpr(f, ExprType::Void, &_))
                 return false;
         }
         if (!EmitExpr(f, type, def))
             return false;
     }
-    return true;
+    return f.finishBlock();
 }
 
-typedef bool HasLabel;
-
 static bool
-EmitContinue(FunctionCompiler& f, bool hasLabel)
+EmitBr(FunctionCompiler& f)
 {
-    if (!hasLabel)
-        return f.addContinue(nullptr);
-    uint32_t labelId = f.readVarU32();
-    return f.addContinue(&labelId);
+    uint32_t relativeDepth = f.readVarU32();
+    return f.br(relativeDepth);
 }
 
 static bool
-EmitBreak(FunctionCompiler& f, bool hasLabel)
+EmitBrIf(FunctionCompiler& f)
 {
-    if (!hasLabel)
-        return f.addBreak(nullptr);
-    uint32_t labelId = f.readVarU32();
-    return f.addBreak(&labelId);
+    uint32_t relativeDepth = f.readVarU32();
+
+    MDefinition* condition;
+    if (!EmitExpr(f, ExprType::I32, &condition))
+        return false;
+
+    return f.brIf(relativeDepth, condition);
 }
 
 static bool
-EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* maybeLabels)
+EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def)
 {
     if (!f.mirGen().ensureBallast())
         return false;
@@ -2734,27 +2585,14 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
       case Expr::If:
       case Expr::IfElse:
         return EmitIfElse(f, HasElseBlock(op == Expr::IfElse), type, def);
-      case Expr::TableSwitch:
-        return EmitTableSwitch(f);
-      case Expr::While:
-        return EmitWhile(f, maybeLabels);
-      case Expr::DoWhile:
-        return EmitDoWhile(f, maybeLabels);
-      case Expr::ForInitInc:
-      case Expr::ForInitNoInc:
-      case Expr::ForNoInitNoInc:
-      case Expr::ForNoInitInc:
-        return EmitFor(f, op, maybeLabels);
-      case Expr::Label:
-        return EmitLabel(f, maybeLabels);
-      case Expr::Continue:
-        return EmitContinue(f, HasLabel(false));
-      case Expr::ContinueLabel:
-        return EmitContinue(f, HasLabel(true));
-      case Expr::Break:
-        return EmitBreak(f, HasLabel(false));
-      case Expr::BreakLabel:
-        return EmitBreak(f, HasLabel(true));
+      case Expr::Loop:
+        return EmitLoop(f, type, def);
+      case Expr::Br:
+        return EmitBr(f);
+      case Expr::BrIf:
+        return EmitBrIf(f);
+      case Expr::BrTable:
+        return EmitBrTable(f);
       case Expr::Return:
         return EmitReturn(f);
       case Expr::Call:
@@ -2806,6 +2644,8 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
         return EmitUnary(f, ExprType::F64, def);
       case Expr::I32Clz:
         return EmitUnary(f, ExprType::I32, def);
+      case Expr::I32Ctz:
+        return EmitUnary(f, ExprType::I32, def);
       case Expr::I32Popcnt:
         return EmitUnary(f, ExprType::I32, def);
       case Expr::I32Abs:
@@ -3040,11 +2880,7 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
                           SimdSign::Unsigned, def);
 
       // Future opcodes
-      case Expr::Loop:
       case Expr::Select:
-      case Expr::Br:
-      case Expr::BrIf:
-      case Expr::I32Ctz:
       case Expr::F32CopySign:
       case Expr::F32Trunc:
       case Expr::F32Nearest:
@@ -3090,12 +2926,6 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
     MOZ_CRASH("unexpected wasm opcode");
 }
 
-static bool
-EmitExprStmt(FunctionCompiler& f, MDefinition** def, LabelVector* maybeLabels)
-{
-    return EmitExpr(f, ExprType::Void, def, maybeLabels);
-}
-
 bool
 wasm::IonCompileFunction(IonCompileTask* task)
 {
diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp
index 5853b0e048..685d824ddb 100644
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -192,6 +192,7 @@ enum class WasmAstExprKind
     BinaryOperator,
     Block,
     Branch,
+    BranchTable,
     Call,
     CallIndirect,
     ComparisonOperator,
@@ -204,7 +205,6 @@ enum class WasmAstExprKind
     Return,
     SetLocal,
     Store,
-    TableSwitch,
     UnaryOperator,
 };
 
@@ -453,15 +453,15 @@ class WasmAstStore : public WasmAstExpr
     WasmAstExpr& value() const { return *value_; }
 };
 
-class WasmAstTableSwitch : public WasmAstExpr
+class WasmAstBranchTable : public WasmAstExpr
 {
     WasmAstExpr& index_;
     WasmRef default_;
     WasmRefVector table_;
 
   public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::TableSwitch;
-    explicit WasmAstTableSwitch(WasmAstExpr& index, WasmRef def, WasmRefVector&& table)
+    static const WasmAstExprKind Kind = WasmAstExprKind::BranchTable;
+    explicit WasmAstBranchTable(WasmAstExpr& index, WasmRef def, WasmRefVector&& table)
       : WasmAstExpr(Kind),
         index_(index),
         default_(def),
@@ -755,6 +755,7 @@ class WasmToken
         Block,
         Br,
         BrIf,
+        BrTable,
         Call,
         CallImport,
         CallIndirect,
@@ -791,7 +792,6 @@ class WasmToken
         SetLocal,
         Store,
         Table,
-        TableSwitch,
         Text,
         Type,
         UnaryOpcode,
@@ -1371,6 +1371,8 @@ WasmTokenStream::next()
         if (consume(MOZ_UTF16("block")))
             return WasmToken(WasmToken::Block, begin, cur_);
         if (consume(MOZ_UTF16("br"))) {
+            if (consume(MOZ_UTF16("_table")))
+                return WasmToken(WasmToken::BrTable, begin, cur_);
             if (consume(MOZ_UTF16("_if")))
                 return WasmToken(WasmToken::BrIf, begin, cur_);
             return WasmToken(WasmToken::Br, begin, cur_);
@@ -1909,11 +1911,8 @@ WasmTokenStream::next()
         break;
 
       case 't':
-        if (consume(MOZ_UTF16("table"))) {
-            if (consume(MOZ_UTF16("switch")))
-                return WasmToken(WasmToken::TableSwitch, begin, cur_);
+        if (consume(MOZ_UTF16("table")))
             return WasmToken(WasmToken::Table, begin, cur_);
-        }
         if (consume(MOZ_UTF16("type")))
             return WasmToken(WasmToken::Type, begin, cur_);
         break;
@@ -2604,8 +2603,8 @@ ParseStore(WasmParseContext& c, Expr expr)
     return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value);
 }
 
-static WasmAstTableSwitch*
-ParseTableSwitch(WasmParseContext& c)
+static WasmAstBranchTable*
+ParseBranchTable(WasmParseContext& c)
 {
     WasmAstExpr* index = ParseExpr(c);
     if (!index)
@@ -2648,7 +2647,7 @@ ParseTableSwitch(WasmParseContext& c)
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
 
-    return new(c.lifo) WasmAstTableSwitch(*index, def, Move(table));
+    return new(c.lifo) WasmAstBranchTable(*index, def, Move(table));
 }
 
 static WasmAstExpr*
@@ -2667,6 +2666,8 @@ ParseExprInsideParens(WasmParseContext& c)
         return ParseBranch(c, Expr::Br);
       case WasmToken::BrIf:
         return ParseBranch(c, Expr::BrIf);
+      case WasmToken::BrTable:
+        return ParseBranchTable(c);
       case WasmToken::Call:
         return ParseCall(c, Expr::Call);
       case WasmToken::CallImport:
@@ -2695,8 +2696,6 @@ ParseExprInsideParens(WasmParseContext& c)
         return ParseSetLocal(c);
       case WasmToken::Store:
         return ParseStore(c, token.expr());
-      case WasmToken::TableSwitch:
-        return ParseTableSwitch(c);
       case WasmToken::UnaryOpcode:
         return ParseUnaryOperator(c, token.expr());
       default:
@@ -3321,17 +3320,17 @@ ResolveReturn(Resolver& r, WasmAstReturn& ret)
 }
 
 static bool
-ResolveTableSwitch(Resolver& r, WasmAstTableSwitch& ts)
+ResolveBranchTable(Resolver& r, WasmAstBranchTable& bt)
 {
-    if (!ts.def().name().empty() && !r.resolveTarget(ts.def()))
+    if (!bt.def().name().empty() && !r.resolveTarget(bt.def()))
         return r.fail("switch default not found");
 
-    for (WasmRef& elem : ts.table()) {
+    for (WasmRef& elem : bt.table()) {
         if (!elem.name().empty() && !r.resolveTarget(elem))
             return r.fail("switch element not found");
     }
 
-    return ResolveExpr(r, ts.index());
+    return ResolveExpr(r, bt.index());
 }
 
 static bool
@@ -3368,8 +3367,8 @@ ResolveExpr(Resolver& r, WasmAstExpr& expr)
         return ResolveSetLocal(r, expr.as());
       case WasmAstExprKind::Store:
         return ResolveStore(r, expr.as());
-      case WasmAstExprKind::TableSwitch:
-        return ResolveTableSwitch(r, expr.as());
+      case WasmAstExprKind::BranchTable:
+        return ResolveBranchTable(r, expr.as());
       case WasmAstExprKind::UnaryOperator:
         return ResolveUnaryOperator(r, expr.as());
     }
@@ -3639,23 +3638,23 @@ EncodeReturn(Encoder& e, WasmAstReturn& r)
 }
 
 static bool
-EncodeTableSwitch(Encoder& e, WasmAstTableSwitch& ts)
+EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt)
 {
-    if (!e.writeExpr(Expr::TableSwitch))
+    if (!e.writeExpr(Expr::BrTable))
         return false;
 
-    if (!e.writeVarU32(ts.def().index()))
+    if (!e.writeVarU32(bt.def().index()))
         return false;
 
-    if (!e.writeVarU32(ts.table().length()))
+    if (!e.writeVarU32(bt.table().length()))
         return false;
 
-    for (const WasmRef& elem : ts.table()) {
+    for (const WasmRef& elem : bt.table()) {
         if (!e.writeVarU32(elem.index()))
             return false;
     }
 
-    return EncodeExpr(e, ts.index());
+    return EncodeExpr(e, bt.index());
 }
 
 static bool
@@ -3692,8 +3691,8 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr)
         return EncodeSetLocal(e, expr.as());
       case WasmAstExprKind::Store:
         return EncodeStore(e, expr.as());
-      case WasmAstExprKind::TableSwitch:
-        return EncodeTableSwitch(e, expr.as());
+      case WasmAstExprKind::BranchTable:
+        return EncodeBranchTable(e, expr.as());
       case WasmAstExprKind::UnaryOperator:
         return EncodeUnaryOperator(e, expr.as());
     }
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 09856da1b4..3ca6ba76b6 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1171,13 +1171,7 @@ SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc, Value* vp)
     }
 
     HelperThreadState().waitForAllThreads();
-    js::oom::targetThread = targetThread;
-    if (uint64_t(OOM_counter) + count >= UINT32_MAX) {
-        JS_ReportError(cx, "OOM cutoff out of range");
-        return false;
-    }
-    OOM_maxAllocations = OOM_counter + count;
-    OOM_failAlways = failAlways;
+    js::oom::SimulateOOMAfter(count, targetThread, failAlways);
     args.rval().setUndefined();
     return true;
 }
@@ -1198,9 +1192,9 @@ static bool
 ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setBoolean(OOM_counter >= OOM_maxAllocations);
-    js::oom::targetThread = js::oom::THREAD_TYPE_NONE;
-    OOM_maxAllocations = UINT32_MAX;
+    args.rval().setBoolean(js::oom::HadSimulatedOOM());
+    HelperThreadState().waitForAllThreads();
+    js::oom::ResetSimulatedOOM();
     return true;
 }
 
@@ -1278,15 +1272,14 @@ OOMTest(JSContext* cx, unsigned argc, Value* vp)
             MOZ_ASSERT(!cx->isExceptionPending());
             MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
 
-            OOM_maxAllocations = OOM_counter + allocation;
-            OOM_failAlways = false;
+            js::oom::SimulateOOMAfter(allocation, thread, false);
 
             RootedValue result(cx);
             bool ok = JS_CallFunction(cx, cx->global(), function,
                                       HandleValueArray::empty(), &result);
 
-            handledOOM = OOM_counter >= OOM_maxAllocations;
-            OOM_maxAllocations = UINT32_MAX;
+            handledOOM = js::oom::HadSimulatedOOM();
+            js::oom::ResetSimulatedOOM();
 
             MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
 
@@ -1316,8 +1309,6 @@ OOMTest(JSContext* cx, unsigned argc, Value* vp)
         }
     }
 
-    js::oom::targetThread = js::oom::THREAD_TYPE_NONE;
-
     args.rval().setUndefined();
     return true;
 }
diff --git a/js/src/jit-test/tests/asm.js/testControlFlow.js b/js/src/jit-test/tests/asm.js/testControlFlow.js
index ac7f1a9c9c..98f7f7bd38 100644
--- a/js/src/jit-test/tests/asm.js/testControlFlow.js
+++ b/js/src/jit-test/tests/asm.js/testControlFlow.js
@@ -22,6 +22,9 @@ assertEq(asmLink(asmCompile(USE_ASM + "function f() { while (0) ; return 0} retu
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { for (;0;) ; return 0} return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { do ; while(0); return 0} return f"))(), 0);
 
+assertEq(asmLink(asmCompile(USE_ASM + "function f() { do {} while (0); while (0); return 0} return f"))(), 0);
+assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i = 0; do {} while (0); return i|0} return f"))(), 0);
+
 assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; while (d) {}; return 0} return f");
 assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; for (;d;) {}; return 0} return f");
 assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; do {} while (d); return 0} return f");
diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js
index 6cdf0c7114..f124b6449d 100644
--- a/js/src/jit-test/tests/wasm/basic-control-flow.js
+++ b/js/src/jit-test/tests/wasm/basic-control-flow.js
@@ -168,5 +168,5 @@ wasmTextToBinary('(module (func (loop $a $a (br $a))))');
 // ----------------------------------------------------------------------------
 // tableswitch
 
-wasmTextToBinary('(module (func (param i32) (block $b1 (block $b2 (tableswitch (get_local 0) (table (br $b1) (br $b2)) (br $b2))))))');
-wasmTextToBinary('(module (func (param i32) (block $b1 (tableswitch (get_local 0) (table) (br $b1)))))');
+wasmTextToBinary('(module (func (param i32) (block $b1 (block $b2 (br_table (get_local 0) (table (br $b1) (br $b2)) (br $b2))))))');
+wasmTextToBinary('(module (func (param i32) (block $b1 (br_table (get_local 0) (table) (br $b1)))))');
diff --git a/js/src/jit-test/tests/wasm/basic-integer.js b/js/src/jit-test/tests/wasm/basic-integer.js
index 97a1ac870b..3344428f21 100644
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -41,7 +41,10 @@ function testComparison(type, opcode, lhs, rhs, expect) {
 }
 
 testUnary('i32', 'clz', 40, 26);
-//testUnary('i32', 'ctz', 40, 0); // TODO: NYI
+testUnary('i32', 'ctz', 40, 3);
+testUnary('i32', 'ctz', 0, 32);
+testUnary('i32', 'ctz', -2147483648, 31);
+
 testUnary('i32', 'popcnt', 40, 2);
 testUnary('i32', 'popcnt', 0, 0);
 testUnary('i32', 'popcnt', 0xFFFFFFFF, 32);
diff --git a/js/src/jit-test/tests/wasm/basic-memory.js b/js/src/jit-test/tests/wasm/basic-memory.js
index 6293d40b49..424ee46c40 100644
--- a/js/src/jit-test/tests/wasm/basic-memory.js
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -79,7 +79,10 @@ function testStoreError(type, ext, base, offset, align, errorMsg) {
 }
 
 testLoad('i32', '', 0, 0, 0, 0x03020100);
+
+testLoad('i32', '', 1, 0, 0, 0x03020100);   // TODO: unaligned NYI
 //testLoad('i32', '', 1, 0, 0, 0x04030201); // TODO: unaligned NYI
+
 //testLoad('i32', '', 0, 1, 0, 0x01020304); // TODO: offsets NYI
 //testLoad('i32', '', 1, 1, 4, 0x02030405); // TODO: offsets NYI
 //testLoad('i64', '', 0, 0, 0, 0x0001020304050607); // TODO: i64 NYI
diff --git a/js/src/jit/Label.h b/js/src/jit/Label.h
index b829234bac..8a102f0063 100644
--- a/js/src/jit/Label.h
+++ b/js/src/jit/Label.h
@@ -89,7 +89,7 @@ class Label : public LabelBase
 #ifdef DEBUG
         // The assertion below doesn't hold if an error occurred.
         JitContext* context = MaybeGetJitContext();
-        bool hadError = OOM_counter >= OOM_maxAllocations ||
+        bool hadError = js::oom::HadSimulatedOOM() ||
                         (context && context->runtime->hadOutOfMemory());
         MOZ_ASSERT_IF(!hadError, !used());
 #endif
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index e4fd6242c2..175579ab10 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1393,6 +1393,15 @@ LIRGenerator::visitClz(MClz* ins)
     define(lir, ins);
 }
 
+void
+LIRGenerator::visitCtz(MCtz* ins)
+{
+    MDefinition* num = ins->num();
+
+    LCtzI* lir = new(alloc()) LCtzI(useRegisterAtStart(num));
+    define(lir, ins);
+}
+
 void
 LIRGenerator::visitPopcnt(MPopcnt* ins)
 {
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index ba1a9d60a4..d8c7fcbdd8 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -129,6 +129,7 @@ class LIRGenerator : public LIRGeneratorSpecific
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
+    void visitCtz(MCtz* ins);
     void visitSqrt(MSqrt* ins);
     void visitPopcnt(MPopcnt* ins);
     void visitAtan2(MAtan2* ins);
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index c531aba13b..6c59c21f09 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5099,6 +5099,19 @@ MClz::foldsTo(TempAllocator& alloc)
     return this;
 }
 
+MDefinition*
+MCtz::foldsTo(TempAllocator& alloc)
+{
+    if (num()->isConstant()) {
+        int32_t n = num()->toConstant()->toInt32();
+        if (n == 0)
+            return MConstant::New(alloc, Int32Value(32));
+        return MConstant::New(alloc, Int32Value(mozilla::CountTrailingZeroes32(n)));
+    }
+
+    return this;
+}
+
 MDefinition*
 MPopcnt::foldsTo(TempAllocator& alloc)
 {
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 55839878ed..3786b1c293 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5997,8 +5997,8 @@ class MAbs
 };
 
 class MClz
-    : public MUnaryInstruction
-    , public BitwisePolicy::Data
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
 {
     bool operandIsNeverZero_;
 
@@ -6040,9 +6040,53 @@ class MClz
     void collectRangeInfoPreTrunc() override;
 };
 
+class MCtz
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
+{
+    bool operandIsNeverZero_;
+
+    explicit MCtz(MDefinition* num)
+      : MUnaryInstruction(num),
+        operandIsNeverZero_(false)
+    {
+        MOZ_ASSERT(IsNumberType(num->type()));
+        specialization_ = MIRType_Int32;
+        setResultType(MIRType_Int32);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(Ctz)
+    static MCtz* New(TempAllocator& alloc, MDefinition* num) {
+        return new(alloc) MCtz(num);
+    }
+    static MCtz* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
+        return new(alloc) MCtz(num);
+    }
+    MDefinition* num() const {
+        return getOperand(0);
+    }
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    bool operandIsNeverZero() const {
+        return operandIsNeverZero_;
+    }
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+    void computeRange(TempAllocator& alloc) override;
+    void collectRangeInfoPreTrunc() override;
+};
+
 class MPopcnt
-    : public MUnaryInstruction
-    , public BitwisePolicy::Data
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
 {
     explicit MPopcnt(MDefinition* num)
       : MUnaryInstruction(num)
diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h
index 7cbdd56b22..cc2375b29c 100644
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -368,8 +368,11 @@ class MBasicBlock : public TempObject, public InlineListNode
         }
         MOZ_CRASH();
     }
+    bool hasAnyIns() const {
+        return !instructions_.empty();
+    }
     bool hasLastIns() const {
-        return !instructions_.empty() && instructions_.rbegin()->isControlInstruction();
+        return hasAnyIns() && instructions_.rbegin()->isControlInstruction();
     }
     MControlInstruction* lastIns() const {
         MOZ_ASSERT(hasLastIns());
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index f32c30e08f..cb7f1567fd 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -86,6 +86,7 @@ namespace jit {
     _(MinMax)                                                               \
     _(Abs)                                                                  \
     _(Clz)                                                                  \
+    _(Ctz)                                                                  \
     _(Popcnt)                                                               \
     _(Sqrt)                                                                 \
     _(Atan2)                                                                \
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
index 3319b8ba2e..b444699de3 100644
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1470,6 +1470,12 @@ MClz::computeRange(TempAllocator& alloc)
     setRange(Range::NewUInt32Range(alloc, 0, 32));
 }
 
+void
+MCtz::computeRange(TempAllocator& alloc)
+{
+    setRange(Range::NewUInt32Range(alloc, 0, 32));
+}
+
 void
 MPopcnt::computeRange(TempAllocator& alloc)
 {
@@ -3216,6 +3222,14 @@ MClz::collectRangeInfoPreTrunc()
         operandIsNeverZero_ = true;
 }
 
+void
+MCtz::collectRangeInfoPreTrunc()
+{
+    Range inputRange(input());
+    if (!inputRange.canBeZero())
+        operandIsNeverZero_ = true;
+}
+
 void
 MDiv::collectRangeInfoPreTrunc()
 {
diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp
index 2bcbfda21c..f8168e8f85 100644
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -954,6 +954,19 @@ CodeGeneratorARM::visitClzI(LClzI* ins)
     masm.ma_clz(input, output);
 }
 
+void
+CodeGeneratorARM::visitCtzI(LCtzI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+    ScratchRegisterScope scratch(masm);
+
+    masm.ma_rsb(input, Imm32(0), scratch, SetCC);
+    masm.ma_and(input, scratch, input);
+    masm.ma_clz(input, output);
+    masm.ma_rsb(input, Imm32(0x1f), output, LeaveCC, Assembler::NotEqual);
+}
+
 void
 CodeGeneratorARM::visitPopcntI(LPopcntI* ins)
 {
diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h
index 5de45c3fa5..d26bb7fb52 100644
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -123,6 +123,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
     virtual void visitUrshD(LUrshD* ins);
 
     virtual void visitClzI(LClzI* ins);
+    virtual void visitCtzI(LCtzI* ins);
     virtual void visitPopcntI(LPopcntI* ins);
 
     virtual void visitTestIAndBranch(LTestIAndBranch* test);
diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp
index dd746d5bf3..2f5a432491 100644
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -352,6 +352,12 @@ CodeGeneratorARM64::visitClzI(LClzI* lir)
     MOZ_CRASH("visitClzI");
 }
 
+void
+CodeGeneratorARM64::visitCtzI(LCtzI* lir)
+{
+    MOZ_CRASH("visitCtzI");
+}
+
 void
 CodeGeneratorARM64::emitRoundDouble(FloatRegister src, Register dest, Label* fail)
 {
diff --git a/js/src/jit/arm64/CodeGenerator-arm64.h b/js/src/jit/arm64/CodeGenerator-arm64.h
index 13a1ba0035..b5d03a623a 100644
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -163,6 +163,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
     virtual void visitTruncateFToInt32(LTruncateFToInt32* ins);
 
     virtual void visitClzI(LClzI* lir);
+    virtual void visitCtzI(LCtzI* lir);
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
     void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index 85ec6dab8c..e0784cc956 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3208,6 +3208,20 @@ class LClzI : public LInstructionHelper<1, 1, 0>
     }
 };
 
+// Count trailing zeroes
+class LCtzI : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(CtzI)
+    explicit LCtzI(const LAllocation& num) {
+        setOperand(0, num);
+    }
+
+    MCtz* mir() const {
+        return mir_->toCtz();
+    }
+};
+
 // Count population
 class LPopcntI : public LInstructionHelper<1, 1, 1>
 {
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index f442b60732..efdc8aacb8 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -142,6 +142,7 @@
     _(AbsD)                         \
     _(AbsF)                         \
     _(ClzI)                         \
+    _(CtzI)                         \
     _(PopcntI)                      \
     _(SqrtD)                        \
     _(SqrtF)                        \
diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h
index 854389898c..671401e4b7 100644
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1548,6 +1548,9 @@ class AssemblerX86Shared : public AssemblerShared
     void bsr(const Register& src, const Register& dest) {
         masm.bsr_rr(src.encoding(), dest.encoding());
     }
+    void bsf(const Register& src, const Register& dest) {
+        masm.bsf_rr(src.encoding(), dest.encoding());
+    }
     void popcnt(const Register& src, const Register& dest) {
         masm.popcnt_rr(src.encoding(), dest.encoding());
     }
diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
index 4d5acf2f19..c21aab4cdc 100644
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -1285,6 +1285,12 @@ public:
         m_formatter.twoByteOp(OP2_BSR_GvEv, src, dst);
     }
 
+    void bsf_rr(RegisterID src, RegisterID dst)
+    {
+        spew("bsf        %s, %s", GPReg32Name(src), GPReg32Name(dst));
+        m_formatter.twoByteOp(OP2_BSF_GvEv, src, dst);
+    }
+
     void popcnt_rr(RegisterID src, RegisterID dst)
     {
         spew("popcnt     %s, %s", GPReg32Name(src), GPReg32Name(dst));
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
index 5a7002e25e..a79a5efa28 100644
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -724,6 +724,26 @@ CodeGeneratorX86Shared::visitClzI(LClzI* ins)
     masm.bind(&done);
 }
 
+void
+CodeGeneratorX86Shared::visitCtzI(LCtzI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    // bsf is undefined on 0
+    Label done, nonzero;
+    if (!ins->mir()->operandIsNeverZero()) {
+        masm.test32(input, input);
+        masm.j(Assembler::NonZero, &nonzero);
+        masm.move32(Imm32(32), output);
+        masm.jump(&done);
+    }
+
+    masm.bind(&nonzero);
+    masm.bsf(input, output);
+    masm.bind(&done);
+}
+
 void
 CodeGeneratorX86Shared::visitPopcntI(LPopcntI* ins)
 {
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
index 548cf0e77d..9ab3364461 100644
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -216,6 +216,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
     virtual void visitAbsD(LAbsD* ins);
     virtual void visitAbsF(LAbsF* ins);
     virtual void visitClzI(LClzI* ins);
+    virtual void visitCtzI(LCtzI* ins);
     virtual void visitPopcntI(LPopcntI* ins);
     virtual void visitSqrtD(LSqrtD* ins);
     virtual void visitSqrtF(LSqrtF* ins);
diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h
index a443623588..6269cd7ceb 100644
--- a/js/src/jit/x86-shared/Encoding-x86-shared.h
+++ b/js/src/jit/x86-shared/Encoding-x86-shared.h
@@ -202,6 +202,7 @@ enum TwoByteOpcodeID {
     OP2_CMPXCHG_GvEb    = 0xB0,
     OP2_CMPXCHG_GvEw    = 0xB1,
     OP2_POPCNT_GvEv     = 0xB8,
+    OP2_BSF_GvEv        = 0xBC,
     OP2_BSR_GvEv        = 0xBD,
     OP2_MOVSX_GvEb      = 0xBE,
     OP2_MOVSX_GvEw      = 0xBF,
diff --git a/js/src/jsapi-tests/testHashTable.cpp b/js/src/jsapi-tests/testHashTable.cpp
index 62ca3b1176..f27f1b89b1 100644
--- a/js/src/jsapi-tests/testHashTable.cpp
+++ b/js/src/jsapi-tests/testHashTable.cpp
@@ -376,17 +376,13 @@ LookupWithDefaultUntilResize() {
 
 BEGIN_TEST(testHashMapLookupWithDefaultOOM)
 {
-    js::oom::targetThread = js::oom::THREAD_TYPE_MAIN;
     uint32_t timeToFail;
     for (timeToFail = 1; timeToFail < 1000; timeToFail++) {
-        OOM_maxAllocations = OOM_counter + timeToFail;
-        OOM_failAlways = false;
-
+        js::oom::SimulateOOMAfter(timeToFail, js::oom::THREAD_TYPE_MAIN, false);
         LookupWithDefaultUntilResize();
     }
 
-    js::oom::targetThread = js::oom::THREAD_TYPE_NONE;
-
+    js::oom::ResetSimulatedOOM();
     return true;
 }
 
diff --git a/js/src/jsapi-tests/testOOM.cpp b/js/src/jsapi-tests/testOOM.cpp
index f38f7af20a..b98177f5a1 100644
--- a/js/src/jsapi-tests/testOOM.cpp
+++ b/js/src/jsapi-tests/testOOM.cpp
@@ -28,7 +28,7 @@ virtual JSRuntime * createRuntime() override
 }
 END_TEST(testOOM)
 
-#ifdef DEBUG  // OOM_maxAllocations is only available in debug builds.
+#ifdef DEBUG  // js::oom functions are only available in debug builds.
 
 const uint32_t maxAllocsPerTest = 100;
 
@@ -36,18 +36,18 @@ const uint32_t maxAllocsPerTest = 100;
     testName = name;                                                          \
     printf("Test %s: started\n", testName);                                   \
     for (oomAfter = 1; oomAfter < maxAllocsPerTest; ++oomAfter) {             \
-        setOOMAfter(oomAfter)
+    js::oom::SimulateOOMAfter(oomAfter, js::oom::THREAD_TYPE_MAIN, true)
 
 #define OOM_TEST_FINISHED                                                     \
     {                                                                         \
-        printf("Test %s: finished with %d allocations\n",                     \
+        printf("Test %s: finished with %" PRIu64 " allocations\n",            \
                testName, oomAfter - 1);                                       \
         break;                                                                \
     }
 
 #define END_OOM_TEST                                                          \
     }                                                                         \
-    cancelOOMAfter();                                                         \
+    js::oom::ResetSimulatedOOM();                                             \
     CHECK(oomAfter != maxAllocsPerTest)
 
 BEGIN_TEST(testNewRuntime)
@@ -65,21 +65,7 @@ BEGIN_TEST(testNewRuntime)
 }
 
 const char* testName;
-uint32_t oomAfter;
-
-void
-setOOMAfter(uint32_t numAllocs)
-{
-    if (uint64_t(OOM_counter) + numAllocs >= UINT32_MAX)
-	MOZ_CRASH("Can't set maxAllocations - out of range");
-    OOM_maxAllocations = OOM_counter + numAllocs;
-}
-
-void
-cancelOOMAfter()
-{
-    OOM_maxAllocations = UINT32_MAX;
-}
+uint64_t oomAfter;
 END_TEST(testNewRuntime)
 
 #endif
diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp
index b9ebfa128f..db930a0167 100644
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -29,15 +29,15 @@ using mozilla::CeilingLog2Size;
 using mozilla::PodArrayZero;
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-/* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */
-JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX;
-JS_PUBLIC_DATA(uint32_t) OOM_counter = 0;
-JS_PUBLIC_DATA(bool) OOM_failAlways = true;
+/* For OOM testing functionality in Utility.h. */
 namespace js {
 namespace oom {
 
 JS_PUBLIC_DATA(uint32_t) targetThread = 0;
 JS_PUBLIC_DATA(MOZ_THREAD_LOCAL(uint32_t)) threadType;
+JS_PUBLIC_DATA(uint64_t) maxAllocations = UINT64_MAX;
+JS_PUBLIC_DATA(uint64_t) counter = 0;
+JS_PUBLIC_DATA(bool) failAlways = true;
 
 bool
 InitThreadType(void) {
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index d8c30bef2d..b6ee484114 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -10,6 +10,7 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SizePrintfMacros.h"
 
@@ -7259,7 +7260,7 @@ main(int argc, char** argv, char** envp)
 
 #ifdef DEBUG
     if (OOM_printAllocationCount)
-        printf("OOM max count: %u\n", OOM_counter);
+        printf("OOM max count: %" PRIu64 "\n", js::oom::counter);
 #endif
 
     JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index b240c3a95b..13f99abf20 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5143,6 +5143,31 @@ nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot)
   } while (subtrees.Length() != 0);
 }
 
+/* static */
+void
+nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame)
+{
+  AutoTArray stack;
+  stack.AppendElement(aFrame);
+
+  do {
+    nsIFrame* f = stack.ElementAt(stack.Length() - 1);
+    stack.RemoveElementAt(stack.Length() - 1);
+
+    if (!f->HasAnyStateBits(
+        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+      continue;
+    }
+    f->MarkIntrinsicISizesDirty();
+
+    for (nsIFrame::ChildListIterator lists(f); !lists.IsDone(); lists.Next()) {
+      for (nsIFrame* kid : lists.CurrentList()) {
+        stack.AppendElement(kid);
+      }
+    }
+  } while (stack.Length() != 0);
+}
+
 /* static */
 LogicalSize
 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h
index dd32232994..c373128a42 100644
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1542,6 +1542,8 @@ public:
 
   static void MarkDescendantsDirty(nsIFrame *aSubtreeRoot);
 
+  static void MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame);
+
   /*
    * Calculate the used values for 'width' and 'height' for a replaced element.
    *
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index b34e4613eb..c361af0fe0 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -186,6 +186,7 @@
 #include "nsSubDocumentFrame.h"
 #include "nsQueryObject.h"
 #include "nsLayoutStylesheetCache.h"
+#include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/ScrollInputMethods.h"
 #include "nsStyleSet.h"
 #include "mozilla/StyleSetHandle.h"
@@ -1826,6 +1827,9 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  const bool isHeightChanging =
+    (mPresContext->GetVisibleArea().height != aHeight);
+
   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
 
   // There isn't anything useful we can do if the initial reflow hasn't happened.
@@ -1853,6 +1857,14 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
       // XXX Do a full invalidate at the beginning so that invalidates along
       // the way don't have region accumulation issues?
 
+      if (isHeightChanging) {
+        // For BSize changes driven by style, RestyleManager handles this.
+        // For height:auto BSizes (i.e. layout-controlled), descendant
+        // intrinsic sizes can't depend on them. So the only other case is
+        // viewport-controlled BSizes which we handle here.
+        nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
+      }
+
       {
         nsAutoCauseReflowNotifier crNotifier(this);
         WillDoReflow();
@@ -4340,14 +4352,12 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
 
   // We should check that aChild does not contain pointer capturing elements.
   // If it does we should release the pointer capture for the elements.
-  if (aChild) {
-    for (auto iter = gPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
-      nsIPresShell::PointerCaptureInfo* data = iter.UserData();
-      if (data && data->mOverrideContent &&
-          nsContentUtils::ContentIsDescendantOf(data->mOverrideContent,
-                                                aChild)) {
-        nsIPresShell::ReleasePointerCapturingContent(iter.Key());
-      }
+  for (auto iter = gPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
+    nsIPresShell::PointerCaptureInfo* data = iter.UserData();
+    if (data && data->mOverrideContent &&
+        nsContentUtils::ContentIsDescendantOf(data->mOverrideContent,
+                                              aChild)) {
+      nsIPresShell::ReleasePointerCapturingContent(iter.Key());
     }
   }
 
@@ -4923,6 +4933,8 @@ PresShell::PaintRangePaintInfo(const nsTArray>& aItems
 
     pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
     pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
+    if (!pixelArea.width || !pixelArea.height)
+      return nullptr;
 
     // adjust the screen position based on the rescaled size
     nscoord left = rootScreenRect.x + pixelArea.x;
@@ -5531,6 +5543,13 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
 
   nsCOMPtr shell = pointVM->GetPresShell();
   if (shell) {
+    // Since this gets run in a refresh tick there isn't an InputAPZContext on
+    // the stack from the nsBaseWidget. We need to simulate one with at least
+    // the correct target guid, so that the correct callback transform gets
+    // applied if this event goes to a child process. The input block id is set
+    // to 0 because this is a synthetic event which doesn't really belong to any
+    // input block. Same for the APZ response field.
+    InputAPZContext apzContext(mMouseEventTargetGuid, 0, nsEventStatus_eIgnore);
     shell->DispatchSynthMouseMove(&event, !aFromScroll);
   }
 
@@ -5981,20 +6000,15 @@ bool
 PresShell::AssumeAllImagesVisible()
 {
   static bool sImageVisibilityEnabled = true;
-  static bool sImageVisibilityEnabledForBrowserElementsOnly = false;
   static bool sImageVisibilityPrefCached = false;
 
   if (!sImageVisibilityPrefCached) {
     Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
       "layout.imagevisibility.enabled", true);
-    Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly,
-      "layout.imagevisibility.enabled_for_browser_elements_only", false);
     sImageVisibilityPrefCached = true;
   }
 
-  if ((!sImageVisibilityEnabled &&
-       !sImageVisibilityEnabledForBrowserElementsOnly) ||
-      !mPresContext || !mDocument) {
+  if (!sImageVisibilityEnabled || !mPresContext || !mDocument) {
     return true;
   }
 
@@ -6008,14 +6022,6 @@ PresShell::AssumeAllImagesVisible()
     return true;
   }
 
-  if (!sImageVisibilityEnabled &&
-      sImageVisibilityEnabledForBrowserElementsOnly) {
-    nsCOMPtr docshell(mPresContext->GetDocShell());
-    if (!docshell || !docshell->GetIsInBrowserElement()) {
-      return true;
-    }
-  }
-
   return false;
 }
 
@@ -6714,9 +6720,11 @@ PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
       nsView* rootView = mViewManager->GetRootView();
       mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext,
         aEvent->widget, aEvent->refPoint, rootView);
+      mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
     } else {
       mMouseLocation =
         nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
+      mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
     }
 #ifdef DEBUG_MOUSE_LOCATION
     if (aEvent->mMessage == eMouseEnterIntoWidget) {
@@ -6736,6 +6744,7 @@ PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
     // this won't matter at all since we'll get the mouse move or enter after
     // the mouse exit when the mouse moves from one of our widgets into another.
     mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+    mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
 #ifdef DEBUG_MOUSE_LOCATION
     printf("[ps=%p]got mouse exit for %p\n",
            this, aEvent->widget);
diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h
index 9f194aaa90..c71bec5974 100644
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -800,6 +800,10 @@ protected:
   // over our window or there is no last observed mouse location for some
   // reason.
   nsPoint                   mMouseLocation;
+  // This is an APZ state variable that tracks the target guid for the last
+  // mouse event that was processed (corresponding to mMouseLocation). This is
+  // needed for the synthetic mouse events.
+  mozilla::layers::ScrollableLayerGuid mMouseEventTargetGuid;
 
   // mStyleSet owns it but we maintain a ref, may be null
   mozilla::StyleSheetHandle::RefPtr mPrefStyleSheet;
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index f491d37531..04f9856691 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -320,6 +320,10 @@ nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState,
   if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
       (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar &&
        ScrolledContentDependsOnHeight(aState))) {
+    if (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar) {
+      nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
+          mHelper.mScrolledFrame);
+    }
     ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll, aKidMetrics,
                         false);
   }
@@ -423,7 +427,8 @@ nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState* aState)
 {
   // Return true if ReflowScrolledFrame is going to do something different
   // based on the presence of a horizontal scrollbar.
-  return (mHelper.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) ||
+  return mHelper.mScrolledFrame->HasAnyStateBits(
+      NS_FRAME_CONTAINS_RELATIVE_BSIZE | NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE) ||
     aState->mReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
     aState->mReflowState.ComputedMinBSize() > 0 ||
     aState->mReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE;
diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp
index 6355ec883f..04d9bf2934 100644
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -142,21 +142,6 @@ struct TabwidthAdaptor
   }
 };
 
-/**
- * Helper that is useful to help port the code is this file to typed rects.
- * The code here is particularly horrible because it uses gfxRect to
- * store app unit values (because we want fractional app unit values), but
- * virtually everywhere else gfxRect is in device pixels. :-/
- */
-LayoutDeviceRect AppUnitGfxRectToDevRect(gfxRect aRect,
-                                         int32_t aAppUnitsPerDevPixel)
-{
-  return LayoutDeviceRect(aRect.x / aAppUnitsPerDevPixel,
-                          aRect.y / aAppUnitsPerDevPixel,
-                          aRect.width / aAppUnitsPerDevPixel,
-                          aRect.height / aAppUnitsPerDevPixel);
-}
-
 } // namespace
 
 void
@@ -2699,7 +2684,7 @@ BuildTextRunsScanner::AssignTextRun(gfxTextRun* aTextRun, float aInflation)
 
 NS_QUERYFRAME_HEAD(nsTextFrame)
   NS_QUERYFRAME_ENTRY(nsTextFrame)
-NS_QUERYFRAME_TAIL_INHERITING(nsTextFrameBase)
+NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
 
 gfxSkipCharsIterator
 nsTextFrame::EnsureTextRun(TextRunType aWhichTextRun,
@@ -4464,7 +4449,7 @@ nsTextFrame::InvalidateFrame(uint32_t aDisplayItemKey)
     svgTextFrame->InvalidateFrame();
     return;
   }
-  nsTextFrameBase::InvalidateFrame(aDisplayItemKey);
+  nsFrame::InvalidateFrame(aDisplayItemKey);
 }
 
 void
@@ -4477,7 +4462,7 @@ nsTextFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemK
     svgTextFrame->InvalidateFrame();
     return;
   }
-  nsTextFrameBase::InvalidateFrameWithRect(aRect, aDisplayItemKey);
+  nsFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
 }
 
 gfxTextRun*
@@ -4831,9 +4816,9 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
   // antialiased pixels beyond the measured text extents.
   // This is temporary until we do this in the actual calculation of text extents.
-  nsRect extraVisible = mVisibleRect;
-  nscoord appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
-  extraVisible.Inflate(appUnitsPerDevPixel, appUnitsPerDevPixel);
+  LayoutDeviceRect extraVisible = LayoutDeviceRect::FromAppUnits(
+    mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
+  extraVisible.Inflate(1);
   nsTextFrame* f = static_cast(mFrame);
 
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
@@ -4841,8 +4826,8 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
   gfxContext* ctx = aCtx->ThebesContext();
   gfxContextAutoSaveRestore save(ctx);
 
-  gfxRect pixelVisible =
-    nsLayoutUtils::RectToGfxRect(extraVisible, appUnitsPerDevPixel);
+  gfxRect pixelVisible(extraVisible.x, extraVisible.y,
+                       extraVisible.width, extraVisible.height);
   pixelVisible.Inflate(2);
   pixelVisible.RoundOut();
 
@@ -5511,7 +5496,7 @@ nsTextFrame::ComputeSelectionUnderlineHeight(
 
 void
 nsTextFrame::PaintDecorationLine(gfxContext* const aCtx,
-                                 const gfxRect& aDirtyRect,
+                                 const LayoutDeviceRect& aDirtyRect,
                                  nscolor aColor,
                                  const nscolor* aOverrideColor,
                                  const gfxPoint& aPt,
@@ -5527,13 +5512,15 @@ nsTextFrame::PaintDecorationLine(gfxContext* const aCtx,
                                  gfxFloat aDescentLimit /* = -1.0 */)
 {
   nscolor lineColor = aOverrideColor ? *aOverrideColor : aColor;
+  gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
+                    aDirtyRect.width, aDirtyRect.height);
   if (aCallbacks) {
     if (aDecorationType == eNormalDecoration) {
       aCallbacks->NotifyBeforeDecorationLine(lineColor);
     } else {
       aCallbacks->NotifyBeforeSelectionDecorationLine(lineColor);
     }
-    nsCSSRendering::DecorationLineToPath(this, aCtx, aDirtyRect, lineColor,
+    nsCSSRendering::DecorationLineToPath(this, aCtx, dirtyRect, lineColor,
       aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
       aVertical, aDescentLimit);
     if (aDecorationType == eNormalDecoration) {
@@ -5543,7 +5530,7 @@ nsTextFrame::PaintDecorationLine(gfxContext* const aCtx,
     }
   } else {
     nsCSSRendering::PaintDecorationLine(this, *aCtx->GetDrawTarget(),
-                                        ToRect(aDirtyRect), lineColor,
+                                        ToRect(dirtyRect), lineColor,
       aPt, Float(aICoordInFrame), aLineSize, aAscent, aOffset, aDecoration, aStyle,
       aVertical, aDescentLimit);
   }
@@ -5555,7 +5542,7 @@ nsTextFrame::PaintDecorationLine(gfxContext* const aCtx,
  */
 void
 nsTextFrame::DrawSelectionDecorations(gfxContext* aContext,
-                                      const gfxRect& aDirtyRect,
+                                      const LayoutDeviceRect& aDirtyRect,
                                       SelectionType aType,
                                       nsTextPaintStyle& aTextPaintStyle,
                                       const TextRangeStyle &aRangeStyle,
@@ -5885,7 +5872,8 @@ AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun,
 void
 nsTextFrame::PaintOneShadow(Range aRange,
                             nsCSSShadowItem* aShadowDetails,
-                            PropertyProvider* aProvider, const nsRect& aDirtyRect,
+                            PropertyProvider* aProvider,
+                            const LayoutDeviceRect& aDirtyRect,
                             const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
                             gfxContext* aCtx, const nscolor& aForegroundColor,
                             const nsCharClipDisplayItem::ClipEdges& aClipEdges,
@@ -5926,10 +5914,10 @@ nsTextFrame::PaintOneShadow(Range aRange,
                     NSToCoordRound(shadowGfxRect.Height()));
 
   nsContextBoxBlur contextBoxBlur;
-  gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
-                                                  PresContext()->AppUnitsPerDevPixel(),
-                                                  aCtx, aDirtyRect, nullptr,
-                                                  aBlurFlags);
+  const auto A2D = PresContext()->AppUnitsPerDevPixel();
+  gfxContext* shadowContext = contextBoxBlur.Init(
+    shadowRect, 0, blurRadius, A2D, aCtx,
+    LayoutDevicePixel::ToAppUnits(aDirtyRect, A2D), nullptr, aBlurFlags);
   if (!shadowContext)
     return;
 
@@ -5954,8 +5942,7 @@ nsTextFrame::PaintOneShadow(Range aRange,
   nsTextPaintStyle textPaintStyle(this);
   DrawTextParams params(shadowContext);
   params.advanceWidth = &advanceWidth;
-  params.dirtyRect = gfxRect(aDirtyRect.x, aDirtyRect.y,
-                             aDirtyRect.width, aDirtyRect.height);
+  params.dirtyRect = aDirtyRect;
   params.framePt = aFramePt + shadowOffset;
   params.provider = aProvider;
   params.textStyle = &textPaintStyle;
@@ -5974,7 +5961,7 @@ nsTextFrame::PaintOneShadow(Range aRange,
 bool
 nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
-    const gfxRect& aDirtyRect,
+    const LayoutDeviceRect& aDirtyRect,
     PropertyProvider& aProvider,
     Range aContentRange,
     nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails,
@@ -6039,7 +6026,6 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
   // Draw background colors
   if (anyBackgrounds) {
     int32_t appUnitsPerDevPixel = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
-    LayoutDeviceRect dirtyRect = AppUnitGfxRectToDevRect(aDirtyRect, appUnitsPerDevPixel);
     SelectionIterator iterator(prevailingSelections, aContentRange,
                                aProvider, mTextRun, startIOffset);
     while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
@@ -6051,18 +6037,19 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
       gfxFloat advance = hyphenWidth +
         mTextRun->GetAdvanceWidth(range, &aProvider);
       if (NS_GET_A(background) > 0) {
-        gfxRect bgRect;
+        nsRect bgRect;
         gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0);
         if (vertical) {
-          bgRect = gfxRect(aFramePt.x, aFramePt.y + offs,
-                           GetSize().width, advance);
+          bgRect = nsRect(aFramePt.x, aFramePt.y + offs,
+                          GetSize().width, advance);
         } else {
-          bgRect = gfxRect(aFramePt.x + offs, aFramePt.y,
-                           advance, GetSize().height);
+          bgRect = nsRect(aFramePt.x + offs, aFramePt.y,
+                          advance, GetSize().height);
         }
-        PaintSelectionBackground(*aCtx->GetDrawTarget(), background, dirtyRect,
-                                 AppUnitGfxRectToDevRect(bgRect, appUnitsPerDevPixel),
-                                 aCallbacks);
+        PaintSelectionBackground(
+          *aCtx->GetDrawTarget(), background, aDirtyRect,
+          LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel),
+          aCallbacks);
       }
       iterator.UpdateWithAdvance(advance);
     }
@@ -6080,8 +6067,6 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
   
   // Draw text
   const nsStyleText* textStyle = StyleText();
-  nsRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
-                   aDirtyRect.width, aDirtyRect.height);
   SelectionIterator iterator(prevailingSelections, aContentRange,
                              aProvider, mTextRun, startIOffset);
   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
@@ -6103,7 +6088,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
         startEdge -= hyphenWidth +
           mTextRun->GetAdvanceWidth(range, &aProvider);
       }
-      PaintShadows(shadow, range, dirtyRect, aFramePt, textBaselinePt,
+      PaintShadows(shadow, range, aDirtyRect, aFramePt, textBaselinePt,
           startEdge, aProvider, foreground, aClipEdges, aCtx);
     }
 
@@ -6120,7 +6105,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
 void
 nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
     const gfxPoint& aFramePt,
-    const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
+    const gfxPoint& aTextBaselinePt, const LayoutDeviceRect& aDirtyRect,
     PropertyProvider& aProvider, Range aContentRange,
     nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails,
     SelectionType aSelectionType,
@@ -6185,8 +6170,6 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
   } else {
     pt.y = (aTextBaselinePt.y - mAscent) / app;
   }
-  gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
-                    aDirtyRect.width / app, aDirtyRect.height / app);
   gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
   SelectionType type;
   TextRangeStyle selectedStyle;
@@ -6204,7 +6187,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
       }
       gfxFloat width = Abs(advance) / app;
       gfxFloat xInFrame = pt.x - (aFramePt.x / app);
-      DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType,
+      DrawSelectionDecorations(aCtx, aDirtyRect, aSelectionType,
                                aTextPaintStyle, selectedStyle, pt, xInFrame,
                                width, mAscent / app, decorationMetrics,
                                aCallbacks, verticalRun, decorationOffsetDir,
@@ -6217,7 +6200,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
 bool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
     const gfxPoint& aFramePt,
-    const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
+    const gfxPoint& aTextBaselinePt, const LayoutDeviceRect& aDirtyRect,
     PropertyProvider& aProvider,
     Range aContentRange,
     nsTextPaintStyle& aTextPaintStyle,
@@ -6466,7 +6449,7 @@ nsTextFrame::MeasureCharClippedText(PropertyProvider& aProvider,
 void
 nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow,
                           Range aRange,
-                          const nsRect& aDirtyRect,
+                          const LayoutDeviceRect& aDirtyRect,
                           const gfxPoint& aFramePt,
                           const gfxPoint& aTextBaselinePt,
                           nscoord aLeftEdgeOffset,
@@ -6526,7 +6509,7 @@ nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow,
 
 void
 nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
-                       const nsRect& aDirtyRect,
+                       const LayoutDeviceRect& aDirtyRect,
                        const nsCharClipDisplayItem& aItem,
                        gfxTextContextPaint* aContextPaint,
                        nsTextFrame::DrawPathCallbacks* aCallbacks,
@@ -6587,8 +6570,6 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
   nsTextPaintStyle textPaintStyle(this);
   textPaintStyle.SetResolveColors(!aCallbacks);
 
-  gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
-                    aDirtyRect.width, aDirtyRect.height);
   // Fork off to the (slower) paint-with-selection path if necessary.
   if (aItem.mIsFrameSelected.value()) {
     MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!");
@@ -6596,7 +6577,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
     Range contentRange(
       uint32_t(tmp.ConvertSkippedToOriginal(startOffset)),
       uint32_t(tmp.ConvertSkippedToOriginal(startOffset + maxLength)));
-    if (PaintTextWithSelection(ctx, framePt, textBaselinePt, dirtyRect,
+    if (PaintTextWithSelection(ctx, framePt, textBaselinePt, aDirtyRect,
                                provider, contentRange, textPaintStyle,
                                clipEdges, aContextPaint, aCallbacks)) {
       return;
@@ -6620,7 +6601,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
 
   gfxFloat advanceWidth;
   DrawTextParams params(ctx);
-  params.dirtyRect = dirtyRect;
+  params.dirtyRect = aDirtyRect;
   params.framePt = framePt;
   params.provider = &provider;
   params.advanceWidth = &advanceWidth;
@@ -6730,11 +6711,6 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange,
       ascent = -ascent;
     }
 
-    gfxRect dirtyRect(aParams.dirtyRect.x / app,
-                      aParams.dirtyRect.y / app,
-                      aParams.dirtyRect.Width() / app,
-                      aParams.dirtyRect.Height() / app);
-
     nscoord inflationMinFontSize =
       nsLayoutUtils::InflationMinFontSizeFor(this);
 
@@ -6758,7 +6734,7 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange,
       decSize.height = metrics.underlineSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
-      PaintDecorationLine(aParams.context, dirtyRect, dec.mColor,
+      PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor,
         aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent,
         decorationOffsetDir * metrics.underlineOffset,
         NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
@@ -6780,7 +6756,7 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange,
       decSize.height = metrics.underlineSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
-      PaintDecorationLine(aParams.context, dirtyRect, dec.mColor,
+      PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor,
         aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent,
         decorationOffsetDir * metrics.maxAscent,
         NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
@@ -6811,7 +6787,7 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange,
       decSize.height = metrics.strikeoutSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
-      PaintDecorationLine(aParams.context, dirtyRect, dec.mColor,
+      PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor,
         aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent,
         decorationOffsetDir * metrics.strikeoutOffset,
         NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h
index bfe55ceefa..8c70e09981 100644
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -29,8 +29,6 @@ class PropertyProvider;
 struct SelectionDetails;
 class nsTextFragment;
 
-typedef nsFrame nsTextFrameBase;
-
 class nsDisplayTextGeometry;
 class nsDisplayText;
 
@@ -40,7 +38,8 @@ public:
   static void Shutdown();
 };
 
-class nsTextFrame : public nsTextFrameBase {
+class nsTextFrame : public nsFrame {
+  typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::TextRangeStyle TextRangeStyle;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Rect Rect;
@@ -56,7 +55,7 @@ public:
   friend class nsDisplayText;
 
   explicit nsTextFrame(nsStyleContext* aContext)
-    : nsTextFrameBase(aContext)
+    : nsFrame(aContext)
   {
     NS_ASSERTION(mContentOffset == 0, "Bogus content offset");
   }
@@ -421,7 +420,7 @@ public:
   struct DrawTextParams : DrawTextRunParams
   {
     gfxPoint framePt;
-    gfxRect dirtyRect;
+    LayoutDeviceRect dirtyRect;
     const nsTextPaintStyle* textStyle = nullptr;
     const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr;
     const nscolor* decorationOverrideColor = nullptr;
@@ -434,7 +433,8 @@ public:
   // object.  The private DrawText() is what applies the text to a graphics
   // context.
   void PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
-                 const nsRect& aDirtyRect, const nsCharClipDisplayItem& aItem,
+                 const LayoutDeviceRect& aDirtyRect,
+                 const nsCharClipDisplayItem& aItem,
                  gfxTextContextPaint* aContextPaint = nullptr,
                  DrawPathCallbacks* aCallbacks = nullptr,
                  float aOpacity = 1.0f);
@@ -444,7 +444,7 @@ public:
   bool PaintTextWithSelection(gfxContext* aCtx,
                               const gfxPoint& aFramePt,
                               const gfxPoint& aTextBaselinePt,
-                              const gfxRect& aDirtyRect,
+                              const LayoutDeviceRect& aDirtyRect,
                               PropertyProvider& aProvider,
                               Range aRange,
                               nsTextPaintStyle& aTextPaintStyle,
@@ -459,7 +459,7 @@ public:
   bool PaintTextWithSelectionColors(gfxContext* aCtx,
                                     const gfxPoint& aFramePt,
                                     const gfxPoint& aTextBaselinePt,
-                                    const gfxRect& aDirtyRect,
+                                    const LayoutDeviceRect& aDirtyRect,
                                     PropertyProvider& aProvider,
                                     Range aContentRange,
                                     nsTextPaintStyle& aTextPaintStyle,
@@ -471,7 +471,7 @@ public:
   void PaintTextSelectionDecorations(gfxContext* aCtx,
                                      const gfxPoint& aFramePt,
                                      const gfxPoint& aTextBaselinePt,
-                                     const gfxRect& aDirtyRect,
+                                     const LayoutDeviceRect& aDirtyRect,
                                      PropertyProvider& aProvider,
                                      Range aContentRange,
                                      nsTextPaintStyle& aTextPaintStyle,
@@ -638,7 +638,7 @@ protected:
   void PaintOneShadow(Range aRange,
                       nsCSSShadowItem* aShadowDetails,
                       PropertyProvider* aProvider,
-                      const nsRect& aDirtyRect,
+                      const LayoutDeviceRect& aDirtyRect,
                       const gfxPoint& aFramePt,
                       const gfxPoint& aTextBaselinePt,
                       gfxContext* aCtx,
@@ -650,7 +650,7 @@ protected:
 
   void PaintShadows(nsCSSShadowArray* aShadow,
                     Range aRange,
-                    const nsRect& aDirtyRect,
+                    const LayoutDeviceRect& aDirtyRect,
                     const gfxPoint& aFramePt,
                     const gfxPoint& aTextBaselinePt,
                     nscoord aLeftEdgeOffset,
@@ -752,7 +752,7 @@ protected:
    * Utility methods to paint selection.
    */
   void DrawSelectionDecorations(gfxContext* aContext,
-                                const gfxRect& aDirtyRect,
+                                const LayoutDeviceRect& aDirtyRect,
                                 SelectionType aType,
                                 nsTextPaintStyle& aTextPaintStyle,
                                 const TextRangeStyle &aRangeStyle,
@@ -771,7 +771,7 @@ protected:
     eSelectionDecoration
   };
   void PaintDecorationLine(gfxContext* const aCtx,
-                           const gfxRect& aDirtyRect,
+                           const LayoutDeviceRect& aDirtyRect,
                            nscolor aColor,
                            const nscolor* aOverrideColor,
                            const gfxPoint& aPt,
diff --git a/layout/reftests/bugs/1242172-1-ref.html b/layout/reftests/bugs/1242172-1-ref.html
new file mode 100644
index 0000000000..376ee8fd3b
--- /dev/null
+++ b/layout/reftests/bugs/1242172-1-ref.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/layout/reftests/bugs/1242172-1-subdoc.html b/layout/reftests/bugs/1242172-1-subdoc.html
new file mode 100644
index 0000000000..97a34e5092
--- /dev/null
+++ b/layout/reftests/bugs/1242172-1-subdoc.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/bugs/1242172-1.html b/layout/reftests/bugs/1242172-1.html new file mode 100644 index 0000000000..069ccba45f --- /dev/null +++ b/layout/reftests/bugs/1242172-1.html @@ -0,0 +1,12 @@ + + + + + + + diff --git a/layout/reftests/bugs/1242172-2-ref.html b/layout/reftests/bugs/1242172-2-ref.html new file mode 100644 index 0000000000..d5fb96cdce --- /dev/null +++ b/layout/reftests/bugs/1242172-2-ref.html @@ -0,0 +1,11 @@ + + + + +
+
+
+
+ + diff --git a/layout/reftests/bugs/1242172-2.html b/layout/reftests/bugs/1242172-2.html new file mode 100644 index 0000000000..7cacd54f13 --- /dev/null +++ b/layout/reftests/bugs/1242172-2.html @@ -0,0 +1,9 @@ + + + +
+
+
+
+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 46d5fa2cbd..5d0a308785 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1943,3 +1943,5 @@ fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html == 1209994-4.html 1209994-4-ref.html == 1222226-1.html 1222226-1-ref.html pref(browser.display.focus_ring_width,2) == 1234758-1.html 1234758-1-ref.html +== 1242172-1.html 1242172-1-ref.html +== 1242172-2.html 1242172-2-ref.html diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index cbad10ef0f..fac7e8b76b 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -3703,8 +3703,8 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, // SVG frames' PaintSVG methods paint in CSS px, but normally frames paint in // dev pixels. Here we multiply a CSS-px-to-dev-pixel factor onto aTransform // so our non-SVG nsTextFrame children paint correctly. - float cssPxPerDevPx = presContext-> - AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel()); + auto auPerDevPx = presContext->AppUnitsPerDevPixel(); + float cssPxPerDevPx = presContext->AppUnitsToFloatCSSPixels(auPerDevPx); gfxMatrix canvasTMForChildren = aTransform; canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx); initialMatrix.Scale(1 / cssPxPerDevPx, 1 / cssPxPerDevPx); @@ -3756,7 +3756,8 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, aContext.SetMatrix(runTransform); if (drawMode != DrawMode(0)) { - nsRect frameRect = frame->GetVisualOverflowRect(); + LayoutDeviceRect frameRect = LayoutDevicePixel:: + FromAppUnits(frame->GetVisualOverflowRect(), auPerDevPx); bool paintSVGGlyphs; if (ShouldRenderAsPath(frame, paintSVGGlyphs)) { SVGTextDrawPathCallbacks callbacks(&rendCtx, frame, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 9445de0f0f..f93ff7488c 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -143,12 +143,6 @@ pref("dom.workers.maxPerDomain", 20); pref("dom.serviceWorkers.enabled", false); -// Allow service workers to intercept network requests using the fetch event -pref("dom.serviceWorkers.interception.enabled", false); - -// Allow service workers to intercept opaque (cross origin) responses -pref("dom.serviceWorkers.interception.opaque.enabled", false); - // The amount of time (milliseconds) service workers keep running after each event. pref("dom.serviceWorkers.idle_timeout", 30000); @@ -1240,10 +1234,6 @@ pref("nglayout.debug.widget_update_flashing", false); // Whether image visibility is enabled globally (ie we will try to unlock images // that are not visible). pref("layout.imagevisibility.enabled", true); -// Whether image visibility is enabled in documents that are within a browser -// element as defined by nsDocShell::FrameType and GetInheritedFrameType. This -// pref only has an effect if layout.imagevisibility.enabled is false. -pref("layout.imagevisibility.enabled_for_browser_elements_only", false); pref("layout.imagevisibility.numscrollportwidths", 0); pref("layout.imagevisibility.numscrollportheights", 1); @@ -1638,6 +1628,9 @@ pref("network.http.max-persistent-connections-per-proxy", 16); // max-connections or max-connections-per-server has also been reached. pref("network.http.request.max-start-delay", 10); +// If a connection is reset, we will retry it max-attempts times. +pref("network.http.request.max-attempts", 3); + // Headers pref("network.http.accept.default", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); diff --git a/netwerk/cache2/CacheIOThread.cpp b/netwerk/cache2/CacheIOThread.cpp index 4edee9caf3..4f5eef27d4 100644 --- a/netwerk/cache2/CacheIOThread.cpp +++ b/netwerk/cache2/CacheIOThread.cpp @@ -27,7 +27,9 @@ CacheIOThread::CacheIOThread() , mHasXPCOMEvents(false) , mRerunCurrentEvent(false) , mShutdown(false) +#ifdef DEBUG , mInsideLoop(true) +#endif { sSelf = this; } @@ -242,8 +244,10 @@ loopStart: MOZ_ASSERT(!EventsPending()); +#ifdef DEBUG // This is for correct assertion on XPCOM events dispatch. mInsideLoop = false; +#endif } // lock if (threadInternal) diff --git a/netwerk/cache2/CacheIOThread.h b/netwerk/cache2/CacheIOThread.h index d685fa3256..553bd5e1f2 100644 --- a/netwerk/cache2/CacheIOThread.h +++ b/netwerk/cache2/CacheIOThread.h @@ -96,7 +96,9 @@ private: Atomic mHasXPCOMEvents; bool mRerunCurrentEvent; bool mShutdown; - DebugOnly mInsideLoop; +#ifdef DEBUG + bool mInsideLoop; +#endif }; } // namespace net diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 596e68850c..5e2bda527c 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -7179,14 +7179,11 @@ nsHttpChannel::MaybeWarnAboutAppCache() /*Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, true);*/ - // Then, issue a deprecation warning if service worker interception is - // enabled. - if (nsContentUtils::ServiceWorkerInterceptionEnabled()) { - nsCOMPtr warner; - GetCallback(warner); - if (warner) { - warner->IssueWarning(nsIDocument::eAppCache, false); - } + // Then, issue a deprecation warning. + nsCOMPtr warner; + GetCallback(warner); + if (warner) { + warner->IssueWarning(nsIDocument::eAppCache, false); } } diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index d473f150ea..80396a5695 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -146,7 +146,7 @@ nsHttpHandler::nsHttpHandler() , mResponseTimeout(PR_SecondsToInterval(300)) , mResponseTimeoutEnabled(false) , mNetworkChangedTimeout(5000) - , mMaxRequestAttempts(10) + , mMaxRequestAttempts(3) , mMaxRequestDelay(10) , mIdleSynTimeout(250) , mH2MandatorySuiteEnabled(false) diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index f9565f8e88..21a1274e62 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -87,6 +87,8 @@ nsHttpTransaction::nsHttpTransaction() , mConnection(nullptr) , mRequestHead(nullptr) , mResponseHead(nullptr) + , mReader(nullptr) + , mWriter(nullptr) , mContentLength(-1) , mContentRead(0) , mTransferSize(0) @@ -581,7 +583,7 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport, // and the correct value will be returned in nsPerformance. if (TimingEnabled() && GetRequestStart().IsNull()) { if (status == NS_NET_STATUS_RESOLVING_HOST) { - SetDomainLookupStart(TimeStamp::Now()); + SetDomainLookupStart(TimeStamp::Now(), true); } else if (status == NS_NET_STATUS_RESOLVED_HOST) { SetDomainLookupEnd(TimeStamp::Now()); } else if (status == NS_NET_STATUS_CONNECTING_TO) { @@ -971,11 +973,6 @@ nsHttpTransaction::Close(nsresult reason) PR_Now(), 0, EmptyCString()); } - // we must no longer reference the connection! find out if the - // connection was being reused before letting it go. - bool connReused = false; - if (mConnection) - connReused = mConnection->IsReused(); mConnected = false; mTunnelProvider = nullptr; @@ -986,6 +983,9 @@ nsHttpTransaction::Close(nsresult reason) // should) assume that we wrote to a stale connection and we must therefore // repeat the request over a new connection. // + // We have decided to retry not only in case of the reused connections, but + // all safe methods(bug 1236277). + // // NOTE: the conditions under which we will automatically retry the HTTP // request have to be carefully selected to avoid duplication of the // request from the point-of-view of the server. such duplication could @@ -1025,7 +1025,8 @@ nsHttpTransaction::Close(nsresult reason) mSentData && (!mConnection || mConnection->BytesWritten()); if (!mReceivedData && - (!reallySentData || connReused || mPipelinePosition)) { + ((mRequestHead && mRequestHead->IsSafeMethod()) || + !reallySentData)) { // if restarting fails, then we must proceed to close the pipe, // which will notify the channel that the transaction failed. @@ -1677,16 +1678,19 @@ nsHttpTransaction::HandleContentStart() // we're done with the socket. please note that _all_ other // decoding is done when the channel receives the content data // so as not to block the socket transport thread too much. - // ignore chunked responses from HTTP/1.0 servers and proxies. - if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 && + if (mResponseHead->Version() >= NS_HTTP_VERSION_1_0 && mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) { // we only support the "chunked" transfer encoding right now. mChunkedDecoder = new nsHttpChunkedDecoder(); - if (!mChunkedDecoder) - return NS_ERROR_OUT_OF_MEMORY; - LOG(("chunked decoder created\n")); + LOG(("nsHttpTransaction %p chunked decoder created\n", this)); // Ignore server specified Content-Length. - mContentLength = -1; + if (mContentLength != int64_t(-1)) { + LOG(("nsHttpTransaction %p chunked with C-L ignores C-L\n", this)); + mContentLength = -1; + if (mConnection) { + mConnection->DontReuse(); + } + } } else if (mContentLength == int64_t(-1)) LOG(("waiting for the server to close the connection.\n")); diff --git a/netwerk/test/unit/test_protocolproxyservice.js b/netwerk/test/unit/test_protocolproxyservice.js index 21351fffc7..b08865ee40 100644 --- a/netwerk/test/unit/test_protocolproxyservice.js +++ b/netwerk/test/unit/test_protocolproxyservice.js @@ -17,7 +17,7 @@ // run_myipaddress_test(); // run_failed_script_test(); // run_isresolvable_test(); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); @@ -169,14 +169,10 @@ resolveCallback.prototype = { }; function run_filter_test() { - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Verify initial state var cb = new resolveCallback(); @@ -199,14 +195,10 @@ function filter_test0_1(pi) { var cb = new resolveCallback(); cb.nextFunction = filter_test0_2; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -219,14 +211,10 @@ function filter_test0_2(pi) var cb = new resolveCallback(); cb.nextFunction = filter_test0_3; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -241,14 +229,10 @@ function filter_test0_3(pi) var cb = new resolveCallback(); cb.nextFunction = filter_test0_4; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -261,14 +245,10 @@ function filter_test0_4(pi) pps.registerChannelFilter(filter03, 10); var cb = new resolveCallback(); cb.nextFunction = filter_test0_5; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -350,14 +330,10 @@ function run_filter_test2() { var cb = new resolveCallback(); cb.nextFunction = filter_test1_1; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -369,14 +345,10 @@ function filter_test1_1(pi) { var cb = new resolveCallback(); cb.nextFunction = filter_test1_2; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -389,14 +361,10 @@ function filter_test1_2(pi) { var cb = new resolveCallback(); cb.nextFunction = filter_test1_3; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -408,15 +376,10 @@ function filter_test1_3(pi) { var filter_3_1; function run_filter_test3() { - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Push a filter and verify the results asynchronously filter_3_1 = new TestFilter("http", "foo", 8080, 0, 10); @@ -434,15 +397,10 @@ function filter_test3_1(pi) { } function run_pref_test() { - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Verify 'direct' setting prefs.setIntPref("network.proxy.type", 0); @@ -461,14 +419,10 @@ function pref_test1_1(pi) var cb = new resolveCallback(); cb.nextFunction = pref_test1_2; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -483,14 +437,10 @@ function pref_test1_2(pi) var cb = new resolveCallback(); cb.nextFunction = pref_test1_3; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -507,14 +457,10 @@ function pref_test1_3(pi) var cb = new resolveCallback(); cb.nextFunction = pref_test1_4; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); var req = pps.asyncResolve(channel, 0, cb); } @@ -572,15 +518,10 @@ function run_pac_test() { 'function FindProxyForURL(url, host) {' + ' return "PROXY foopy:8080; DIRECT";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Configure PAC prefs.setIntPref("network.proxy.type", 2); @@ -593,15 +534,10 @@ function run_pac2_test() { 'function FindProxyForURL(url, host) {' + ' return "HTTPS foopy:8080; DIRECT";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Configure PAC originalTLSProxy = prefs.getBoolPref("network.proxy.proxy_over_tls"); @@ -616,15 +552,10 @@ function run_pac3_test() { 'function FindProxyForURL(url, host) {' + ' return "HTTPS foopy:8080; DIRECT";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Configure PAC prefs.setCharPref("network.proxy.autoconfig_url", pac); prefs.setBoolPref("network.proxy.proxy_over_tls", false); @@ -659,14 +590,10 @@ function run_pac4_test() { ' && myAppOrigin() == "' + appOrigin + '")' + ' return "PROXY foopy:8080; DIRECT";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); channel.notificationCallbacks = AppsUtils.createLoadContext(appId, isInBrowser); @@ -710,15 +637,10 @@ TestResolveCancelationCallback.prototype = { }; function run_pac_cancel_test() { - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); // Configure PAC var pac = 'data:text/plain,' + 'function FindProxyForURL(url, host) {' + @@ -758,15 +680,10 @@ function check_host_filters_cb() function check_host_filter(i) { var uri; dump("*** uri=" + hostList[i] + " bShouldBeFiltered=" + bShouldBeFiltered + "\n"); - var channel = ios.newChannel2(hostList[i], - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: hostList[i], + loadUsingSystemPrincipal: true + }); var cb = new resolveCallback(); cb.nextFunction = host_filter_cb; var req = pps.asyncResolve(channel, 0, cb); @@ -869,15 +786,10 @@ function run_myipaddress_test() // no traffic to this IP is ever sent, it is just a public IP that // does not require DNS to determine a route. - var channel = ios.newChannel2("http://192.0.43.10/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://192.0.43.10/", + loadUsingSystemPrincipal: true + }); prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); @@ -912,14 +824,10 @@ function run_myipaddress_test_2() ' return "PROXY " + myaddr + ":5678";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); @@ -948,15 +856,10 @@ function run_failed_script_test() var pac = 'data:text/plain,' + '\nfor(;\n'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); @@ -982,15 +885,11 @@ function failed_script_callback(pi) obs = obs.QueryInterface(Components.interfaces.nsIObserverService); obs.addObserver(directFilterListener, "http-on-modify-request", false); - var chan = ios.newChannel2("http://127.0.0.1:7247", - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - chan.asyncOpen(directFilterListener, chan); + var chan = NetUtil.newChannel({ + uri: "http://127.0.0.1:7247", + loadUsingSystemPrincipal: true + }); + chan.asyncOpen2(directFilterListener); } var directFilterListener = { @@ -1001,8 +900,8 @@ var directFilterListener = { onStopRequest: function test_onStop(request, ctx, status) { // check on the PI from the channel itself - ctx.QueryInterface(Components.interfaces.nsIProxiedChannel); - check_proxy(ctx.proxyInfo, "http", "127.0.0.1", 7246, 0, 0, false); + request.QueryInterface(Components.interfaces.nsIProxiedChannel); + check_proxy(request.proxyInfo, "http", "127.0.0.1", 7246, 0, 0, false); pps.unregisterFilter(directFilter); // check on the PI from on-modify-request @@ -1035,15 +934,10 @@ function run_isresolvable_test() ' return "PROXY 127.0.0.1:1234";' + '}'; - var channel = ios.newChannel2("http://www.mozilla.org/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://www.mozilla.org/", + loadUsingSystemPrincipal: true + }); prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index 3243db034a..0358303479 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -473,7 +473,9 @@ Connection::Connection(Service *aService, , threadOpenedOn(do_GetCurrentThread()) , mDBConn(nullptr) , mAsyncExecutionThreadShuttingDown(false) +#ifdef DEBUG , mAsyncExecutionThreadIsAlive(false) +#endif , mConnectionClosed(false) , mTransactionInProgress(false) , mProgressHandler(nullptr) @@ -559,7 +561,10 @@ Connection::getAsyncExecutionTarget() mAsyncExecutionThread); } +#ifdef DEBUG mAsyncExecutionThreadIsAlive = true; +#endif + return mAsyncExecutionThread; } @@ -892,7 +897,9 @@ Connection::shutdownAsyncThread(nsIThread *aThread) { DebugOnly rv = aThread->Shutdown(); MOZ_ASSERT(NS_SUCCEEDED(rv)); +#ifdef DEBUG mAsyncExecutionThreadIsAlive = false; +#endif } nsresult diff --git a/storage/mozStorageConnection.h b/storage/mozStorageConnection.h index e5e0649565..93ce0a4d3f 100644 --- a/storage/mozStorageConnection.h +++ b/storage/mozStorageConnection.h @@ -320,7 +320,9 @@ private: * Tracks whether the async thread has been initialized and Shutdown() has * not yet been invoked on it. */ - DebugOnly mAsyncExecutionThreadIsAlive; +#ifdef DEBUG + bool mAsyncExecutionThreadIsAlive; +#endif /** * Set to true just prior to calling sqlite3_close on the diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html index 277485b75f..6c8fc35248 100644 --- a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html +++ b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html @@ -10,7 +10,6 @@ SimpleTest.waitForExplicitFinish(); SimpleTest.expectRegisteredServiceWorker(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, function() { diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html index 468b948a44..105fa6614e 100644 --- a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html +++ b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html @@ -9,7 +9,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] ]}, function() { diff --git a/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini b/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini index e05911f92d..7d3a07b354 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini @@ -1 +1 @@ -prefs: [dom.serviceWorkers.enabled: true, dom.serviceWorkers.interception.enabled: true, dom.serviceWorkers.exemptFromPerDomainMax:true, dom.caches.enabled:true] +prefs: [dom.serviceWorkers.enabled: true, dom.serviceWorkers.exemptFromPerDomainMax:true, dom.caches.enabled:true] diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini index 57c38e8b32..ae99e9ff71 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini @@ -1,3 +1 @@ -prefs: [dom.serviceWorkers.enabled: true, - dom.serviceWorkers.interception.enabled: true, - dom.serviceWorkers.interception.opaque.enabled: true] +prefs: [dom.serviceWorkers.enabled:true] diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index 64f421453a..9a1e5aaf36 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -697,10 +697,14 @@ nsBaseDragService::DrawDragForImage(nsIImageLoadingContent* aImageLoader, if (!ctx) return NS_ERROR_FAILURE; - imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize), - imgIContainer::FRAME_CURRENT, - Filter::GOOD, Nothing(), - imgIContainer::FLAG_SYNC_DECODE); + DrawResult res = + imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize), + imgIContainer::FRAME_CURRENT, + Filter::GOOD, Nothing(), + imgIContainer::FLAG_SYNC_DECODE); + if (res == DrawResult::BAD_IMAGE || res == DrawResult::BAD_ARGS) { + return NS_ERROR_FAILURE; + } *aSurface = dt->Snapshot(); } else { *aSurface = aCanvas->GetSurfaceSnapshot();