diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index b15386074cb..119bdcc288b 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -206,8 +206,9 @@ InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aConte return context; } +// static bool -InternalRequest::IsNavigationRequest() const +InternalRequest::IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType) { // https://fetch.spec.whatwg.org/#navigation-request-context // @@ -215,17 +216,22 @@ InternalRequest::IsNavigationRequest() const // "iframe", "internal" (as long as context frame type is not "none"), // "location", "metarefresh", and "prerender". // - // TODO: include equivalent check for "form" context - // TODO: include equivalent check for "prerender" context - return mContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || - mContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT || - mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME || - mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME || - mContentPolicyType == nsIContentPolicy::TYPE_REFRESH; + // Note, all of these request types are effectively initiated by nsDocShell. + // + // The TYPE_REFRESH is used in some code paths for metarefresh, but will not + // be seen during the actual load. Instead the new load gets a normal + // nsDocShell policy type. We include it here in case this utility method + // is called before the load starts. + return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT || + aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME || + aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME || + aContentPolicyType == nsIContentPolicy::TYPE_REFRESH; } +// static bool -InternalRequest::IsWorkerRequest() const +InternalRequest::IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType) { // https://fetch.spec.whatwg.org/#worker-request-context // @@ -235,8 +241,20 @@ InternalRequest::IsWorkerRequest() const // Note, service workers are not included here because currently there is // no way to generate a Request with a "serviceworker" RequestContext. // ServiceWorker scripts cannot be intercepted. - return mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER || - mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER; + return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER || + aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER; +} + +bool +InternalRequest::IsNavigationRequest() const +{ + return IsNavigationContentPolicy(mContentPolicyType); +} + +bool +InternalRequest::IsWorkerRequest() const +{ + return IsWorkerContentPolicy(mContentPolicyType); } bool @@ -245,5 +263,63 @@ InternalRequest::IsClientRequest() const return IsNavigationRequest() || IsWorkerRequest(); } +// static +RequestMode +InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel) +{ + MOZ_ASSERT(aChannel); + + nsCOMPtr loadInfo; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aChannel->GetLoadInfo(getter_AddRefs(loadInfo)))); + + // RequestMode deviates from our internal security mode for navigations. + // While navigations normally allow cross origin we must set a same-origin + // RequestMode to get the correct service worker interception restrictions + // in place. + // TODO: remove the worker override once securityMode is fully implemented (bug 1189945) + nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType(); + if (IsNavigationContentPolicy(contentPolicy) || + IsWorkerContentPolicy(contentPolicy)) { + return RequestMode::Same_origin; + } + + uint32_t securityMode; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSecurityMode(&securityMode))); + + switch(securityMode) { + case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS: + case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED: + return RequestMode::Same_origin; + case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS: + case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL: + return RequestMode::No_cors; + case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS: + // TODO: Check additional flag force-preflight after bug 1199693 (bug 1189945) + return RequestMode::Cors; + default: + // TODO: assert never reached after CorsMode flag removed (bug 1189945) + MOZ_ASSERT(securityMode == nsILoadInfo::SEC_NORMAL); + break; + } + + // TODO: remove following code once securityMode is fully implemented (bug 1189945) + + // We only support app:// protocol interception in non-release builds. +#ifndef RELEASE_BUILD + nsCOMPtr jarChannel = do_QueryInterface(aChannel); + if (jarChannel) { + return RequestMode::No_cors; + } +#endif + + nsCOMPtr httpChannel = do_QueryInterface(aChannel); + + uint32_t corsMode; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(httpChannel->GetCorsMode(&corsMode))); + + // This cast is valid due to static asserts in ServiceWorkerManager.cpp. + return static_cast(corsMode); +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index 77258ae85dc..caa005756bc 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -377,6 +377,8 @@ public: bool IsClientRequest() const; + static RequestMode + MapChannelToRequestMode(nsIChannel* aChannel); private: // Does not copy mBodyStream. Use fallible Clone() for complete copy. @@ -387,6 +389,12 @@ private: static RequestContext MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType); + static bool + IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType); + + static bool + IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType); + nsCString mMethod; // mURL always stores the url with the ref stripped nsCString mURL; diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 72a57162da1..32a5301f457 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -150,7 +150,7 @@ class RespondWithHandler final : public PromiseNativeHandler nsMainThreadPtrHandle mInterceptedChannel; nsMainThreadPtrHandle mServiceWorker; const RequestMode mRequestMode; - const bool mIsClientRequest; + const DebugOnly mIsClientRequest; const bool mIsNavigationRequest; public: NS_DECL_ISUPPORTS @@ -272,8 +272,6 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu // 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". @@ -282,19 +280,13 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } + MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin); + if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE); 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::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; diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 1ed17dfe20c..59f77ff41d8 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -3709,22 +3709,7 @@ public: nsCOMPtr internalChannel = do_QueryInterface(httpChannel); NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); - uint32_t corsMode; - internalChannel->GetCorsMode(&corsMode); - switch (corsMode) { - case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN: - mRequestMode = RequestMode::Same_origin; - break; - case nsIHttpChannelInternal::CORS_MODE_NO_CORS: - mRequestMode = RequestMode::No_cors; - break; - case nsIHttpChannelInternal::CORS_MODE_CORS: - case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT: - mRequestMode = RequestMode::Cors; - break; - default: - MOZ_CRASH("Unexpected CORS mode"); - } + mRequestMode = InternalRequest::MapChannelToRequestMode(channel); // This is safe due to static_asserts at top of file. uint32_t redirectMode; @@ -3757,6 +3742,8 @@ public: mMethod = "GET"; + mRequestMode = InternalRequest::MapChannelToRequestMode(channel); + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { mRequestCredentials = RequestCredentials::Omit; }