|
@@ -0,0 +1,401 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Tom Tung <shes050117@gmail.com>
|
|
|
+# Date 1509522335 -28800
|
|
|
+# Node ID e5aff04d76e0818e9b1f27ec5a76ba9fd6793607
|
|
|
+# Parent cc3e67d904b2d515be4d01ce90e4a7f6528c1963
|
|
|
+Bug 1222008 - P1: Propagate the URLs from respondWith() if the response's URL is different from the request's. r=bkelly
|
|
|
+
|
|
|
+diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
|
|
|
+--- a/dom/workers/ServiceWorkerEvents.cpp
|
|
|
++++ b/dom/workers/ServiceWorkerEvents.cpp
|
|
|
+@@ -337,18 +337,18 @@ public:
|
|
|
+ auto castLoadInfo = static_cast<LoadInfo*>(loadInfo.get());
|
|
|
+ castLoadInfo->SynthesizeServiceWorkerTainting(mInternalResponse->GetTainting());
|
|
|
+
|
|
|
+ nsCOMPtr<nsIInputStream> body;
|
|
|
+ mInternalResponse->GetUnfilteredBody(getter_AddRefs(body));
|
|
|
+ RefPtr<BodyCopyHandle> copyHandle;
|
|
|
+ copyHandle = new BodyCopyHandle(Move(mClosure));
|
|
|
+
|
|
|
+- rv = mChannel->StartSynthesizedResponse(body, copyHandle,
|
|
|
+- mResponseURLSpec);
|
|
|
++ rv = mChannel->StartSynthesizedResponse(body, copyHandle, mResponseURLSpec,
|
|
|
++ mInternalResponse->IsRedirected());
|
|
|
+ if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
+ mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
|
|
|
+ return NS_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
|
|
+ if (obsService) {
|
|
|
+ obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
|
|
|
+@@ -653,29 +653,37 @@ RespondWithHandler::ResolvedCallback(JSC
|
|
|
+ NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ RefPtr<InternalResponse> ir = response->GetInternalResponse();
|
|
|
+ if (NS_WARN_IF(!ir)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+- // When an opaque response is encountered, we need the original channel's principal
|
|
|
+- // to reflect the final URL. Non-opaque responses are either same-origin or CORS-enabled
|
|
|
+- // cross-origin responses, which are treated as same-origin by consumers.
|
|
|
+- nsCString responseURL;
|
|
|
+- if (response->Type() == ResponseType::Opaque) {
|
|
|
+- responseURL = ir->GetUnfilteredURL();
|
|
|
+- if (NS_WARN_IF(responseURL.IsEmpty())) {
|
|
|
+- return;
|
|
|
+- }
|
|
|
++
|
|
|
++ // An extra safety check to make sure our invariant that opaque and cors
|
|
|
++ // responses always have a URL does not break.
|
|
|
++ if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
|
|
|
++ response->Type() == ResponseType::Cors) &&
|
|
|
++ ir->GetUnfilteredURL().IsEmpty())) {
|
|
|
++ MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL");
|
|
|
++ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mRequestMode == RequestMode::Same_origin &&
|
|
|
+ response->Type() == ResponseType::Cors) {
|
|
|
++ // XXXtt: quirkResponse, will be implement in the follow-up patch.
|
|
|
++ }
|
|
|
++
|
|
|
++ // Propagate the URL to the content if the request mode is not "navigate".
|
|
|
++ // Note that, we only reflect the final URL if the response.redirected is
|
|
|
++ // false. We propagate all the URLs if the response.redirected is true.
|
|
|
++ nsCString responseURL;
|
|
|
++ if (mRequestMode != RequestMode::Navigate) {
|
|
|
++ responseURL = ir->GetUnfilteredURL();
|
|
|
+ }
|
|
|
+
|
|
|
+ UniquePtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel,
|
|
|
+ mRegistration,
|
|
|
+ mRequestURL,
|
|
|
+ mRespondWithScriptSpec,
|
|
|
+ mRespondWithLineNumber,
|
|
|
+ mRespondWithColumnNumber));
|
|
|
+diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
|
|
|
+--- a/netwerk/base/nsINetworkInterceptController.idl
|
|
|
++++ b/netwerk/base/nsINetworkInterceptController.idl
|
|
|
+@@ -73,20 +73,25 @@ interface nsIInterceptedChannel : nsISup
|
|
|
+ * empty body is assumed.
|
|
|
+ * - A callback may be optionally passed. It will be invoked
|
|
|
+ * when the body is complete. For a nullptr body this may be
|
|
|
+ * synchronously on the current thread. Otherwise it will be invoked
|
|
|
+ * asynchronously on the current thread.
|
|
|
+ * - The caller may optionally pass a spec for a URL that this response
|
|
|
+ * originates from; an empty string will cause the original
|
|
|
+ * intercepted request's URL to be used instead.
|
|
|
++ * - The responseRedirected flag is false will cause the channel do an
|
|
|
++ * internal redirect when the original intercepted reauest's URL is
|
|
|
++ * different from the response's URL. The flag is true will cause the
|
|
|
++ * chaanel do a non-internal redirect when the URLs are different.
|
|
|
+ */
|
|
|
+ void startSynthesizedResponse(in nsIInputStream body,
|
|
|
+ in nsIInterceptedBodyCallback callback,
|
|
|
+- in ACString finalURLSpec);
|
|
|
++ in ACString finalURLSpec,
|
|
|
++ in bool responseRedirected);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Instruct a channel that has been intercepted that response synthesis
|
|
|
+ * has completed and all outstanding resources can be closed.
|
|
|
+ */
|
|
|
+ void finishSynthesizedResponse();
|
|
|
+
|
|
|
+ /**
|
|
|
+diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
|
|
|
+--- a/netwerk/protocol/http/HttpChannelChild.cpp
|
|
|
++++ b/netwerk/protocol/http/HttpChannelChild.cpp
|
|
|
+@@ -1821,24 +1821,33 @@ HttpChannelChild::Redirect1Begin(const u
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NS_FAILED(rv))
|
|
|
+ OnRedirectVerifyCallback(rv);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ HttpChannelChild::BeginNonIPCRedirect(nsIURI* responseURI,
|
|
|
+- const nsHttpResponseHead* responseHead)
|
|
|
++ const nsHttpResponseHead* responseHead,
|
|
|
++ bool aResponseRedirected)
|
|
|
+ {
|
|
|
+ LOG(("HttpChannelChild::BeginNonIPCRedirect [this=%p]\n", this));
|
|
|
+
|
|
|
++ // If the response has been redirected, propagate all the URLs to content.
|
|
|
++ // Thus, the exact value of the redirect flag does not matter as long as it's
|
|
|
++ // not REDIRECT_INTERNAL.
|
|
|
++ const uint32_t redirectFlag =
|
|
|
++ aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY
|
|
|
++ : nsIChannelEventSink::REDIRECT_INTERNAL;
|
|
|
++
|
|
|
++
|
|
|
+ nsCOMPtr<nsIChannel> newChannel;
|
|
|
+ nsresult rv = SetupRedirect(responseURI,
|
|
|
+ responseHead,
|
|
|
+- nsIChannelEventSink::REDIRECT_INTERNAL,
|
|
|
++ redirectFlag,
|
|
|
+ getter_AddRefs(newChannel));
|
|
|
+
|
|
|
+ if (NS_SUCCEEDED(rv)) {
|
|
|
+ // Ensure that the new channel shares the original channel's security information,
|
|
|
+ // since it won't be provided via IPC. In particular, if the target of this redirect
|
|
|
+ // is a synthesized response that has its own security info, the pre-redirect channel
|
|
|
+ // has already received it and it must be propagated to the post-redirect channel.
|
|
|
+ nsCOMPtr<nsIHttpChannelChild> channelChild = do_QueryInterface(newChannel);
|
|
|
+@@ -1847,17 +1856,17 @@ HttpChannelChild::BeginNonIPCRedirect(ns
|
|
|
+ httpChannelChild->OverrideSecurityInfoForNonIPCRedirect(mSecurityInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
|
|
|
+ MOZ_ASSERT(target);
|
|
|
+
|
|
|
+ rv = gHttpHandler->AsyncOnChannelRedirect(this,
|
|
|
+ newChannel,
|
|
|
+- nsIChannelEventSink::REDIRECT_INTERNAL,
|
|
|
++ redirectFlag,
|
|
|
+ target);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NS_FAILED(rv))
|
|
|
+ OnRedirectVerifyCallback(rv);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h
|
|
|
+--- a/netwerk/protocol/http/HttpChannelChild.h
|
|
|
++++ b/netwerk/protocol/http/HttpChannelChild.h
|
|
|
+@@ -429,17 +429,18 @@ private:
|
|
|
+ // response headers.
|
|
|
+ MOZ_MUST_USE nsresult SetupRedirect(nsIURI* uri,
|
|
|
+ const nsHttpResponseHead* responseHead,
|
|
|
+ const uint32_t& redirectFlags,
|
|
|
+ nsIChannel** outChannel);
|
|
|
+
|
|
|
+ // Perform a redirection without communicating with the parent process at all.
|
|
|
+ void BeginNonIPCRedirect(nsIURI* responseURI,
|
|
|
+- const nsHttpResponseHead* responseHead);
|
|
|
++ const nsHttpResponseHead* responseHead,
|
|
|
++ bool responseRedirected);
|
|
|
+
|
|
|
+ // Override the default security info pointer during a non-IPC redirection.
|
|
|
+ void OverrideSecurityInfoForNonIPCRedirect(nsISupports* securityInfo);
|
|
|
+
|
|
|
+ friend class AssociateApplicationCacheEvent;
|
|
|
+ friend class StartRequestEvent;
|
|
|
+ friend class StopRequestEvent;
|
|
|
+ friend class TransportAndDataEvent;
|
|
|
+diff --git a/netwerk/protocol/http/HttpChannelParentListener.cpp b/netwerk/protocol/http/HttpChannelParentListener.cpp
|
|
|
+--- a/netwerk/protocol/http/HttpChannelParentListener.cpp
|
|
|
++++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
|
|
|
+@@ -315,17 +315,17 @@ public:
|
|
|
+ , mChannel(aChannel)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ NS_IMETHOD Run() override
|
|
|
+ {
|
|
|
+ // The URL passed as an argument here doesn't matter, since the child will
|
|
|
+ // receive a redirection notification as a result of this synthesized response.
|
|
|
+- mChannel->StartSynthesizedResponse(nullptr, nullptr, EmptyCString());
|
|
|
++ mChannel->StartSynthesizedResponse(nullptr, nullptr, EmptyCString(), false);
|
|
|
+ mChannel->FinishSynthesizedResponse();
|
|
|
+ return NS_OK;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ NS_IMETHODIMP
|
|
|
+ HttpChannelParentListener::ChannelIntercepted(nsIInterceptedChannel* aChannel)
|
|
|
+ {
|
|
|
+diff --git a/netwerk/protocol/http/InterceptedChannel.cpp b/netwerk/protocol/http/InterceptedChannel.cpp
|
|
|
+--- a/netwerk/protocol/http/InterceptedChannel.cpp
|
|
|
++++ b/netwerk/protocol/http/InterceptedChannel.cpp
|
|
|
+@@ -254,17 +254,18 @@ InterceptedChannelContent::SynthesizeHea
|
|
|
+ }
|
|
|
+
|
|
|
+ return DoSynthesizeHeader(aName, aValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ NS_IMETHODIMP
|
|
|
+ InterceptedChannelContent::StartSynthesizedResponse(nsIInputStream* aBody,
|
|
|
+ nsIInterceptedBodyCallback* aBodyCallback,
|
|
|
+- const nsACString& aFinalURLSpec)
|
|
|
++ const nsACString& aFinalURLSpec,
|
|
|
++ bool aResponseRedirected)
|
|
|
+ {
|
|
|
+ if (NS_WARN_IF(mClosed)) {
|
|
|
+ return NS_ERROR_NOT_AVAILABLE;
|
|
|
+ }
|
|
|
+
|
|
|
+ EnsureSynthesizedResponse();
|
|
|
+
|
|
|
+ nsCOMPtr<nsIURI> originalURI;
|
|
|
+@@ -281,17 +282,18 @@ InterceptedChannelContent::StartSynthesi
|
|
|
+ } else {
|
|
|
+ responseURI = originalURI;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool equal = false;
|
|
|
+ originalURI->Equals(responseURI, &equal);
|
|
|
+ if (!equal) {
|
|
|
+ mChannel->ForceIntercepted(aBody, aBodyCallback);
|
|
|
+- mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr());
|
|
|
++ mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr(),
|
|
|
++ aResponseRedirected);
|
|
|
+ } else {
|
|
|
+ mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ref(),
|
|
|
+ aBody, aBodyCallback,
|
|
|
+ mStreamListener);
|
|
|
+ }
|
|
|
+
|
|
|
+ return NS_OK;
|
|
|
+ }
|
|
|
+diff --git a/netwerk/protocol/http/InterceptedChannel.h b/netwerk/protocol/http/InterceptedChannel.h
|
|
|
+--- a/netwerk/protocol/http/InterceptedChannel.h
|
|
|
++++ b/netwerk/protocol/http/InterceptedChannel.h
|
|
|
+@@ -177,17 +177,18 @@ public:
|
|
|
+ InterceptedChannelContent(HttpChannelChild* aChannel,
|
|
|
+ nsINetworkInterceptController* aController,
|
|
|
+ InterceptStreamListener* aListener,
|
|
|
+ bool aSecureUpgrade);
|
|
|
+
|
|
|
+ NS_IMETHOD ResetInterception() override;
|
|
|
+ NS_IMETHOD StartSynthesizedResponse(nsIInputStream* aBody,
|
|
|
+ nsIInterceptedBodyCallback* aBodyCallback,
|
|
|
+- const nsACString& aFinalURLSpec) override;
|
|
|
++ const nsACString& aFinalURLSpec,
|
|
|
++ bool aResponseRedirected) override;
|
|
|
+ NS_IMETHOD FinishSynthesizedResponse() override;
|
|
|
+ NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
|
|
|
+ NS_IMETHOD GetSecureUpgradedChannelURI(nsIURI** aURI) override;
|
|
|
+ NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
|
|
|
+ NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
|
|
|
+ NS_IMETHOD CancelInterception(nsresult aStatus) override;
|
|
|
+ NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
|
|
|
+ NS_IMETHOD GetInternalContentPolicyType(nsContentPolicyType *aInternalContentPolicyType) override;
|
|
|
+diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp
|
|
|
+--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
|
|
|
++++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
|
|
|
+@@ -228,23 +228,25 @@ InterceptedHttpChannel::FollowSyntheticR
|
|
|
+ if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
+ OnRedirectVerifyCallback(rv);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rv;
|
|
|
+ }
|
|
|
+
|
|
|
+ nsresult
|
|
|
+-InterceptedHttpChannel::RedirectForOpaqueResponse(nsIURI* aResponseURI)
|
|
|
++InterceptedHttpChannel::RedirectForResponseURL(nsIURI* aResponseURI,
|
|
|
++ bool aResponseRedirected)
|
|
|
+ {
|
|
|
+- // Perform an internal redirect to another InterceptedHttpChannel using
|
|
|
+- // the given cross-origin response URL. The resulting channel will then
|
|
|
+- // process the synthetic response as normal. This extra redirect is
|
|
|
+- // performed so that listeners treat the result as unsafe cross-origin
|
|
|
+- // data.
|
|
|
++ // Perform a service worker redirect to another InterceptedHttpChannel using
|
|
|
++ // the given response URL. It allows content to see the final URL where
|
|
|
++ // appropriate and also helps us enforce cross-origin restrictions. The
|
|
|
++ // resulting channel will then process the synthetic response as normal. This
|
|
|
++ // extra redirect is performed so that listeners treat the result as unsafe
|
|
|
++ // cross-origin data.
|
|
|
+
|
|
|
+ nsresult rv = NS_OK;
|
|
|
+
|
|
|
+ // We want to pass ownership of the body callback to the new synthesized
|
|
|
+ // channel. We need to hold a reference to the callbacks on the stack
|
|
|
+ // as well, though, so we can call them if a failure occurs.
|
|
|
+ nsCOMPtr<nsIInterceptedBodyCallback> bodyCallback = mBodyCallback.forget();
|
|
|
+
|
|
|
+@@ -252,17 +254,21 @@ InterceptedHttpChannel::RedirectForOpaqu
|
|
|
+ CreateForSynthesis(mResponseHead, mBodyReader, bodyCallback,
|
|
|
+ mChannelCreationTime, mChannelCreationTimestamp,
|
|
|
+ mAsyncOpenTime);
|
|
|
+
|
|
|
+ rv = newChannel->Init(aResponseURI, mCaps,
|
|
|
+ static_cast<nsProxyInfo*>(mProxyInfo.get()),
|
|
|
+ mProxyResolveFlags, mProxyURI, mChannelId);
|
|
|
+
|
|
|
+- uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
|
|
|
++ // If the response has been redirected, propagate all the URLs to content.
|
|
|
++ // Thus, the exact value of the redirect flag does not matter as long as it's
|
|
|
++ // not REDIRECT_INTERNAL.
|
|
|
++ uint32_t flags = aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY
|
|
|
++ : nsIChannelEventSink::REDIRECT_INTERNAL;
|
|
|
+
|
|
|
+ nsCOMPtr<nsILoadInfo> redirectLoadInfo =
|
|
|
+ CloneLoadInfoForRedirect(aResponseURI, flags);
|
|
|
+ newChannel->SetLoadInfo(redirectLoadInfo);
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+
|
|
|
+ rv = SetupReplacementChannel(aResponseURI, newChannel, true, flags);
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+@@ -725,17 +731,18 @@ InterceptedHttpChannel::SynthesizeHeader
|
|
|
+ nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header);
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+ return NS_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ NS_IMETHODIMP
|
|
|
+ InterceptedHttpChannel::StartSynthesizedResponse(nsIInputStream* aBody,
|
|
|
+ nsIInterceptedBodyCallback* aBodyCallback,
|
|
|
+- const nsACString& aFinalURLSpec)
|
|
|
++ const nsACString& aFinalURLSpec,
|
|
|
++ bool aResponseRedirected)
|
|
|
+ {
|
|
|
+ nsresult rv = NS_OK;
|
|
|
+
|
|
|
+ auto autoCleanup = MakeScopeExit([&] {
|
|
|
+ // Auto-cancel on failure. Do this first to get mStatus set, if necessary.
|
|
|
+ if (NS_FAILED(rv)) {
|
|
|
+ Cancel(rv);
|
|
|
+ }
|
|
|
+@@ -791,17 +798,17 @@ InterceptedHttpChannel::StartSynthesized
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+ } else {
|
|
|
+ responseURI = mURI;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool equal = false;
|
|
|
+ Unused << mURI->Equals(responseURI, &equal);
|
|
|
+ if (!equal) {
|
|
|
+- rv = RedirectForOpaqueResponse(responseURI);
|
|
|
++ rv = RedirectForResponseURL(responseURI, aResponseRedirected);
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+
|
|
|
+ return NS_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ rv = StartPump();
|
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
|
+
|
|
|
+diff --git a/netwerk/protocol/http/InterceptedHttpChannel.h b/netwerk/protocol/http/InterceptedHttpChannel.h
|
|
|
+--- a/netwerk/protocol/http/InterceptedHttpChannel.h
|
|
|
++++ b/netwerk/protocol/http/InterceptedHttpChannel.h
|
|
|
+@@ -109,18 +109,22 @@ private:
|
|
|
+ AsyncOpenInternal();
|
|
|
+
|
|
|
+ bool
|
|
|
+ ShouldRedirect() const;
|
|
|
+
|
|
|
+ nsresult
|
|
|
+ FollowSyntheticRedirect();
|
|
|
+
|
|
|
++ // If the response's URL is different from the request's then do a service
|
|
|
++ // worker redirect. If Response.redirected is false we do an internal
|
|
|
++ // redirect. Otherwise, if Response.redirect is true do a non-internal
|
|
|
++ // redirect so end consumers detect the redirected state.
|
|
|
+ nsresult
|
|
|
+- RedirectForOpaqueResponse(nsIURI* aResponseURI);
|
|
|
++ RedirectForResponseURL(nsIURI* aResponseURI, bool aResponseRedirected);
|
|
|
+
|
|
|
+ nsresult
|
|
|
+ StartPump();
|
|
|
+
|
|
|
+ nsresult
|
|
|
+ OpenRedirectChannel();
|
|
|
+
|
|
|
+ void
|