Bug 1184607 P4 Handle the RequestRedirect mode during service worker interception. r=nsm

* * *
Bug 1184607 P4 interdiff 001 fix manual redirect assertion for navigations r=nsm
* * *
Bug 1184607 P4 interdiff 002 dom/worker nits
This commit is contained in:
Ben Kelly 2015-08-31 14:26:29 -07:00
parent 3f82333cf8
commit e965d22802
6 changed files with 58 additions and 12 deletions

View File

@ -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);

View File

@ -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) {

View File

@ -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.

View File

@ -130,7 +130,8 @@ public:
return rv;
}
mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(),
mInternalResponse->GetUnfilteredStatusText());
nsAutoTArray<InternalHeaders::Entry, 5> entries;
mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
@ -148,18 +149,21 @@ class RespondWithHandler final : public PromiseNativeHandler
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
RequestMode mRequestMode;
bool mIsClientRequest;
const RequestMode mRequestMode;
const bool mIsClientRequest;
const bool mIsNavigationRequest;
public:
NS_DECL_ISUPPORTS
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& 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<JS::Value> 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<JS::Value> 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<JS::Value> aValu
nsAutoPtr<RespondWithClosure> closure(
new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo()));
nsCOMPtr<nsIInputStream> 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<RespondWithHandler> handler =
new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode(),
ir->IsClientRequest());
ir->IsClientRequest(), ir->IsNavigationRequest());
aArg.AppendNativeHandler(handler);
}

View File

@ -94,6 +94,15 @@ static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(Re
static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
"RequestMode enumeration value should match Necko CORS mode value.");
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow),
"RequestRedirect enumeration value should make Necko Redirect mode value.");
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error),
"RequestRedirect enumeration value should make Necko Redirect mode value.");
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
"RequestRedirect enumeration value should make Necko Redirect mode value.");
static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
"RequestRedirect enumeration value should make Necko Redirect mode value.");
static StaticRefPtr<ServiceWorkerManager> 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<bool> mIsHttpChannel;
RequestMode mRequestMode;
RequestRedirect mRequestRedirect;
RequestCredentials mRequestCredentials;
nsContentPolicyType mContentPolicyType;
nsCOMPtr<nsIInputStream> 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<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (httpChannel) {
mIsHttpChannel = true;
rv = httpChannel->GetRequestMethod(mMethod);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannelInternal> 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<RequestRedirect>(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<FetchEventInit> init(aCx);
init.mRequest.Construct();
init.mRequest.Value() = request;

View File

@ -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