Bug 1556489 - P18. Add DocumentChannel and hook it up. r=nika,mayhemer

DocumentChannel acts as a replacement for HttpChannel where redirects are now entirely handled in the DocumentChannelParent. The ContentChild will receive the final nsIChannel once all redirects have been handled.

Differential Revision: https://phabricator.services.mozilla.com/D37490
This commit is contained in:
Matt Woodrow 2019-08-07 15:54:53 +10:00 committed by Jean-Yves Avenard
parent 20e71d4c8a
commit 1b5f7c772a
19 changed files with 1929 additions and 15 deletions

View File

@ -70,6 +70,7 @@
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "ReferrerInfo.h"
@ -9760,12 +9761,27 @@ static bool IsConsideredSameOriginForUIR(nsIPrincipal* aTriggeringPrincipal,
return aTriggeringPrincipal->Equals(tmpResultPrincipal);
}
static bool HasHttpScheme(nsIURI* aURI) {
return aURI && (aURI->SchemeIs("http") || aURI->SchemeIs("https"));
}
/* static */ bool nsDocShell::CreateChannelForLoadState(
nsDocShellLoadState* aLoadState, LoadInfo* aLoadInfo,
nsIInterfaceRequestor* aCallbacks, nsDocShell* aDocShell,
const nsString* aInitiatorType, nsLoadFlags aLoadFlags, uint32_t aLoadType,
uint32_t aCacheKey, bool aIsActive, bool aIsTopLevelDoc, nsresult& aRv,
nsIChannel** aChannel) {
if (StaticPrefs::browser_tabs_documentchannel() && XRE_IsContentProcess() &&
HasHttpScheme(aLoadState->URI())) {
RefPtr<DocumentChannelChild> child = new DocumentChannelChild(
aLoadState, aLoadInfo, aInitiatorType, aLoadFlags, aLoadType, aCacheKey,
aIsActive, aIsTopLevelDoc);
child->SetNotificationCallbacks(aCallbacks);
child.forget(aChannel);
aRv = NS_OK;
return true;
}
nsCOMPtr<nsIChannel> channel;
nsAutoString srcdoc;
bool isSrcdoc = aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);

View File

@ -879,6 +879,12 @@
value: 2048
mirror: always
# If set, use DocumentChannel with nsDocShell in place of HttpChannel.
- name: browser.tabs.documentchannel
type: bool
value: false
mirror: always
- name: browser.tabs.remote.desktopbehavior
type: bool
value: false

View File

@ -0,0 +1,564 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "DocumentChannelChild.h"
#include "SerializedLoadContext.h"
#include "mozIThirdPartyUtil.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/NeckoChild.h"
#include "nsContentSecurityManager.h"
#include "nsDocShellLoadState.h"
#include "nsQueryObject.h"
#include "nsSerializationHelper.h"
#include "nsStringStream.h"
#include "mozilla/net/UrlClassifierCommon.h"
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
NS_INTERFACE_MAP_BEGIN(DocumentChannelChild)
if (mWasOpened && aIID == NS_GET_IID(nsIHttpChannel)) {
// DocumentChannelChild generally is doing an http connection
// internally, but doesn't implement the interface. Everything
// before AsyncOpen should be duplicated in the parent process
// on the real http channel, but anything trying to QI to nsIHttpChannel
// after that will be failing and get confused.
NS_WARNING(
"Trying to request nsIHttpChannel from DocumentChannelChild, this is "
"likely broken");
}
NS_INTERFACE_MAP_ENTRY(nsIClassifiedChannel)
NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannelChild)
NS_INTERFACE_MAP_END_INHERITING(nsBaseChannel)
NS_IMPL_ADDREF_INHERITED(DocumentChannelChild, nsBaseChannel)
NS_IMPL_RELEASE_INHERITED(DocumentChannelChild, nsBaseChannel)
DocumentChannelChild::DocumentChannelChild(
nsDocShellLoadState* aLoadState, net::LoadInfo* aLoadInfo,
const nsString* aInitiatorType, nsLoadFlags aLoadFlags, uint32_t aLoadType,
uint32_t aCacheKey, bool aIsActive, bool aIsTopLevelDoc)
: mLoadState(aLoadState),
mInitiatorType(aInitiatorType ? Some(*aInitiatorType) : Nothing()),
mLoadType(aLoadType),
mCacheKey(aCacheKey),
mIsActive(aIsActive),
mIsTopLevelDoc(aIsTopLevelDoc) {
mEventQueue = new ChannelEventQueue(static_cast<nsIChannel*>(this));
SetURI(aLoadState->URI());
SetLoadInfo(aLoadInfo);
SetLoadFlags(aLoadFlags);
}
NS_IMETHODIMP
DocumentChannelChild::AsyncOpen(nsIStreamListener* aListener) {
nsresult rv = NS_OK;
nsCOMPtr<nsIStreamListener> listener = aListener;
rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(gNeckoChild, NS_ERROR_FAILURE);
NS_ENSURE_ARG_POINTER(listener);
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
// Port checked in parent, but duplicate here so we can return with error
// immediately, as we've done since before e10s.
rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
// because in the child ipdl,
// a typedef URI is defined...
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> topWindowURI;
nsCOMPtr<nsIURI> uriBeingLoaded =
AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(this);
nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal;
nsCOMPtr<mozIThirdPartyUtil> util = services::GetThirdPartyUtil();
if (util) {
nsCOMPtr<mozIDOMWindowProxy> win;
rv =
util->GetTopWindowForChannel(this, uriBeingLoaded, getter_AddRefs(win));
if (NS_SUCCEEDED(rv)) {
util->GetURIFromWindow(win, getter_AddRefs(topWindowURI));
Unused << util->GetContentBlockingAllowListPrincipalFromWindow(
win, uriBeingLoaded,
getter_AddRefs(contentBlockingAllowListPrincipal));
}
}
// add ourselves to the load group.
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
if (mCanceled) {
// We may have been canceled already, either by on-modify-request
// listeners or by load group observers; in that case, don't create IPDL
// connection. See nsHttpChannel::AsyncOpen().
return mStatus;
}
DocumentChannelCreationArgs args;
SerializeURI(topWindowURI, args.topWindowURI());
args.loadState() = mLoadState->Serialize();
Maybe<LoadInfoArgs> maybeArgs;
rv = LoadInfoToLoadInfoArgs(mLoadInfo, &maybeArgs);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_DIAGNOSTIC_ASSERT(maybeArgs);
if (contentBlockingAllowListPrincipal) {
PrincipalInfo principalInfo;
rv = PrincipalToPrincipalInfo(contentBlockingAllowListPrincipal,
&principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
args.contentBlockingAllowListPrincipal() = Some(principalInfo);
}
args.loadInfo() = *maybeArgs;
GetLoadFlags(&args.loadFlags());
args.initiatorType() = mInitiatorType;
args.loadType() = mLoadType;
args.cacheKey() = mCacheKey;
args.isActive() = mIsActive;
args.isTopLevelDoc() = mIsTopLevelDoc;
args.channelId() = mChannelId;
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
if (loadContext) {
nsCOMPtr<mozIDOMWindowProxy> domWindow;
loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
if (domWindow) {
auto* pDomWindow = nsPIDOMWindowOuter::From(domWindow);
nsIDocShell* docshell = pDomWindow->GetDocShell();
if (docshell) {
docshell->GetCustomUserAgent(args.customUserAgent());
}
}
}
nsCOMPtr<nsIBrowserChild> iBrowserChild;
GetCallback(iBrowserChild);
BrowserChild* browserChild = static_cast<BrowserChild*>(iBrowserChild.get());
if (MissingRequiredBrowserChild(browserChild, "ftp")) {
return NS_ERROR_ILLEGAL_VALUE;
}
// TODO: What happens if the caller has called other methods on the
// nsIChannel after the ctor, but before this?
gNeckoChild->SendPDocumentChannelConstructor(
this, browserChild, IPC::SerializedLoadContext(this), args);
mIsPending = true;
mWasOpened = true;
mListener = listener;
return NS_OK;
}
class DocumentFailedAsyncOpenEvent
: public NeckoTargetChannelEvent<DocumentChannelChild> {
public:
DocumentFailedAsyncOpenEvent(DocumentChannelChild* aChild,
nsresult aStatusCode)
: NeckoTargetChannelEvent<DocumentChannelChild>(aChild),
mStatus(aStatusCode) {}
void Run() override { mChild->DoFailedAsyncOpen(mStatus); }
private:
nsresult mStatus;
};
IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
const nsresult& aStatusCode) {
mEventQueue->RunOrEnqueue(
new DocumentFailedAsyncOpenEvent(this, aStatusCode));
return IPC_OK();
}
void DocumentChannelChild::DoFailedAsyncOpen(const nsresult& aStatusCode) {
ShutdownListeners(aStatusCode);
}
void DocumentChannelChild::ShutdownListeners(nsresult aStatusCode) {
mStatus = aStatusCode;
nsCOMPtr<nsIStreamListener> l = mListener;
if (l) {
l->OnStartRequest(this);
}
mIsPending = false;
l = mListener; // it might have changed!
if (l) {
l->OnStopRequest(this, aStatusCode);
}
mListener = nullptr;
mCallbacks = nullptr;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
mLoadGroup = nullptr;
}
if (CanSend()) {
Send__delete__(this);
}
}
IPCResult DocumentChannelChild::RecvCancelForProcessSwitch() {
ShutdownListeners(NS_BINDING_ABORTED);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvDeleteSelf() {
// This calls NeckoChild::DeallocPGenericChannel(), which deletes |this| if
// IPDL holds the last reference. Don't rely on |this| existing after here!
Send__delete__(this);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
const uint32_t& aRegistrarId, nsIURI* aURI, const uint32_t& aNewLoadFlags,
const Maybe<ReplacementChannelConfigInit>& aInit,
const Maybe<LoadInfoArgs>& aLoadInfo, const uint64_t& aChannelId,
nsIURI* aOriginalURI, const uint32_t& aRedirectMode,
const uint32_t& aRedirectFlags, const Maybe<uint32_t>& aContentDisposition,
const Maybe<nsString>& aContentDispositionFilename,
RedirectToRealChannelResolver&& aResolve) {
nsCOMPtr<nsILoadInfo> originalLoadInfo;
RefPtr<dom::Document> loadingDocument;
GetLoadInfo(getter_AddRefs(originalLoadInfo));
if (originalLoadInfo) {
originalLoadInfo->GetLoadingDocument(getter_AddRefs(loadingDocument));
}
nsCOMPtr<nsILoadInfo> loadInfo;
nsresult rv = LoadInfoArgsToLoadInfo(aLoadInfo, loadingDocument,
getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
MOZ_DIAGNOSTIC_ASSERT(false, "LoadInfoArgsToLoadInfo failed");
return IPC_OK();
}
mRedirectResolver = std::move(aResolve);
nsCOMPtr<nsIChannel> newChannel;
rv = NS_NewChannelInternal(getter_AddRefs(newChannel), aURI, loadInfo,
nullptr, // PerformanceStorage
mLoadGroup, // aLoadGroup
nullptr, // aCallbacks
aNewLoadFlags);
RefPtr<HttpChannelChild> httpChild = do_QueryObject(newChannel);
RefPtr<nsIChildChannel> childChannel = do_QueryObject(newChannel);
if (NS_FAILED(rv)) {
MOZ_DIAGNOSTIC_ASSERT(false, "NS_NewChannelInternal failed");
return IPC_OK();
}
// This is used to report any errors back to the parent by calling
// CrossProcessRedirectFinished.
auto scopeExit = MakeScopeExit([&]() {
mRedirectResolver(rv);
mRedirectResolver = nullptr;
});
if (httpChild) {
rv = httpChild->SetChannelId(aChannelId);
}
if (NS_FAILED(rv)) {
return IPC_OK();
}
rv = newChannel->SetOriginalURI(aOriginalURI);
if (NS_FAILED(rv)) {
return IPC_OK();
}
if (httpChild) {
rv = httpChild->SetRedirectMode(aRedirectMode);
}
if (NS_FAILED(rv)) {
return IPC_OK();
}
newChannel->SetNotificationCallbacks(mCallbacks);
if (aInit) {
HttpBaseChannel::ReplacementChannelConfig config(*aInit);
HttpBaseChannel::ConfigureReplacementChannel(newChannel, config);
}
if (aContentDisposition) {
newChannel->SetContentDisposition(*aContentDisposition);
}
if (aContentDispositionFilename) {
newChannel->SetContentDispositionFilename(*aContentDispositionFilename);
}
// transfer any properties. This appears to be entirely a content-side
// interface and isn't copied across to the parent. Copying the values
// for this from this into the new actor will work, since the parent
// won't have the right details anyway.
// TODO: What about the process switch equivalent
// (ContentChild::RecvCrossProcessRedirect)? In that case there is no local
// existing actor in the destination process... We really need all information
// to go up to the parent, and then come down to the new child actor.
nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
if (bag) {
for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
bag->SetProperty(iter.Key(), iter.UserData());
}
}
// connect parent.
if (childChannel) {
rv = childChannel->ConnectParent(aRegistrarId); // creates parent channel
if (NS_FAILED(rv)) {
return IPC_OK();
}
mRedirectChannel = childChannel;
}
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
MOZ_ASSERT(target);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, aRedirectFlags,
target);
if (NS_SUCCEEDED(rv)) {
scopeExit.release();
}
// scopeExit will call CrossProcessRedirectFinished(rv) here
return IPC_OK();
}
NS_IMETHODIMP
DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
if (mRedirectChannel) {
if (NS_SUCCEEDED(aStatusCode) && NS_SUCCEEDED(mStatus)) {
mRedirectChannel->CompleteRedirectSetup(mListener, nullptr);
} else {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRedirectChannel);
channel->SetNotificationCallbacks(nullptr);
}
}
mRedirectChannel = nullptr;
mRedirectResolver(aStatusCode);
mRedirectResolver = nullptr;
if (NS_FAILED(aStatusCode)) {
ShutdownListeners(aStatusCode);
return NS_OK;
}
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
}
mCallbacks = nullptr;
mListener = nullptr;
// This calls NeckoChild::DeallocPDocumentChannel(), which deletes |this| if
// IPDL holds the last reference. Don't rely on |this| existing after here!
if (CanSend()) {
Send__delete__(this);
}
return NS_OK;
}
IPCResult DocumentChannelChild::RecvConfirmRedirect(
nsIURI* aNewUri, ConfirmRedirectResolver&& aResolve) {
// This is effectively the same as AsyncOnChannelRedirect, except since we're
// not propagating the redirect into this process, we don't have an nsIChannel
// for the redirection and we have to do the checks manually.
// This just checks CSP thus far, hopefully there's not much else needed.
nsCOMPtr<nsIURI> originalUri;
nsresult rv = GetOriginalURI(getter_AddRefs(originalUri));
if (NS_FAILED(rv)) {
aResolve(rv);
return IPC_OK();
}
int16_t decision = nsIContentPolicy::ACCEPT;
rv = CSPService::ConsultCSPForRedirect(originalUri, aNewUri, mLoadInfo,
&decision);
if (NS_FAILED(rv)) {
aResolve(rv);
return IPC_OK();
}
if (NS_CP_REJECTED(decision)) {
aResolve(NS_BINDING_FAILED);
} else {
aResolve(NS_OK);
}
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvNotifyChannelClassifierProtectionDisabled(
const uint32_t& aAcceptedReason) {
UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(
this, aAcceptedReason);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvNotifyCookieAllowed() {
AntiTrackingCommon::NotifyBlockingDecision(
this, AntiTrackingCommon::BlockingDecision::eAllow, 0);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvNotifyCookieBlocked(
const uint32_t& aRejectedReason) {
AntiTrackingCommon::NotifyBlockingDecision(
this, AntiTrackingCommon::BlockingDecision::eBlock, aRejectedReason);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvSetClassifierMatchedInfo(
const nsCString& aList, const nsCString& aProvider,
const nsCString& aFullHash) {
SetMatchedInfo(aList, aProvider, aFullHash);
return IPC_OK();
}
IPCResult DocumentChannelChild::RecvSetClassifierMatchedTrackingInfo(
const nsCString& aLists, const nsCString& aFullHash) {
nsTArray<nsCString> lists, fullhashes;
for (const nsACString& token : aLists.Split(',')) {
lists.AppendElement(token);
}
for (const nsACString& token : aFullHash.Split(',')) {
fullhashes.AppendElement(token);
}
SetMatchedTrackingInfo(lists, fullhashes);
return IPC_OK();
}
//-----------------------------------------------------------------------------
// DocumentChannelChild::nsIClassifiedChannel
NS_IMETHODIMP
DocumentChannelChild::GetMatchedList(nsACString& aList) {
aList = mMatchedList;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::GetMatchedProvider(nsACString& aProvider) {
aProvider = mMatchedProvider;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::GetMatchedFullHash(nsACString& aFullHash) {
aFullHash = mMatchedFullHash;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::SetMatchedInfo(const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash) {
NS_ENSURE_ARG(!aList.IsEmpty());
mMatchedList = aList;
mMatchedProvider = aProvider;
mMatchedFullHash = aFullHash;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::GetMatchedTrackingLists(nsTArray<nsCString>& aLists) {
aLists = mMatchedTrackingLists;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::GetMatchedTrackingFullHashes(
nsTArray<nsCString>& aFullHashes) {
aFullHashes = mMatchedTrackingFullHashes;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::SetMatchedTrackingInfo(
const nsTArray<nsCString>& aLists, const nsTArray<nsCString>& aFullHashes) {
NS_ENSURE_ARG(!aLists.IsEmpty());
// aFullHashes can be empty for non hash-matching algorithm, for example,
// host based test entries in preference.
mMatchedTrackingLists = aLists;
mMatchedTrackingFullHashes = aFullHashes;
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::Cancel(nsresult aStatusCode) {
if (mCanceled) {
return NS_OK;
}
mCanceled = true;
if (CanSend()) {
SendCancel(aStatusCode);
}
ShutdownListeners(aStatusCode);
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::Suspend() {
NS_ENSURE_TRUE(CanSend(), NS_ERROR_NOT_AVAILABLE);
if (!mSuspendCount++) {
SendSuspend();
}
mEventQueue->Suspend();
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelChild::Resume() {
NS_ENSURE_TRUE(CanSend(), NS_ERROR_NOT_AVAILABLE);
MOZ_ASSERT(mSuspendCount);
if (!--mSuspendCount) {
SendResume();
}
mEventQueue->Resume();
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_DocumentChannelChild_h
#define mozilla_net_DocumentChannelChild_h
#include "mozilla/net/ChannelEventQueue.h"
#include "mozilla/net/PDocumentChannelChild.h"
#include "nsBaseChannel.h"
#include "nsIChildChannel.h"
#include "nsIClassifiedChannel.h"
#define DOCUMENT_CHANNEL_CHILD_IID \
{ \
0x6977bc44, 0xb1db, 0x41b7, { \
0xb5, 0xc5, 0xe2, 0x13, 0x68, 0x22, 0xc9, 0x8f \
} \
}
namespace mozilla {
namespace net {
class DocumentChannelChild final : public PDocumentChannelChild,
public nsBaseChannel,
public nsIClassifiedChannel {
public:
DocumentChannelChild(nsDocShellLoadState* aLoadState,
class LoadInfo* aLoadInfo,
const nsString* aInitiatorType, nsLoadFlags aLoadFlags,
uint32_t aLoadType, uint32_t aCacheKey, bool aIsActive,
bool aIsTopLevelDoc);
NS_DECL_ISUPPORTS_INHERITED;
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
NS_DECL_NSICLASSIFIEDCHANNEL
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_CHILD_IID)
// nsIRequest
NS_IMETHOD Cancel(nsresult status) override;
NS_IMETHOD Suspend() override;
NS_IMETHOD Resume() override;
// nsIChannel
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
nsresult OpenContentStream(bool aAsync, nsIInputStream** aStream,
nsIChannel** aChannel) override {
return NS_ERROR_NOT_IMPLEMENTED;
}
mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& aStatusCode);
mozilla::ipc::IPCResult RecvCancelForProcessSwitch();
mozilla::ipc::IPCResult RecvDeleteSelf();
mozilla::ipc::IPCResult RecvRedirectToRealChannel(
const uint32_t& aRegistrarId, nsIURI* aURI, const uint32_t& aNewLoadFlags,
const Maybe<ReplacementChannelConfigInit>& aInit,
const Maybe<LoadInfoArgs>& aLoadInfo, const uint64_t& aChannelId,
nsIURI* aOriginalURI, const uint32_t& aRedirectMode,
const uint32_t& aRedirectFlags,
const Maybe<uint32_t>& aContentDisposition,
const Maybe<nsString>& aContentDispositionFilename,
RedirectToRealChannelResolver&& aResolve);
mozilla::ipc::IPCResult RecvNotifyChannelClassifierProtectionDisabled(
const uint32_t& aAcceptedReason);
mozilla::ipc::IPCResult RecvNotifyCookieAllowed();
mozilla::ipc::IPCResult RecvNotifyCookieBlocked(
const uint32_t& aRejectedReason);
mozilla::ipc::IPCResult RecvSetClassifierMatchedInfo(
const nsCString& aList, const nsCString& aProvider,
const nsCString& aFullHash);
mozilla::ipc::IPCResult RecvSetClassifierMatchedTrackingInfo(
const nsCString& aLists, const nsCString& aFullHash);
mozilla::ipc::IPCResult RecvConfirmRedirect(
nsIURI* aNewUri, ConfirmRedirectResolver&& aResolve);
void DoFailedAsyncOpen(const nsresult& aStatusCode);
friend class NeckoTargetChannelEvent<DocumentChannelChild>;
private:
void ShutdownListeners(nsresult aStatusCode);
~DocumentChannelChild() = default;
RefPtr<ChannelEventQueue> mEventQueue;
nsCOMPtr<nsIChildChannel> mRedirectChannel;
// Classified channel's matched information
nsCString mMatchedList;
nsCString mMatchedProvider;
nsCString mMatchedFullHash;
nsTArray<nsCString> mMatchedTrackingLists;
nsTArray<nsCString> mMatchedTrackingFullHashes;
RedirectToRealChannelResolver mRedirectResolver;
const RefPtr<nsDocShellLoadState> mLoadState;
const Maybe<nsString> mInitiatorType;
const uint32_t mLoadType;
const uint32_t mCacheKey;
const bool mIsActive;
const bool mIsTopLevelDoc;
bool mCanceled = false;
uint32_t mSuspendCount = 0;
bool mIsPending = false;
bool mWasOpened = false;
};
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentChannelChild, DOCUMENT_CHANNEL_CHILD_IID)
} // namespace net
} // namespace mozilla
#endif // mozilla_net_DocumentChannelChild_h

View File

@ -0,0 +1,808 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "DocumentChannelParent.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/ClientChannelHelper.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/net/RedirectChannelRegistrar.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsHttpChannel.h"
#include "nsIHttpProtocolHandler.h"
#include "nsISecureBrowserUI.h"
#include "nsRedirectHistoryEntry.h"
#include "nsSerializationHelper.h"
using namespace mozilla::dom;
namespace mozilla {
namespace net {
NS_IMPL_ADDREF(DocumentChannelParent)
NS_IMPL_RELEASE(DocumentChannelParent)
NS_INTERFACE_MAP_BEGIN(DocumentChannelParent)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
NS_INTERFACE_MAP_ENTRY(nsICrossProcessSwitchChannel)
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannelParent)
NS_INTERFACE_MAP_END
DocumentChannelParent::DocumentChannelParent(
const PBrowserOrId& iframeEmbedding, nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mLoadContext(aLoadContext), mPBOverride(aOverrideStatus) {
if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
mBrowserParent =
static_cast<dom::BrowserParent*>(iframeEmbedding.get_PBrowserParent());
}
}
bool DocumentChannelParent::Init(const DocumentChannelCreationArgs& aArgs) {
RefPtr<nsDocShellLoadState> loadState =
new nsDocShellLoadState(aArgs.loadState());
RefPtr<LoadInfo> loadInfo;
nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(Some(aArgs.loadInfo()),
getter_AddRefs(loadInfo));
MOZ_ASSERT(NS_SUCCEEDED(rv));
mListener = new ParentChannelListener(this);
bool result = nsDocShell::CreateChannelForLoadState(
loadState, loadInfo, mListener, nullptr,
aArgs.initiatorType().ptrOr(nullptr), aArgs.loadFlags(), aArgs.loadType(),
aArgs.cacheKey(), aArgs.isActive(), aArgs.isTopLevelDoc(), rv,
getter_AddRefs(mChannel));
if (!result) {
return SendFailedAsyncOpen(rv);
}
nsDocShell::ConfigureChannel(mChannel, loadState,
aArgs.initiatorType().ptrOr(nullptr),
aArgs.loadType(), aArgs.cacheKey());
// Computation of the top window uses the docshell tree, so only
// works in the source process. We compute it manually and override
// it so that it gets the right value.
// This probably isn't fission compatible.
RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, &rv);
if (httpBaseChannel) {
nsCOMPtr<nsIURI> topWindowURI = DeserializeURI(aArgs.topWindowURI());
rv = httpBaseChannel->SetTopWindowURI(topWindowURI);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (aArgs.contentBlockingAllowListPrincipal()) {
nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal =
PrincipalInfoToPrincipal(*aArgs.contentBlockingAllowListPrincipal());
httpBaseChannel->SetContentBlockingAllowListPrincipal(
contentBlockingAllowListPrincipal);
}
// The content process docshell can specify a custom override
// for the user agent value. If we had one, then add it to
// the header now.
if (!aArgs.customUserAgent().IsEmpty()) {
NS_ConvertUTF16toUTF8 utf8CustomUserAgent(aArgs.customUserAgent());
rv = httpBaseChannel->SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
utf8CustomUserAgent, false);
}
}
nsCOMPtr<nsIPrivateBrowsingChannel> privateChannel =
do_QueryInterface(mChannel);
if (mPBOverride != kPBOverride_Unset) {
privateChannel->SetPrivate(mPBOverride == kPBOverride_Private);
}
nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
if (identChannel) {
Unused << identChannel->SetChannelId(aArgs.channelId());
}
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
if (httpChannelImpl) {
httpChannelImpl->SetWarningReporter(this);
}
// Setup a ClientChannelHelper to watch for redirects, and copy
// across any serviceworker related data between channels as needed.
AddClientChannelHelperInParent(mChannel, GetMainThreadSerialEventTarget());
rv = mChannel->AsyncOpen(mListener);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
mChannelCreationURI = loadState->URI();
return true;
}
void DocumentChannelParent::ActorDestroy(ActorDestroyReason why) {
// The nsHttpChannel may have a reference to this parent, release it
// to avoid circular references.
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
if (httpChannelImpl) {
httpChannelImpl->SetWarningReporter(nullptr);
}
}
void DocumentChannelParent::CancelChildForProcessSwitch() {
MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
MOZ_ASSERT(NS_IsMainThread());
mDoingProcessSwitch = true;
if (CanSend()) {
Unused << SendCancelForProcessSwitch();
}
}
bool DocumentChannelParent::RecvCancel(const nsresult& aStatusCode) {
if (mDoingProcessSwitch) {
return IPC_OK();
}
if (mChannel) {
mChannel->Cancel(aStatusCode);
}
return true;
}
bool DocumentChannelParent::RecvSuspend() {
if (mChannel) {
mChannel->Suspend();
}
return true;
}
bool DocumentChannelParent::RecvResume() {
if (mChannel) {
mChannel->Resume();
}
return true;
}
void DocumentChannelParent::RedirectToRealChannelFinished(nsresult aRv) {
if (NS_FAILED(aRv)) {
FinishReplacementChannelSetup(false);
return;
}
// Wait for background channel ready on target channel
nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(redirectReg);
nsCOMPtr<nsIParentChannel> redirectParentChannel;
redirectReg->GetParentChannel(mRedirectChannelId,
getter_AddRefs(redirectParentChannel));
if (!redirectParentChannel) {
FinishReplacementChannelSetup(false);
return;
}
nsCOMPtr<nsIParentRedirectingChannel> redirectingParent =
do_QueryInterface(redirectParentChannel);
if (!redirectingParent) {
// Continue verification procedure if redirecting to non-Http protocol
FinishReplacementChannelSetup(true);
return;
}
// Ask redirected channel if verification can proceed.
// ReadyToVerify will be invoked when redirected channel is ready.
redirectingParent->ContinueVerification(this);
return;
}
NS_IMETHODIMP
DocumentChannelParent::ReadyToVerify(nsresult aResultCode) {
FinishReplacementChannelSetup(NS_SUCCEEDED(aResultCode));
return NS_OK;
}
void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
nsresult rv;
nsCOMPtr<nsIParentChannel> redirectChannel;
if (mRedirectChannelId) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
rv = registrar->GetParentChannel(mRedirectChannelId,
getter_AddRefs(redirectChannel));
if (NS_FAILED(rv) || !redirectChannel) {
// Redirect might get canceled before we got AsyncOnChannelRedirect
nsCOMPtr<nsIChannel> newChannel;
rv = registrar->GetRegisteredChannel(mRedirectChannelId,
getter_AddRefs(newChannel));
MOZ_ASSERT(newChannel, "Already registered channel not found");
if (NS_SUCCEEDED(rv)) {
newChannel->Cancel(NS_BINDING_ABORTED);
}
}
// Release all previously registered channels, they are no longer need to be
// kept in the registrar from this moment.
registrar->DeregisterChannels(mRedirectChannelId);
mRedirectChannelId = 0;
}
if (!redirectChannel) {
aSucceeded = false;
}
if (!aSucceeded) {
if (redirectChannel) {
redirectChannel->Delete();
}
if (mSuspendedChannel) {
mChannel->Resume();
}
return;
}
MOZ_ASSERT(
!SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
Delete();
if (!mStopRequestValue) {
mListener->SetListenerAfterRedirect(redirectChannel);
}
redirectChannel->SetParentListener(mListener);
// We stored the values from all nsIParentChannel functions called since we
// couldn't handle them. Copy them across to the real channel since it should
// know what to do.
for (auto& variant : mIParentChannelFunctions) {
variant.match(
[redirectChannel](const nsIHttpChannel::FlashPluginState& aState) {
redirectChannel->NotifyFlashPluginStateChanged(aState);
},
[redirectChannel](const ClassifierMatchedInfoParams& aParams) {
redirectChannel->SetClassifierMatchedInfo(
aParams.mList, aParams.mProvider, aParams.mFullHash);
},
[redirectChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
redirectChannel->SetClassifierMatchedTrackingInfo(
aParams.mLists, aParams.mFullHashes);
},
[redirectChannel](const ClassificationFlagsParams& aParams) {
redirectChannel->NotifyClassificationFlags(
aParams.mClassificationFlags, aParams.mIsThirdParty);
});
}
RefPtr<HttpChannelParent> httpParent = do_QueryObject(redirectChannel);
if (httpParent) {
RefPtr<HttpChannelSecurityWarningReporter> reporter = httpParent;
for (auto& variant : mSecurityWarningFunctions) {
variant.match(
[reporter](const ReportSecurityMessageParams& aParams) {
Unused << reporter->ReportSecurityMessage(aParams.mMessageTag,
aParams.mMessageCategory);
},
[reporter](const LogBlockedCORSRequestParams& aParams) {
Unused << reporter->LogBlockedCORSRequest(aParams.mMessage,
aParams.mCategory);
},
[reporter](const LogMimeTypeMismatchParams& aParams) {
Unused << reporter->LogMimeTypeMismatch(
aParams.mMessageName, aParams.mWarning, aParams.mURL,
aParams.mContentType);
});
}
}
if (mSuspendedChannel) {
nsTArray<OnDataAvailableRequest> pendingRequests =
std::move(mPendingRequests);
MOZ_ASSERT(mPendingRequests.IsEmpty());
nsCOMPtr<nsHttpChannel> httpChannel = do_QueryInterface(mChannel);
if (httpChannel) {
httpChannel->SetApplyConversion(mOldApplyConversion);
}
rv = redirectChannel->OnStartRequest(mChannel);
if (NS_FAILED(rv)) {
mChannel->Cancel(rv);
}
// If we failed to suspend the channel, then we might have received
// some messages while the redirected was being handled.
// Manually send them on now.
if (NS_SUCCEEDED(rv) &&
(!mStopRequestValue || NS_SUCCEEDED(*mStopRequestValue))) {
for (auto& request : pendingRequests) {
nsCOMPtr<nsIInputStream> stringStream;
rv = NS_NewByteInputStream(
getter_AddRefs(stringStream),
Span<const char>(request.data.get(), request.count),
NS_ASSIGNMENT_DEPEND);
if (NS_SUCCEEDED(rv)) {
rv = redirectChannel->OnDataAvailable(mChannel, stringStream,
request.offset, request.count);
}
if (NS_FAILED(rv)) {
mChannel->Cancel(rv);
mStopRequestValue = Some(rv);
break;
}
}
}
if (mStopRequestValue) {
redirectChannel->OnStopRequest(mChannel, *mStopRequestValue);
}
mChannel->Resume();
}
}
NS_IMETHODIMP
DocumentChannelParent::FinishCrossProcessSwitch(
nsIAsyncVerifyRedirectCallback* aCallback, nsresult aStatusCode) {
// We only manually Suspend mChannel when we initiate the redirect
// from OnStartRequest, which is currently only in the same-process
// case.
MOZ_ASSERT(!mSuspendedChannel);
if (NS_SUCCEEDED(aStatusCode)) {
// This updates ParentChannelListener to point to this parent and at
// the same time cancels the old channel.
FinishReplacementChannelSetup(true);
}
aCallback->OnRedirectVerifyCallback(aStatusCode);
return NS_OK;
}
nsresult DocumentChannelParent::TriggerCrossProcessSwitch(
nsIHttpChannel* aChannel, uint64_t aIdentifier) {
CancelChildForProcessSwitch();
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(aChannel);
MOZ_DIAGNOSTIC_ASSERT(httpChannel,
"Must be called with nsHttpChannel object");
RefPtr<nsHttpChannel::ContentProcessIdPromise> p =
httpChannel->TakeRedirectContentProcessIdPromise();
p->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr<DocumentChannelParent>(this),
channel = nsCOMPtr<nsIChannel>(aChannel), aIdentifier](uint64_t aCpId) {
self->TriggerRedirectToRealChannel(channel, Some(aCpId), aIdentifier);
},
[httpChannel](nsresult aStatusCode) {
MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
httpChannel->OnRedirectVerifyCallback(aStatusCode);
});
return NS_OK;
}
void DocumentChannelParent::TriggerRedirectToRealChannel(
nsIChannel* aChannel, const Maybe<uint64_t>& aDestinationProcess,
uint64_t aIdentifier) {
// This initiates replacing the current DocumentChannel with a
// protocol specific 'real' channel, maybe in a different process than
// the current DocumentChannelChild, if aDestinationProces is set.
// It registers the current mChannel with the registrar to get an ID
// so that the remote end can setup a new IPDL channel and lookup
// the same underlying channel.
// We expect this process to finish with FinishReplacementChannelSetup
// (for both in-process and process switch cases), where we cleanup
// the registrar and copy across any needed state to the replacing
// IPDL parent object.
// Use the original URI of the current channel, as this is what
// we'll use to construct the channel in the content process.
nsCOMPtr<nsIURI> uri = mChannelCreationURI;
if (!uri) {
aChannel->GetOriginalURI(getter_AddRefs(uri));
}
// I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
// clears the principal to inherit, which fails tests (probably because this
// 'redirect' is usually just an implementation detail). It's also http only,
// and aChannel can be anything that we redirected to.
nsCOMPtr<nsILoadInfo> redirectLoadInfo;
aChannel->GetLoadInfo(getter_AddRefs(redirectLoadInfo));
nsCOMPtr<nsIPrincipal> principalToInherit;
redirectLoadInfo->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
RefPtr<nsHttpChannel> baseChannel = do_QueryObject(aChannel);
if (baseChannel) {
redirectLoadInfo = baseChannel->CloneLoadInfoForRedirect(
uri, nsIChannelEventSink::REDIRECT_INTERNAL);
redirectLoadInfo->SetResultPrincipalURI(uri);
// The clone process clears this, and then we fail tests..
// docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
if (principalToInherit) {
redirectLoadInfo->SetPrincipalToInherit(principalToInherit);
}
} else {
redirectLoadInfo =
static_cast<mozilla::net::LoadInfo*>(redirectLoadInfo.get())->Clone();
nsCOMPtr<nsIPrincipal> uriPrincipal;
nsIScriptSecurityManager* sm = nsContentUtils::GetSecurityManager();
sm->GetChannelURIPrincipal(aChannel, getter_AddRefs(uriPrincipal));
nsCOMPtr<nsIRedirectHistoryEntry> entry =
new nsRedirectHistoryEntry(uriPrincipal, nullptr, EmptyCString());
redirectLoadInfo->AppendRedirectHistoryEntry(entry, true);
}
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
nsresult rv = registrar->RegisterChannel(aChannel, &mRedirectChannelId);
NS_ENSURE_SUCCESS_VOID(rv);
Maybe<LoadInfoArgs> loadInfoArgs;
MOZ_ALWAYS_SUCCEEDS(
ipc::LoadInfoToLoadInfoArgs(redirectLoadInfo, &loadInfoArgs));
uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
MOZ_ALWAYS_SUCCEEDS(aChannel->GetLoadFlags(&newLoadFlags));
if (!aDestinationProcess) {
newLoadFlags |= nsIChannel::LOAD_REPLACE;
}
nsCOMPtr<nsIURI> originalURI;
aChannel->GetOriginalURI(getter_AddRefs(originalURI));
uint64_t channelId = 0;
// aChannel can be a nsHttpChannel as well as InterceptedHttpChannel so we
// can't use baseChannel here.
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
MOZ_ALWAYS_SUCCEEDS(httpChannel->GetChannelId(&channelId));
}
uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
do_QueryInterface(aChannel);
if (httpChannelInternal) {
MOZ_ALWAYS_SUCCEEDS(httpChannelInternal->GetRedirectMode(&redirectMode));
}
// If we didn't have any redirects, then we pass the REDIRECT_INTERNAL flag
// for this channel switch so that it isn't recorded in session history etc.
// If there were redirect(s), then we want this switch to be recorded as a
// real one, since we have a new URI.
uint32_t redirectFlags = 0;
if (!mDidUpstreamRedirect) {
redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
}
Maybe<ReplacementChannelConfigInit> config;
if (baseChannel) {
uint32_t loadFlags = 0;
if (!aDestinationProcess) {
loadFlags |= nsIChannel::LOAD_REPLACE;
}
config =
Some(baseChannel
->CloneReplacementChannelConfig(true, redirectFlags, loadFlags)
.Serialize());
}
Maybe<uint32_t> contentDisposition;
uint32_t contentDispositionTemp;
rv = aChannel->GetContentDisposition(&contentDispositionTemp);
if (NS_SUCCEEDED(rv)) {
contentDisposition = Some(contentDispositionTemp);
}
Maybe<nsString> contentDispositionFilename;
nsString contentDispositionFilenameTemp;
rv = aChannel->GetContentDispositionFilename(contentDispositionFilenameTemp);
if (NS_SUCCEEDED(rv)) {
contentDispositionFilename = Some(contentDispositionFilenameTemp);
}
if (aDestinationProcess) {
dom::ContentParent* cp =
dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
ContentParentId{*aDestinationProcess});
if (!cp) {
return;
}
MOZ_ASSERT(config);
auto result = cp->SendCrossProcessRedirect(
mRedirectChannelId, uri, *config, loadInfoArgs, channelId, originalURI,
aIdentifier, redirectMode);
MOZ_ASSERT(result, "SendCrossProcessRedirect failed");
Unused << result;
} else {
RefPtr<DocumentChannelParent> channel = this;
SendRedirectToRealChannel(mRedirectChannelId, uri, newLoadFlags, config,
loadInfoArgs, channelId, originalURI,
redirectMode, redirectFlags, contentDisposition,
contentDispositionFilename)
->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[channel](nsresult aRv) {
channel->RedirectToRealChannelFinished(aRv);
},
[channel](const mozilla::ipc::ResponseRejectReason) {
channel->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
});
}
}
NS_IMETHODIMP
DocumentChannelParent::OnStartRequest(nsIRequest* aRequest) {
nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(aRequest);
mChannel = do_QueryInterface(aRequest);
MOZ_DIAGNOSTIC_ASSERT(mChannel);
// If this is a download, then redirect entirely within the parent.
// TODO, see bug 1574372.
if (!CanSend()) {
return NS_ERROR_UNEXPECTED;
}
// Once we initiate a process switch, we ask the child to notify the listeners
// that we have completed. If the switch promise then gets rejected we also
// cancel the parent, which results in this being called. We don't need
// to forward it on though, since the child side is already completed.
if (mDoingProcessSwitch) {
return NS_OK;
}
mChannel->Suspend();
mSuspendedChannel = true;
// The caller of this OnStartRequest will install a conversion
// helper after we return if we haven't disabled conversion. Normally
// HttpChannelParent::OnStartRequest would disable conversion, but we're
// defering calling that until later. Manually disable it now to prevent the
// converter from being installed (since we want the child to do it), and
// also save the value so that when we do call
// HttpChannelParent::OnStartRequest, we can have the value as it originally
// was.
if (channel) {
Unused << channel->GetApplyConversion(&mOldApplyConversion);
channel->SetApplyConversion(false);
}
TriggerRedirectToRealChannel(mChannel);
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::OnStopRequest(nsIRequest* aRequest,
nsresult aStatusCode) {
mStopRequestValue = Some(aStatusCode);
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* aInputStream,
uint64_t aOffset, uint32_t aCount) {
// This isn't supposed to happen, since we suspended the channel, but
// sometimes Suspend just doesn't work. This can happen when we're routing
// through nsUnknownDecoder to sniff the content type, and it doesn't handle
// being suspended. Let's just store the data and manually forward it to our
// redirected channel when it's ready.
nsCString data;
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
NS_ENSURE_SUCCESS(rv, rv);
mPendingRequests.AppendElement(
OnDataAvailableRequest({data, aOffset, aCount}));
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::SetParentListener(
mozilla::net::ParentChannelListener* listener) {
// We don't need this (do we?)
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::GetInterface(const nsIID& aIID, void** result) {
// Only support nsILoadContext if child channel's callbacks did too
if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
nsCOMPtr<nsILoadContext> copy = mLoadContext;
copy.forget(result);
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
aIID.Equals(NS_GET_IID(nsISecureBrowserUI)) ||
aIID.Equals(NS_GET_IID(nsIRemoteTab))) {
if (mBrowserParent) {
return mBrowserParent->QueryInterface(aIID, result);
}
}
nsresult rv = QueryInterface(aIID, result);
return rv;
}
// Rather than forwarding all these nsIParentChannel functions to the child, we
// cache a list of them, and then ask the 'real' channel to forward them for us
// after it's created.
NS_IMETHODIMP
DocumentChannelParent::NotifyChannelClassifierProtectionDisabled(
uint32_t aAcceptedReason) {
if (CanSend()) {
Unused << SendNotifyChannelClassifierProtectionDisabled(aAcceptedReason);
}
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::NotifyCookieAllowed() {
if (CanSend()) {
Unused << SendNotifyCookieAllowed();
}
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
if (CanSend()) {
Unused << SendNotifyCookieBlocked(aRejectedReason);
}
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::NotifyFlashPluginStateChanged(
nsIHttpChannel::FlashPluginState aState) {
mIParentChannelFunctions.AppendElement(
IParentChannelFunction{VariantIndex<0>{}, aState});
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash) {
ClassifierMatchedInfoParams params;
params.mList = aList;
params.mProvider = aProvider;
params.mFullHash = aFullHash;
if (CanSend()) {
Unused << SendSetClassifierMatchedInfo(params.mList, params.mProvider,
params.mFullHash);
}
mIParentChannelFunctions.AppendElement(
IParentChannelFunction{VariantIndex<1>{}, std::move(params)});
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::SetClassifierMatchedTrackingInfo(
const nsACString& aLists, const nsACString& aFullHash) {
ClassifierMatchedTrackingInfoParams params;
params.mLists = aLists;
params.mFullHashes = aFullHash;
if (CanSend()) {
Unused << SendSetClassifierMatchedTrackingInfo(params.mLists,
params.mFullHashes);
}
mIParentChannelFunctions.AppendElement(
IParentChannelFunction{VariantIndex<2>{}, std::move(params)});
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
bool aIsThirdParty) {
mIParentChannelFunctions.AppendElement(IParentChannelFunction{
VariantIndex<3>{},
ClassificationFlagsParams{aClassificationFlags, aIsThirdParty}});
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::Delete() {
// TODO - not sure we need it, but should delete the child or call on the
// child to release the parent.
if (!CanSend()) {
return NS_ERROR_UNEXPECTED;
}
Unused << SendDeleteSelf();
return NS_OK;
}
NS_IMETHODIMP
DocumentChannelParent::AsyncOnChannelRedirect(
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
nsIAsyncVerifyRedirectCallback* aCallback) {
// We generally don't want to notify the content process about redirects,
// so just update our channel and tell the callback that we're good to go.
mChannel = aNewChannel;
// We need the original URI of the current channel to use to open the real
// channel in the content process. Unfortunately we overwrite the original
// uri of the new channel with the original pre-redirect URI, so grab
// a copy of it now.
aNewChannel->GetOriginalURI(getter_AddRefs(mChannelCreationURI));
// We don't need to confirm internal redirects or record any
// history for them, so just immediately verify and return.
if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
aCallback->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
} else {
mDidUpstreamRedirect = true;
}
if (!CanSend()) {
return NS_BINDING_ABORTED;
}
// Currently the CSP code expects to run in the content
// process so that it can send events. Send a message to
// our content process to ask CSP if we should allow this
// redirect, and wait for confirmation.
nsCOMPtr<nsIURI> newUri;
nsresult rv = aNewChannel->GetURI(getter_AddRefs(newUri));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(aCallback);
nsCOMPtr<nsIChannel> oldChannel(aOldChannel);
SendConfirmRedirect(newUri)->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[callback, oldChannel](nsresult aRv) {
if (NS_FAILED(aRv)) {
oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
}
callback->OnRedirectVerifyCallback(aRv);
},
[callback, oldChannel](const mozilla::ipc::ResponseRejectReason) {
oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
callback->OnRedirectVerifyCallback(NS_BINDING_ABORTED);
});
// Clear out our nsIParentChannel functions, since a normal parent
// channel would actually redirect and not have those values on the new one.
// We expect the URI classifier to run on the redirected channel with
// the new URI and set these again.
mIParentChannelFunctions.Clear();
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -0,0 +1,220 @@
/* 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_DocumentChannelParent_h
#define mozilla_net_DocumentChannelParent_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 "nsICrossProcessSwitchChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsIParentChannel.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIRedirectResultListener.h"
#define DOCUMENT_CHANNEL_PARENT_IID \
{ \
0x3b393c56, 0x9e01, 0x11e9, { \
0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
} \
}
namespace mozilla {
namespace net {
// TODO: We currently don't implement nsIProgressEventSink and forward those
// to the child. Should we? We get the interface requested.
class DocumentChannelParent : public nsIInterfaceRequestor,
public PDocumentChannelParent,
public nsIAsyncVerifyRedirectReadyCallback,
public nsIParentChannel,
public nsIChannelEventSink,
public nsICrossProcessSwitchChannel,
public HttpChannelSecurityWarningReporter {
public:
explicit DocumentChannelParent(const dom::PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus);
bool Init(const DocumentChannelCreationArgs& aArgs);
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPARENTCHANNEL
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSICROSSPROCESSSWITCHCHANNEL
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_PARENT_IID)
bool RecvCancel(const nsresult& status);
bool RecvSuspend();
bool RecvResume();
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;
}
virtual void ActorDestroy(ActorDestroyReason why) override;
// Notify the DocumentChannelChild that we're switching
// to a different process and that it can notify listeners
// that it's finished.
void CancelChildForProcessSwitch();
private:
virtual ~DocumentChannelParent() = default;
void TriggerRedirectToRealChannel(
nsIChannel* aChannel,
const Maybe<uint64_t>& aDestinationProcess = Nothing(),
uint64_t aIdentifier = 0);
void RedirectToRealChannelFinished(nsresult aRv);
void FinishReplacementChannelSetup(bool aSucceeded);
// 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;
};
typedef mozilla::Variant<
nsIHttpChannel::FlashPluginState, ClassifierMatchedInfoParams,
ClassifierMatchedTrackingInfoParams, ClassificationFlagsParams>
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;
RefPtr<ParentChannelListener> mListener;
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;
RefPtr<mozilla::dom::BrowserParent> mBrowserParent;
// 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 we went through a redirect.
bool mDidUpstreamRedirect = false;
};
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentChannelParent,
DOCUMENT_CHANNEL_PARENT_IID)
} // namespace net
} // namespace mozilla
#endif // mozilla_net_DocumentChannelParent_h

View File

@ -348,5 +348,21 @@ struct CookieStruct
int32_t rawSameSite;
};
struct DocumentChannelCreationArgs {
URIParams? topWindowURI;
DocShellLoadStateInit loadState;
LoadInfoArgs loadInfo;
PrincipalInfo? contentBlockingAllowListPrincipal;
nsString? initiatorType;
nsString customUserAgent;
uint64_t channelId;
uint32_t loadFlags;
uint32_t loadType;
uint32_t cacheKey;
bool isActive;
bool isTopLevelDoc;
};
} // namespace ipc
} // namespace mozilla

View File

@ -131,6 +131,13 @@ bool NeckoChild::DeallocPAltDataOutputStreamChild(
return true;
}
already_AddRefed<PDocumentChannelChild> NeckoChild::AllocPDocumentChannelChild(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& args) {
MOZ_ASSERT_UNREACHABLE("AllocPDocumentChannelChild should not be called");
return nullptr;
}
PFTPChannelChild* NeckoChild::AllocPFTPChannelChild(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const FTPChannelCreationArgs& aOpenArgs) {

View File

@ -36,6 +36,10 @@ class NeckoChild : public PNeckoChild {
PHttpChannelChild* channel);
bool DeallocPAltDataOutputStreamChild(PAltDataOutputStreamChild* aActor);
already_AddRefed<PDocumentChannelChild> AllocPDocumentChannelChild(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& args);
PCookieServiceChild* AllocPCookieServiceChild();
bool DeallocPCookieServiceChild(PCookieServiceChild*);
PFTPChannelChild* AllocPFTPChannelChild(

View File

@ -124,6 +124,17 @@ inline bool MissingRequiredBrowserChild(
return false;
}
class HttpChannelSecurityWarningReporter : public nsISupports {
public:
virtual MOZ_MUST_USE nsresult ReportSecurityMessage(
const nsAString& aMessageTag, const nsAString& aMessageCategory) = 0;
virtual MOZ_MUST_USE nsresult LogBlockedCORSRequest(
const nsAString& aMessage, const nsACString& aCategory) = 0;
virtual MOZ_MUST_USE nsresult
LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
const nsAString& aURL, const nsAString& aContentType) = 0;
};
} // namespace net
} // namespace mozilla

View File

@ -18,6 +18,7 @@
#include "mozilla/net/WebSocketChannelParent.h"
#include "mozilla/net/WebSocketEventListenerParent.h"
#include "mozilla/net/DataChannelParent.h"
#include "mozilla/net/DocumentChannelParent.h"
#include "mozilla/net/SimpleChannelParent.h"
#include "mozilla/net/AltDataOutputStreamParent.h"
#include "mozilla/Unused.h"
@ -408,6 +409,37 @@ mozilla::ipc::IPCResult NeckoParent::RecvPFTPChannelConstructor(
return IPC_OK();
}
already_AddRefed<PDocumentChannelParent>
NeckoParent::AllocPDocumentChannelParent(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& args) {
nsCOMPtr<nsIPrincipal> requestingPrincipal =
GetRequestingPrincipal(Some(args.loadInfo()));
nsCOMPtr<nsILoadContext> loadContext;
const char* error = CreateChannelLoadContext(
aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext);
if (error) {
return nullptr;
}
PBOverrideStatus overrideStatus =
PBOverrideStatusFromLoadContext(aSerialized);
RefPtr<DocumentChannelParent> p =
new DocumentChannelParent(aBrowser, loadContext, overrideStatus);
return p.forget();
}
mozilla::ipc::IPCResult NeckoParent::RecvPDocumentChannelConstructor(
PDocumentChannelParent* aActor, const PBrowserOrId& aBrowser,
const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& aArgs) {
DocumentChannelParent* p = static_cast<DocumentChannelParent*>(aActor);
if (!p->Init(aArgs)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
PCookieServiceParent* NeckoParent::AllocPCookieServiceParent() {
return new CookieServiceParent();
}

View File

@ -127,6 +127,15 @@ class NeckoParent : public PNeckoParent {
PTCPSocketParent* AllocPTCPSocketParent(const nsString& host,
const uint16_t& port);
already_AddRefed<PDocumentChannelParent> AllocPDocumentChannelParent(
const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& args);
virtual mozilla::ipc::IPCResult RecvPDocumentChannelConstructor(
PDocumentChannelParent* aActor, const PBrowserOrId& aBrowser,
const SerializedLoadContext& aSerialized,
const DocumentChannelCreationArgs& aArgs) override;
bool DeallocPDocumentChannelParent(PDocumentChannelParent* channel);
bool DeallocPTCPSocketParent(PTCPSocketParent*);
PTCPServerSocketParent* AllocPTCPServerSocketParent(
const uint16_t& aLocalPort, const uint16_t& aBacklog,

View File

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
/* 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/. */
include protocol PNecko;
include protocol PStreamFilter;
include InputStreamParams;
include URIParams;
include PBackgroundSharedTypes;
include NeckoChannelParams;
include IPCServiceWorkerDescriptor;
include IPCStream;
include DOMTypes;
include "mozilla/net/NeckoMessageUtils.h";
using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
using mozilla::net::NetAddr from "mozilla/net/DNS.h";
using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
namespace mozilla {
namespace net {
refcounted protocol PDocumentChannel
{
manager PNecko;
parent:
async Suspend();
async Resume();
async Cancel(nsresult status);
async __delete__();
child:
// Used to cancel child channel if we hit errors during creating and
// AsyncOpen of nsHttpChannel on the parent.
async FailedAsyncOpen(nsresult status);
async NotifyChannelClassifierProtectionDisabled(uint32_t acceptedReason);
async NotifyCookieAllowed();
async NotifyCookieBlocked(uint32_t rejectedReason);
async SetClassifierMatchedInfo(nsCString list, nsCString provider, nsCString fullHash);
async SetClassifierMatchedTrackingInfo(nsCString lists, nsCString fullHash);
// This message is sent to a child that has been redirected to another process.
// As a consequence, it should cleanup the channel listeners and remove the
// request from the loadGroup.
async CancelForProcessSwitch();
async RedirectToRealChannel(uint32_t aRegistrarId,
nsIURI aURI,
uint32_t aNewLoadFlags,
ReplacementChannelConfigInit? aInit,
LoadInfoArgs? aLoadInfo,
uint64_t aChannelId,
nsIURI aOriginalURI,
uint32_t aRedirectMode,
uint32_t aRedirectFlags,
uint32_t? aContentDisposition,
nsString? aContentDispositionFilename) returns(nsresult rv);
async ConfirmRedirect(nsIURI aNewURI) returns(nsresult rv);
// Tell child to delete channel (all IPDL deletes must be done from child to
// avoid races: see bug 591708).
async DeleteSelf();
};
} // namespace net
} // namespace mozilla

View File

@ -29,6 +29,7 @@ include protocol PClassifierDummyChannel;
include protocol PWebrtcProxyChannel;
include protocol PSocketProcessBridge;
include protocol PProxyConfigLookup;
include protocol PDocumentChannel;
include IPCStream;
include URIParams;
@ -69,6 +70,7 @@ nested(upto inside_cpow) sync protocol PNecko
manages PClassifierDummyChannel;
manages PWebrtcProxyChannel;
manages PProxyConfigLookup;
manages PDocumentChannel;
parent:
async __delete__();
@ -88,6 +90,10 @@ parent:
async PDNSRequest(nsCString hostName, OriginAttributes originAttributes,
uint32_t flags);
async PDocumentChannel(PBrowserOrId browser,
SerializedLoadContext loadContext,
DocumentChannelCreationArgs args);
async PWebSocketEventListener(uint64_t aInnerWindowID);
/* Predictor Methods */

View File

@ -6,6 +6,8 @@
EXPORTS.mozilla.net += [
'ChannelEventQueue.h',
'DocumentChannelChild.h',
'DocumentChannelParent.h',
'NeckoChild.h',
'NeckoCommon.h',
'NeckoMessageUtils.h',
@ -21,6 +23,8 @@ EXPORTS.mozilla.net += [
UNIFIED_SOURCES += [
'ChannelEventQueue.cpp',
'DocumentChannelChild.cpp',
'DocumentChannelParent.cpp',
'NeckoChild.cpp',
'NeckoCommon.cpp',
'NeckoParent.cpp',
@ -37,6 +41,7 @@ IPDL_SOURCES = [
'NeckoChannelParams.ipdlh',
'PChannelDiverter.ipdl',
'PDataChannel.ipdl',
'PDocumentChannel.ipdl',
'PFileChannel.ipdl',
'PNecko.ipdl',
'PSimpleChannel.ipdl',

View File

@ -753,8 +753,14 @@ bool HttpChannelParent::ConnectChannel(const uint32_t& registrarId,
nsCOMPtr<nsINetworkInterceptController> controller;
NS_QueryNotificationCallbacks(channel, controller);
RefPtr<ParentChannelListener> parentListener = do_QueryObject(controller);
MOZ_ASSERT(parentListener);
parentListener->SetupInterceptionAfterRedirect(shouldIntercept);
if (parentListener) {
// It's possible with DocumentChannel redirects that we failed to Suspend
// the nsHttpChannel, so it has delivered OnDataAvailable/OnStopRequest
// to the DocumentChannelParent, and then cleared the listener pointer.
// In that case the DocumentChannelParent will handle forwarding
// those messages to us so we don't need to add the listener again.
parentListener->SetupInterceptionAfterRedirect(shouldIntercept);
}
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB

View File

@ -51,6 +51,7 @@ EXPORTS.mozilla.net += [
'HttpInfo.h',
'nsServerTiming.h',
'NullHttpChannel.h',
'ParentChannelListener.h',
'PHttpChannelParams.h',
'PSpdyPush.h',
'TimingStruct.h',

View File

@ -46,17 +46,7 @@ namespace net {
class nsChannelClassifier;
class Http2PushedStream;
class HttpChannelSecurityWarningReporter : public nsISupports {
public:
virtual MOZ_MUST_USE nsresult ReportSecurityMessage(
const nsAString& aMessageTag, const nsAString& aMessageCategory) = 0;
virtual MOZ_MUST_USE nsresult LogBlockedCORSRequest(
const nsAString& aMessage, const nsACString& aCategory) = 0;
virtual MOZ_MUST_USE nsresult
LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
const nsAString& aURL, const nsAString& aContentType) = 0;
};
class HttpChannelSecurityWarningReporter;
//-----------------------------------------------------------------------------
// nsHttpChannel

View File

@ -247,7 +247,11 @@ add_task(async function() {
}
);
let browser1LoadHasStopped = BrowserTestUtils.browserStopped(browser1);
let browser1LoadHasStopped = BrowserTestUtils.browserStopped(
browser1,
undefined,
true
);
await BrowserTestUtils.loadURI(
browser1,
@ -267,7 +271,11 @@ add_task(async function() {
"example.org",
true
);
let browser1LoadHasStoppedAgain = BrowserTestUtils.browserStopped(browser1);
let browser1LoadHasStoppedAgain = BrowserTestUtils.browserStopped(
browser1,
undefined,
true
);
await BrowserTestUtils.loadURI(
browser1,
kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html"