/* -*- 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/. */ nsGlobalWindowInner::InnerWindowByIdTable *nsGlobalWindowInner::sInnerWindowsById = nullptr; /** * 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, const char* 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; } void ObserveStorageNotification(StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) override { if (mWindow) { mWindow->ObserveStorageNotification(aEvent, aStorageType, aPrivateBrowsing); } } nsIPrincipal* GetPrincipal() const override { return mWindow ? mWindow->GetPrincipal() : nullptr; } bool IsPrivateBrowsing() const override { return mWindow ? mWindow->IsPrivateBrowsing() : false; } nsIEventTarget* GetEventTarget() const override { return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr; } private: ~nsGlobalWindowObserver() = default; // This reference is non-owning and safe because it's cleared by // nsGlobalWindowInner::CleanUp(). nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow; }; NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) class IdleRequestExecutor; class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler { public: explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor) : mExecutor(aExecutor) { } NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler) nsresult Call() override; private: ~IdleRequestExecutorTimeoutHandler() override {} RefPtr mExecutor; }; NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor) NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler) NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler) NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler) 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); MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow()); mIdlePeriodLimit = { mDeadline, mWindow->LastIdleRequestHandle() }; mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this); } NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable) NS_DECL_NSIRUNNABLE NS_DECL_NSINAMED nsresult Cancel() override; void SetDeadline(TimeStamp aDeadline) override; bool IsCancelled() const { return !mWindow || mWindow->AsInner()->InnerObjectsFreed(); } // 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(); private: struct IdlePeriodLimit { TimeStamp mEndOfIdlePeriod; uint32_t mLastRequestIdInIdlePeriod; }; void DelayedDispatch(uint32_t aDelay); ~IdleRequestExecutor() override {} bool mDispatched; TimeStamp mDeadline; IdlePeriodLimit mIdlePeriodLimit; RefPtr 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. nsCOMPtr 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 mDelayedExecutorHandle; }; NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor) NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor) NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDelayedExecutorDispatcher) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDelayedExecutorDispatcher) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor) NS_INTERFACE_MAP_ENTRY(nsIRunnable) NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable) NS_INTERFACE_MAP_ENTRY(nsINamed) NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable) NS_INTERFACE_MAP_END NS_IMETHODIMP IdleRequestExecutor::GetName(nsACString& aName) { aName.AssignASCII("IdleRequestExecutor"); return NS_OK; } NS_IMETHODIMP IdleRequestExecutor::Run() { MOZ_ASSERT(NS_IsMainThread()); mDispatched = false; if (mWindow) { return mWindow->ExecuteIdleRequest(mDeadline); } return NS_OK; } nsresult IdleRequestExecutor::Cancel() { MOZ_ASSERT(NS_IsMainThread()); if (mDelayedExecutorHandle && mWindow) { mWindow->AsInner()->TimeoutManager().ClearTimeout( mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout); } mWindow = nullptr; return NS_OK; } void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) { MOZ_ASSERT(NS_IsMainThread()); if (!mWindow) { return; } mDeadline = aDeadline; } void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() { if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) { mIdlePeriodLimit = { mDeadline, mWindow->LastIdleRequestHandle() }; } } 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->AsOuter()->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; } TimeDuration delay = aDelayUntil - now; DelayedDispatch(static_cast(delay.ToMilliseconds())); } void IdleRequestExecutor::ScheduleDispatch() { MOZ_ASSERT(mWindow); mDelayedExecutorHandle = Nothing(); RefPtr request = this; NS_IdleDispatchToCurrentThread(request.forget()); } void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) { MOZ_ASSERT(mWindow); MOZ_ASSERT(mDelayedExecutorHandle.isNothing()); int32_t handle; mWindow->AsInner()->TimeoutManager().SetTimeout( mDelayedExecutorDispatcher, aDelay, false, Timeout::Reason::eIdleCallbackTimeout, &handle); mDelayedExecutorHandle = Some(handle); } nsresult IdleRequestExecutorTimeoutHandler::Call() { if (!mExecutor->IsCancelled()) { mExecutor->ScheduleDispatch(); } return NS_OK; } void nsGlobalWindowInner::ScheduleIdleRequestDispatch() { AssertIsOnMainThread(); if (!mIdleRequestExecutor) { mIdleRequestExecutor = new IdleRequestExecutor(AssertInner()); } mIdleRequestExecutor->MaybeDispatch(); } void nsGlobalWindowInner::SuspendIdleRequests() { if (mIdleRequestExecutor) { mIdleRequestExecutor->Cancel(); mIdleRequestExecutor = nullptr; } } void nsGlobalWindowInner::ResumeIdleRequests() { MOZ_ASSERT(!mIdleRequestExecutor); ScheduleIdleRequestDispatch(); } void nsGlobalWindowInner::InsertIdleCallback(IdleRequest* aRequest) { AssertIsOnMainThread(); mIdleRequestCallbacks.insertBack(aRequest); aRequest->AddRef(); } void nsGlobalWindowInner::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest) { AssertIsOnMainThread(); if (aRequest->HasTimeout()) { mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(), Timeout::Reason::eIdleCallbackTimeout); } aRequest->removeFrom(mIdleRequestCallbacks); aRequest->Release(); } nsresult nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest, DOMHighResTimeStamp aDeadline, bool aDidTimeout) { AssertIsOnMainThread(); RefPtr request(aRequest); RemoveIdleCallback(request); return request->IdleRun(AsInner(), aDeadline, aDidTimeout); } nsresult nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) { AssertIsOnMainThread(); RefPtr request = mIdleRequestCallbacks.getFirst(); if (!request) { // There are no more idle requests, so stop scheduling idle // request callbacks. return NS_OK; } // 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 NS_OK; } DOMHighResTimeStamp deadline = 0.0; if (Performance* perf = GetPerformance()) { deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline); } mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit(); nsresult result = RunIdleRequest(request, deadline, false); // Running the idle callback could've suspended the window, in which // case mIdleRequestExecutor will be null. if (mIdleRequestExecutor) { mIdleRequestExecutor->MaybeDispatch(); } return result; } class IdleRequestTimeoutHandler final : public TimeoutHandler { public: IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest, nsPIDOMWindowInner* aWindow) : TimeoutHandler(aCx) , mIdleRequest(aIdleRequest) , mWindow(aWindow) { } NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler) nsresult Call() override { return nsGlobalWindowInner::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true); } private: ~IdleRequestTimeoutHandler() override {} RefPtr mIdleRequest; nsCOMPtr mWindow; }; NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler, mIdleRequest, mWindow) NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler) NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler) NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler) uint32_t nsGlobalWindowInner::RequestIdleCallback(JSContext* aCx, IdleRequestCallback& aCallback, const IdleRequestOptions& aOptions, ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsInnerWindow()); AssertIsOnMainThread(); uint32_t handle = mIdleRequestCallbackCounter++; RefPtr request = new IdleRequest(&aCallback, handle); if (aOptions.mTimeout.WasPassed()) { int32_t timeoutHandle; nsCOMPtr handler(new IdleRequestTimeoutHandler(aCx, request, AsInner())); nsresult rv = mTimeoutManager->SetTimeout( handler, aOptions.mTimeout.Value(), false, Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle); if (NS_WARN_IF(NS_FAILED(rv))) { return 0; } request->SetTimeoutHandle(timeoutHandle); } // mIdleRequestCallbacks now owns request InsertIdleCallback(request); if (!IsSuspended()) { ScheduleIdleRequestDispatch(); } return handle; } void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) { MOZ_RELEASE_ASSERT(IsInnerWindow()); for (IdleRequest* r : mIdleRequestCallbacks) { if (r->Handle() == aHandle) { RemoveIdleCallback(r); break; } } } void nsGlobalWindowInner::DisableIdleCallbackRequests() { if (mIdleRequestExecutor) { mIdleRequestExecutor->Cancel(); mIdleRequestExecutor = nullptr; } while (!mIdleRequestCallbacks.isEmpty()) { RefPtr request = mIdleRequestCallbacks.getFirst(); RemoveIdleCallback(request); } } bool nsGlobalWindowInner::IsBackgroundInternal() const { return !mOuterWindow || mOuterWindow->IsBackground(); } //***************************************************************************** //*** nsGlobalWindowInner: Object Management //***************************************************************************** nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter *aOuterWindow) : nsPIDOMWindowInner(aOuterWindow->AsOuter()), mIdleFuzzFactor(0), mIdleCallbackIndex(-1), mCurrentlyIdle(false), mAddActiveEventFuzzTime(true), mFullScreen(false), mFullscreenMode(false), mIsClosed(false), mInClose(false), mHavePendingClose(false), mHadOriginalOpener(false), mOriginalOpenerWasSecureContext(false), mIsPopupSpam(false), mBlockScriptedClosingFlag(false), mWasOffline(false), mHasHadSlowScript(false), mNotifyIdleObserversIdleOnThaw(false), mNotifyIdleObserversActiveOnThaw(false), mCreatingInnerWindow(false), mIsChrome(false), mCleanMessageManager(false), mNeedsFocus(true), mHasFocus(false), mShowFocusRingForContent(false), mFocusByKeyOccurred(false), mHasGamepad(false), mHasVREvents(false), mHasVRDisplayActivateEvents(false), mHasSeenGamepadInput(false), mNotifiedIDDestroyed(false), mAllowScriptsToClose(false), mTopLevelOuterContentWindow(false), mSuspendDepth(0), mFreezeDepth(0), mFocusMethod(0), mSerial(0), mIdleRequestCallbackCounter(1), mIdleRequestExecutor(nullptr), #ifdef DEBUG mSetOpenerWindowCalled(false), #endif mCleanedUp(false), mDialogAbuseCount(0), mAreDialogsEnabled(true), #ifdef DEBUG mIsValidatingTabGroup(false), #endif mCanSkipCCGeneration(0), mAutoActivateVRDisplayID(0), mBeforeUnloadListenerCount(0) { AssertIsOnMainThread(); nsLayoutStatics::AddRef(); // Initialize the PRCList (this). PR_INIT_CLIST(this); if (aOuterWindow) { // |this| is an inner window, add this inner window to the outer // window list of inners. PR_INSERT_AFTER(this, aOuterWindow); mObserver = new nsGlobalWindowObserver(AssertInner()); if (mObserver) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { // 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); } Preferences::AddStrongObserver(mObserver, "intl.accept_languages"); // Watch for storage notifications so we can fire storage events. RefPtr sns = StorageNotifierService::GetOrCreate(); if (sns) { sns->Register(mObserver); } } } else { // |this| is an outer window. Outer windows start out frozen and // remain frozen until they get an inner window. MOZ_ASSERT(IsFrozen()); } if (XRE_IsContentProcess()) { nsCOMPtr docShell = GetDocShell(); if (docShell) { mTabChild = docShell->GetTabChild(); } } // We could have failed the first time through trying // to create the entropy collector, so we should // try to get one until we succeed. gRefCnt++; EnsurePrefCaches(); 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; } } mSerial = ++gSerialCounter; #ifdef DEBUG if (!PR_GetEnv("MOZ_QUIET")) { printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", gRefCnt, static_cast(ToCanonicalSupports(this)), getpid(), gSerialCounter, static_cast(ToCanonicalSupports(aOuterWindow))); } #endif MOZ_LOG(gDOMLeakPRLog, 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->Get(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->Put(mWindowID, AssertInner()); } } #ifdef DEBUG /* static */ void nsGlobalWindowInner::AssertIsOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); } #endif // DEBUG /* static */ void nsGlobalWindowInner::Init() { AssertIsOnMainThread(); NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!"); sInnerWindowsById = new InnerWindowByIdTable(); } nsGlobalWindowInner::~nsGlobalWindowInner() { AssertIsOnMainThread(); if (IsChromeWindow()) { MOZ_ASSERT(mCleanMessageManager, "chrome windows may always disconnect the msg manager"); DisconnectAndClearGroupMessageManagers(); if (mChromeFields.mMessageManager) { static_cast( mChromeFields.mMessageManager.get())->Disconnect(); } mCleanMessageManager = false; } DisconnectEventTargetObjects(); if (IsOuterWindow()) { if (nsGlobalWindowOuter::sOuterWindowsById) { MOZ_ASSERT(nsGlobalWindowOuter::sOuterWindowsById->Get(mWindowID), "This window should be in the hash table"); nsGlobalWindowOuter::sOuterWindowsById->Remove(mWindowID); } } else { if (nsGlobalWindowInner::sInnerWindowsById) { MOZ_ASSERT(nsGlobalWindowInner::sInnerWindowsById->Get(mWindowID), "This window should be in the hash table"); nsGlobalWindowInner::sInnerWindowsById->Remove(mWindowID); } } --gRefCnt; #ifdef DEBUG if (!PR_GetEnv("MOZ_QUIET")) { 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); } } nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow); printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n", gRefCnt, static_cast(ToCanonicalSupports(this)), getpid(), mSerial, static_cast(ToCanonicalSupports(outer)), url.get()); } #endif MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, ("DOMWINDOW %p destroyed", this)); if (IsOuterWindow()) { JSObject *proxy = GetWrapperMaybeDead(); if (proxy) { js::SetProxyReservedSlot(proxy, 0, js::PrivateValue(nullptr)); } // An outer window is destroyed with inner windows still possibly // alive, iterate through the inner windows and null out their // back pointer to this outer, and pull them out of the list of // inner windows. // // Our linked list of inner windows both contains (an nsGlobalWindowOuter), // and our inner windows (nsGlobalWindowInners). This means that we need to // use PRCList*. We can then compare that PRCList* to `this` to see if its an // inner or outer window. PRCList* w; while ((w = PR_LIST_HEAD(this)) != this) { PR_REMOVE_AND_INIT_LINK(w); } DropOuterWindowDocs(); } else { Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, mMutationBits ? 1 : 0); if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nullptr; } // 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(AssertInner()); } } // We don't have to leave the tab group if we are an inner window. if (mTabGroup && IsOuterWindow()) { mTabGroup->Leave(AsOuter()); } // Outer windows are always supposed to call CleanUp before letting themselves // be destroyed. And while CleanUp generally seems to be intended to clean up // outers, we've historically called it for both. Changing this would probably // involve auditing all of the references that inners and outers can have, and // separating the handling into CleanUp() and FreeInnerObjects. if (IsInnerWindow()) { CleanUp(); } else { MOZ_ASSERT(mCleanedUp); } nsCOMPtr ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); if (ac) ac->RemoveWindowAsListener(this); nsLayoutStatics::Release(); } void nsGlobalWindowInner::AddEventTargetObject(DOMEventTargetHelper* aObject) { MOZ_ASSERT(IsInnerWindow()); mEventTargetObjects.PutEntry(aObject); } void nsGlobalWindowInner::RemoveEventTargetObject(DOMEventTargetHelper* aObject) { MOZ_ASSERT(IsInnerWindow()); mEventTargetObjects.RemoveEntry(aObject); } void nsGlobalWindowInner::DisconnectEventTargetObjects() { for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) { RefPtr target = iter.Get()->GetKey(); target->DisconnectFromOwner(); } mEventTargetObjects.Clear(); } // static void nsGlobalWindowInner::ShutDown() { AssertIsOnMainThread(); // nsGlobalWindowOuter::ShutDown() handles closing gDumpFile. delete sInnerWindowsById; sInnerWindowsById = nullptr; } // static void nsGlobalWindowInner::CleanupCachedXBLHandlers() { if (mCachedXBLPrototypeHandlers && mCachedXBLPrototypeHandlers->Count() > 0) { mCachedXBLPrototypeHandlers->Clear(); } } void nsGlobalWindowInner::MaybeForgiveSpamCount() { if (IsOuterWindow() && IsPopupSpamWindow()) { SetIsPopupSpamWindow(false); } } void nsGlobalWindowInner::SetIsPopupSpamWindow(bool aIsPopupSpam) { MOZ_ASSERT(IsOuterWindow()); mIsPopupSpam = aIsPopupSpam; if (aIsPopupSpam) { ++gOpenPopupSpamCount; } else { --gOpenPopupSpamCount; NS_ASSERTION(gOpenPopupSpamCount >= 0, "Unbalanced decrement of gOpenPopupSpamCount"); } } void nsGlobalWindowInner::DropOuterWindowDocs() { MOZ_ASSERT(IsOuterWindow()); MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); mDoc = nullptr; mSuspendedDoc = nullptr; } void nsGlobalWindowInner::CleanUp() { // Guarantee idempotence. if (mCleanedUp) return; mCleanedUp = true; StartDying(); DisconnectEventTargetObjects(); if (mObserver) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC); } RefPtr sns = StorageNotifierService::GetOrCreate(); if (sns) { sns->Unregister(mObserver); } if (mIdleService) { mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); } Preferences::RemoveObserver(mObserver, "intl.accept_languages"); // Drop its reference to this dying window, in case for some bogus reason // the object stays around. mObserver->Forget(); } if (mNavigator) { mNavigator->Invalidate(); mNavigator = nullptr; } mScreen = nullptr; mMenubar = nullptr; mToolbar = nullptr; mLocationbar = nullptr; mPersonalbar = nullptr; mStatusbar = nullptr; mScrollbars = nullptr; mHistory = nullptr; mCustomElements = nullptr; mFrames = nullptr; mWindowUtils = nullptr; mApplicationCache = nullptr; mIndexedDB = nullptr; mConsole = nullptr; mAudioWorklet = nullptr; mPaintWorklet = nullptr; mExternal = nullptr; mMozSelfSupport = nullptr; mPerformance = nullptr; #ifdef MOZ_WEBSPEECH mSpeechSynthesis = nullptr; #endif #if defined(MOZ_WIDGET_ANDROID) mOrientationChangeObserver = nullptr; #endif ClearControllers(); mOpener = nullptr; // Forces Release if (mContext) { mContext = nullptr; // Forces Release } mChromeEventHandler = nullptr; // Forces Release mParentTarget = nullptr; if (IsOuterWindow()) { nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(); if (inner) { inner->CleanUp(); } } if (IsInnerWindow()) { DisableGamepadUpdates(); mHasGamepad = false; DisableVRUpdates(); mHasVREvents = false; mHasVRDisplayActivateEvents = false; DisableIdleCallbackRequests(); } else { MOZ_ASSERT(!mHasGamepad); MOZ_ASSERT(!mHasVREvents); MOZ_ASSERT(!mHasVRDisplayActivateEvents); } if (mCleanMessageManager) { MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned"); if (mChromeFields.mMessageManager) { static_cast( mChromeFields.mMessageManager.get())->Disconnect(); } } mArguments = nullptr; CleanupCachedXBLHandlers(); for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { mAudioContexts[i]->Shutdown(); } mAudioContexts.Clear(); if (mIdleTimer) { mIdleTimer->Cancel(); mIdleTimer = nullptr; } mServiceWorkerRegistrationTable.Clear(); mIntlUtils = nullptr; } void nsGlobalWindowInner::ClearControllers() { if (mControllers) { uint32_t count; mControllers->GetControllerCount(&count); while (count--) { nsCOMPtr controller; mControllers->GetControllerAt(count, getter_AddRefs(controller)); nsCOMPtr context = do_QueryInterface(controller); if (context) context->SetCommandContext(nullptr); } mControllers = nullptr; } } void nsGlobalWindowInner::FreeInnerObjects() { NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); // Make sure that this is called before we null out the document and // other members that the window destroyed observers could // re-create. NotifyDOMWindowDestroyed(AssertInner()); if (auto* reporter = nsWindowMemoryReporter::Get()) { reporter->ObserveDOMWindowDetached(AssertInner()); } mInnerObjectsFreed = true; // Kill all of the workers for this window. mozilla::dom::workers::CancelWorkersForWindow(AsInner()); if (mTimeoutManager) { mTimeoutManager->ClearAllTimeouts(); } if (mIdleTimer) { mIdleTimer->Cancel(); mIdleTimer = nullptr; } mIdleObservers.Clear(); DisableIdleCallbackRequests(); mChromeEventHandler = nullptr; if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nullptr; } mHistory = nullptr; mCustomElements = nullptr; if (mNavigator) { mNavigator->OnNavigation(); mNavigator->Invalidate(); mNavigator = nullptr; } if (mScreen) { mScreen = nullptr; } #if defined(MOZ_WIDGET_ANDROID) mOrientationChangeObserver = nullptr; #endif if (mDoc) { // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); mDocBaseURI = mDoc->GetDocBaseURI(); while (mDoc->EventHandlingSuppressed()) { mDoc->UnsuppressEventHandlingAndFireEvents(false); } } // Remove our reference to the document and the document principal. mFocusedNode = nullptr; if (mApplicationCache) { static_cast(mApplicationCache.get())->Disconnect(); mApplicationCache = nullptr; } mIndexedDB = nullptr; UnlinkHostObjectURIs(); NotifyWindowIDDestroyed("inner-window-destroyed"); CleanupCachedXBLHandlers(); for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { mAudioContexts[i]->Shutdown(); } mAudioContexts.Clear(); DisableGamepadUpdates(); mHasGamepad = false; mGamepads.Clear(); DisableVRUpdates(); mHasVREvents = false; mHasVRDisplayActivateEvents = false; mVRDisplays.Clear(); if (mTabChild) { while (mBeforeUnloadListenerCount-- > 0) { mTabChild->BeforeUnloadRemoved(); } } } //***************************************************************************** // nsGlobalWindowInner::nsISupports //***************************************************************************** // QueryInterface implementation for nsGlobalWindowInner NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY // Make sure this matches the cast in nsGlobalWindowInner::FromWrapper() NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) if (aIID.Equals(NS_GET_IID(nsPIDOMWindowInner))) { foundInterface = AsInner(); } else if (aIID.Equals(NS_GET_IID(mozIDOMWindow)) && IsInnerWindow()) { foundInterface = AsInner(); } else if (aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter))) { foundInterface = AsOuter(); } else if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) && IsOuterWindow()) { foundInterface = AsOuter(); } else if (aIID.Equals(NS_GET_IID(nsIDOMChromeWindow)) && IsChromeWindow()) { foundInterface = static_cast(this); } else NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner) if (tmp->IsBlackForCC(false)) { if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) { return true; } tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration; if (tmp->mCachedXBLPrototypeHandlers) { for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter(); !iter.Done(); iter.Next()) { iter.Data().exposeToActiveJS(); } } if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { elm->MarkForCC(); } if (tmp->mTimeoutManager) { tmp->mTimeoutManager->UnmarkGrayTimers(); } return true; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner) return tmp->IsBlackForCC(true); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner) return tmp->IsBlackForCC(false); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner) if (MOZ_UNLIKELY(cb.WantDebugInfo())) { char name[512]; nsAutoCString uri; if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) { uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault(); } SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " %s %s", tmp->mWindowID, tmp->IsInnerWindow() ? "inner" : "outer", uri.get()); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); } else { NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get()) } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable) #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) #endif NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) if (tmp->mTimeoutManager) { tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) { cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout)); }); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor) for (IdleRequest* request : tmp->mIdleRequestCallbacks) { cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest)); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays) // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioWorklet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) tmp->TraverseHostObjectURIs(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mOpenerForInitialContentBrowser) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner) tmp->CleanupCachedXBLHandlers(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable) #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) #endif if (tmp->mOuterWindow) { nsGlobalWindowOuter::Cast(tmp->mOuterWindow)-> MaybeClearInnerWindow(tmp->AssertInner()); NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) } if (tmp->mListenerManager) { tmp->mListenerManager->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) } tmp->UpdateTopInnerWindow(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) if (tmp->mApplicationCache) { static_cast(tmp->mApplicationCache.get())->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) } NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays) // Unlink stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F) NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) tmp->UnlinkHostObjectURIs(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor) tmp->DisableIdleCallbackRequests(); if (tmp->IsChromeWindow()) { NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow) if (tmp->mChromeFields.mMessageManager) { static_cast( tmp->mChromeFields.mMessageManager.get())->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager) } tmp->DisconnectAndClearGroupMessageManagers(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mOpenerForInitialContentBrowser) } NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END #ifdef DEBUG void nsGlobalWindowInner::RiskyUnlink() { NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); } #endif NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner) if (tmp->mCachedXBLPrototypeHandlers) { for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter(); !iter.Done(); iter.Next()) { aCallbacks.Trace(&iter.Data(), "Cached XBL prototype handler", aClosure); } } NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) { if (!nsCCUncollectableMarker::sGeneration) { return false; } return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || HasKnownLiveWrapper()) && (!aTracingNeeded || HasNothingToTrace(static_cast(this))); } //***************************************************************************** // nsGlobalWindowInner::nsIScriptGlobalObject //***************************************************************************** nsresult nsGlobalWindowInner::EnsureScriptEnvironment() { nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (!outer) { NS_WARNING("No outer window available!"); return NS_ERROR_FAILURE; } if (outer->GetWrapperPreserveColor()) { return NS_OK; } NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(), "No cached wrapper, but we have an inner window?"); // If this window is a [i]frame, don't bother GC'ing when the frame's context // is destroyed since a GC will happen when the frameset or host document is // destroyed anyway. nsCOMPtr context = new nsJSContext(!IsFrame(), outer); NS_ASSERTION(!outer->mContext, "Will overwrite mContext!"); // should probably assert the context is clean??? context->WillInitializeContext(); nsresult rv = context->InitContext(); NS_ENSURE_SUCCESS(rv, rv); outer->mContext = context; return NS_OK; } nsIScriptContext * nsGlobalWindowInner::GetScriptContext() { nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (!outer) { return nullptr; } return outer->mContext; } JSObject * nsGlobalWindowInner::GetGlobalJSObject() { return FastGetGlobalJSObject(); } void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) { TraceWrapper(aTrc, "active window global"); } bool nsGlobalWindowInner::WouldReuseInnerWindow(nsIDocument* aNewDocument) { MOZ_ASSERT(IsOuterWindow()); // We reuse the inner window when: // a. We are currently at our original document. // b. At least one of the following conditions are true: // -- The new document is the same as the old document. This means that we're // getting called from document.open(). // -- The new document has the same origin as what we have loaded right now. if (!mDoc || !aNewDocument) { return false; } if (!mDoc->IsInitialDocument()) { return false; } #ifdef DEBUG { nsCOMPtr uri; mDoc->GetDocumentURI()->CloneIgnoringRef(getter_AddRefs(uri)); NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?"); } #endif // Great, we're the original document, check for one of the other // conditions. if (mDoc == aNewDocument) { return true; } bool equal; if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), &equal)) && equal) { // The origin is the same. return true; } return false; } void nsGlobalWindowInner::SetInitialPrincipalToSubject() { MOZ_ASSERT(IsOuterWindow()); // First, grab the subject principal. nsCOMPtr newWindowPrincipal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller(); // We should never create windows with an expanded principal. // If we have a system principal, make sure we're not using it for a content // docshell. // NOTE: Please keep this logic in sync with nsWebShellWindow::Initialize(). if (nsContentUtils::IsExpandedPrincipal(newWindowPrincipal) || (nsContentUtils::IsSystemPrincipal(newWindowPrincipal) && GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome)) { newWindowPrincipal = nullptr; } // If there's an existing document, bail if it either: if (mDoc) { // (a) is not an initial about:blank document, or if (!mDoc->IsInitialDocument()) return; // (b) already has the correct principal. if (mDoc->NodePrincipal() == newWindowPrincipal) return; #ifdef DEBUG // If we have a document loaded at this point, it had better be about:blank. // Otherwise, something is really weird. An about:blank page has a // NullPrincipal. bool isNullPrincipal; MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) && isNullPrincipal); #endif } GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal); if (mDoc) { mDoc->SetIsInitialDocument(true); } nsCOMPtr shell = GetDocShell()->GetPresShell(); if (shell && !shell->DidInitialize()) { // Ensure that if someone plays with this document they will get // layout happening. nsRect r = shell->GetPresContext()->GetVisibleArea(); shell->Initialize(r.Width(), r.Height()); } } PopupControlState nsGlobalWindowInner::PushPopupControlState(PopupControlState aState, bool aForce) const { return ::PushPopupControlState(aState, aForce); } void nsGlobalWindowInner::PopPopupControlState(PopupControlState aState) const { ::PopPopupControlState(aState); } PopupControlState nsGlobalWindowInner::GetPopupControlState() const { MOZ_ASSERT(NS_IsMainThread()); return gPopupControlState; } bool nsGlobalWindowInner::ComputeIsSecureContext(nsIDocument* aDocument, SecureContextFlags aFlags) { MOZ_CRASH("Outer Window Only - notbuilding"); } nsresult nsGlobalWindowInner::SetNewDocument(nsIDocument* aDocument, nsISupports* aState, bool aForceReuseInnerWindow) { NS_PRECONDITION(mDocumentPrincipal == nullptr, "mDocumentPrincipal prematurely set!"); MOZ_ASSERT(aDocument); if (!mOuterWindow) { return NS_ERROR_NOT_INITIALIZED; } // Refuse to set a new document if the call came from an inner // window that's not the current inner window. if (mOuterWindow->GetCurrentInnerWindow() != AsInner()) { return NS_ERROR_NOT_AVAILABLE; } return GetOuterWindowInternal()->SetNewDocument(aDocument, aState, aForceReuseInnerWindow); } void nsGlobalWindowInner::PreloadLocalStorage() { MOZ_ASSERT(IsOuterWindow()); if (!Preferences::GetBool(kStorageEnabled)) { return; } if (IsChromeWindow()) { return; } nsIPrincipal* principal = GetPrincipal(); if (!principal) { return; } nsresult rv; nsCOMPtr storageManager = do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); if (NS_FAILED(rv)) { return; } // private browsing windows do not persist local storage to disk so we should // only try to precache storage when we're not a private browsing window. if (principal->GetPrivateBrowsingId() == 0) { nsCOMPtr storage; rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage)); if (NS_SUCCEEDED(rv)) { mLocalStorage = static_cast(storage.get()); } } } void nsGlobalWindowInner::DispatchDOMWindowCreated() { MOZ_ASSERT(IsOuterWindow()); if (!mDoc) { return; } // Fire DOMWindowCreated at chrome event listeners nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"), true /* bubbles */, false /* not cancellable */); nsCOMPtr observerService = mozilla::services::GetObserverService(); // The event dispatching could possibly cause docshell destory, and // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(), // so check it again here. if (observerService && mDoc) { nsAutoString origin; nsIPrincipal* principal = mDoc->NodePrincipal(); nsContentUtils::GetUTFOrigin(principal, origin); observerService-> NotifyObservers(static_cast(this), nsContentUtils::IsSystemPrincipal(principal) ? "chrome-document-global-created" : "content-document-global-created", origin.get()); } } void nsGlobalWindowInner::ClearStatus() { SetStatusOuter(EmptyString()); } void nsGlobalWindowInner::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument) { NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows"); MOZ_ASSERT(aDocument); if (MOZ_LOG_TEST(gDOMLeakPRLog, LogLevel::Debug)) { nsIURI *uri = aDocument->GetDocumentURI(); MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, ("DOMWINDOW %p SetNewDocument %s", this, uri ? uri->GetSpecOrDefault().get() : "")); } mDoc = aDocument; ClearDocumentDependentSlots(aCx); mFocusedNode = nullptr; mLocalStorage = nullptr; mSessionStorage = nullptr; #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); #endif Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, mMutationBits ? 1 : 0); // Clear our mutation bitfield. mMutationBits = 0; } void nsGlobalWindowInner::SetDocShell(nsIDocShell* aDocShell) { MOZ_CRASH("Outer Window Only"); } void nsGlobalWindowInner::DetachFromDocShell() { NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!"); // DetachFromDocShell means the window is being torn down. Drop our // reference to the script context, allowing it to be deleted // later. Meanwhile, keep our weak reference to the script object // so that it can be retrieved later (until it is finalized by the JS GC). // Call FreeInnerObjects on all inner windows, not just the current // one, since some could be held by WindowStateHolder objects that // are GC-owned. RefPtr inner; for (PRCList* node = PR_LIST_HEAD(this); node != this; node = PR_NEXT_LINK(inner)) { // This cast is safe because `node != this`. Non-this nodes are inner windows. inner = static_cast(node); MOZ_ASSERT(inner->IsInnerWindow()); MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == AsOuter()); inner->FreeInnerObjects(); } // Don't report that we were detached to the nsWindowMemoryReporter, as it // only tracks inner windows. NotifyWindowIDDestroyed("outer-window-destroyed"); nsGlobalWindowInner *currentInner = GetCurrentInnerWindowInternal(); if (currentInner) { NS_ASSERTION(mDoc, "Must have doc!"); // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); mDocBaseURI = mDoc->GetDocBaseURI(); // Release our document reference DropOuterWindowDocs(); mFocusedNode = nullptr; } ClearControllers(); mChromeEventHandler = nullptr; // force release now if (mContext) { // When we're about to destroy a top level content window // (for example a tab), we trigger a full GC by passing null as the last // param. We also trigger a full GC for chrome windows. nsJSContext::PokeGC(JS::gcreason::SET_DOC_SHELL, (mTopLevelOuterContentWindow || mIsChrome) ? nullptr : GetWrapperPreserveColor()); mContext = nullptr; } mDocShell = nullptr; // Weak Reference NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); if (mFrames) { mFrames->SetDocShell(nullptr); } MaybeForgiveSpamCount(); CleanUp(); } void nsGlobalWindowInner::SetOpenerWindow(nsPIDOMWindowOuter* aOpener, bool aOriginalOpener) { FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener)); nsWeakPtr opener = do_GetWeakReference(aOpener); if (opener == mOpener) { return; } NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled, "aOriginalOpener is true, but not first call to " "SetOpenerWindow!"); NS_ASSERTION(aOpener || !aOriginalOpener, "Shouldn't set mHadOriginalOpener if aOpener is null"); mOpener = opener.forget(); NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!"); // Check that the js visible opener matches! We currently don't depend on this // being true outside of nightly, so we disable the assertion in optimized // release / beta builds. nsPIDOMWindowOuter* contentOpener = GetSanitizedOpener(aOpener); // contentOpener is not used when the DIAGNOSTIC_ASSERT is compiled out. mozilla::Unused << contentOpener; MOZ_DIAGNOSTIC_ASSERT(!contentOpener || !mTabGroup || mTabGroup == nsGlobalWindowOuter::Cast(contentOpener)->mTabGroup); if (aOriginalOpener) { MOZ_ASSERT(!mHadOriginalOpener, "Probably too late to call ComputeIsSecureContext again"); mHadOriginalOpener = true; mOriginalOpenerWasSecureContext = aOpener->GetCurrentInnerWindow()->IsSecureContext(); } #ifdef DEBUG mSetOpenerWindowCalled = true; #endif } void nsGlobalWindowInner::UpdateParentTarget() { // Try to get our frame element's tab child global (its in-process message // manager). If that fails, fall back to the chrome event handler's tab // child global, and if it doesn't have one, just use the chrome event // handler itself. nsCOMPtr frameElement = GetOuterWindow()->GetFrameElementInternal(); nsCOMPtr eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement); if (!eventTarget) { nsGlobalWindowOuter* topWin = GetScriptableTopInternal(); if (topWin) { frameElement = topWin->AsOuter()->GetFrameElementInternal(); eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement); } } if (!eventTarget) { eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler); } if (!eventTarget) { eventTarget = mChromeEventHandler; } mParentTarget = eventTarget; } EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() { return GetOuterWindowInternal(); } EventTarget* nsGlobalWindowInner::GetTargetForEventTargetChain() { return IsInnerWindow() ? this : GetCurrentInnerWindowInternal(); } nsresult nsGlobalWindowInner::WillHandleEvent(EventChainPostVisitor& aVisitor) { return NS_OK; } nsresult nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) { NS_PRECONDITION(IsInnerWindow(), "GetEventTargetParent is used on outer window!?"); EventMessage msg = aVisitor.mEvent->mMessage; aVisitor.mCanHandle = true; aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 if (msg == eResize && aVisitor.mEvent->IsTrusted()) { // QIing to window so that we can keep the old behavior also in case // a child window is handling resize. nsCOMPtr window = do_QueryInterface(aVisitor.mEvent->mOriginalTarget); if (window) { mIsHandlingResizeEvent = true; } } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) { gMouseDown = true; } else if ((msg == eMouseUp || msg == eDragEnd) && aVisitor.mEvent->IsTrusted()) { gMouseDown = false; if (gDragServiceDisabled) { nsCOMPtr ds = do_GetService("@mozilla.org/widget/dragservice;1"); if (ds) { gDragServiceDisabled = false; ds->Unsuppress(); } } } aVisitor.mParentTarget = GetParentTarget(); // Handle 'active' event. if (!mIdleObservers.IsEmpty() && aVisitor.mEvent->IsTrusted() && (aVisitor.mEvent->HasMouseEventMessage() || aVisitor.mEvent->HasDragEventMessage())) { mAddActiveEventFuzzTime = false; } return NS_OK; } bool nsGlobalWindowInner::ShouldPromptToBlockDialogs() { MOZ_ASSERT(IsOuterWindow()); nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal(); if (!topWindowOuter) { NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?"); return true; } nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal(); if (!topWindow) { return true; } return topWindow->DialogsAreBeingAbused(); } bool nsGlobalWindowInner::AreDialogsEnabled() { MOZ_ASSERT(IsOuterWindow()); nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal(); if (!topWindowOuter) { NS_ERROR("AreDialogsEnabled() called without a top window?"); return false; } // TODO: Warn if no top window? nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal(); if (!topWindow) { return false; } // Dialogs are blocked if the content viewer is hidden if (mDocShell) { nsCOMPtr cv; mDocShell->GetContentViewer(getter_AddRefs(cv)); bool isHidden; cv->GetIsHidden(&isHidden); if (isHidden) { return false; } } // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS // (or if we have no document, of course). Which document? Who knows; the // spec is daft. See . For now // just go ahead and check mDoc, since in everything except edge cases in // which a frame is allow-same-origin but not allow-scripts and is being poked // at by some other window this should be the right thing anyway. if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) { return false; } return topWindow->mAreDialogsEnabled; } bool nsGlobalWindowInner::DialogsAreBeingAbused() { MOZ_ASSERT(IsInnerWindow()); NS_ASSERTION(GetScriptableTopInternal() && GetScriptableTopInternal()->GetCurrentInnerWindowInternal() == this, "DialogsAreBeingAbused called with invalid window"); if (mLastDialogQuitTime.IsNull() || nsContentUtils::IsCallerChrome()) { return false; } TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime); if (dialogInterval.ToSeconds() < Preferences::GetInt("dom.successive_dialog_time_limit", DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) { mDialogAbuseCount++; return GetPopupControlState() > openAllowed || mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT; } // Reset the abuse counter mDialogAbuseCount = 0; return false; } bool nsGlobalWindowInner::ConfirmDialogIfNeeded() { MOZ_ASSERT(IsOuterWindow()); NS_ENSURE_TRUE(mDocShell, false); nsCOMPtr promptSvc = do_GetService("@mozilla.org/embedcomp/prompt-service;1"); if (!promptSvc) { return true; } // Reset popup state while opening a modal dialog, and firing events // about the dialog, to prevent the current state from being active // the whole time a modal dialog is open. nsAutoPopupStatePusher popupStatePusher(openAbused, true); bool disableDialog = false; nsAutoString label, title; nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label); nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogPreventTitle", title); promptSvc->Confirm(AsOuter(), title.get(), label.get(), &disableDialog); if (disableDialog) { DisableDialogs(); return false; } return true; } void nsGlobalWindowInner::DisableDialogs() { nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal(); if (!topWindowOuter) { NS_ERROR("DisableDialogs() called without a top window?"); return; } nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal(); // TODO: Warn if no top window? if (topWindow) { topWindow->mAreDialogsEnabled = false; } } void nsGlobalWindowInner::EnableDialogs() { nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal(); if (!topWindowOuter) { NS_ERROR("EnableDialogs() called without a top window?"); return; } // TODO: Warn if no top window? nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal(); if (topWindow) { topWindow->mAreDialogsEnabled = true; } } nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) { NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?"); // Return early if there is nothing to do. switch (aVisitor.mEvent->mMessage) { case eResize: case eUnload: case eLoad: break; default: return NS_OK; } /* mChromeEventHandler and mContext go dangling in the middle of this function under some circumstances (events that destroy the window) without this addref. */ nsCOMPtr kungFuDeathGrip1(mChromeEventHandler); mozilla::Unused << kungFuDeathGrip1; // These aren't referred to through the function nsCOMPtr kungFuDeathGrip2(GetContextInternal()); mozilla::Unused << kungFuDeathGrip2; // These aren't referred to through the function if (aVisitor.mEvent->mMessage == eResize) { mIsHandlingResizeEvent = false; } else if (aVisitor.mEvent->mMessage == eUnload && aVisitor.mEvent->IsTrusted()) { // If any VR display presentation is active at unload, the next page // will receive a vrdisplayactive event to indicate that it should // immediately begin vr presentation. This should occur when navigating // forwards, navigating backwards, and on page reload. for (const auto& display : mVRDisplays) { if (display->IsPresenting()) { // Save this VR display ID to trigger vrdisplayactivate event // after the next load event. nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (outer) { outer->SetAutoActivateVRDisplayID(display->DisplayId()); } // XXX The WebVR 1.1 spec does not define which of multiple VR // presenting VR displays will be chosen during navigation. // As the underlying platform VR API's currently only allow a single // VR display, it is safe to choose the first VR display for now. break; } } // Execute bindingdetached handlers before we tear ourselves // down. if (mDoc) { mDoc->BindingManager()->ExecuteDetachedHandlers(); } mIsDocumentLoaded = false; } else if (aVisitor.mEvent->mMessage == eLoad && aVisitor.mEvent->IsTrusted()) { // This is page load event since load events don't propagate to |window|. // @see nsDocument::GetEventTargetParent. mIsDocumentLoaded = true; mTimeoutManager->OnDocumentLoaded(); nsCOMPtr element = GetOuterWindow()->GetFrameElementInternal(); nsIDocShell* docShell = GetDocShell(); if (element && GetParentInternal() && docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) { // If we're not in chrome, or at a chrome boundary, fire the // onload event for the frame element. nsEventStatus status = nsEventStatus_eIgnore; WidgetEvent event(aVisitor.mEvent->IsTrusted(), eLoad); event.mFlags.mBubbles = false; event.mFlags.mCancelable = false; // Most of the time we could get a pres context to pass in here, // but not always (i.e. if this window is not shown there won't // be a pres context available). Since we're not firing a GUI // event we don't need a pres context anyway so we just pass // null as the pres context all the time here. EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status); } uint32_t autoActivateVRDisplayID = 0; nsGlobalWindowOuter* outer = GetOuterWindowInternal(); if (outer) { autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID(); } if (autoActivateVRDisplayID) { DispatchVRDisplayActivate(autoActivateVRDisplayID, VRDisplayEventReason::Navigation); } } return NS_OK; } void nsGlobalWindowInner::PoisonOuterWindowProxy(JSObject *aObject) { MOZ_ASSERT(IsOuterWindow()); if (aObject == GetWrapperMaybeDead()) { PoisonWrapper(); } } nsresult nsGlobalWindowInner::SetArguments(nsIArray *aArguments) { MOZ_ASSERT(IsOuterWindow()); nsresult rv; // Historically, we've used the same machinery to handle openDialog arguments // (exposed via window.arguments) and showModalDialog arguments (exposed via // window.dialogArguments), even though the former is XUL-only and uses an XPCOM // array while the latter is web-exposed and uses an arbitrary JS value. // Moreover, per-spec |dialogArguments| is a property of the browsing context // (outer), whereas |arguments| lives on the inner. // // We've now mostly separated them, but the difference is still opaque to // nsWindowWatcher (the caller of SetArguments in this little back-and-forth // embedding waltz we do here). // // So we need to demultiplex the two cases here. nsGlobalWindowInner *currentInner = GetCurrentInnerWindowInternal(); mArguments = aArguments; rv = currentInner->DefineArgumentsProperty(aArguments); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray *aArguments) { MOZ_ASSERT(IsInnerWindow()); nsIScriptContext *ctx = GetOuterWindowInternal()->mContext; NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED); JS::Rooted obj(RootingCx(), GetWrapperPreserveColor()); return ctx->SetProperty(obj, "arguments", aArguments); } //***************************************************************************** // nsGlobalWindowInner::nsIScriptObjectPrincipal //***************************************************************************** nsIPrincipal* nsGlobalWindowInner::GetPrincipal() { if (mDoc) { // If we have a document, get the principal from the document return mDoc->NodePrincipal(); } if (mDocumentPrincipal) { return mDocumentPrincipal; } // If we don't have a principal and we don't have a document we // ask the parent window for the principal. This can happen when // loading a frameset that has a , in // that case the global window is used in JS before we've loaded // a document into the window. nsCOMPtr objPrincipal = do_QueryInterface(GetParentInternal()); if (objPrincipal) { return objPrincipal->GetPrincipal(); } return nullptr; } //***************************************************************************** // nsGlobalWindowInner::nsIDOMWindow //***************************************************************************** bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) { MOZ_ASSERT(IsInnerWindow()); mAudioContexts.AppendElement(aAudioContext); // Return true if the context should be muted and false if not. nsIDocShell* docShell = GetDocShell(); return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline(); } void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) { MOZ_ASSERT(IsInnerWindow()); mAudioContexts.RemoveElement(aAudioContext); } void nsPIDOMWindowInner::MuteAudioContexts() { MOZ_ASSERT(IsInnerWindow()); for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { if (!mAudioContexts[i]->IsOffline()) { mAudioContexts[i]->Mute(); } } } void nsPIDOMWindowInner::UnmuteAudioContexts() { MOZ_ASSERT(IsInnerWindow()); for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { if (!mAudioContexts[i]->IsOffline()) { mAudioContexts[i]->Unmute(); } } } nsGlobalWindowInner* nsGlobalWindowInner::Window() { return AssertInner(); } nsGlobalWindowInner* nsGlobalWindowInner::Self() { return AssertInner(); } Navigator* nsGlobalWindowInner::Navigator() { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mNavigator) { mNavigator = new mozilla::dom::Navigator(AsInner()); } return mNavigator; } nsIDOMNavigator* nsGlobalWindowInner::GetNavigator() { FORWARD_TO_INNER(GetNavigator, (), nullptr); return Navigator(); } nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mScreen) { mScreen = nsScreen::Create(AsInner()); if (!mScreen) { aError.Throw(NS_ERROR_UNEXPECTED); return nullptr; } } return mScreen; } nsIDOMScreen* nsGlobalWindowInner::GetScreen() { FORWARD_TO_INNER(GetScreen, (), nullptr); ErrorResult dummy; nsIDOMScreen* screen = GetScreen(dummy); dummy.SuppressException(); return screen; } nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mHistory) { mHistory = new nsHistory(AsInner()); } return mHistory; } CustomElementRegistry* nsGlobalWindowInner::CustomElements() { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mCustomElements) { mCustomElements = new CustomElementRegistry(AsInner()); } return mCustomElements; } Performance* nsPIDOMWindowInner::GetPerformance() { MOZ_ASSERT(IsInnerWindow()); CreatePerformanceObjectIfNeeded(); return mPerformance; } Performance* nsGlobalWindowInner::GetPerformance() { return AsInner()->GetPerformance(); } void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() { MOZ_ASSERT(IsInnerWindow()); if (mPerformance || !mDoc) { return; } RefPtr timing = mDoc->GetNavigationTiming(); nsCOMPtr timedChannel(do_QueryInterface(mDoc->GetChannel())); bool timingEnabled = false; if (!timedChannel || !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || !timingEnabled) { timedChannel = nullptr; } if (timing) { mPerformance = Performance::CreateForMainThread(this, timing, timedChannel); } } bool nsPIDOMWindowInner::IsSecureContext() const { return nsGlobalWindowInner::Cast(this)->IsSecureContext(); } bool nsPIDOMWindowInner::IsSecureContextIfOpenerIgnored() const { return nsGlobalWindowInner::Cast(this)->IsSecureContextIfOpenerIgnored(); } void nsPIDOMWindowInner::Suspend() { nsGlobalWindowInner::Cast(this)->Suspend(); } void nsPIDOMWindowInner::Resume() { nsGlobalWindowInner::Cast(this)->Resume(); } void nsPIDOMWindowInner::Freeze() { nsGlobalWindowInner::Cast(this)->Freeze(); } void nsPIDOMWindowInner::Thaw() { nsGlobalWindowInner::Cast(this)->Thaw(); } void nsPIDOMWindowInner::SyncStateFromParentWindow() { nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow(); } void nsGlobalWindowInner::UpdateTopInnerWindow() { if (!IsInnerWindow() || AsInner()->IsTopInnerWindow() || !mTopInnerWindow) { return; } mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets); } void nsPIDOMWindowInner::AddPeerConnection() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsInnerWindow()); mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections++ : mActivePeerConnections++; } void nsPIDOMWindowInner::RemovePeerConnection() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsInnerWindow()); MOZ_ASSERT(mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections : mActivePeerConnections); mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections-- : mActivePeerConnections--; } bool nsPIDOMWindowInner::HasActivePeerConnections() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsInnerWindow()); return mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections : mActivePeerConnections; } bool nsPIDOMWindowInner::IsPlayingAudio() { for (uint32_t i = 0; i < mAudioContexts.Length(); i++) { if (mAudioContexts[i]->IsRunning()) { return true; } } RefPtr acs = AudioChannelService::Get(); if (!acs) { return false; } auto outer = GetOuterWindow(); if (!outer) { // We've been unlinked and are about to die. Not a good time to pretend to // be playing audio. return false; } return acs->IsWindowActive(outer); } bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; } mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() { return *mTimeoutManager; } bool nsPIDOMWindowInner::IsRunningTimeout() { return TimeoutManager().IsRunningTimeout(); } void nsPIDOMWindowInner::TryToCacheTopInnerWindow() { if (mHasTriedToCacheTopInnerWindow) { return; } MOZ_ASSERT(!mInnerObjectsFreed); mHasTriedToCacheTopInnerWindow = true; nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(AsInner()); MOZ_ASSERT(window); if (nsCOMPtr topOutter = window->GetScriptableTop()) { mTopInnerWindow = topOutter->GetCurrentInnerWindow(); } } void nsPIDOMWindowInner::UpdateActiveIndexedDBTransactionCount(int32_t aDelta) { MOZ_ASSERT(NS_IsMainThread()); if (aDelta == 0) { return; } TabGroup()->IndexedDBTransactionCounter() += aDelta; } void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) { MOZ_ASSERT(NS_IsMainThread()); if (aDelta == 0) { return; } // We count databases but not transactions because only active databases // could block throttling. uint32_t& counter = mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases : mNumOfIndexedDBDatabases; counter+= aDelta; TabGroup()->IndexedDBDatabaseCounter() += aDelta; } bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() { MOZ_ASSERT(NS_IsMainThread()); return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0 : mNumOfIndexedDBDatabases > 0; } void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) { MOZ_ASSERT(NS_IsMainThread()); if (aDelta == 0) { return; } if (mTopInnerWindow && !IsTopInnerWindow()) { mTopInnerWindow->UpdateWebSocketCount(aDelta); } MOZ_DIAGNOSTIC_ASSERT( aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets)); mNumOfOpenWebSockets += aDelta; } bool nsPIDOMWindowInner::HasOpenWebSockets() const { MOZ_ASSERT(NS_IsMainThread()); return mNumOfOpenWebSockets || (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets); } bool nsPIDOMWindowInner::GetAudioCaptured() const { MOZ_ASSERT(IsInnerWindow()); return mAudioCaptured; } nsresult nsPIDOMWindowInner::SetAudioCapture(bool aCapture) { MOZ_ASSERT(IsInnerWindow()); mAudioCaptured = aCapture; RefPtr service = AudioChannelService::GetOrCreate(); if (service) { service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture); } return NS_OK; } // nsISpeechSynthesisGetter #ifdef MOZ_WEBSPEECH SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mSpeechSynthesis) { mSpeechSynthesis = new SpeechSynthesis(AsInner()); } return mSpeechSynthesis; } bool nsGlobalWindowInner::HasActiveSpeechSynthesis() { MOZ_ASSERT(IsInnerWindow()); if (mSpeechSynthesis) { return !mSpeechSynthesis->HasEmptyQueue(); } return false; } #endif already_AddRefed nsGlobalWindowInner::GetParentOuter() { MOZ_RELEASE_ASSERT(IsOuterWindow()); if (!mDocShell) { return nullptr; } nsCOMPtr parent; if (mDocShell->GetIsMozBrowser()) { parent = AsOuter(); } else { parent = GetParent(); } return parent.forget(); } already_AddRefed nsGlobalWindowInner::GetParent(ErrorResult& aError) { FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr); } /** * GetScriptableParent is called when script reads window.parent. * * In contrast to GetRealParent, GetScriptableParent respects