Bug 1255894: Part 1 - Allow applying content conversions in parent rather than child when necessary. r?dragana
In order to allow extensions to monitor and filter response streams, we need
to make sure conversions are applied before the extension listeners, and that
additional conversions are not applied afterwards. This patch adds a channel
method which moves the conversions to the parent process when filters are
about to be applied.
Ideally, we'd offload the conversions to a background thread in this
circumstance, but that's a larger task than fits easily within this bug.
MozReview-Commit-ID: DNSNeFr1OTX
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2961,16 +2961,22 @@ HttpChannelChild::ForceIntercepted(uint6
void
HttpChannelChild::ForceIntercepted(nsIInputStream* aSynthesizedInput)
{
mSynthesizedInput = aSynthesizedInput;
mSynthesizedResponse = true;
mRedirectingForSubsequentSynthesizedResponse = true;
}
+NS_IMETHODIMP
+HttpChannelChild::ApplyContentConversionsInParent()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
mozilla::ipc::IPCResult
HttpChannelChild::RecvIssueDeprecationWarning(const uint32_t& warning,
const bool& asError)
{
nsCOMPtr<nsIDeprecationWarner> warner;
GetCallback(warner);
if (warner) {
warner->IssueWarning(warning, asError);
@@ -3012,10 +3018,18 @@ HttpChannelChild::ShouldInterceptURI(nsI
mozilla::ipc::IPCResult
HttpChannelChild::RecvSetPriority(const int16_t& aPriority)
{
mPriority = aPriority;
return IPC_OK();
}
+mozilla::ipc::IPCResult
+HttpChannelChild::RecvApplyContentConversionsInParent()
+{
+ MOZ_ASSERT(!mCompressListener);
+ mApplyConversion = false;
+ return IPC_OK();
+}
+
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -83,16 +83,17 @@ public:
const nsACString& aValue,
bool aMerge) override;
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
NS_IMETHOD ForceIntercepted(uint64_t aInterceptionID) override;
+ NS_IMETHOD ApplyContentConversionsInParent() override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
// nsIResumableChannel
NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID) override;
@@ -152,16 +153,18 @@ protected:
mozilla::ipc::IPCResult RecvReportSecurityMessage(const nsString& messageTag,
const nsString& messageCategory) override;
mozilla::ipc::IPCResult RecvIssueDeprecationWarning(const uint32_t& warning,
const bool& asError) override;
mozilla::ipc::IPCResult RecvSetPriority(const int16_t& aPriority) override;
+ mozilla::ipc::IPCResult RecvApplyContentConversionsInParent() override;
+
MOZ_MUST_USE bool
GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
virtual void DoNotifyListenerCleanup() override;
NS_IMETHOD GetResponseSynthesized(bool* aSynthesized) override;
private:
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -49,16 +49,17 @@ using namespace mozilla::ipc;
namespace mozilla {
namespace net {
HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mIPCClosed(false)
+ , mApplyConversion(false)
, mIgnoreProgress(false)
, mSentRedirect1Begin(false)
, mSentRedirect1BeginFailed(false)
, mReceivedRedirect2Verify(false)
, mPBOverride(aOverrideStatus)
, mLoadContext(aLoadContext)
, mStatus(NS_OK)
, mPendingDiversion(false)
@@ -1149,17 +1150,17 @@ HttpChannelParent::OnStartRequest(nsIReq
!SendAssociateApplicationCache(appCacheGroupId, appCacheClientId))
{
return NS_ERROR_UNEXPECTED;
}
}
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(aRequest);
if (encodedChannel)
- encodedChannel->SetApplyConversion(false);
+ encodedChannel->SetApplyConversion(mApplyConversion);
// Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
// It could be already released by nsHttpChannel at that time.
nsCOMPtr<nsISupports> cacheEntry;
chan->GetCacheToken(getter_AddRefs(cacheEntry));
mCacheEntry = do_QueryInterface(cacheEntry);
nsresult channelStatus = NS_OK;
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -86,16 +86,17 @@ public:
void StartDiversion();
// Handles calling OnStart/Stop if there are errors during diversion.
// Called asynchronously from FailDiversion.
void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
// Forwarded to nsHttpChannel::SetApplyConversion.
void SetApplyConversion(bool aApplyConversion) {
+ mApplyConversion = aApplyConversion;
if (mChannel) {
mChannel->SetApplyConversion(aApplyConversion);
}
}
MOZ_MUST_USE nsresult OpenAlternativeOutputStream(const nsACString & type,
nsIOutputStream * *_retval);
@@ -214,16 +215,17 @@ private:
friend class DivertDataAvailableEvent;
friend class DivertStopRequestEvent;
friend class DivertCompleteEvent;
RefPtr<nsHttpChannel> mChannel;
nsCOMPtr<nsICacheEntry> mCacheEntry;
nsCOMPtr<nsIAssociatedContentSecurity> mAssociatedContentSecurity;
bool mIPCClosed; // PHttpChannel actor has been Closed()
+ bool mApplyConversion;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;
// OnStatus is always called before OnProgress.
// Set true in OnStatus if next OnProgress can be ignored
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -168,16 +168,19 @@ child:
async DeleteSelf();
// Tell the child to issue a deprecation warning.
async IssueDeprecationWarning(uint32_t warning, bool asError);
// Tell the child information of matched URL againts SafeBrowsing list
async SetClassifierMatchedInfo(ClassifierInfo info);
+ // Tell the child that content conversions will be applied in the parent
+ async ApplyContentConversionsInParent();
+
both:
// After receiving this message, the parent also calls
// SendFinishInterceptedRedirect, and makes sure not to send any more messages
// after that. When receiving this message, the child will call
// Send__delete__() and complete the steps required to finish the redirect.
async FinishInterceptedRedirect();
async SetPriority(int16_t priority);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6220,16 +6220,31 @@ nsHttpChannel::ForceIntercepted(uint64_t
}
MarkIntercepted();
mResponseCouldBeSynthesized = true;
mInterceptionID = aInterceptionID;
return NS_OK;
}
+NS_IMETHODIMP
+nsHttpChannel::ApplyContentConversionsInParent()
+{
+ nsCOMPtr<nsIParentChannel> parentChannel;
+ NS_QueryNotificationCallbacks(this, parentChannel);
+ RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
+ if (httpParent) {
+ if (!httpParent->SendApplyContentConversionsInParent()) {
+ return NS_ERROR_FAILURE;
+ }
+ httpParent->SetApplyConversion(true);
+ }
+ return NS_OK;
+}
+
//-----------------------------------------------------------------------------
// nsHttpChannel::nsISupportsPriority
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::SetPriority(int32_t value)
{
int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX);
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -150,16 +150,17 @@ public:
NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
// nsIHttpChannel
NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
NS_IMETHOD ForceIntercepted(uint64_t aInterceptionID) override;
+ NS_IMETHOD ApplyContentConversionsInParent() override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
// nsIResumableChannel
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -222,16 +222,18 @@ interface nsIHttpChannelInternal : nsISu
* Force a channel that has not been AsyncOpen'ed to skip any check for possible
* interception and proceed immediately to open a previously-synthesized cache
* entry using the provided ID.
*/
[must_use] void forceIntercepted(in uint64_t aInterceptionID);
[must_use] readonly attribute boolean responseSynthesized;
+ [must_use] void applyContentConversionsInParent();
+
/**
* Set by nsCORSListenerProxy if credentials should be included in
* cross-origin requests. false indicates "same-origin", users should still
* check flag LOAD_ANONYMOUS!
*/
[must_use] attribute boolean corsIncludeCredentials;
const unsigned long CORS_MODE_SAME_ORIGIN = 0;