/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 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/. */
#ifdef NS_PRINTING # include "nsIPrintSettings.h" #endif
#ifdef MOZ_WEBSPEECH # include "mozilla/dom/SpeechSynthesis.h" #endif
#ifdef ANDROID # include <android/log.h> #endif
#ifdef XP_WIN # include "mozilla/Debug.h" # include <process.h> # define getpid _getpid #else # include <unistd.h> // for getpid() #endif
usingnamespace mozilla; usingnamespace mozilla::dom; usingnamespace mozilla::dom::ipc; using mozilla::TimeDuration; using mozilla::TimeStamp; using mozilla::dom::GamepadHandle; using mozilla::dom::cache::CacheStorage;
#define FORWARD_TO_OUTER(method, args, err_rval) \
PR_BEGIN_MACRO \
RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \ if (!HasActiveDocument()) { \
NS_WARNING(outer ? "Inner window does not have active document." \
: "No outer window available!"); \ return err_rval; \
} \ return outer->method args; \
PR_END_MACRO
/** * An indirect observer object that means we don't have to implement nsIObserver * on nsGlobalWindow, where any script could see it.
*/ class nsGlobalWindowObserver final : public nsIObserver, public nsIInterfaceRequestor, public StorageNotificationObserver { public: explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
: mWindow(aWindow) {}
NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports* aSubject, constchar* aTopic, const char16_t* aData) override { if (!mWindow) return NS_OK; return mWindow->Observe(aSubject, aTopic, aData);
} void Forget() { mWindow = nullptr; }
NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override { if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { return mWindow->QueryInterface(aIID, aResult);
} return NS_NOINTERFACE;
}
// This reference is non-owning and safe because it's cleared by // nsGlobalWindowInner::FreeInnerObjects().
nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
};
class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler { public: explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
: mExecutor(aExecutor) {}
class IdleRequestExecutor final : public nsIRunnable, public nsICancelableRunnable, public nsINamed, public nsIIdleRunnable { public: explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
: mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
MOZ_DIAGNOSTIC_ASSERT(mWindow);
mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
}
bool IsCancelled() const { return !mWindow || mWindow->IsDying(); } // Checks if aRequest shouldn't execute in the current idle period // since it has been queued from a chained call to // requestIdleCallback from within a running idle callback. bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const { return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
}
void MaybeUpdateIdlePeriodLimit();
// Maybe dispatch the IdleRequestExecutor. MabyeDispatch will // schedule a delayed dispatch if the associated window is in the // background or if given a time to wait until dispatching. void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp()); void ScheduleDispatch();
bool mDispatched;
TimeStamp mDeadline;
IdlePeriodLimit mIdlePeriodLimit;
RefPtr<nsGlobalWindowInner> mWindow; // The timeout handler responsible for dispatching this executor in // the case of immediate dispatch to the idle queue isn't // desirable. This is used if we've dispatched all idle callbacks // that are allowed to run in the current idle period, or if the // associated window is currently in the background.
RefPtr<TimeoutHandler> mDelayedExecutorDispatcher; // If not Nothing() then this value is the handle to the currently // scheduled delayed executor dispatcher. This is needed to be able // to cancel the timeout handler in case of the executor being // cancelled.
Maybe<int32_t> mDelayedExecutorHandle;
};
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT. // See bug 1535398.
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
MOZ_ASSERT(NS_IsMainThread());
mDispatched = false; if (mWindow) {
RefPtr<nsGlobalWindowInner> window(mWindow);
window->ExecuteIdleRequest(mDeadline);
}
void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) { // If we've already dispatched the executor we don't want to do it // again. Also, if we've called IdleRequestExecutor::Cancel mWindow // will be null, which indicates that we shouldn't dispatch this // executor either. if (mDispatched || IsCancelled()) { return;
}
mDispatched = true;
nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow(); if (outer && outer->IsBackground()) { // Set a timeout handler with a timeout of 0 ms to throttle idle // callback requests coming from a backround window using // background timeout throttling.
DelayedDispatch(0); return;
}
TimeStamp now = TimeStamp::Now(); if (!aDelayUntil || aDelayUntil < now) {
ScheduleDispatch(); return;
}
if (aRequest->HasTimeout()) {
mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
Timeout::Reason::eIdleCallbackTimeout);
}
aRequest->removeFrom(mIdleRequestCallbacks);
}
void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
DOMHighResTimeStamp aDeadline, bool aDidTimeout) {
AssertIsOnMainThread(); // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should // guarantee that caller is holding a strong ref on the stack.
RefPtr<IdleRequest> request(aRequest);
RemoveIdleCallback(request);
request->IdleRun(this, aDeadline, aDidTimeout);
}
if (!request) { // There are no more idle requests, so stop scheduling idle // request callbacks. return;
}
// If the request that we're trying to execute has been queued // during the current idle period, then dispatch it again at the end // of the idle period. if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
mIdleRequestExecutor->MaybeDispatch(aDeadline); return;
}
DOMHighResTimeStamp deadline = 0.0;
if (Performance* perf = GetPerformance()) {
deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
}
// Running the idle callback could've suspended the window, in which // case mIdleRequestExecutor will be null. if (mIdleRequestExecutor) {
mIdleRequestExecutor->MaybeDispatch();
}
}
class IdleRequestTimeoutHandler final : public TimeoutHandler { public:
IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
nsPIDOMWindowInner* aWindow)
: TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
mObserver = new nsGlobalWindowObserver(this); if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) { // Watch for online/offline status changes so we can fire events. Use // a strong reference.
os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
os->AddObserver(mObserver, "screen-information-changed", false);
}
// Watch for storage notifications so we can fire storage events.
RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate(); if (sns) {
sns->Register(mObserver);
}
if (XRE_IsContentProcess()) {
nsCOMPtr<nsIDocShell> docShell = GetDocShell(); if (docShell) {
mBrowserChild = docShell->GetBrowserChild();
}
}
if (gDumpFile == nullptr) {
nsAutoCString fname;
Preferences::GetCString("browser.dom.window.dump.file", fname); if (!fname.IsEmpty()) { // If this fails to open, Dump() knows to just go to stdout on null.
gDumpFile = fopen(fname.get(), "wb+");
} else {
gDumpFile = stdout;
}
}
MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
("DOMWINDOW %p created outer=%p", this, aOuterWindow));
// Add ourselves to the inner windows list.
MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID), "This window shouldn't be in the hash table yet!"); // We seem to see crashes in release builds because of null // |sInnerWindowsById|. if (sInnerWindowsById) {
sInnerWindowsById->InsertOrUpdate(mWindowID, this);
}
}
if (IsChromeWindow()) {
MOZ_ASSERT(mCleanMessageManager, "chrome windows may always disconnect the msg manager");
DisconnectAndClearGroupMessageManagers();
if (mChromeFields.mMessageManager) { static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
->Disconnect();
}
mCleanMessageManager = false;
}
// In most cases this should already have been called, but call it again // here to catch any corner cases.
FreeInnerObjects();
if (sInnerWindowsById) {
sInnerWindowsById->Remove(mWindowID);
}
nsContentUtils::InnerOrOuterWindowDestroyed();
#ifdef DEBUG if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
nsAutoCString url; if (mLastOpenedURI) {
url = mLastOpenedURI->GetSpecOrDefault();
// Data URLs can be very long, so truncate to avoid flooding the log. const uint32_t maxURLLength = 1000; if (url.Length() > maxURLLength) {
url.Truncate(maxURLLength);
}
}
// An inner window is destroyed, pull it out of the outer window's // list if inner windows.
PR_REMOVE_LINK(this);
// If our outer window's inner window is this window, null out the // outer window's reference to this window that's being deleted.
nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (outer) {
outer->MaybeClearInnerWindow(this);
}
// We don't have to leave the tab group if we are an inner window.
nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); if (ac) ac->RemoveWindowAsListener(this);
void nsGlobalWindowInner::FreeInnerObjects() { if (IsDying()) { return;
}
StartDying();
if (mDoc && mDoc->GetWindowContext()) { // The document is about to lose its window, so this is a good time to send // our page use counters. // // (We also do this in Document::SetScriptGlobalObject(nullptr), which // catches most cases of documents losing their window, but not all.)
mDoc->SendPageUseCounters();
}
// Make sure that this is called before we null out the document and // other members that the window destroyed observers could // re-create. if (auto* reporter = nsWindowMemoryReporter::Get()) {
reporter->ObserveDOMWindowDetached(this);
}
// Kill all of the workers for this window.
CancelWorkersForWindow(*this);
for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
mSharedWorkers.ForwardRange()) {
pinnedWorker->Close();
}
if (mTimeoutManager) {
mTimeoutManager->ClearAllTimeouts();
}
DisableIdleCallbackRequests();
mChromeEventHandler = nullptr;
if (mListenerManager) {
mListenerManager->RemoveAllListeners();
mListenerManager->Disconnect();
mListenerManager = nullptr;
}
mHistory = nullptr;
if (mNavigator) {
mNavigator->OnNavigation();
mNavigator->Invalidate();
mNavigator = nullptr;
}
// This breaks a cycle between the window and the ClientSource object.
mClientSource.reset();
if (mWindowGlobalChild) { // Remove any remaining listeners.
int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners(); for (int64_t i = 0; i < nListeners; ++i) {
mWindowGlobalChild->BeforeUnloadRemoved();
}
MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
}
// If we have any promiseDocumentFlushed callbacks, fire them now so // that the Promises can resolve.
CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
if (mLocalStorage) {
mLocalStorage->Disconnect();
mLocalStorage = nullptr;
}
mSessionStorage = nullptr; if (mPerformance) { // Since window is dying, nothing is going to be painted // with meaningful sizes, so these temp data for LCP is // no longer needed. static_cast<PerformanceMainThread*>(mPerformance.get())
->ClearGeneratedTempDataForLCP();
}
mPerformance = nullptr;
mContentMediaController = nullptr;
if (mWebTaskScheduler) {
mWebTaskScheduler->Disconnect();
mWebTaskScheduler = nullptr;
}
if (mCleanMessageManager) {
MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned"); if (mChromeFields.mMessageManager) {
mChromeFields.mMessageManager->Disconnect();
}
}
if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
mWindowGlobalChild->Destroy();
}
for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE if (sInnerWindowsById) {
sInnerWindowsById->Remove(tmp->mWindowID);
}
JSObject* wrapper = tmp->GetWrapperPreserveColor(); if (wrapper) { // Mark our realm as dead, so the JS engine won't hand out our // global after this point.
JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
if (tmp->mWebTaskScheduler) {
tmp->mWebTaskScheduler->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
}
if (tmp->mOuterWindow) {
nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
}
if (tmp->mListenerManager) {
tmp->mListenerManager->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
}
// Here the Timeouts list would've been unlinked, but we rely on // that Timeout objects have been traced and will remove themselves // while unlinking.
// Unlink stuff from nsPIDOMWindow
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
MOZ_DIAGNOSTIC_ASSERT(
!tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(), "How are we unlinking a window before its actor has been destroyed?");
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
// Here the IdleRequest list would've been unlinked, but we rely on // that IdleRequest objects have been traced and will remove // themselves while unlinking.
NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
if (tmp->IsChromeWindow()) { if (tmp->mChromeFields.mMessageManager) { static_cast<nsFrameMessageManager*>(
tmp->mChromeFields.mMessageManager.get())
->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
}
tmp->DisconnectAndClearGroupMessageManagers();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
}
for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
}
tmp->mDocumentFlushedResolvers.Clear();
bool nsGlobalWindowInner::ShouldResistFingerprinting(RFPTarget aTarget) const { if (mDoc) { return mDoc->ShouldResistFingerprinting(aTarget);
} return nsContentUtils::ShouldResistFingerprinting( "If we do not have a document then we do not have any context" "to make an informed RFP choice, so we fall back to the global pref",
aTarget);
}
// Block expanded and null principals, let content and system through. if (principalInfo.type() !=
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
principalInfo.type() !=
mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { return Err(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult nsGlobalWindowInner::EnsureScriptEnvironment() { // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if // we're called on an inactive inner window.
nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (!outer) {
NS_WARNING("No outer window available!"); return NS_ERROR_FAILURE;
} return outer->EnsureScriptEnvironment();
}
void nsGlobalWindowInner::UpdateAutoplayPermission() { if (!GetWindowContext()) { return;
}
uint32_t perm =
media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal()); if (GetWindowContext()->GetAutoplayPermission() == perm) { return;
}
// Setting autoplay permission on a discarded context has no effect.
Unused << GetWindowContext()->SetAutoplayPermission(perm);
}
void nsGlobalWindowInner::UpdateShortcutsPermission() { if (!GetWindowContext() ||
!GetWindowContext()->GetBrowsingContext()->IsTop()) { // We only cache the shortcuts permission on top-level WindowContexts // since we always check the top-level principal for the permission. return;
}
// This must be called after nullifying the internal objects because here we // could recreate them, calling the getter methods, and store them into the JS // slots. If we nullify them after, the slot values and the objects will be // out of sync.
ClearDocumentDependentSlots(aCx);
if (!mWindowGlobalChild) {
mWindowGlobalChild = WindowGlobalChild::Create(this);
}
MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(), "WindowContext should always not have user gesture activation at " "this point.");
if (permDelegateHandler) {
permDelegateHandler->PopulateAllDelegatedPermissions();
}
#ifdefined(MOZ_WIDGET_ANDROID) // When we insert the new document to the window in the top-level browsing // context, we should reset the status of the request which is used for the // previous document. if (mWindowGlobalChild && GetBrowsingContext() &&
!GetBrowsingContext()->GetParent()) { // Return value of setting synced field should be checked. See bug 1656492.
Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
} #endif
// Get the load info for the document if we performed a load. Be careful not // to look at local URLs, though. Local URLs are those that have a scheme of: // * about: // * data: // * blob: // We also do an additional check here so that we only treat about:blank // and about:srcdoc as local URLs. Other internal firefox about: URLs should // not be treated this way.
nsCOMPtr<nsILoadInfo> loadInfo;
nsCOMPtr<nsIChannel> channel = mDoc->GetChannel(); if (channel) {
nsCOMPtr<nsIURI> uri;
Unused << channel->GetURI(getter_AddRefs(uri));
bool ignoreLoadInfo = false;
if (uri->SchemeIs("about")) {
ignoreLoadInfo =
NS_IsAboutBlankAllowQueryAndFragment(uri) || NS_IsAboutSrcdoc(uri);
} else { // Its not an about: URL, so now check for our other URL types.
ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
}
if (!ignoreLoadInfo) {
loadInfo = channel->LoadInfo();
}
}
// Take the initial client source from the docshell immediately. Even if we // don't end up using it here we should consume it.
UniquePtr<ClientSource> initialClientSource;
nsIDocShell* docshell = GetDocShell(); if (docshell) {
initialClientSource = docshell->TakeInitialClientSource();
}
// Try to get the reserved client from the LoadInfo. A Client is // reserved at the start of the channel load if there is not an // initial about:blank document that will be reused. It is also // created if the channel load encounters a cross-origin redirect. if (loadInfo) {
UniquePtr<ClientSource> reservedClient =
loadInfo->TakeReservedClientSource(); if (reservedClient) {
mClientSource.reset();
mClientSource = std::move(reservedClient);
newClientSource = true;
}
}
// We don't have a LoadInfo reserved client, but maybe we should // be inheriting an initial one from the docshell. This means // that the docshell started the channel load before creating the // initial about:blank document. This is an optimization, though, // and it created an initial Client as a placeholder for the document. // In this case we want to inherit this placeholder Client here. if (!mClientSource) {
mClientSource = std::move(initialClientSource); if (mClientSource) {
newClientSource = true;
}
}
// Verify the final ClientSource principal matches the final document // principal. The ClientChannelHelper handles things like network // redirects, but there are other ways the document principal can change. // For example, if something sets the nsIChannel.owner property, then // the final channel principal can be anything. Unfortunately there is // no good way to detect this until after the channel completes loading. // // For now we handle this just by reseting the ClientSource. This will // result in a new ClientSource with the correct principal being created. // To APIs like ServiceWorker and Clients API it will look like there was // an initial content page created that was then immediately replaced. // This is pretty close to what we are actually doing. if (mClientSource) { auto principalOrErr = mClientSource->Info().GetPrincipal();
nsCOMPtr<nsIPrincipal> clientPrincipal =
principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr; if (!clientPrincipal ||
!clientPrincipal->Equals(foreignPartitionedPrincipal)) {
mClientSource.reset();
}
}
// If we don't have a reserved client or an initial client, then create // one now. This can happen in certain cases where we avoid preallocating // the client in the docshell. This mainly occurs in situations where // the principal is not clearly inherited from the parent; e.g. sandboxed // iframes, window.open(), etc. //
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.