1337 lines
45 KiB
C++
1337 lines
45 KiB
C++
/* 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 "mozilla/dom/WindowGlobalChild.h"
|
|
|
|
#include "GeckoProfiler.h"
|
|
#include "Navigator.h"
|
|
#include "Units.h"
|
|
#include "mozilla/AntiTrackingUtils.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/ErrorResult.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/StaticPrefs_dom.h"
|
|
#include "mozilla/dom/AudioSession.h"
|
|
#include "mozilla/dom/BrowserBridgeChild.h"
|
|
#include "mozilla/dom/BrowserChild.h"
|
|
#include "mozilla/dom/BrowsingContext.h"
|
|
#include "mozilla/dom/BrowsingContextGroup.h"
|
|
#include "mozilla/dom/CloseWatcherManager.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/IdentityCredential.h"
|
|
#include "mozilla/dom/InProcessChild.h"
|
|
#include "mozilla/dom/InProcessParent.h"
|
|
#include "mozilla/dom/JSActorService.h"
|
|
#include "mozilla/dom/JSWindowActorBinding.h"
|
|
#include "mozilla/dom/JSWindowActorChild.h"
|
|
#include "mozilla/dom/ModelContext.h"
|
|
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
|
|
#include "mozilla/dom/PolicyContainer.h"
|
|
#include "mozilla/dom/Promise-inl.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/ReportingUtils.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "mozilla/dom/SecurityPolicyViolationEvent.h"
|
|
#include "mozilla/dom/SessionStoreRestoreData.h"
|
|
#include "mozilla/dom/WindowContext.h"
|
|
#include "mozilla/dom/WindowGlobalActorsBinding.h"
|
|
#include "mozilla/dom/WindowGlobalParent.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "nsAtom.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsDocShell.h"
|
|
#include "nsFocusManager.h"
|
|
#include "nsFrameLoader.h"
|
|
#include "nsFrameLoaderOwner.h"
|
|
#include "nsGlobalWindowInner.h"
|
|
#include "nsIDocumentEncoder.h"
|
|
#include "nsIHttpChannelInternal.h"
|
|
#include "nsITimer.h"
|
|
#include "nsIURIMutator.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsQueryObject.h"
|
|
#include "nsScriptSecurityManager.h"
|
|
#include "nsSerializationHelper.h"
|
|
#include "nsURLHelper.h"
|
|
#include "xpcpublic.h"
|
|
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::dom::ipc;
|
|
|
|
namespace mozilla::dom {
|
|
|
|
// Retrieves language metadata for the current document.
|
|
//
|
|
// The request waits for the document to load up to a configured limit. Once
|
|
// collection begins, it may retry until the visible text sample meets the
|
|
// requested minimum or the configured retry limit is reached. The request
|
|
// completes without metadata if the current document is no longer eligible for
|
|
// collection.
|
|
class WindowGlobalChild::DocumentLanguageMetadataRequest final {
|
|
public:
|
|
NS_INLINE_DECL_REFCOUNTING(DocumentLanguageMetadataRequest)
|
|
|
|
DocumentLanguageMetadataRequest(
|
|
WindowGlobalChild* aWindowGlobalChild, uint32_t aTextSampleMinCodeUnits,
|
|
uint32_t aTextSampleTargetCodeUnits,
|
|
RequestDocumentLanguageMetadataResolver&& aResolver)
|
|
: mWindowGlobalChild(aWindowGlobalChild),
|
|
mResolver(std::move(aResolver)),
|
|
mTextSampleMinCodeUnits(aTextSampleMinCodeUnits),
|
|
mTextSampleTargetCodeUnits(aTextSampleTargetCodeUnits) {
|
|
MOZ_ASSERT(aWindowGlobalChild);
|
|
MOZ_ASSERT(aTextSampleMinCodeUnits <= aTextSampleTargetCodeUnits);
|
|
}
|
|
|
|
// Starts the process of collecting language metadata from the document
|
|
// by waiting for the document to load until the configured timeout, or
|
|
// by collecting immediately if the document has already loaded.
|
|
void Start() {
|
|
RefPtr<WindowGlobalChild> windowGlobalChild = mWindowGlobalChild.get();
|
|
if (!windowGlobalChild ||
|
|
!windowGlobalChild->CanCollectDocumentLanguageMetadata()) {
|
|
Cancel();
|
|
return;
|
|
}
|
|
|
|
if (windowGlobalChild->GetWindowGlobal()->IsDocumentLoaded()) {
|
|
OnDocumentLoaded();
|
|
return;
|
|
}
|
|
|
|
uint32_t loadTimeoutMs =
|
|
StaticPrefs::dom_document_language_metadata_load_timeout_ms();
|
|
if (!ScheduleTimer(loadTimeoutMs)) {
|
|
mLoadTimedOut = true;
|
|
CollectOrScheduleRetry();
|
|
}
|
|
}
|
|
|
|
// Reacts to the document loading by starting the collection process.
|
|
void OnDocumentLoaded() {
|
|
if (mCompleted) {
|
|
return;
|
|
}
|
|
|
|
CancelTimer();
|
|
CollectOrScheduleRetry();
|
|
}
|
|
|
|
// Resolves the request without language metadata.
|
|
void Cancel() {
|
|
CancelTimer();
|
|
Resolve(Nothing());
|
|
}
|
|
|
|
// Returns whether the request has completed.
|
|
bool IsCompleted() const { return mCompleted; }
|
|
|
|
private:
|
|
~DocumentLanguageMetadataRequest() { CancelTimer(); }
|
|
|
|
// Schedules another attempt to collect language metadata.
|
|
bool ScheduleRetry() {
|
|
mRetryCount++;
|
|
uint32_t retryDelayMs =
|
|
StaticPrefs::dom_document_language_metadata_retry_delay_base_ms() *
|
|
mRetryCount;
|
|
return ScheduleTimer(retryDelayMs);
|
|
}
|
|
|
|
// Schedules language metadata collection after the requested delay.
|
|
bool ScheduleTimer(uint32_t aDelayMs) {
|
|
CancelTimer();
|
|
|
|
WeakPtr<WindowGlobalChild> weakWindowGlobalChild = mWindowGlobalChild;
|
|
RefPtr<DocumentLanguageMetadataRequest> request = this;
|
|
nsresult rv = NS_NewTimerWithCallback(
|
|
getter_AddRefs(mTimer),
|
|
[weakWindowGlobalChild, request](nsITimer*) {
|
|
RefPtr<WindowGlobalChild> windowGlobalChild =
|
|
weakWindowGlobalChild.get();
|
|
|
|
if (!windowGlobalChild) {
|
|
return;
|
|
}
|
|
|
|
if (!request->mCompleted) {
|
|
if (request->mRetryCount == 0) {
|
|
request->mLoadTimedOut = true;
|
|
}
|
|
request->CancelTimer();
|
|
request->CollectOrScheduleRetry();
|
|
}
|
|
windowGlobalChild->RemoveCompletedDocumentLanguageMetadataRequests();
|
|
},
|
|
aDelayMs, nsITimer::TYPE_ONE_SHOT,
|
|
"WindowGlobalChild::DocumentLanguageMetadataRequest"_ns);
|
|
return NS_SUCCEEDED(rv);
|
|
}
|
|
|
|
void CancelTimer() {
|
|
if (mTimer) {
|
|
mTimer->Cancel();
|
|
mTimer = nullptr;
|
|
}
|
|
}
|
|
|
|
// Returns whether another collection attempt should be made.
|
|
bool ShouldRetry(const DocumentLanguageMetadata& aMetadata) const {
|
|
if (mLoadTimedOut) {
|
|
// The page did not load in time. We will make only one attempt
|
|
// to extract whatever text is available.
|
|
return false;
|
|
}
|
|
|
|
uint32_t maxRetries =
|
|
StaticPrefs::dom_document_language_metadata_max_retries();
|
|
return aMetadata.mTextSample.Length() < mTextSampleMinCodeUnits &&
|
|
mRetryCount < maxRetries;
|
|
}
|
|
|
|
// Collects language metadata. If the text sample is too short, the request
|
|
// may retry before resolving with the most recently collected metadata.
|
|
void CollectOrScheduleRetry() {
|
|
if (mCompleted) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<WindowGlobalChild> windowGlobalChild = mWindowGlobalChild.get();
|
|
if (!windowGlobalChild ||
|
|
!windowGlobalChild->CanCollectDocumentLanguageMetadata()) {
|
|
Cancel();
|
|
return;
|
|
}
|
|
|
|
Maybe<DocumentLanguageMetadata> metadata =
|
|
windowGlobalChild->GetDocumentLanguageMetadata(
|
|
mTextSampleTargetCodeUnits);
|
|
if (metadata.isNothing()) {
|
|
Cancel();
|
|
return;
|
|
}
|
|
|
|
if (ShouldRetry(*metadata)) {
|
|
if (ScheduleRetry()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
Resolve(std::move(metadata));
|
|
}
|
|
|
|
// Resolves the request with the provided result.
|
|
void Resolve(Maybe<DocumentLanguageMetadata>&& aMetadata) {
|
|
if (mCompleted) {
|
|
return;
|
|
}
|
|
|
|
mCompleted = true;
|
|
mWindowGlobalChild = nullptr;
|
|
CancelTimer();
|
|
|
|
auto resolver = std::move(mResolver);
|
|
resolver(std::move(aMetadata));
|
|
}
|
|
|
|
WeakPtr<WindowGlobalChild> mWindowGlobalChild;
|
|
RequestDocumentLanguageMetadataResolver mResolver;
|
|
nsCOMPtr<nsITimer> mTimer;
|
|
uint32_t mTextSampleMinCodeUnits;
|
|
uint32_t mTextSampleTargetCodeUnits;
|
|
uint32_t mRetryCount = 0;
|
|
bool mCompleted = false;
|
|
bool mLoadTimedOut = false;
|
|
};
|
|
|
|
WindowGlobalChild::WindowGlobalChild(dom::WindowContext* aWindowContext,
|
|
nsIPrincipal* aPrincipal,
|
|
nsIURI* aDocumentURI)
|
|
: mWindowContext(aWindowContext),
|
|
mDocumentPrincipal(aPrincipal),
|
|
mDocumentURI(aDocumentURI) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
|
|
MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);
|
|
MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
|
|
mWindowContext->IsLocalIP());
|
|
|
|
if (!mDocumentURI) {
|
|
NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
|
|
}
|
|
|
|
// Registers a DOM Window with the profiler. It re-registers the same Inner
|
|
// Window ID with different URIs because when a Browsing context is first
|
|
// loaded, the first url loaded in it will be about:blank. This call keeps the
|
|
// first non-about:blank registration of window and discards the previous one.
|
|
uint64_t embedderInnerWindowID = 0;
|
|
if (BrowsingContext()->GetParent()) {
|
|
embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
|
|
}
|
|
profiler_register_page(
|
|
BrowsingContext()->BrowserId(), InnerWindowId(),
|
|
nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
|
|
embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
|
|
}
|
|
|
|
already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
|
|
nsGlobalWindowInner* aWindow) {
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
// Opener policy is set when we start to load a document. Here, we ensure we
|
|
// have set the correct Opener policy so that it will be available in the
|
|
// parent process through window global child.
|
|
nsCOMPtr<nsIChannel> chan = aWindow->GetDocument()->GetChannel();
|
|
nsCOMPtr<nsILoadInfo> loadInfo = chan ? chan->LoadInfo() : nullptr;
|
|
nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(chan);
|
|
nsILoadInfo::CrossOriginOpenerPolicy policy;
|
|
if (httpChan &&
|
|
loadInfo->GetExternalContentPolicyType() ==
|
|
ExtContentPolicy::TYPE_DOCUMENT &&
|
|
NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
|
|
MOZ_DIAGNOSTIC_ASSERT(policy ==
|
|
aWindow->GetBrowsingContext()->GetOpenerPolicy());
|
|
}
|
|
#endif
|
|
|
|
WindowGlobalInit init = WindowGlobalActor::WindowInitializer(aWindow);
|
|
RefPtr<WindowGlobalChild> wgc = CreateDisconnected(init);
|
|
|
|
// Send the link constructor over PBrowser, or link over PInProcess.
|
|
if (XRE_IsParentProcess()) {
|
|
InProcessChild* ipChild = InProcessChild::Singleton();
|
|
InProcessParent* ipParent = InProcessParent::Singleton();
|
|
if (!ipChild || !ipParent) {
|
|
return nullptr;
|
|
}
|
|
|
|
ManagedEndpoint<PWindowGlobalParent> endpoint =
|
|
ipChild->OpenPWindowGlobalEndpoint(wgc);
|
|
ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
|
|
wgc->WindowContext()->Canonical());
|
|
} else {
|
|
RefPtr<BrowserChild> browserChild =
|
|
BrowserChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow));
|
|
MOZ_ASSERT(browserChild);
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
dom::BrowsingContext* bc = aWindow->GetBrowsingContext();
|
|
#endif
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(bc->AncestorsAreCurrent());
|
|
MOZ_DIAGNOSTIC_ASSERT(bc->IsInProcess());
|
|
|
|
MOZ_RELEASE_ASSERT(VerifyPartitionedPrincipalMatchesDocumentPrincipal(
|
|
init.principal(), init.partitionedPrincipal()));
|
|
|
|
ManagedEndpoint<PWindowGlobalParent> endpoint =
|
|
browserChild->OpenPWindowGlobalEndpoint(wgc);
|
|
browserChild->SendNewWindowGlobal(std::move(endpoint), init);
|
|
}
|
|
|
|
wgc->Init();
|
|
wgc->InitWindowGlobal(aWindow);
|
|
return wgc.forget();
|
|
}
|
|
|
|
already_AddRefed<WindowGlobalChild> WindowGlobalChild::CreateDisconnected(
|
|
const WindowGlobalInit& aInit) {
|
|
RefPtr<dom::BrowsingContext> browsingContext =
|
|
dom::BrowsingContext::Get(aInit.context().mBrowsingContextId);
|
|
|
|
RefPtr<dom::WindowContext> windowContext =
|
|
dom::WindowContext::GetById(aInit.context().mInnerWindowId);
|
|
MOZ_RELEASE_ASSERT(!windowContext, "Creating duplicate WindowContext");
|
|
|
|
// Create our new WindowContext
|
|
if (XRE_IsParentProcess()) {
|
|
windowContext = WindowGlobalParent::CreateDisconnected(aInit, nullptr);
|
|
} else {
|
|
dom::WindowContext::FieldValues fields = aInit.context().mFields;
|
|
windowContext = new dom::WindowContext(
|
|
browsingContext, aInit.context().mInnerWindowId,
|
|
aInit.context().mOuterWindowId, std::move(fields));
|
|
}
|
|
|
|
RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild(
|
|
windowContext, aInit.principal(), aInit.documentURI());
|
|
windowContext->mIsInProcess = true;
|
|
windowContext->mWindowGlobalChild = windowChild;
|
|
return windowChild.forget();
|
|
}
|
|
|
|
void WindowGlobalChild::Init() {
|
|
MOZ_ASSERT(mWindowContext->mWindowGlobalChild == this);
|
|
mWindowContext->Init();
|
|
}
|
|
|
|
void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner* aWindow) {
|
|
mWindowGlobal = aWindow;
|
|
}
|
|
|
|
void WindowGlobalChild::OnNewDocument(Document* aDocument) {
|
|
MOZ_RELEASE_ASSERT(mWindowGlobal);
|
|
MOZ_RELEASE_ASSERT(aDocument);
|
|
|
|
MOZ_RELEASE_ASSERT(mDocumentPrincipal->Equals(aDocument->NodePrincipal()),
|
|
"Cannot change document principal origin");
|
|
MOZ_RELEASE_ASSERT(
|
|
VerifyPartitionedPrincipalMatchesDocumentPrincipal(
|
|
aDocument->NodePrincipal(), aDocument->PartitionedPrincipal()),
|
|
"Invalid partitioned principal");
|
|
|
|
CancelDocumentLanguageMetadataRequests();
|
|
|
|
mDocumentPrincipal = aDocument->NodePrincipal();
|
|
|
|
// Send a series of messages to update document-specific state on
|
|
// WindowGlobalParent, when we change documents on an existing WindowGlobal.
|
|
// This data is also all sent when we construct a WindowGlobal, so anything
|
|
// added here should also be added to WindowGlobalActor::WindowInitializer.
|
|
|
|
// FIXME: Perhaps these should be combined into a smaller number of messages?
|
|
SendSetIsInitialDocument(aDocument->IsInitialDocument());
|
|
SetDocumentURI(aDocument->GetDocumentURI());
|
|
SendUpdateDocumentPrincipal(WrapNotNull(aDocument->NodePrincipal()),
|
|
WrapNotNull(aDocument->PartitionedPrincipal()));
|
|
SendUpdatePrincipalPartitioning(!aDocument->UseRegularPrincipal());
|
|
|
|
RefPtr<ParentProcessChannelHandle> documentChannelHandle;
|
|
if (nsIChannel* chan = aDocument->GetChannel()) {
|
|
(void)chan->GetParentProcessChannelHandle(
|
|
getter_AddRefs(documentChannelHandle));
|
|
}
|
|
RefPtr<ParentProcessChannelHandle> failedChannelHandle;
|
|
if (nsIChannel* chan = aDocument->GetFailedChannel()) {
|
|
(void)chan->GetParentProcessChannelHandle(
|
|
getter_AddRefs(failedChannelHandle));
|
|
}
|
|
SendUpdateChannels(documentChannelHandle, failedChannelHandle);
|
|
|
|
SendUpdateDocumentCspSettings(aDocument->GetBlockAllMixedContent(false),
|
|
aDocument->GetUpgradeInsecureRequests(false));
|
|
SendUpdateSandboxFlags(aDocument->GetSandboxFlags());
|
|
|
|
net::CookieJarSettingsArgs csArgs;
|
|
net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
|
|
->Serialize(csArgs);
|
|
if (!SendUpdateCookieJarSettings(csArgs)) {
|
|
NS_WARNING(
|
|
"Failed to update document's cookie jar settings on the "
|
|
"WindowGlobalParent");
|
|
}
|
|
|
|
SendUpdateHttpsOnlyStatus(aDocument->HttpsOnlyStatus());
|
|
|
|
// Update window context fields for the newly loaded Document.
|
|
WindowContext::Transaction txn;
|
|
txn.SetCookieBehavior(
|
|
Some(aDocument->CookieJarSettings()->GetCookieBehavior()));
|
|
txn.SetIsOnContentBlockingAllowList(
|
|
aDocument->CookieJarSettings()->GetIsOnContentBlockingAllowList());
|
|
txn.SetIsThirdPartyWindow(aDocument->HasThirdPartyChannel());
|
|
txn.SetIsThirdPartyTrackingResourceWindow(
|
|
nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal));
|
|
txn.SetIsSecureContext(mWindowGlobal->IsSecureContext());
|
|
txn.SetIsFramebustingAllowed(
|
|
mWindowGlobal->GetBrowsingContext()->ComputeIsFramebustingAllowed());
|
|
if (auto policy = aDocument->GetEmbedderPolicy()) {
|
|
txn.SetEmbedderPolicy(*policy);
|
|
}
|
|
txn.SetShouldResistFingerprinting(aDocument->ShouldResistFingerprinting(
|
|
RFPTarget::IsAlwaysEnabledForPrecompute));
|
|
txn.SetOverriddenFingerprintingSettings(
|
|
aDocument->GetOverriddenFingerprintingSettings());
|
|
|
|
if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
|
|
nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
|
|
txn.SetIsOriginalFrameSource(loadInfo->GetOriginalFrameSrcLoad());
|
|
|
|
nsILoadInfo::StoragePermissionState storageAccess =
|
|
loadInfo->GetStoragePermission();
|
|
txn.SetUsingStorageAccess(
|
|
storageAccess == nsILoadInfo::HasStoragePermission ||
|
|
storageAccess == nsILoadInfo::StoragePermissionAllowListed);
|
|
} else {
|
|
txn.SetIsOriginalFrameSource(false);
|
|
}
|
|
|
|
// Init Mixed Content Fields
|
|
nsCOMPtr<nsIURI> innerDocURI =
|
|
NS_GetInnermostURI(aDocument->GetDocumentURI());
|
|
if (innerDocURI) {
|
|
txn.SetIsSecure(innerDocURI->SchemeIs("https"));
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
|
|
mWindowContext->IsLocalIP());
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
|
|
}
|
|
|
|
void WindowGlobalChild::OnDocumentLoaded() {
|
|
for (const RefPtr<DocumentLanguageMetadataRequest>& request :
|
|
mDocumentLanguageMetadataRequests) {
|
|
request->OnDocumentLoaded();
|
|
}
|
|
RemoveCompletedDocumentLanguageMetadataRequests();
|
|
}
|
|
|
|
void WindowGlobalChild::OnDocumentUnloaded() {
|
|
CancelDocumentLanguageMetadataRequests();
|
|
}
|
|
|
|
bool WindowGlobalChild::CanCollectDocumentLanguageMetadata() {
|
|
if (!mWindowGlobal || IsClosed() || !mWindowGlobal->IsCurrentInnerWindow()) {
|
|
return false;
|
|
}
|
|
|
|
Document* document = mWindowGlobal->GetExtantDoc();
|
|
if (!document || !document->IsTopLevelContentDocument() ||
|
|
document->IsInitialDocument() || !document->IsCurrentActiveDocument() ||
|
|
document->GetWindowGlobalChild() != this) {
|
|
return false;
|
|
}
|
|
|
|
nsIURI* uri = document->GetDocumentURI();
|
|
if (!uri) {
|
|
return false;
|
|
}
|
|
|
|
return uri->SchemeIs("https") || uri->SchemeIs("http") ||
|
|
uri->SchemeIs("file") || uri->SchemeIs("moz-extension");
|
|
}
|
|
|
|
Maybe<DocumentLanguageMetadata> WindowGlobalChild::GetDocumentLanguageMetadata(
|
|
uint32_t aTextSampleTargetCodeUnits) {
|
|
if (!CanCollectDocumentLanguageMetadata()) {
|
|
return Nothing();
|
|
}
|
|
|
|
Document* document = mWindowGlobal->GetExtantDoc();
|
|
DocumentLanguageMetadata metadata;
|
|
|
|
if (Element* root = document->GetRootElement()) {
|
|
if (nsAtom* langAtom = root->GetLang()) {
|
|
langAtom->ToString(metadata.mHtmlLangAttribute);
|
|
}
|
|
}
|
|
|
|
AUTO_PROFILER_MARKER_INNERWINDOWID("DocumentLanguageMetadata", DOM,
|
|
InnerWindowId());
|
|
|
|
nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
|
|
uint32_t flags = nsIDocumentEncoder::OutputBodyOnly |
|
|
nsIDocumentEncoder::SkipInvisibleContent |
|
|
nsIDocumentEncoder::AllowCrossShadowBoundary |
|
|
nsIDocumentEncoder::OutputForPlainTextClipboardCopy |
|
|
nsIDocumentEncoder::OutputDisallowLineBreaking |
|
|
nsIDocumentEncoder::OutputDropInvisibleBreak |
|
|
nsIDocumentEncoder::OutputLFLineBreak;
|
|
|
|
nsresult rv = encoder->Init(document, u"text/plain"_ns, flags);
|
|
if (NS_FAILED(rv)) {
|
|
return Some(std::move(metadata));
|
|
}
|
|
|
|
nsAutoString textSample;
|
|
rv = encoder->EncodeToStringWithMaxLength(aTextSampleTargetCodeUnits,
|
|
textSample);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
metadata.mTextSample = textSample;
|
|
}
|
|
|
|
return Some(std::move(metadata));
|
|
}
|
|
|
|
void WindowGlobalChild::RemoveCompletedDocumentLanguageMetadataRequests() {
|
|
mDocumentLanguageMetadataRequests.RemoveElementsBy(
|
|
[](const RefPtr<DocumentLanguageMetadataRequest>& aRequest) {
|
|
return aRequest->IsCompleted();
|
|
});
|
|
}
|
|
|
|
void WindowGlobalChild::CancelDocumentLanguageMetadataRequests() {
|
|
for (const RefPtr<DocumentLanguageMetadataRequest>& request :
|
|
mDocumentLanguageMetadataRequests) {
|
|
request->Cancel();
|
|
}
|
|
mDocumentLanguageMetadataRequests.Clear();
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId(
|
|
uint64_t aInnerWindowId) {
|
|
if (RefPtr<dom::WindowContext> context =
|
|
dom::WindowContext::GetById(aInnerWindowId)) {
|
|
return do_AddRef(context->GetWindowGlobalChild());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
dom::BrowsingContext* WindowGlobalChild::BrowsingContext() {
|
|
return mWindowContext->GetBrowsingContext();
|
|
}
|
|
|
|
Nullable<WindowProxyHolder> WindowGlobalChild::GetContentWindow() {
|
|
if (IsCurrentGlobal()) {
|
|
return WindowProxyHolder(BrowsingContext());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
uint64_t WindowGlobalChild::InnerWindowId() {
|
|
return mWindowContext->InnerWindowId();
|
|
}
|
|
|
|
uint64_t WindowGlobalChild::OuterWindowId() {
|
|
return mWindowContext->OuterWindowId();
|
|
}
|
|
|
|
bool WindowGlobalChild::IsCurrentGlobal() {
|
|
return CanSend() && mWindowGlobal->IsCurrentInnerWindow();
|
|
}
|
|
|
|
already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() {
|
|
if (!CanSend()) {
|
|
return nullptr;
|
|
}
|
|
IProtocol* otherSide = InProcessChild::ParentActorFor(this);
|
|
return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
|
|
}
|
|
|
|
already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
|
|
if (IsInProcess() || !CanSend()) {
|
|
return nullptr;
|
|
}
|
|
return do_AddRef(static_cast<BrowserChild*>(Manager()));
|
|
}
|
|
|
|
uint64_t WindowGlobalChild::ContentParentId() {
|
|
if (XRE_IsParentProcess()) {
|
|
return 0;
|
|
}
|
|
return ContentChild::GetSingleton()->GetID();
|
|
}
|
|
|
|
// A WindowGlobalChild is the root in its process if it has no parent, or its
|
|
// embedder is in a different process.
|
|
bool WindowGlobalChild::IsProcessRoot() {
|
|
if (!BrowsingContext()->GetParent()) {
|
|
return true;
|
|
}
|
|
|
|
return !BrowsingContext()->GetEmbedderElement();
|
|
}
|
|
|
|
// When a "beforeunload" handler is added, it's recorded to be able to know when
|
|
// dispatching "beforeunload" is needed.
|
|
void WindowGlobalChild::BeforeUnloadAdded() {
|
|
// Don't bother notifying the parent if we don't have an IPC link open.
|
|
if (mBeforeUnloadListeners == 0 && CanSend()) {
|
|
(void)mWindowContext->SetNeedsBeforeUnload(true);
|
|
}
|
|
|
|
mBeforeUnloadListeners++;
|
|
MOZ_ASSERT(mBeforeUnloadListeners > 0);
|
|
}
|
|
|
|
// This is the inverse of `BeforeUnloadAdded`, making sure that "beforeunload"
|
|
// isn't dispatched if all "beforeunload" handlers have been removed.
|
|
void WindowGlobalChild::BeforeUnloadRemoved() {
|
|
mBeforeUnloadListeners--;
|
|
MOZ_ASSERT(mBeforeUnloadListeners >= 0);
|
|
|
|
if (mBeforeUnloadListeners == 0) {
|
|
(void)mWindowContext->SetNeedsBeforeUnload(false);
|
|
}
|
|
}
|
|
|
|
// This is very similar to what is done for "beforeunload" and uses the same
|
|
// state to keep track, but is only ever used for a top level window. It's used
|
|
// to be able to track when a "navigate" event needs to be dispatched to the top
|
|
// level window's navigation object, which needs to happen right after a
|
|
// "beforeunload" event for that window would be dispatched, regardless of if it
|
|
// is.
|
|
void WindowGlobalChild::NavigateAdded() {
|
|
if (!BrowsingContext()->IsTop()) {
|
|
return;
|
|
}
|
|
BeforeUnloadAdded();
|
|
}
|
|
|
|
// The inverse of `NavigateAdded`, again only ever used for a top level window.
|
|
void WindowGlobalChild::NavigateRemoved() {
|
|
if (!BrowsingContext()->IsTop()) {
|
|
return;
|
|
}
|
|
BeforeUnloadRemoved();
|
|
}
|
|
|
|
void WindowGlobalChild::Destroy() {
|
|
CancelDocumentLanguageMetadataRequests();
|
|
|
|
JSActorWillDestroy();
|
|
|
|
mWindowContext->Discard();
|
|
|
|
// Perform async IPC shutdown unless we're not in-process, and our
|
|
// BrowserChild is in the process of being destroyed, which will destroy
|
|
// us as well.
|
|
RefPtr<BrowserChild> browserChild = GetBrowserChild();
|
|
if (!browserChild || !browserChild->IsDestroyed()) {
|
|
SendDestroy();
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
|
|
const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
|
|
uint64_t aPendingSwitchId) {
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
|
|
|
|
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
|
|
("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext.ContextId()));
|
|
|
|
if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
|
|
return IPC_OK();
|
|
}
|
|
dom::BrowsingContext* frameContext = aFrameContext.get();
|
|
|
|
RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
|
|
if (NS_WARN_IF(!embedderElt)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (NS_WARN_IF(embedderElt->GetDocumentGlobal() != GetWindowGlobal())) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
|
|
MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
|
|
|
|
// Trigger a process switch into the current process.
|
|
RemotenessOptions options;
|
|
options.mRemoteType = dom::RemoteType::NotRemote().Stringify();
|
|
options.mPendingSwitchID.Construct(aPendingSwitchId);
|
|
options.mSwitchingInProgressLoad = true;
|
|
flo->ChangeRemoteness(options, IgnoreErrors());
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
|
|
const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
|
|
ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
|
|
const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve) {
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
|
|
|
|
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
|
|
("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext.ContextId()));
|
|
|
|
if (!aLayersId.IsValid()) {
|
|
return IPC_FAIL(this, "Received an invalid LayersId");
|
|
}
|
|
|
|
// Resolve the promise when this function exits, as we'll have fully unloaded
|
|
// at that point.
|
|
auto scopeExit = MakeScopeExit([&] { aResolve(true); });
|
|
|
|
// Get a BrowsingContext if we're not null or discarded. We don't want to
|
|
// early-return before we connect the BrowserBridgeChild, as otherwise we'll
|
|
// never break the channel in the parent.
|
|
RefPtr<dom::BrowsingContext> frameContext;
|
|
if (!aFrameContext.IsDiscarded()) {
|
|
frameContext = aFrameContext.get();
|
|
}
|
|
|
|
// Immediately construct the BrowserBridgeChild so we can destroy it cleanly
|
|
// if the process switch fails.
|
|
RefPtr<BrowserBridgeChild> bridge =
|
|
new BrowserBridgeChild(frameContext, aTabId, aLayersId);
|
|
RefPtr<BrowserChild> manager = GetBrowserChild();
|
|
if (NS_WARN_IF(
|
|
!manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// Synchronously delete de actor here rather than using SendBeginDestroy(), as
|
|
// we haven't initialized it yet.
|
|
auto deleteBridge =
|
|
MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge); });
|
|
|
|
// Immediately tear down the actor if we don't have a valid FrameContext.
|
|
if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
|
|
if (NS_WARN_IF(!embedderElt)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (NS_WARN_IF(embedderElt->GetDocumentGlobal() != GetWindowGlobal())) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
|
|
MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
|
|
|
|
// Trgger a process switch into the specified process.
|
|
IgnoredErrorResult rv;
|
|
flo->ChangeRemotenessWithBridge(bridge, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// Everything succeeded, so don't delete the bridge.
|
|
deleteBridge.release();
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
|
|
const Maybe<IntRect>& aRect, const float& aScale,
|
|
const nscolor& aBackgroundColor, const gfx::CrossProcessPaintFlags& aFlags,
|
|
DrawSnapshotResolver&& aResolve) {
|
|
aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect, aScale,
|
|
aBackgroundColor, aFlags));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvRequestDocumentLanguageMetadata(
|
|
uint32_t aTextSampleMinCodeUnits, uint32_t aTextSampleTargetCodeUnits,
|
|
RequestDocumentLanguageMetadataResolver&& aResolver) {
|
|
MOZ_ASSERT(aTextSampleMinCodeUnits <= aTextSampleTargetCodeUnits);
|
|
|
|
RefPtr request = MakeRefPtr<DocumentLanguageMetadataRequest>(
|
|
this, aTextSampleMinCodeUnits, aTextSampleTargetCodeUnits,
|
|
std::move(aResolver));
|
|
mDocumentLanguageMetadataRequests.AppendElement(request);
|
|
request->Start();
|
|
RemoveCompletedDocumentLanguageMetadataRequests();
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
|
|
nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
|
|
if (inner) {
|
|
inner->SaveStorageAccessPermissionGranted();
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
|
|
const nsString& aViolationEventJSON, const nsString& aReportGroupName) {
|
|
nsGlobalWindowInner* window = GetWindowGlobal();
|
|
if (!window) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
Document* doc = window->GetDocument();
|
|
if (!doc) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
ReportingUtils::DeserializeSecurityViolationEventAndReport(
|
|
doc->GetTargetForDOMEvent(), window, aViolationEventJSON,
|
|
aReportGroupName);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
|
|
const MaybeDiscardedBrowsingContext& aNode) {
|
|
if (aNode.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsGlobalWindowInner* window = GetWindowGlobal();
|
|
if (!window) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
Document* doc = window->GetDocument();
|
|
if (!doc) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc);
|
|
doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement());
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvResetScalingZoom() {
|
|
if (Document* doc = mWindowGlobal->GetExtantDoc()) {
|
|
if (PresShell* ps = doc->GetPresShell()) {
|
|
ps->SetResolutionAndScaleTo(1.0,
|
|
ResolutionChangeOrigin::MainThreadAdjustment);
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState(
|
|
const dom::sessionstore::DocShellRestoreState& aState,
|
|
RestoreDocShellStateResolver&& aResolve) {
|
|
if (mWindowGlobal) {
|
|
SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(),
|
|
aState);
|
|
}
|
|
aResolve(true);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
|
|
dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
|
|
aData->RestoreInto(BrowsingContext());
|
|
aResolve(true);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvRawMessage(const JSActorMessageMeta& aMeta,
|
|
JSIPCValue&& aData,
|
|
StructuredCloneData* aStack) {
|
|
ReceiveRawMessage(aMeta, std::move(aData), aStack);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvNotifyAudioSessionStateChanged(
|
|
const AudioSessionState& aState) {
|
|
nsGlobalWindowInner* window = GetWindowGlobal();
|
|
if (!window) {
|
|
return IPC_OK();
|
|
}
|
|
if (RefPtr<dom::AudioSession> session = window->Navigator()->AudioSession()) {
|
|
session->SetState(aState);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvNotifyPermissionChange(const nsCString& aType,
|
|
uint32_t aPermission) {
|
|
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
NS_ENSURE_TRUE(observerService,
|
|
IPC_FAIL(this, "Failed to get observer service"));
|
|
nsPIDOMWindowInner* notifyTarget =
|
|
static_cast<nsPIDOMWindowInner*>(this->GetWindowGlobal());
|
|
observerService->NotifyObservers(notifyTarget, "perm-changed-notify-only",
|
|
NS_ConvertUTF8toUTF16(aType).get());
|
|
// We only need to handle the revoked permission case here. The permission
|
|
// grant case is handled via the Storage Access API code.
|
|
if (this->GetWindowGlobal() &&
|
|
this->GetWindowGlobal()->UsingStorageAccess() &&
|
|
aPermission != nsIPermissionManager::ALLOW_ACTION) {
|
|
this->GetWindowGlobal()->SaveStorageAccessPermissionRevoked();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvProcessCloseRequest(
|
|
const MaybeDiscarded<dom::BrowsingContext>& aFocused) {
|
|
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
|
RefPtr<dom::BrowsingContext> focusedContext =
|
|
focusManager ? focusManager->GetFocusedBrowsingContext() : nullptr;
|
|
if (!focusedContext) {
|
|
return IPC_OK();
|
|
}
|
|
// Only the currently focused context's CloseWatcher should be processed.
|
|
if (RefPtr<Document> doc = focusedContext->GetExtantDocument()) {
|
|
RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
|
|
if (win && win->IsFullyActive()) {
|
|
RefPtr manager = win->EnsureCloseWatcherManager();
|
|
manager->ProcessCloseRequest();
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvGetModelContextTools(
|
|
GetModelContextToolsResolver&& aResolver) {
|
|
if (!IsCurrentGlobal()) {
|
|
aResolver(std::make_tuple(NS_ERROR_DOM_INVALID_ACCESS_ERR,
|
|
nsTArray<IPCModelContextToolDefinition>()));
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (!BrowsingContext()->IsTop()) {
|
|
aResolver(std::make_tuple(NS_ERROR_DOM_INVALID_ACCESS_ERR,
|
|
nsTArray<IPCModelContextToolDefinition>()));
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsTArray<IPCModelContextToolDefinition> tools;
|
|
if (nsGlobalWindowInner* win = GetWindowGlobal()) {
|
|
if (Navigator* nav = win->Navigator()) {
|
|
if (ModelContext* mc = nav->ModelContext()) {
|
|
mc->GetIPCToolDefinitions(tools);
|
|
}
|
|
}
|
|
}
|
|
|
|
aResolver(std::make_tuple(NS_OK, std::move(tools)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvGetContentMetrics(
|
|
GetContentMetricsResolver&& aResolver) {
|
|
CSSSize size;
|
|
float devicePixelRatio = 1.0f;
|
|
|
|
if (IsCurrentGlobal()) {
|
|
if (RefPtr<nsGlobalWindowInner> win = GetWindowGlobal()) {
|
|
if (RefPtr<Document> doc = win->GetExtantDoc()) {
|
|
if (RefPtr<Element> root = doc->GetDocumentElement()) {
|
|
size = CSSPixel::FromAppUnits(root->GetScrollSize());
|
|
}
|
|
}
|
|
|
|
IgnoredErrorResult rv;
|
|
double dpr = win->GetDevicePixelRatio(CallerType::System, rv);
|
|
if (!rv.Failed() && dpr > 0.0) {
|
|
devicePixelRatio = float(dpr);
|
|
}
|
|
}
|
|
}
|
|
aResolver(std::make_tuple(size, devicePixelRatio));
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult WindowGlobalChild::RecvInvokeModelContextTool(
|
|
const nsCString& aToolName, NotNull<StructuredCloneData*> aInput,
|
|
InvokeModelContextToolResolver&& aResolver) {
|
|
if (!IsCurrentGlobal()) {
|
|
aResolver(std::make_tuple(
|
|
CopyableErrorResult(NS_ERROR_DOM_INVALID_ACCESS_ERR), nullptr));
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (!BrowsingContext()->IsTop()) {
|
|
aResolver(std::make_tuple(
|
|
CopyableErrorResult(NS_ERROR_DOM_INVALID_ACCESS_ERR), nullptr));
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsGlobalWindowInner* win = GetWindowGlobal();
|
|
Navigator* nav = win ? win->Navigator() : nullptr;
|
|
RefPtr<ModelContext> mc = nav ? nav->ModelContext() : nullptr;
|
|
if (!mc) {
|
|
aResolver(std::make_tuple(
|
|
CopyableErrorResult(NS_ERROR_DOM_INVALID_ACCESS_ERR), nullptr));
|
|
return IPC_OK();
|
|
}
|
|
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(win)) {
|
|
aResolver(std::make_tuple(CopyableErrorResult(NS_ERROR_FAILURE), nullptr));
|
|
return IPC_OK();
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
JS::Rooted<JS::Value> inputVal(cx);
|
|
IgnoredErrorResult deserializeRv;
|
|
aInput->Read(cx, &inputVal, deserializeRv);
|
|
if (deserializeRv.Failed()) {
|
|
aResolver(std::make_tuple(CopyableErrorResult(NS_ERROR_DOM_DATA_CLONE_ERR),
|
|
nullptr));
|
|
return IPC_OK();
|
|
}
|
|
|
|
ErrorResult rv;
|
|
RefPtr<Promise> domPromise = mc->InvokeToolInternal(
|
|
cx, NS_ConvertUTF8toUTF16(aToolName), inputVal, rv);
|
|
if (!domPromise) {
|
|
aResolver(std::make_tuple(CopyableErrorResult(std::move(rv)), nullptr));
|
|
return IPC_OK();
|
|
}
|
|
|
|
domPromise->AddCallbacksWithCycleCollectedArgs(
|
|
[aResolver](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult&) {
|
|
RefPtr<StructuredCloneData> cloneData =
|
|
MakeRefPtr<StructuredCloneData>();
|
|
IgnoredErrorResult rv;
|
|
cloneData->Write(aCx, aValue, rv);
|
|
if (rv.Failed()) {
|
|
aResolver(std::make_tuple(
|
|
CopyableErrorResult(NS_ERROR_DOM_DATA_CLONE_ERR), nullptr));
|
|
return;
|
|
}
|
|
aResolver(std::make_tuple(CopyableErrorResult(), std::move(cloneData)));
|
|
},
|
|
[aResolver](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult&) {
|
|
RefPtr<StructuredCloneData> cloneData =
|
|
MakeRefPtr<StructuredCloneData>();
|
|
IgnoredErrorResult rv;
|
|
cloneData->Write(aCx, aValue, rv);
|
|
if (rv.Failed()) {
|
|
aResolver(
|
|
std::make_tuple(CopyableErrorResult(NS_ERROR_FAILURE), nullptr));
|
|
return;
|
|
}
|
|
aResolver(std::make_tuple(CopyableErrorResult(NS_ERROR_FAILURE),
|
|
std::move(cloneData)));
|
|
});
|
|
return IPC_OK();
|
|
}
|
|
|
|
void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
|
|
// Registers a DOM Window with the profiler. It re-registers the same Inner
|
|
// Window ID with different URIs because when a Browsing context is first
|
|
// loaded, the first url loaded in it will be about:blank. This call keeps the
|
|
// first non-about:blank registration of window and discards the previous one.
|
|
uint64_t embedderInnerWindowID = 0;
|
|
if (BrowsingContext()->GetParent()) {
|
|
embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
|
|
}
|
|
profiler_register_page(
|
|
BrowsingContext()->BrowserId(), InnerWindowId(),
|
|
nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
|
|
embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
|
|
|
|
nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
|
|
if (mDocumentPrincipal->GetIsNullPrincipal()) {
|
|
if (nsCOMPtr<nsIPrincipal> precursor =
|
|
mDocumentPrincipal->GetPrecursorPrincipal()) {
|
|
principalURI = precursor->GetURI();
|
|
}
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(
|
|
principalURI, aDocumentURI),
|
|
"Setting DocumentURI with a different origin "
|
|
"than principal URI");
|
|
|
|
mDocumentURI = aDocumentURI;
|
|
SendUpdateDocumentURI(WrapNotNull(aDocumentURI));
|
|
}
|
|
|
|
const RemoteType& WindowGlobalChild::GetRemoteType() const {
|
|
if (XRE_IsContentProcess()) {
|
|
return ContentChild::GetSingleton()->GetRemoteType();
|
|
}
|
|
|
|
return RemoteType::NotRemote();
|
|
}
|
|
|
|
already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
|
|
JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
|
|
return JSActorManager::GetActor(aCx, aName, aRv)
|
|
.downcast<JSWindowActorChild>();
|
|
}
|
|
|
|
already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetExistingActor(
|
|
const nsACString& aName) {
|
|
return JSActorManager::GetExistingActor(aName).downcast<JSWindowActorChild>();
|
|
}
|
|
|
|
already_AddRefed<JSActor> WindowGlobalChild::InitJSActor(
|
|
JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
|
|
ErrorResult& aRv) {
|
|
RefPtr<JSWindowActorChild> actor;
|
|
if (aMaybeActor.get()) {
|
|
aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
actor = new JSWindowActorChild();
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(!actor->GetManager(),
|
|
"mManager was already initialized once!");
|
|
actor->Init(aName, this);
|
|
return actor.forget();
|
|
}
|
|
|
|
void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
|
|
"Destroying WindowGlobalChild can run script");
|
|
|
|
CancelDocumentLanguageMetadataRequests();
|
|
|
|
// If our WindowContext hasn't been marked as discarded yet, ensure it's
|
|
// marked as discarded at this point.
|
|
mWindowContext->Discard();
|
|
|
|
profiler_unregister_page(InnerWindowId());
|
|
|
|
// Destroy our JSActors, and reject any pending queries.
|
|
JSActorDidDestroy();
|
|
}
|
|
|
|
bool WindowGlobalChild::IsSameOriginWith(
|
|
const dom::WindowContext* aOther) const {
|
|
if (aOther == WindowContext()) {
|
|
return true;
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group());
|
|
if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) {
|
|
return mDocumentPrincipal->Equals(otherWin->GetPrincipal());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WindowGlobalChild::SameOriginWithTop() {
|
|
return IsSameOriginWith(WindowContext()->TopWindowContext());
|
|
}
|
|
|
|
// For historical context, see:
|
|
//
|
|
// Bug 13871: Prevent frameset spoofing
|
|
// Bug 103638: Targets with same name in different windows open in wrong
|
|
// window with javascript
|
|
// Bug 408052: Adopt "ancestor" frame navigation policy
|
|
// Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
|
|
// origin attribute isolation
|
|
// Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
|
|
bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget,
|
|
bool aConsiderOpener) {
|
|
return nsContentUtils::CanNavigate(BrowsingContext(), aTarget,
|
|
DocumentPrincipal(), aConsiderOpener);
|
|
}
|
|
|
|
// FindWithName follows the rules for choosing a browsing context,
|
|
// with the exception of sandboxing for iframes. The implementation
|
|
// for arbitrarily choosing between two browsing contexts with the
|
|
// same name is as follows:
|
|
//
|
|
// 1) The start browsing context, i.e. 'this'
|
|
// 2) Descendants in insertion order
|
|
// 3) The parent
|
|
// 4) Siblings and their children, both in insertion order
|
|
// 5) After this we iteratively follow the parent chain, repeating 3
|
|
// and 4 until
|
|
// 6) If there is no parent, consider all other top level browsing
|
|
// contexts and their children, both in insertion order
|
|
//
|
|
// See
|
|
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
|
|
dom::BrowsingContext* WindowGlobalChild::FindBrowsingContextWithName(
|
|
const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
|
|
RefPtr<WindowGlobalChild> requestingContext = this;
|
|
if (aUseEntryGlobalForAccessCheck) {
|
|
if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) {
|
|
if (caller->GetBrowsingContextGroup() == WindowContext()->Group()) {
|
|
requestingContext = caller->GetWindowGlobalChild();
|
|
} else {
|
|
MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(),
|
|
"caller must be either same-group or system");
|
|
}
|
|
}
|
|
}
|
|
MOZ_ASSERT(requestingContext, "must have a requestingContext");
|
|
|
|
dom::BrowsingContext* found = nullptr;
|
|
if (aName.IsEmpty()) {
|
|
// You can't find a browsing context with an empty name.
|
|
found = nullptr;
|
|
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
|
|
// Just return null. Caller must handle creating a new window with
|
|
// a blank name.
|
|
found = nullptr;
|
|
} else if (nsContentUtils::IsSpecialName(aName)) {
|
|
found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
|
|
} else if (dom::BrowsingContext* child =
|
|
BrowsingContext()->FindWithNameInSubtree(aName,
|
|
requestingContext)) {
|
|
found = child;
|
|
} else {
|
|
dom::WindowContext* current = WindowContext();
|
|
|
|
do {
|
|
Span<RefPtr<dom::BrowsingContext>> siblings;
|
|
dom::WindowContext* parent = current->GetParentWindowContext();
|
|
|
|
if (!parent) {
|
|
// We've reached the root of the tree, consider browsing
|
|
// contexts in the same browsing context group.
|
|
siblings = WindowContext()->Group()->Toplevels();
|
|
} else if (dom::BrowsingContext* bc = parent->GetBrowsingContext();
|
|
bc && bc->NameEquals(aName) &&
|
|
requestingContext->CanNavigate(bc) && bc->IsTargetable()) {
|
|
found = bc;
|
|
break;
|
|
} else {
|
|
siblings = parent->NonSyntheticChildren();
|
|
}
|
|
|
|
for (dom::BrowsingContext* sibling : siblings) {
|
|
if (sibling == current->GetBrowsingContext()) {
|
|
continue;
|
|
}
|
|
|
|
if (dom::BrowsingContext* relative =
|
|
sibling->FindWithNameInSubtree(aName, requestingContext)) {
|
|
found = relative;
|
|
// Breaks the outer loop
|
|
parent = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
current = parent;
|
|
} while (current);
|
|
}
|
|
|
|
// Helpers should perform access control checks, which means that we
|
|
// only need to assert that we can access found.
|
|
MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanNavigate(found));
|
|
|
|
return found;
|
|
}
|
|
|
|
void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) {
|
|
SendUpdateBFCacheStatus(0, aStatus);
|
|
}
|
|
|
|
void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus) {
|
|
SendUpdateBFCacheStatus(aStatus, 0);
|
|
}
|
|
|
|
WindowGlobalChild::~WindowGlobalChild() = default;
|
|
|
|
JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
nsISupports* WindowGlobalChild::GetParentObject() {
|
|
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowGlobalChild)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowGlobalChild)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobal)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContainerFeaturePolicy)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowContext)
|
|
tmp->UnlinkManager();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowGlobalChild)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobal)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContainerFeaturePolicy)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowContext)
|
|
if (!tmp->IsInProcess()) {
|
|
CycleCollectionNoteChild(cb, static_cast<BrowserChild*>(tmp->Manager()),
|
|
"Manager()");
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
|
|
|
|
} // namespace mozilla::dom
|