Backed out 7 changesets (bug 1641737) assertion failures. CLOSED TREE

Backed out changeset 7f7b98339065 (bug 1641737)
Backed out changeset 32fba417ebd0 (bug 1641737)
Backed out changeset abd9cd77f3cb (bug 1641737)
Backed out changeset e3bf9a45db6a (bug 1641737)
Backed out changeset 94d47578009c (bug 1641737)
Backed out changeset 28bca5c5b8b4 (bug 1641737)
Backed out changeset 0296d0f6c1d1 (bug 1641737)
This commit is contained in:
Razvan Maries 2020-06-09 18:55:08 +03:00
Родитель ef73a88351
Коммит caec984d54
12 изменённых файлов: 382 добавлений и 473 удалений

Просмотреть файл

@ -0,0 +1,60 @@
/* 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_ADocumentChannelBridge_h
#define mozilla_net_ADocumentChannelBridge_h
#include "mozilla/net/PDocumentChannelParent.h"
#include "mozilla/dom/nsCSPContext.h"
namespace mozilla {
namespace net {
/**
* ADocumentChannelBridge is the interface for DocumentLoadListener to
* communicate with the nsIChannel placeholder in the docshell. It may be
* implemented over IPDL.
*/
class ADocumentChannelBridge {
public:
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
// Notify the destination docshell that we're not going to send
// a response to it (usually because we've redirected to a different
// process), and drop any references to the parent DocumentLoadListener.
// This should remove the nsIChannel from the loadgroup, and
// fire OnStart/StopRequest with aStatus.
// aLoadGroupStatus is used as mStatus when we remove the child channel
// from the loadgroup (but aStatus is passed as the parameter to
// RemoveRequest).
// We do this so we can remove using NS_BINDING_RETARGETED, but still have
// the channel not be in an error state.
virtual void DisconnectChildListeners(nsresult aStatus,
nsresult aLoadGroupStatus) = 0;
// Delete the bridge, and drop any refs to the DocumentLoadListener
virtual void Delete() = 0;
// Initate a switch from the DocumentChannel to the protocol-specific
// real channel.
virtual RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) = 0;
// Returns the process id that this bridge is connected to.
// If 0 indicates that the load is started from the parent process.
virtual base::ProcessId OtherPid() const = 0;
protected:
virtual ~ADocumentChannelBridge() = default;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_ADocumentChannelBridge_h

Просмотреть файл

@ -125,6 +125,13 @@ IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
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(
RedirectToRealChannelArgs&& aArgs,
nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,

Просмотреть файл

@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocumentChannelParent.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ClientInfo.h"
@ -35,57 +34,28 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
LOG(("DocumentChannelParent Init [this=%p, uri=%s]", this,
loadState->URI()->GetSpecOrDefault().get()));
RefPtr<DocumentLoadListener::OpenPromise> promise;
if (loadState->GetLoadIdentifier()) {
promise = DocumentLoadListener::ClaimParentLoad(
getter_AddRefs(mDocumentLoadListener), loadState->GetLoadIdentifier());
if (!promise) {
return false;
}
} else {
mDocumentLoadListener = new DocumentLoadListener(aContext);
Maybe<ClientInfo> clientInfo;
if (aArgs.initialClientInfo().isSome()) {
clientInfo.emplace(ClientInfo(aArgs.initialClientInfo().ref()));
}
nsresult rv = NS_ERROR_UNEXPECTED;
promise = mDocumentLoadListener->Open(
loadState, aArgs.cacheKey(), Some(aArgs.channelId()),
aArgs.asyncOpenTime(), aArgs.timing().refOr(nullptr),
std::move(clientInfo), aArgs.outerWindowId(),
aArgs.hasValidTransientUserAction(), Some(aArgs.uriModified()),
Some(aArgs.isXFOError()), IProtocol::OtherPid(), &rv);
if (NS_FAILED(rv)) {
MOZ_ASSERT(!promise);
return SendFailedAsyncOpen(rv);
}
mParent = DocumentLoadListener::ClaimParentLoad(
loadState->GetLoadIdentifier(), this);
return !!mParent;
}
RefPtr<DocumentChannelParent> self = this;
promise->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
// The DLL is waiting for us to resolve the
// PDocumentChannel::RedirectToRealChannelPromise given as parameter.
auto promise = self->RedirectToRealChannel(
std::move(aResolveValue.mStreamFilterEndpoints),
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags);
// We chain the promise the DLL is waiting on to the one returned by
// RedirectToRealChannel. As soon as the promise returned is resolved
// or rejected, so will the DLL's promise.
promise->ChainTo(aResolveValue.mPromise.forget(), __func__);
self->mDocumentLoadListener = nullptr;
},
[self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
if (!self->CanSend()) {
return;
}
Unused << self->SendDisconnectChildListeners(
aRejectValue.mStatus, aRejectValue.mLoadGroupStatus);
self->mDocumentLoadListener = nullptr;
});
mParent = new DocumentLoadListener(aContext, this);
Maybe<ClientInfo> clientInfo;
if (aArgs.initialClientInfo().isSome()) {
clientInfo.emplace(ClientInfo(aArgs.initialClientInfo().ref()));
}
nsresult rv = NS_ERROR_UNEXPECTED;
if (!mParent->Open(loadState, aArgs.cacheKey(), Some(aArgs.channelId()),
aArgs.asyncOpenTime(), aArgs.timing().refOr(nullptr),
std::move(clientInfo), aArgs.outerWindowId(),
aArgs.hasValidTransientUserAction(),
Some(aArgs.uriModified()), Some(aArgs.isXFOError()),
&rv)) {
return SendFailedAsyncOpen(rv);
}
return true;
}
@ -95,12 +65,12 @@ DocumentChannelParent::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) {
if (!CanSend()) {
if (!CanSend() || !mParent) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
}
RedirectToRealChannelArgs args;
mDocumentLoadListener->SerializeRedirectData(
mParent->SerializeRedirectData(
args, false, aRedirectFlags, aLoadFlags,
static_cast<ContentParent*>(Manager()->Manager()));
return SendRedirectToRealChannel(args, std::move(aStreamFilterEndpoints));

Просмотреть файл

@ -7,8 +7,8 @@
#ifndef mozilla_net_DocumentChannelParent_h
#define mozilla_net_DocumentChannelParent_h
#include "mozilla/net/DocumentLoadListener.h"
#include "mozilla/net/PDocumentChannelParent.h"
#include "mozilla/net/DocumentLoadListener.h"
namespace mozilla {
namespace dom {
@ -17,10 +17,12 @@ class CanonicalBrowsingContext;
namespace net {
/**
* An actor that forwards all changes across to DocumentChannelChild, the
* nsIChannel implementation owned by a content process docshell.
* An implementation of ADocumentChannelBridge that forwards all changes across
* to DocumentChannelChild, the nsIChannel implementation owned by a content
* process docshell.
*/
class DocumentChannelParent final : public PDocumentChannelParent {
class DocumentChannelParent final : public ADocumentChannelBridge,
public PDocumentChannelParent {
public:
NS_INLINE_DECL_REFCOUNTING(DocumentChannelParent, override);
@ -31,27 +33,45 @@ class DocumentChannelParent final : public PDocumentChannelParent {
// PDocumentChannelParent
bool RecvCancel(const nsresult& aStatus) {
if (mDocumentLoadListener) {
mDocumentLoadListener->Cancel(aStatus);
if (mParent) {
mParent->Cancel(aStatus);
}
return true;
}
void ActorDestroy(ActorDestroyReason aWhy) override {
if (mDocumentLoadListener) {
mDocumentLoadListener->Cancel(NS_BINDING_ABORTED);
if (mParent) {
mParent->DocumentChannelBridgeDisconnected();
mParent = nullptr;
}
}
private:
// DocumentChannelListener
void DisconnectChildListeners(nsresult aStatus,
nsresult aLoadGroupStatus) override {
if (CanSend()) {
Unused << SendDisconnectChildListeners(aStatus, aLoadGroupStatus);
}
mParent = nullptr;
}
void Delete() override {
if (CanSend()) {
Unused << SendDeleteSelf();
}
}
ProcessId OtherPid() const override { return IProtocol::OtherPid(); }
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags);
uint32_t aRedirectFlags, uint32_t aLoadFlags) override;
virtual ~DocumentChannelParent();
RefPtr<DocumentLoadListener> mDocumentLoadListener;
RefPtr<DocumentLoadListener> mParent;
};
} // namespace net

Просмотреть файл

@ -30,9 +30,9 @@
#include "nsDocShellLoadTypes.h"
#include "nsExternalHelperAppService.h"
#include "nsHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIBrowser.h"
#include "nsIE10SUtils.h"
#include "nsIHttpChannelInternal.h"
#include "nsIStreamConverterService.h"
#include "nsIViewSourceChannel.h"
#include "nsImportModule.h"
@ -218,7 +218,7 @@ class ParentProcessDocumentOpenInfo final : public nsDocumentOpenInfo,
// reference cycle.
RefPtr<DocumentLoadListener> doc = do_GetInterface(ToSupports(mListener));
MOZ_ASSERT(doc);
doc->DisconnectListeners(NS_BINDING_RETARGETED, NS_OK);
doc->DisconnectChildListeners(NS_BINDING_RETARGETED, NS_OK);
mListener->SetListenerAfterRedirect(nullptr);
}
@ -254,12 +254,23 @@ NS_INTERFACE_MAP_BEGIN(DocumentLoadListener)
NS_INTERFACE_MAP_END
DocumentLoadListener::DocumentLoadListener(
CanonicalBrowsingContext* aBrowsingContext) {
CanonicalBrowsingContext* aBrowsingContext, ADocumentChannelBridge* aBridge)
: mDocumentChannelBridge(aBridge) {
MOZ_ASSERT(aBridge);
LOG(("DocumentLoadListener ctor [this=%p]", this));
mParentChannelListener = new ParentChannelListener(
this, aBrowsingContext, aBrowsingContext->UsePrivateBrowsing());
}
DocumentLoadListener::DocumentLoadListener(
CanonicalBrowsingContext* aBrowsingContext,
base::ProcessId aPendingBridgeProcess) {
LOG(("DocumentLoadListener ctor [this=%p]", this));
mParentChannelListener = new ParentChannelListener(
this, aBrowsingContext, aBrowsingContext->UsePrivateBrowsing());
mPendingDocumentChannelBridgeProcess = Some(aPendingBridgeProcess);
}
DocumentLoadListener::~DocumentLoadListener() {
LOG(("DocumentLoadListener dtor [this=%p]", this));
}
@ -358,13 +369,12 @@ CanonicalBrowsingContext* DocumentLoadListener::GetBrowsingContext() {
return mParentChannelListener->GetBrowsingContext();
}
auto DocumentLoadListener::Open(
bool DocumentLoadListener::Open(
nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
nsDOMNavigationTiming* aTiming, Maybe<ClientInfo>&& aInfo,
uint64_t aOuterWindowId, bool aHasGesture, Maybe<bool> aUriModified,
Maybe<bool> aIsXFOError, base::ProcessId aPid, nsresult* aRv)
-> RefPtr<OpenPromise> {
Maybe<bool> aIsXFOError, nsresult* aRv) {
LOG(("DocumentLoadListener Open [this=%p, uri=%s]", this,
aLoadState->URI()->GetSpecOrDefault().get()));
RefPtr<CanonicalBrowsingContext> browsingContext =
@ -390,7 +400,7 @@ auto DocumentLoadListener::Open(
nullptr, attrs, loadFlags, aCacheKey, *aRv,
getter_AddRefs(mChannel))) {
mParentChannelListener = nullptr;
return nullptr;
return false;
}
nsCOMPtr<nsIURI> uriBeingLoaded =
@ -441,7 +451,7 @@ auto DocumentLoadListener::Open(
// we want the original request so that we get different ones for
// each part of a multipart channel.
nsCOMPtr<nsIViewSourceChannel> viewSourceChannel;
if (aPid && (viewSourceChannel = do_QueryInterface(mChannel))) {
if (OtherPid() && (viewSourceChannel = do_QueryInterface(mChannel))) {
viewSourceChannel->SetReplaceRequest(false);
}
@ -487,12 +497,12 @@ auto DocumentLoadListener::Open(
if (aValue.IsResolve()) {
bool handled = aValue.ResolveValue();
if (handled) {
self->DisconnectListeners(NS_ERROR_ABORT, NS_ERROR_ABORT);
self->DisconnectChildListeners(NS_ERROR_ABORT, NS_ERROR_ABORT);
mParentChannelListener = nullptr;
} else {
nsresult rv = mChannel->AsyncOpen(openInfo);
if (NS_FAILED(rv)) {
self->DisconnectListeners(rv, rv);
self->DisconnectChildListeners(rv, rv);
mParentChannelListener = nullptr;
}
}
@ -504,11 +514,10 @@ auto DocumentLoadListener::Open(
*aRv = mChannel->AsyncOpen(openInfo);
if (NS_FAILED(*aRv)) {
mParentChannelListener = nullptr;
return nullptr;
return false;
}
}
mOtherPid = aPid;
mChannelCreationURI = aLoadState->URI();
mLoadStateLoadFlags = aLoadState->LoadFlags();
mLoadStateLoadType = aLoadState->LoadType();
@ -524,13 +533,7 @@ auto DocumentLoadListener::Open(
if (auto* ctx = GetBrowsingContext()) {
ctx->StartDocumentLoad(this);
}
*aRv = NS_OK;
mOpenPromise = new OpenPromise::Private(__func__);
// We make the promise use direct task dispatch in order to reduce the number
// of event loops iterations.
mOpenPromise->UseDirectTaskDispatch(__func__);
return mOpenPromise;
return true;
}
/* static */
@ -598,16 +601,15 @@ bool DocumentLoadListener::OpenFromParent(
// not supporting yet.
Maybe<dom::ClientInfo> initialClientInfo;
RefPtr<DocumentLoadListener> listener =
new DocumentLoadListener(aBrowsingContext);
RefPtr<DocumentLoadListener> listener = new DocumentLoadListener(
aBrowsingContext, aBrowsingContext->GetContentParent()->OtherPid());
nsresult rv;
auto promise = listener->Open(
loadState, cacheKey, channelId, TimeStamp::Now(), timing,
std::move(initialClientInfo), aOuterWindowId, false, Nothing(), Nothing(),
aBrowsingContext->GetContentParent()->OtherPid(), &rv);
if (promise) {
MOZ_ASSERT(NS_SUCCEEDED(rv));
bool result =
listener->Open(loadState, cacheKey, channelId, TimeStamp::Now(), timing,
std::move(initialClientInfo), aOuterWindowId, false,
Nothing(), Nothing(), &rv);
if (result) {
// Create an entry in the redirect channel registrar to
// allocate an identifier for this load.
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
@ -618,7 +620,7 @@ bool DocumentLoadListener::OpenFromParent(
rv = registrar->LinkChannels(*aOutIdent, listener, nullptr);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
return !!promise;
return result;
}
void DocumentLoadListener::CleanupParentLoadAttempt(uint32_t aLoadIdent) {
@ -632,15 +634,14 @@ void DocumentLoadListener::CleanupParentLoadAttempt(uint32_t aLoadIdent) {
if (loadListener) {
// If the load listener is still registered, then we must have failed
// to connect DocumentChannel into it. Better cancel it!
loadListener->NotifyDocumentChannelFailed();
loadListener->NotifyBridgeFailed();
}
registrar->DeregisterChannels(aLoadIdent);
}
auto DocumentLoadListener::ClaimParentLoad(DocumentLoadListener** aListener,
uint32_t aLoadIdent)
-> RefPtr<OpenPromise> {
already_AddRefed<DocumentLoadListener> DocumentLoadListener::ClaimParentLoad(
uint32_t aLoadIdent, ADocumentChannelBridge* aBridge) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
@ -649,34 +650,46 @@ auto DocumentLoadListener::ClaimParentLoad(DocumentLoadListener** aListener,
RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
registrar->DeregisterChannels(aLoadIdent);
MOZ_ASSERT(loadListener && loadListener->mOpenPromise);
loadListener.forget(aListener);
return (*aListener)->mOpenPromise;
MOZ_ASSERT(loadListener);
if (loadListener) {
loadListener->NotifyBridgeConnected(aBridge);
}
return loadListener.forget();
}
void DocumentLoadListener::NotifyDocumentChannelFailed() {
LOG(("DocumentLoadListener NotifyDocumentChannelFailed [this=%p]", this));
// There's been no calls to ClaimParentLoad, and so no listeners have been
// attached to mOpenPromise yet. As such we can run Then() on it.
mOpenPromise->Then(
GetMainThreadSerialEventTarget(), __func__,
[](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
aResolveValue.mPromise->Resolve(NS_BINDING_ABORTED, __func__);
},
[]() {});
void DocumentLoadListener::NotifyBridgeConnected(
ADocumentChannelBridge* aBridge) {
LOG(("DocumentLoadListener NotifyBridgeConnected [this=%p]", this));
MOZ_ASSERT(!mDocumentChannelBridge);
MOZ_ASSERT(mPendingDocumentChannelBridgeProcess);
MOZ_ASSERT(aBridge->OtherPid() == *mPendingDocumentChannelBridgeProcess);
mDocumentChannelBridge = aBridge;
mPendingDocumentChannelBridgeProcess.reset();
mBridgePromise.ResolveIfExists(aBridge, __func__);
}
void DocumentLoadListener::NotifyBridgeFailed() {
LOG(("DocumentLoadListener NotifyBridgeFailed [this=%p]", this));
MOZ_ASSERT(!mDocumentChannelBridge);
MOZ_ASSERT(mPendingDocumentChannelBridgeProcess);
mPendingDocumentChannelBridgeProcess.reset();
Cancel(NS_BINDING_ABORTED);
mBridgePromise.RejectIfExists(false, __func__);
}
void DocumentLoadListener::Disconnect() {
LOG(("DocumentLoadListener Disconnect [this=%p]", this));
void DocumentLoadListener::DocumentChannelBridgeDisconnected() {
LOG(("DocumentLoadListener DocumentChannelBridgeDisconnected [this=%p]",
this));
// 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);
}
mDocumentChannelBridge = nullptr;
if (auto* ctx = GetBrowsingContext()) {
ctx->EndDocumentLoad(this);
@ -701,20 +714,32 @@ void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
mChannel->Cancel(aStatusCode);
}
DisconnectListeners(aStatusCode, aStatusCode);
DisconnectChildListeners(aStatusCode, aStatusCode);
}
void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
nsresult aLoadGroupStatus) {
void DocumentLoadListener::DisconnectChildListeners(nsresult aStatus,
nsresult aLoadGroupStatus) {
LOG(
("DocumentLoadListener DisconnectListener [this=%p, "
("DocumentLoadListener DisconnectChildListener [this=%p, "
"aStatus=%" PRIx32 " aLoadGroupStatus=%" PRIx32 " ]",
this, static_cast<uint32_t>(aStatus),
static_cast<uint32_t>(aLoadGroupStatus)));
RejectOpenPromise(aStatus, aLoadGroupStatus, __func__);
Disconnect();
RefPtr<DocumentLoadListener> keepAlive(this);
if (mDocumentChannelBridge) {
// This will drop the bridge's reference to us, so we use keepAlive to
// make sure we don't get deleted until we exit the function.
mDocumentChannelBridge->DisconnectChildListeners(aStatus, aLoadGroupStatus);
} else if (mPendingDocumentChannelBridgeProcess) {
EnsureBridge()->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[keepAlive, aStatus,
aLoadGroupStatus](ADocumentChannelBridge* aBridge) {
aBridge->DisconnectChildListeners(aStatus, aLoadGroupStatus);
keepAlive->mDocumentChannelBridge = nullptr;
},
[](bool aDummy) {});
}
DocumentChannelBridgeDisconnected();
// If we're not going to send anything else to the content process, and
// we haven't yet consumed a stream filter promise, then we're never going
@ -729,7 +754,6 @@ void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
("DocumentLoadListener RedirectToRealChannelFinished [this=%p, "
"aRv=%" PRIx32 " ]",
this, static_cast<uint32_t>(aRv)));
if (NS_FAILED(aRv) || !mRedirectChannelId) {
FinishReplacementChannelSetup(aRv);
return;
@ -773,25 +797,20 @@ void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
"aResult=%x]",
this, int(aResult)));
bool disconnected = false;
if (mDoingProcessSwitch) {
DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
disconnected = true;
DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
}
if (!mRedirectChannelId) {
if (NS_FAILED(aResult)) {
mChannel->Cancel(aResult);
mChannel->Resume();
if (!disconnected) {
DisconnectListeners(aResult, aResult);
}
DisconnectChildListeners(aResult, aResult);
return;
}
ApplyPendingFunctions(mChannel);
// The channel has already been resumed by the ParentProcessDocumentChannel
// so we can return early.
// ResumeSuspendedChannel will be called later as RedirectToRealChannel
// continues, so we can return early.
return;
}
@ -836,6 +855,7 @@ void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
MOZ_ASSERT(
!SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
Delete();
redirectChannel->SetParentListener(mParentChannelListener);
ApplyPendingFunctions(redirectChannel);
@ -1346,7 +1366,7 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
LOG(("Process Switch: Calling nsIBrowser::PerformProcessSwitch"));
// We're switching a toplevel BrowsingContext's process. This has to be done
// using nsIBrowser.
RefPtr<dom::Promise> domPromise;
RefPtr<Promise> domPromise;
browser->PerformProcessSwitch(remoteType, mCrossProcessRedirectIdentifier,
isCOOPSwitch, getter_AddRefs(domPromise));
MOZ_DIAGNOSTIC_ASSERT(domPromise,
@ -1367,6 +1387,17 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
return true;
}
auto DocumentLoadListener::EnsureBridge() -> RefPtr<EnsureBridgePromise> {
MOZ_ASSERT(mDocumentChannelBridge || mPendingDocumentChannelBridgeProcess);
if (mDocumentChannelBridge) {
MOZ_ASSERT(mBridgePromise.IsEmpty());
return EnsureBridgePromise::CreateAndResolve(mDocumentChannelBridge,
__func__);
}
return mBridgePromise.Ensure(__func__);
}
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
DocumentLoadListener::RedirectToRealChannel(
uint32_t aRedirectFlags, uint32_t aLoadFlags,
@ -1433,34 +1464,23 @@ DocumentLoadListener::RedirectToRealChannel(
std::move(aStreamFilterEndpoints));
}
if (mOpenPromiseResolved) {
LOG(
("DocumentLoadListener RedirectToRealChannel [this=%p] "
"promise already resolved. Aborting.",
this));
// The promise has already been resolved or aborted, so we have no way to
// return a promise again to the listener which would cancel the operation.
// Reject the promise immediately.
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndResolve(NS_BINDING_ABORTED, __func__);
}
// This promise will be passed on the promise listener which will
// resolve this promise for us.
auto promise =
MakeRefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>(
__func__);
mOpenPromise->Resolve(
OpenPromiseSucceededType({std::move(aStreamFilterEndpoints),
aRedirectFlags, aLoadFlags, promise}),
__func__);
// There is no way we could come back here if the promise had been resolved
// previously. But for clarity and to avoid all doubt, we set this boolean to
// true.
mOpenPromiseResolved = true;
return promise;
return EnsureBridge()->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[self = RefPtr<DocumentLoadListener>(this),
endpoints = std::move(aStreamFilterEndpoints), aRedirectFlags,
aLoadFlags](ADocumentChannelBridge* aBridge) mutable {
if (self->mCancelled) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndResolve(NS_BINDING_ABORTED, __func__);
}
return aBridge->RedirectToRealChannel(std::move(endpoints),
aRedirectFlags, aLoadFlags);
},
[](bool aDummy) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ipc::ResponseRejectReason::ActorDestroyed,
__func__);
});
}
void DocumentLoadListener::TriggerRedirectToRealChannel(
@ -1560,7 +1580,6 @@ void DocumentLoadListener::TriggerRedirectToRealChannel(
NS_IMETHODIMP
DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
LOG(("DocumentLoadListener OnStartRequest [this=%p]", this));
nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
if (multiPartChannel) {
multiPartChannel->GetBaseChannel(getter_AddRefs(mChannel));
@ -1568,9 +1587,12 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
mChannel = do_QueryInterface(aRequest);
}
MOZ_DIAGNOSTIC_ASSERT(mChannel);
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
if (!mDocumentChannelBridge && !mPendingDocumentChannelBridgeProcess) {
return NS_ERROR_UNEXPECTED;
}
// Enforce CSP frame-ancestors and x-frame-options checks which
// might cancel the channel.
nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
@ -1584,23 +1606,21 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
nsresult status = NS_OK;
aRequest->GetStatus(&status);
if (status == NS_ERROR_NO_CONTENT) {
DisconnectListeners(status, status);
DisconnectChildListeners(status, status);
return NS_OK;
}
mStreamListenerFunctions.AppendElement(StreamListenerFunction{
VariantIndex<0>{}, OnStartRequestParams{aRequest}});
if (mOpenPromiseResolved || mInitiatedRedirectToRealChannel) {
// I we have already resolved the promise, there's no point to continue
// attempting a process switch or redirecting to the real channel.
// We can also have multiple calls to OnStartRequest when dealing with
// multi-part content, but only want to redirect once.
if (!mInitiatedRedirectToRealChannel) {
mChannel->Suspend();
} else {
// This can be called multiple time if we have a multipart
// decoder. Since we've already added the reqest to
// mStreamListenerFunctions, we don't need to do anything else.
return NS_OK;
}
mChannel->Suspend();
mInitiatedRedirectToRealChannel = true;
// Determine if a new process needs to be spawned. If it does, this will
@ -1691,7 +1711,7 @@ DocumentLoadListener::OnAfterLastPart(nsresult aStatus) {
// channel, then it means we never got OnStartRequest (maybe a problem?)
// and we retargeted everything.
LOG(("DocumentLoadListener Disconnecting child"));
DisconnectListeners(NS_BINDING_RETARGETED, NS_OK);
DisconnectChildListeners(NS_BINDING_RETARGETED, NS_OK);
return NS_OK;
}
mStreamListenerFunctions.AppendElement(StreamListenerFunction{
@ -1700,6 +1720,13 @@ DocumentLoadListener::OnAfterLastPart(nsresult aStatus) {
return NS_OK;
}
NS_IMETHODIMP
DocumentLoadListener::SetParentListener(
mozilla::net::ParentChannelListener* listener) {
// We don't need this (do we?)
return NS_OK;
}
NS_IMETHODIMP
DocumentLoadListener::GetInterface(const nsIID& aIID, void** result) {
RefPtr<CanonicalBrowsingContext> browsingContext =
@ -1712,17 +1739,6 @@ DocumentLoadListener::GetInterface(const nsIID& aIID, void** result) {
return QueryInterface(aIID, result);
}
////////////////////////////////////////////////////////////////////////////////
// nsIParentChannel
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
DocumentLoadListener::SetParentListener(
mozilla::net::ParentChannelListener* listener) {
// We don't need this (do we?)
return NS_OK;
}
// 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.
@ -1771,14 +1787,12 @@ DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags,
NS_IMETHODIMP
DocumentLoadListener::Delete() {
MOZ_ASSERT_UNREACHABLE("This method is unused");
if (mDocumentChannelBridge) {
mDocumentChannelBridge->Delete();
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannelEventSink
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
DocumentLoadListener::AsyncOnChannelRedirect(
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,

Просмотреть файл

@ -9,17 +9,18 @@
#include "mozilla/MozPromise.h"
#include "mozilla/Variant.h"
#include "mozilla/dom/SessionHistoryEntry.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 "mozilla/dom/SessionHistoryEntry.h"
#include "nsDOMNavigationTiming.h"
#include "nsIInterfaceRequestor.h"
#include "nsIMultiPartChannel.h"
#include "nsIParentChannel.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIRedirectResultListener.h"
#include "nsIMultiPartChannel.h"
#define DOCUMENT_LOAD_LISTENER_IID \
{ \
@ -90,48 +91,21 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
public HttpChannelSecurityWarningReporter,
public nsIMultiPartChannelListener {
public:
explicit DocumentLoadListener(
dom::CanonicalBrowsingContext* aBrowsingContext);
struct OpenPromiseSucceededType {
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>
mStreamFilterEndpoints;
uint32_t mRedirectFlags;
uint32_t mLoadFlags;
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise::Private>
mPromise;
};
struct OpenPromiseFailedType {
nsresult mStatus;
nsresult mLoadGroupStatus;
};
typedef MozPromise<OpenPromiseSucceededType, OpenPromiseFailedType,
true /* isExclusive */>
OpenPromise;
explicit DocumentLoadListener(dom::CanonicalBrowsingContext* aBrowsingContext,
ADocumentChannelBridge* aBridge);
// Creates the channel, and then calls AsyncOpen on it.
// The DocumentLoadListener will require additional process from the consumer
// in order to complete the redirect to the end channel. This is done by
// returning a RedirectToRealChannelPromise and then waiting for it to be
// resolved or rejected accordingly.
// Once that promise is resolved; the consumer no longer needs to hold a
// reference to the DocumentLoadListener nor will the consumer required to be
// used again.
RefPtr<OpenPromise> Open(nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
const Maybe<uint64_t>& aChannelId,
const TimeStamp& aAsyncOpenTime,
nsDOMNavigationTiming* aTiming,
Maybe<dom::ClientInfo>&& aInfo,
uint64_t aOuterWindowId, bool aHasGesture,
Maybe<bool> aUriModified, Maybe<bool> aIsXFOError,
base::ProcessId aPid, nsresult* aRv);
bool Open(nsDocShellLoadState* aLoadState, uint32_t aCacheKey,
const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
nsDOMNavigationTiming* aTiming, Maybe<dom::ClientInfo>&& aInfo,
uint64_t aOuterWindowId, bool aHasGesture, Maybe<bool> aUriModified,
Maybe<bool> aIsXFOError, nsresult* aRv);
// Creates a DocumentLoadListener directly in the parent process without
// an associated DocumentChannel.
// an associated DocumentChannelBridge.
// If successful it registers a unique identifier (return in aOutIdent) to
// keep it alive until a future DocumentChannel can attach to it, or we fail
// and clean up.
// keep it alive until a future bridge can attach to it, or we fail and clean
// up.
static bool OpenFromParent(dom::CanonicalBrowsingContext* aBrowsingContext,
nsDocShellLoadState* aLoadState,
uint64_t aOuterWindowId, uint32_t* aOutIdent);
@ -142,12 +116,9 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
static void CleanupParentLoadAttempt(uint32_t aLoadIdent);
// Looks up aLoadIdent to find the associated, cleans up the registration
static RefPtr<OpenPromise> ClaimParentLoad(DocumentLoadListener** aListener,
uint32_t aLoadIdent);
// Called by the DocumentChannelParent if actor got destroyed or the parent
// channel got deleted.
void Abort();
// and attaches aBridge as the listener.
static already_AddRefed<DocumentLoadListener> ClaimParentLoad(
uint32_t aLoadIdent, ADocumentChannelBridge* aBridge);
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
@ -168,7 +139,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_LOAD_LISTENER_IID)
// Called by the DocumentChannel if cancelled.
void Cancel(const nsresult& status);
nsIChannel* GetChannel() const { return mChannel; }
@ -206,13 +176,27 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
return NS_OK;
}
// Called by the bridge when it disconnects, so that we can drop
// our reference to it.
void DocumentChannelBridgeDisconnected();
void DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupStatus);
base::ProcessId OtherPid() const {
return mOtherPid;
if (mDocumentChannelBridge) {
return mDocumentChannelBridge->OtherPid();
}
if (mPendingDocumentChannelBridgeProcess) {
return *mPendingDocumentChannelBridgeProcess;
}
return 0;
}
[[nodiscard]] RefPtr<ChildEndpointPromise> AttachStreamFilter(
base::ProcessId aChildProcessId);
using ParentEndpoint = ipc::Endpoint<extensions::PStreamFilterParent>;
// Serializes all data needed to setup the new replacement channel
// in the content process into the RedirectToRealChannelArgs struct.
void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
@ -221,16 +205,25 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
dom::ContentParent* aParent) const;
protected:
DocumentLoadListener(dom::CanonicalBrowsingContext* aBrowsingContext,
base::ProcessId aPendingBridgeProcess);
virtual ~DocumentLoadListener();
private:
friend class ParentProcessDocumentOpenInfo;
// Will reject the promise to notify the DLL consumer that we are done.
void DisconnectListeners(nsresult aStatus, nsresult aLoadGroupStatus);
// Called when we were created without a document channel bridge,
// and now it has been created and attached.
void NotifyBridgeConnected(ADocumentChannelBridge* aBridge);
// Called when we were created without a document channel, and creation has
// failed, and won't ever be attached.
void NotifyDocumentChannelFailed();
// Called when we were created without a document channel bridge,
// and creation has failed, and won't ever be attached.
void NotifyBridgeFailed();
// Returns a promise that resolves with the document channel bridge,
// waiting for a pending one if necessary.
// If we've failed to create a bridge, or a bridge has already been
// detached then rejects.
typedef MozPromise<RefPtr<ADocumentChannelBridge>, bool, false>
EnsureBridgePromise;
RefPtr<EnsureBridgePromise> EnsureBridge();
// Initiates the switch from DocumentChannel to the real protocol-specific
// channel, and ensures that RedirectToRealChannelFinished is called when
@ -259,7 +252,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// A helper for TriggerRedirectToRealChannel that abstracts over
// the same-process and cross-process switch cases and returns
// a single promise to wait on.
using ParentEndpoint = ipc::Endpoint<extensions::PStreamFilterParent>;
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
const Maybe<uint64_t>& aDestinationProcess,
@ -276,8 +268,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
bool HasCrossOriginOpenerPolicyMismatch() const;
void ApplyPendingFunctions(nsISupports* aChannel) const;
void Disconnect();
// This defines a variant that describes all the attribute setters (and their
// parameters) from nsIParentChannel
//
@ -376,6 +366,21 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// 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;
// If we were created without a bridge, then this is set
// to Some() with the process id of the content process
// that will be creating our bridge soon.
Maybe<base::ProcessId> mPendingDocumentChannelBridgeProcess;
// Holds a promise for callers that want to wait on the document
// channel bridge becoming available.
MozPromiseHolder<EnsureBridgePromise> mBridgePromise;
// 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
@ -434,23 +439,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// True if cancelled.
bool mCancelled = false;
// The process id of the content process that we are being called from
// or 0 initiated from a parent process load.
base::ProcessId mOtherPid = 0;
void RejectOpenPromise(nsresult aStatus, nsresult aLoadGroupStatus,
const char* aLocation) {
// It is possible for mOpenPromise to not be set if AsyncOpen failed and
// the DocumentChannel got canceled.
if (!mOpenPromiseResolved && mOpenPromise) {
mOpenPromise->Reject(OpenPromiseFailedType({aStatus, aLoadGroupStatus}),
aLocation);
mOpenPromiseResolved = true;
}
}
RefPtr<OpenPromise::Private> mOpenPromise;
bool mOpenPromiseResolved = false;
};
NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)

Просмотреть файл

@ -52,6 +52,11 @@ child:
// and notifies the listener via a redirect to the new channel.
async RedirectToRealChannel(RedirectToRealChannelArgs args, Endpoint<PStreamFilterParent>[] aEndpoint)
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

Просмотреть файл

@ -17,9 +17,6 @@ extern mozilla::LazyLogModule gDocumentChannelLog;
namespace mozilla {
namespace net {
using RedirectToRealChannelPromise =
typename PDocumentChannelParent::RedirectToRealChannelPromise;
NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel, DocumentChannel,
nsIAsyncVerifyRedirectCallback, nsIObserver)
@ -36,7 +33,7 @@ ParentProcessDocumentChannel::~ParentProcessDocumentChannel() {
LOG(("ParentProcessDocumentChannel dtor [this=%p]", this));
}
RefPtr<RedirectToRealChannelPromise>
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
ParentProcessDocumentChannel::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
@ -59,25 +56,18 @@ ParentProcessDocumentChannel::RedirectToRealChannel(
msg.Insert(
"Attempt to load a non-authorised load in the parent process: ", 0);
NS_ASSERTION(false, msg.get());
return RedirectToRealChannelPromise::CreateAndResolve(
NS_ERROR_CONTENT_BLOCKED, __func__);
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndResolve(NS_ERROR_CONTENT_BLOCKED, __func__);
}
}
mStreamFilterEndpoints = std::move(aStreamFilterEndpoints);
RefPtr<RedirectToRealChannelPromise> p = mPromise.Ensure(__func__);
// We make the promise use direct task dispatch in order to reduce the number
// of event loops iterations.
mPromise.UseDirectTaskDispatch(__func__);
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise> p =
mPromise.Ensure(__func__);
nsresult rv =
gHttpHandler->AsyncOnChannelRedirect(this, channel, aRedirectFlags);
if (NS_FAILED(rv)) {
LOG(
("ParentProcessDocumentChannel RedirectToRealChannel "
"AsyncOnChannelRedirect failed [this=%p "
"aRv=%d]",
this, int(rv)));
OnRedirectVerifyCallback(rv);
}
@ -91,11 +81,11 @@ ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
"aResult=%d]",
this, int(aResult)));
MOZ_ASSERT(mDocumentLoadListener);
MOZ_ASSERT(mCanceled || mDocumentLoadListener);
if (NS_FAILED(aResult)) {
Cancel(aResult);
} else if (mCanceled) {
} else if (mCanceled && NS_SUCCEEDED(aResult)) {
aResult = NS_ERROR_ABORT;
} else {
const nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
@ -103,9 +93,7 @@ ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
// Adding the channel to the loadgroup could have triggered a status
// change with an observer being called destroying the docShell, resulting
// in the PPDC to be canceled.
if (mCanceled) {
aResult = NS_ERROR_ABORT;
} else {
if (!mCanceled) {
mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
for (auto& endpoint : mStreamFilterEndpoints) {
extensions::StreamFilterParent::Attach(channel, std::move(endpoint));
@ -122,7 +110,12 @@ ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
}
}
mPromise.Resolve(aResult, __func__);
mLoadGroup = nullptr;
mListener = nullptr;
mCallbacks = nullptr;
DisconnectDocumentLoadListener();
mPromise.ResolveIfExists(aResult, __func__);
return NS_OK;
}
@ -131,7 +124,7 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
nsIStreamListener* aListener) {
LOG(("ParentProcessDocumentChannel AsyncOpen [this=%p]", this));
mDocumentLoadListener = new DocumentLoadListener(
GetDocShell()->GetBrowsingContext()->Canonical());
GetDocShell()->GetBrowsingContext()->Canonical(), this);
LOG(("Created PPDocumentChannel with listener=%p",
mDocumentLoadListener.get()));
@ -147,17 +140,15 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
nsresult rv = NS_OK;
Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo;
auto promise = mDocumentLoadListener->Open(
mLoadState, mCacheKey, Some(mChannelId), mAsyncOpenTime, mTiming,
std::move(initialClientInfo), GetDocShell()->GetOuterWindowID(),
GetDocShell()
->GetBrowsingContext()
->HasValidTransientUserGestureActivation(),
Some(mUriModified), Some(mIsXFOError), 0 /* ProcessId */, &rv);
if (NS_FAILED(rv)) {
MOZ_ASSERT(!promise);
mDocumentLoadListener = nullptr;
RemoveObserver();
if (!mDocumentLoadListener->Open(
mLoadState, mCacheKey, Some(mChannelId), mAsyncOpenTime, mTiming,
std::move(initialClientInfo), GetDocShell()->GetOuterWindowID(),
GetDocShell()
->GetBrowsingContext()
->HasValidTransientUserGestureActivation(),
Some(mUriModified), Some(mIsXFOError), &rv)) {
MOZ_ASSERT(NS_FAILED(rv));
DisconnectDocumentLoadListener();
return rv;
}
@ -165,47 +156,6 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
RefPtr<ParentProcessDocumentChannel> self = this;
promise->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
// The DLL is waiting for us to resolve the
// RedirectToRealChannelPromise given as parameter.
RefPtr<RedirectToRealChannelPromise> p =
self->RedirectToRealChannel(
std::move(aResolveValue.mStreamFilterEndpoints),
aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags)
->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[self](RedirectToRealChannelPromise::ResolveOrRejectValue&&
aValue) -> RefPtr<RedirectToRealChannelPromise> {
MOZ_ASSERT(aValue.IsResolve());
nsresult rv = aValue.ResolveValue();
if (NS_FAILED(rv)) {
self->DisconnectChildListeners(rv, rv);
}
self->mLoadGroup = nullptr;
self->mListener = nullptr;
self->mCallbacks = nullptr;
self->RemoveObserver();
auto p =
MakeRefPtr<RedirectToRealChannelPromise::Private>(
__func__);
p->UseDirectTaskDispatch(__func__);
p->ResolveOrReject(std::move(aValue), __func__);
return p;
});
// We chain the promise the DLL is waiting on to the one returned by
// RedirectToRealChannel. As soon as the promise returned is
// resolved or rejected, so will the DLL's promise.
p->ChainTo(aResolveValue.mPromise.forget(), __func__);
},
[self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
self->DisconnectChildListeners(aRejectValue.mStatus,
aRejectValue.mLoadGroupStatus);
self->RemoveObserver();
});
return NS_OK;
}
@ -216,13 +166,22 @@ NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) {
}
mCanceled = true;
// This will force the DocumentListener to abort the promise if there's one
// pending.
mDocumentLoadListener->Cancel(aStatus);
ShutdownListeners(aStatus);
return NS_OK;
}
void ParentProcessDocumentChannel::DisconnectDocumentLoadListener() {
if (!mDocumentLoadListener) {
return;
}
mDocumentLoadListener->DocumentChannelBridgeDisconnected();
mDocumentLoadListener = nullptr;
RemoveObserver();
}
void ParentProcessDocumentChannel::RemoveObserver() {
if (nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService()) {
@ -232,7 +191,6 @@ void ParentProcessDocumentChannel::RemoveObserver() {
////////////////////////////////////////////////////////////////////////////////
// nsIObserver
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
ParentProcessDocumentChannel::Observe(nsISupports* aSubject, const char* aTopic,

Просмотреть файл

@ -8,6 +8,7 @@
#define mozilla_net_ParentProcessDocumentChannel_h
#include "ProtocolUtils.h"
#include "mozilla/net/ADocumentChannelBridge.h"
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/DocumentLoadListener.h"
#include "nsIObserver.h"
@ -17,7 +18,8 @@ namespace net {
class ParentProcessDocumentChannel : public DocumentChannel,
public nsIAsyncVerifyRedirectCallback,
public nsIObserver {
public nsIObserver,
public ADocumentChannelBridge {
public:
ParentProcessDocumentChannel(nsDocShellLoadState* aLoadState,
class LoadInfo* aLoadInfo,
@ -35,10 +37,23 @@ class ParentProcessDocumentChannel : public DocumentChannel,
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags);
uint32_t aRedirectFlags, uint32_t aLoadFlags) override;
void DisconnectChildListeners(nsresult aStatus,
nsresult aLoadGroupStatus) override {
DocumentChannel::DisconnectChildListeners(aStatus, aLoadGroupStatus);
RemoveObserver();
mDocumentLoadListener = nullptr;
}
void Delete() override {}
void DeleteIPDL() override {
mPromise.ResolveIfExists(NS_BINDING_ABORTED, __func__);
}
base::ProcessId OtherPid() const override { return 0; }
private:
virtual ~ParentProcessDocumentChannel();
void DisconnectDocumentLoadListener();
void RemoveObserver();
RefPtr<DocumentLoadListener> mDocumentLoadListener;

Просмотреть файл

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.net += [
'ADocumentChannelBridge.h',
'ChannelEventQueue.h',
'DocumentChannel.h',
'DocumentChannelChild.h',

Просмотреть файл

@ -3,14 +3,17 @@
* 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 "VideoUtils.h"
#include "base/message_loop.h"
#include "gtest/gtest.h"
#include "mozilla/MozPromise.h"
#include "mozilla/SharedThreadPool.h"
#include "base/message_loop.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Unused.h"
#include "nsISupportsImpl.h"
#include "mozilla/SharedThreadPool.h"
#include "VideoUtils.h"
using namespace mozilla;
@ -452,21 +455,6 @@ TEST(MozPromise, MessageLoopEventTarget)
NS_ProcessPendingEvents(nullptr);
}
TEST(MozPromise, ChainTo)
{
RefPtr<TestPromise> promise1 = TestPromise::CreateAndResolve(42, __func__);
RefPtr<TestPromise::Private> promise2 = new TestPromise::Private(__func__);
promise2->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[&](int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
DO_FAIL);
promise1->ChainTo(promise2.forget(), __func__);
// Spin the event loop.
NS_ProcessPendingEvents(nullptr);
}
TEST(MozPromise, SynchronousTaskDispatch1)
{
bool value = false;
@ -518,7 +506,6 @@ TEST(MozPromise, DirectTaskDispatch)
EXPECT_EQ(value1, true);
value2 = true;
}));
RefPtr<TestPromise::Private> promise =
new TestPromise::Private(__func__);
promise->UseDirectTaskDispatch(__func__);
@ -538,94 +525,4 @@ TEST(MozPromise, DirectTaskDispatch)
// Spin the event loop.
NS_ProcessPendingEvents(nullptr);
}
TEST(MozPromise, ChainedDirectTaskDispatch)
{
bool value1 = false;
bool value2 = false;
// For direct task dispatch to be working, we must be within a
// nested event loop. So the test itself must be dispatched within
// a task.
GetCurrentThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction("test", [&]() {
GetCurrentThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction("test", [&]() {
EXPECT_EQ(value1, true);
value2 = true;
}));
RefPtr<TestPromise::Private> promise1 =
new TestPromise::Private(__func__);
promise1->UseDirectTaskDispatch(__func__);
promise1->Resolve(42, __func__);
EXPECT_EQ(value1, false);
promise1
->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[&](int aResolveValue) -> RefPtr<TestPromise> {
EXPECT_EQ(aResolveValue, 42);
EXPECT_EQ(value2, false);
RefPtr<TestPromise::Private> promise2 =
new TestPromise::Private(__func__);
promise2->UseDirectTaskDispatch(__func__);
promise2->Resolve(43, __func__);
return promise2;
},
DO_FAIL)
->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[&](int aResolveValue) -> void {
EXPECT_EQ(aResolveValue, 43);
EXPECT_EQ(value2, false);
value1 = true;
},
DO_FAIL);
EXPECT_EQ(value1, false);
}));
// Spin the event loop.
NS_ProcessPendingEvents(nullptr);
}
TEST(MozPromise, ChainToDirectTaskDispatch)
{
bool value1 = false;
bool value2 = false;
// For direct task dispatch to be working, we must be within a
// nested event loop. So the test itself must be dispatched within
// a task.
GetCurrentThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction("test", [&]() {
GetCurrentThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction("test", [&]() {
EXPECT_EQ(value1, true);
value2 = true;
}));
RefPtr<TestPromise::Private> promise1 =
new TestPromise::Private(__func__);
promise1->UseDirectTaskDispatch(__func__);
RefPtr<TestPromise::Private> promise2 =
new TestPromise::Private(__func__);
promise2->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[&](int aResolveValue) -> void {
EXPECT_EQ(aResolveValue, 42);
EXPECT_EQ(value2, false);
value1 = true;
},
DO_FAIL);
promise1->ChainTo(promise2.forget(), __func__);
EXPECT_EQ(value1, false);
promise1->Resolve(42, __func__);
}));
// Spin the event loop.
NS_ProcessPendingEvents(nullptr);
}
#undef DO_FAIL

Просмотреть файл

@ -982,22 +982,6 @@ class MozPromise : public MozPromiseBase {
PROMISE_LOG(
"%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
aCallSite, this, chainedPromise.get(), (int)IsPending());
// We want to use the same type of dispatching method with the chained
// promises.
// We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
// at compilation time to ensure we're not triggering the static_assert in
// UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
// that.
if (mUseDirectTaskDispatch) {
chainedPromise->UseDirectTaskDispatch(aCallSite);
} else if constexpr (IsExclusive) {
if (mUseSynchronousTaskDispatch) {
chainedPromise->UseSynchronousTaskDispatch(aCallSite);
}
}
if (!IsPending()) {
ForwardTo(chainedPromise);
} else {
@ -1319,16 +1303,6 @@ class MozPromiseHolderBase {
}
}
void UseSynchronousTaskDispatch(const char* aSite) {
MOZ_ASSERT(mPromise);
mPromise->UseSynchronousTaskDispatch(aSite);
}
void UseDirectTaskDispatch(const char* aSite) {
MOZ_ASSERT(mPromise);
mPromise->UseDirectTaskDispatch(aSite);
}
private:
RefPtr<typename PromiseType::Private> mPromise;
};