diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 4857ba79fbf..0c0e6cd3147 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4970,6 +4970,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, case NS_ERROR_INTERCEPTED_ERROR_RESPONSE: case NS_ERROR_INTERCEPTED_USED_RESPONSE: case NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION: + case NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION: // ServiceWorker intercepted request, but something went wrong. nsContentUtils::MaybeReportInterceptionErrorToConsole(GetDocument(), aError); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 0b0e1e66d9b..a71c84ff93e 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3458,6 +3458,8 @@ nsContentUtils::MaybeReportInterceptionErrorToConsole(nsIDocument* aDocument, messageName = "InterceptedUsedResponse"; } else if (aError == NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION) { messageName = "ClientRequestOpaqueInterception"; + } else if (aError == NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION) { + messageName = "BadOpaqueRedirectInterception"; } if (messageName) { diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 2377e8c0f7b..66ae0e3c276 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -173,5 +173,7 @@ BadOpaqueInterceptionRequestMode=A ServiceWorker passed an opaque Response to Fe InterceptedErrorResponse=A ServiceWorker passed an Error Response to FetchEvent.respondWith(). This typically means the ServiceWorker performed an invalid fetch() call. # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", or "Response.clone()". InterceptedUsedResponse=A ServiceWorker passed a used Response to FetchEvent.respondWith(). The body of a Response may only be read once. Use Response.clone() to access the body multiple times. -# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", "FetchEvent.request", or "Worker". +# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaque", "Response", "FetchEvent.respondWith()", "FetchEvent.request", or "Worker". ClientRequestOpaqueInterception=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while FetchEvent.request was a client request. A client request is generally a browser navigation or top-level Worker script. +# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent.request". +BadOpaqueRedirectInterception=A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while FetchEvent.request was not a navigation request. diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 10561108801..72a57162da1 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -130,7 +130,8 @@ public: return rv; } - mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText()); + mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(), + mInternalResponse->GetUnfilteredStatusText()); nsAutoTArray entries; mInternalResponse->UnfilteredHeaders()->GetEntries(entries); @@ -148,18 +149,21 @@ class RespondWithHandler final : public PromiseNativeHandler { nsMainThreadPtrHandle mInterceptedChannel; nsMainThreadPtrHandle mServiceWorker; - RequestMode mRequestMode; - bool mIsClientRequest; + const RequestMode mRequestMode; + const bool mIsClientRequest; + const bool mIsNavigationRequest; public: NS_DECL_ISUPPORTS RespondWithHandler(nsMainThreadPtrHandle& aChannel, nsMainThreadPtrHandle& aServiceWorker, - RequestMode aRequestMode, bool aIsClientRequest) + RequestMode aRequestMode, bool aIsClientRequest, + bool aIsNavigationRequest) : mInterceptedChannel(aChannel) , mServiceWorker(aServiceWorker) , mRequestMode(aRequestMode) , mIsClientRequest(aIsClientRequest) + , mIsNavigationRequest(aIsNavigationRequest) { } @@ -264,12 +268,14 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } - // Section 4.2, step 2.2: + // Section "HTTP Fetch", step 2.2: // If one of the following conditions is true, return a network error: // * response's type is "error". // * request's mode is not "no-cors" and response's type is "opaque". // * request is a client request and response's type is neither "basic" // nor "default". + // * request is not a navigation request and response's type is + // "opaqueredirect". if (response->Type() == ResponseType::Error) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_ERROR_RESPONSE); @@ -281,12 +287,19 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } + // TODO: remove this case as its no longer in the spec (bug 1184967) if (mIsClientRequest && response->Type() != ResponseType::Basic && - response->Type() != ResponseType::Default) { + response->Type() != ResponseType::Default && + response->Type() != ResponseType::Opaqueredirect) { autoCancel.SetCancelStatus(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION); return; } + if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) { + autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION); + return; + } + if (NS_WARN_IF(response->BodyUsed())) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_USED_RESPONSE); return; @@ -300,7 +313,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu nsAutoPtr closure( new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo())); nsCOMPtr body; - ir->GetInternalBody(getter_AddRefs(body)); + ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); @@ -359,7 +372,7 @@ FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv) mWaitToRespond = true; nsRefPtr handler = new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode(), - ir->IsClientRequest()); + ir->IsClientRequest(), ir->IsNavigationRequest()); aArg.AppendNativeHandler(handler); } diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 77f4b5326dc..1ed17dfe20c 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -94,6 +94,15 @@ static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast(Re static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast(RequestMode::Cors_with_forced_preflight), "RequestMode enumeration value should match Necko CORS mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast(RequestRedirect::Follow), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast(RequestRedirect::Error), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast(RequestRedirect::Manual), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(3 == static_cast(RequestRedirect::EndGuard_), + "RequestRedirect enumeration value should make Necko Redirect mode value."); + static StaticRefPtr gInstance; // Tracks the "dom.disable_open_click_delay" preference. Modified on main @@ -3616,7 +3625,9 @@ class FetchEventRunnable : public WorkerRunnable nsCString mSpec; nsCString mMethod; bool mIsReload; + DebugOnly mIsHttpChannel; RequestMode mRequestMode; + RequestRedirect mRequestRedirect; RequestCredentials mRequestCredentials; nsContentPolicyType mContentPolicyType; nsCOMPtr mUploadStream; @@ -3632,7 +3643,9 @@ public: , mServiceWorker(aServiceWorker) , mClientInfo(aClientInfo) , mIsReload(aIsReload) + , mIsHttpChannel(false) , mRequestMode(RequestMode::No_cors) + , mRequestRedirect(RequestRedirect::Follow) // By default we set it to same-origin since normal HTTP fetches always // send credentials to same-origin websites unless explicitly forbidden. , mRequestCredentials(RequestCredentials::Same_origin) @@ -3688,15 +3701,17 @@ public: nsCOMPtr httpChannel = do_QueryInterface(channel); if (httpChannel) { + mIsHttpChannel = true; + rv = httpChannel->GetRequestMethod(mMethod); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr internalChannel = do_QueryInterface(httpChannel); NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); - uint32_t mode; - internalChannel->GetCorsMode(&mode); - switch (mode) { + uint32_t corsMode; + internalChannel->GetCorsMode(&corsMode); + switch (corsMode) { case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN: mRequestMode = RequestMode::Same_origin; break; @@ -3711,6 +3726,11 @@ public: MOZ_CRASH("Unexpected CORS mode"); } + // This is safe due to static_asserts at top of file. + uint32_t redirectMode; + internalChannel->GetRedirectMode(&redirectMode); + mRequestRedirect = static_cast(redirectMode); + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { mRequestCredentials = RequestCredentials::Omit; } else { @@ -3803,6 +3823,7 @@ private: reqInit.mHeaders.Value().SetAsHeaders() = headers; reqInit.mMode.Construct(mRequestMode); + reqInit.mRedirect.Construct(mRequestRedirect); reqInit.mCredentials.Construct(mRequestCredentials); ErrorResult result; @@ -3821,6 +3842,11 @@ private: request->SetContentPolicyType(mContentPolicyType); + // TODO: remove conditional on http here once app protocol support is + // removed from service worker interception + MOZ_ASSERT_IF(mIsHttpChannel && internalReq->IsNavigationRequest(), + request->Redirect() == RequestRedirect::Manual); + RootedDictionary init(aCx); init.mRequest.Construct(); init.mRequest.Value() = request; diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 0ed10aece55..de539932737 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -333,6 +333,8 @@ ERROR(NS_ERROR_INTERCEPTED_USED_RESPONSE, FAILURE(104)), /* Service worker intercepted a client request with an opaque response */ ERROR(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION, FAILURE(105)), + /* Service worker intercepted a non-navigation with an opaque redirect */ + ERROR(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION, FAILURE(106)), #undef MODULE