зеркало из https://github.com/mozilla/gecko-dev.git
336 строки
13 KiB
C
336 строки
13 KiB
C
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
||
|
|
||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
|
||
|
#ifndef mozilla_net_DocumentLoadListener_h
|
||
|
#define mozilla_net_DocumentLoadListener_h
|
||
|
|
||
|
#include "mozilla/MozPromise.h"
|
||
|
#include "mozilla/Variant.h"
|
||
|
#include "mozilla/net/NeckoCommon.h"
|
||
|
#include "mozilla/net/NeckoParent.h"
|
||
|
#include "mozilla/net/PDocumentChannelParent.h"
|
||
|
#include "mozilla/net/ParentChannelListener.h"
|
||
|
#include "mozilla/net/ADocumentChannelBridge.h"
|
||
|
#include "nsIInterfaceRequestor.h"
|
||
|
#include "nsIObserver.h"
|
||
|
#include "nsIParentChannel.h"
|
||
|
#include "nsIParentRedirectingChannel.h"
|
||
|
#include "nsIProcessSwitchRequestor.h"
|
||
|
#include "nsIRedirectResultListener.h"
|
||
|
|
||
|
#define DOCUMENT_LOAD_LISTENER_IID \
|
||
|
{ \
|
||
|
0x3b393c56, 0x9e01, 0x11e9, { \
|
||
|
0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
namespace mozilla {
|
||
|
namespace net {
|
||
|
|
||
|
/**
|
||
|
* DocumentLoadListener represents a connecting document load for a
|
||
|
* CanonicalBrowsingContext (in the parent process).
|
||
|
*
|
||
|
* It creates a network channel for the document load and then waits for it to
|
||
|
* receive a response (after all redirects are resolved). It then decides where
|
||
|
* to handle that load (could be in a different process from the initiator),
|
||
|
* and then sets up a real network nsIChannel to deliver the data to the final
|
||
|
* destination docshell, maybe through an nsIParentChannel/nsIChildChannel IPDL
|
||
|
* layer.
|
||
|
*
|
||
|
* In the case where this was initiated from an nsDocShell, we also create an
|
||
|
* nsIChannel to act as a placeholder within the docshell while this process
|
||
|
* completes, and then notify the docshell of a 'redirect' when we replace this
|
||
|
* channel with the real one.
|
||
|
*/
|
||
|
|
||
|
// TODO: We currently don't implement nsIProgressEventSink and forward those
|
||
|
// to the child. Should we? We get the interface requested.
|
||
|
class DocumentLoadListener : public nsIInterfaceRequestor,
|
||
|
public nsIAsyncVerifyRedirectReadyCallback,
|
||
|
public nsIParentChannel,
|
||
|
public nsIChannelEventSink,
|
||
|
public HttpChannelSecurityWarningReporter,
|
||
|
public nsIProcessSwitchRequestor {
|
||
|
public:
|
||
|
explicit DocumentLoadListener(const dom::PBrowserOrId& aIframeEmbedding,
|
||
|
nsILoadContext* aLoadContext,
|
||
|
PBOverrideStatus aOverrideStatus,
|
||
|
ADocumentChannelBridge* aBridge);
|
||
|
|
||
|
// Creates the channel, and then calls AsyncOpen on it.
|
||
|
bool Open(nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
|
||
|
const nsString* aInitiatorType, nsLoadFlags aLoadFlags,
|
||
|
uint32_t aLoadType, uint32_t aCacheKey, bool aIsActive,
|
||
|
bool aIsTopLevelDoc, bool aHasNonEmptySandboxingFlags,
|
||
|
const Maybe<ipc::URIParams>& aTopWindowURI,
|
||
|
const Maybe<ipc::PrincipalInfo>& aContentBlockingAllowListPrincipal,
|
||
|
const nsString& aCustomUserAgent, const uint64_t& aChannelId,
|
||
|
const TimeStamp& aAsyncOpenTime, nsresult* aRv);
|
||
|
|
||
|
NS_DECL_ISUPPORTS
|
||
|
NS_DECL_NSIREQUESTOBSERVER
|
||
|
NS_DECL_NSISTREAMLISTENER
|
||
|
NS_DECL_NSIPARENTCHANNEL
|
||
|
NS_DECL_NSIINTERFACEREQUESTOR
|
||
|
NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
|
||
|
NS_DECL_NSICHANNELEVENTSINK
|
||
|
NS_DECL_NSIPROCESSSWITCHREQUESTOR
|
||
|
|
||
|
// We suspend the underlying channel when replacing ourselves with
|
||
|
// the real listener channel.
|
||
|
// This helper resumes the underlying channel again, and manually
|
||
|
// forwards any nsIStreamListener messages that arrived while we
|
||
|
// were suspended (which might have failed).
|
||
|
void ResumeSuspendedChannel(nsIStreamListener* aListener);
|
||
|
|
||
|
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_LOAD_LISTENER_IID)
|
||
|
|
||
|
void Cancel(const nsresult& status);
|
||
|
void Suspend();
|
||
|
void Resume();
|
||
|
|
||
|
nsresult ReportSecurityMessage(const nsAString& aMessageTag,
|
||
|
const nsAString& aMessageCategory) override {
|
||
|
ReportSecurityMessageParams params;
|
||
|
params.mMessageTag = aMessageTag;
|
||
|
params.mMessageCategory = aMessageCategory;
|
||
|
mSecurityWarningFunctions.AppendElement(
|
||
|
SecurityWarningFunction{VariantIndex<0>{}, std::move(params)});
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult LogBlockedCORSRequest(const nsAString& aMessage,
|
||
|
const nsACString& aCategory) override {
|
||
|
LogBlockedCORSRequestParams params;
|
||
|
params.mMessage = aMessage;
|
||
|
params.mCategory = aCategory;
|
||
|
mSecurityWarningFunctions.AppendElement(
|
||
|
SecurityWarningFunction{VariantIndex<1>{}, std::move(params)});
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
|
||
|
const nsAString& aURL,
|
||
|
const nsAString& aContentType) override {
|
||
|
LogMimeTypeMismatchParams params;
|
||
|
params.mMessageName = aMessageName;
|
||
|
params.mWarning = aWarning;
|
||
|
params.mURL = aURL;
|
||
|
params.mContentType = aContentType;
|
||
|
mSecurityWarningFunctions.AppendElement(
|
||
|
SecurityWarningFunction{VariantIndex<2>{}, std::move(params)});
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
// Called by the bridge when it disconnects, so that we can drop
|
||
|
// our reference to it.
|
||
|
void DocumentChannelBridgeDisconnected();
|
||
|
|
||
|
base::ProcessId OtherPid() const {
|
||
|
if (mDocumentChannelBridge) {
|
||
|
return mDocumentChannelBridge->OtherPid();
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool AttachStreamFilter(
|
||
|
ipc::Endpoint<mozilla::extensions::PStreamFilterParent>&& aEndpoint) {
|
||
|
if (mDocumentChannelBridge) {
|
||
|
return mDocumentChannelBridge->AttachStreamFilter(std::move(aEndpoint));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Serializes all data needed to setup the new replacement channel
|
||
|
// in the content process into the RedirectToRealChannelArgs struct.
|
||
|
void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
|
||
|
bool aIsCrossProcess, uint32_t aRedirectFlags,
|
||
|
uint32_t aLoadFlags);
|
||
|
|
||
|
protected:
|
||
|
virtual ~DocumentLoadListener() = default;
|
||
|
|
||
|
// Initiates the switch from DocumentChannel to the real protocol-specific
|
||
|
// channel, and ensures that RedirectToRealChannelFinished is called when
|
||
|
// this is complete.
|
||
|
void TriggerRedirectToRealChannel(
|
||
|
const Maybe<uint64_t>& aDestinationProcess = Nothing());
|
||
|
|
||
|
// Called once the content-process side on setting up a replacement
|
||
|
// channel is complete. May wait for the new parent channel to
|
||
|
// finish, and then calls into FinishReplacementChannelSetup.
|
||
|
void RedirectToRealChannelFinished(nsresult aRv);
|
||
|
|
||
|
// Completes the replacement of the new channel.
|
||
|
// This redirects the ParentChannelListener to forward any future
|
||
|
// messages to the new channel, manually forwards any being held
|
||
|
// by us, and resumes the underlying source channel.
|
||
|
void FinishReplacementChannelSetup(bool aSucceeded);
|
||
|
|
||
|
// Called when we have a cross-process switch promise. Waits on the
|
||
|
// promise, and then call TriggerRedirectToRealChannel with the
|
||
|
// provided content process id.
|
||
|
void TriggerCrossProcessSwitch();
|
||
|
|
||
|
// A helper for TriggerRedirectToRealChannel that abstracts over
|
||
|
// the same-process and cross-process switch cases and returns
|
||
|
// a single promise to wait on.
|
||
|
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
|
||
|
RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
|
||
|
const Maybe<uint64_t>& aDestinationProcess);
|
||
|
|
||
|
// This defines a variant that describes all the attribute setters (and their
|
||
|
// parameters) from nsIParentChannel
|
||
|
//
|
||
|
// NotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState) = 0;
|
||
|
// SetClassifierMatchedInfo(const nsACString& aList, const nsACString&
|
||
|
// aProvider, const nsACString& aFullHash) = 0;
|
||
|
// SetClassifierMatchedTrackingInfo(const nsACString& aLists, const
|
||
|
// nsACString& aFullHashes) = 0; NotifyClassificationFlags(uint32_t
|
||
|
// aClassificationFlags, bool aIsThirdParty) = 0;
|
||
|
struct ClassifierMatchedInfoParams {
|
||
|
nsCString mList;
|
||
|
nsCString mProvider;
|
||
|
nsCString mFullHash;
|
||
|
};
|
||
|
|
||
|
struct ClassifierMatchedTrackingInfoParams {
|
||
|
nsCString mLists;
|
||
|
nsCString mFullHashes;
|
||
|
};
|
||
|
|
||
|
struct ClassificationFlagsParams {
|
||
|
uint32_t mClassificationFlags;
|
||
|
bool mIsThirdParty;
|
||
|
};
|
||
|
|
||
|
struct NotifyChannelClassifierProtectionDisabledParams {
|
||
|
uint32_t mAcceptedReason;
|
||
|
};
|
||
|
|
||
|
struct NotifyCookieAllowedParams {};
|
||
|
|
||
|
struct NotifyCookieBlockedParams {
|
||
|
uint32_t mRejectedReason;
|
||
|
};
|
||
|
|
||
|
typedef mozilla::Variant<
|
||
|
nsIHttpChannel::FlashPluginState, ClassifierMatchedInfoParams,
|
||
|
ClassifierMatchedTrackingInfoParams, ClassificationFlagsParams,
|
||
|
NotifyChannelClassifierProtectionDisabledParams,
|
||
|
NotifyCookieAllowedParams, NotifyCookieBlockedParams>
|
||
|
IParentChannelFunction;
|
||
|
|
||
|
// Store a list of all the attribute setters that have been called on this
|
||
|
// channel, so that we can repeat them on the real channel that we redirect
|
||
|
// to.
|
||
|
nsTArray<IParentChannelFunction> mIParentChannelFunctions;
|
||
|
|
||
|
// This defines a variant this describes all the functions
|
||
|
// from HttpChannelSecurityWarningReporter so that we can forward
|
||
|
// them on to the real channel.
|
||
|
|
||
|
struct ReportSecurityMessageParams {
|
||
|
nsString mMessageTag;
|
||
|
nsString mMessageCategory;
|
||
|
};
|
||
|
|
||
|
struct LogBlockedCORSRequestParams {
|
||
|
nsString mMessage;
|
||
|
nsCString mCategory;
|
||
|
};
|
||
|
|
||
|
struct LogMimeTypeMismatchParams {
|
||
|
nsCString mMessageName;
|
||
|
bool mWarning;
|
||
|
nsString mURL;
|
||
|
nsString mContentType;
|
||
|
};
|
||
|
|
||
|
typedef mozilla::Variant<ReportSecurityMessageParams,
|
||
|
LogBlockedCORSRequestParams,
|
||
|
LogMimeTypeMismatchParams>
|
||
|
SecurityWarningFunction;
|
||
|
nsTArray<SecurityWarningFunction> mSecurityWarningFunctions;
|
||
|
|
||
|
struct OnDataAvailableRequest {
|
||
|
nsCString data;
|
||
|
uint64_t offset;
|
||
|
uint32_t count;
|
||
|
};
|
||
|
// TODO Backtrack this.
|
||
|
nsTArray<OnDataAvailableRequest> mPendingRequests;
|
||
|
Maybe<nsresult> mStopRequestValue;
|
||
|
|
||
|
nsCOMPtr<nsIChannel> mChannel;
|
||
|
|
||
|
// An instance of ParentChannelListener that we use as a listener
|
||
|
// between mChannel (and any future redirected mChannels) and us.
|
||
|
// This handles service worker interception, and retargetting
|
||
|
// OnDataAvailable/OnStopRequest messages onto the listener that
|
||
|
// replaces us.
|
||
|
RefPtr<ParentChannelListener> mParentChannelListener;
|
||
|
|
||
|
// The bridge to the nsIChannel in the originating docshell.
|
||
|
// This reference forms a cycle with the bridge, and we expect
|
||
|
// the bridge to call DisonnectDocumentChannelBridge when it
|
||
|
// shuts down to break this.
|
||
|
RefPtr<ADocumentChannelBridge> mDocumentChannelBridge;
|
||
|
|
||
|
nsCOMPtr<nsILoadContext> mLoadContext;
|
||
|
|
||
|
PBOverrideStatus mPBOverride;
|
||
|
|
||
|
// The original URI of the current channel. If there are redirects,
|
||
|
// then the value on the channel gets overwritten with the original
|
||
|
// URI of the first channel in the redirect chain, so we cache the
|
||
|
// value we need here.
|
||
|
nsCOMPtr<nsIURI> mChannelCreationURI;
|
||
|
|
||
|
nsTArray<DocumentChannelRedirect> mRedirects;
|
||
|
|
||
|
// Corresponding redirect channel registrar Id for the final channel that
|
||
|
// we want to use when redirecting the child, or doing a process switch.
|
||
|
// 0 means redirection is not started.
|
||
|
uint32_t mRedirectChannelId = 0;
|
||
|
// Set to true if we called Suspend on mChannel to initiate our redirect.
|
||
|
// This isn't currently set when we do a process swap, since that gets
|
||
|
// initiated in nsHttpChannel.
|
||
|
bool mSuspendedChannel = false;
|
||
|
// Set to true if we're currently in the middle of replacing this with
|
||
|
// a new channel connected a different process.
|
||
|
bool mDoingProcessSwitch = false;
|
||
|
// The value of GetApplyConversion on mChannel when OnStartRequest
|
||
|
// was called. We override it to false to prevent a conversion
|
||
|
// helper from being installed, but we need to restore the value
|
||
|
// later.
|
||
|
bool mOldApplyConversion = false;
|
||
|
// Set to true if any previous channel that we redirected away
|
||
|
// from had a COOP mismatch.
|
||
|
bool mHasCrossOriginOpenerPolicyMismatch = false;
|
||
|
|
||
|
typedef MozPromise<uint64_t, nsresult, true /* exclusive */>
|
||
|
ContentProcessIdPromise;
|
||
|
// This promise is set following a on-may-change-process observer
|
||
|
// notification when the associated channel is getting relocated to another
|
||
|
// process. It will be resolved when that process is set up.
|
||
|
RefPtr<ContentProcessIdPromise> mRedirectContentProcessIdPromise;
|
||
|
// This identifier is set at the same time as the
|
||
|
// mRedirectContentProcessIdPromise.
|
||
|
// This identifier is later passed to the childChannel in order to identify it
|
||
|
// once the promise is resolved.
|
||
|
uint64_t mCrossProcessRedirectIdentifier = 0;
|
||
|
};
|
||
|
|
||
|
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)
|
||
|
|
||
|
} // namespace net
|
||
|
} // namespace mozilla
|
||
|
|
||
|
#endif // mozilla_net_DocumentChannelParent_h
|