/* -*- 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/. */ #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/TabGroup.h" #include "mozilla/Telemetry.h" #include "nsIDocShell.h" #include "nsDOMMutationObserver.h" #if defined(XP_WIN) #include // for GetCurrentProcessId() #else #include // for getpid() #endif // defined(XP_WIN) namespace mozilla { namespace dom { AutoTArray, 2>* DocGroup::sPendingDocGroups = nullptr; /* static */ nsresult DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey) { // Use GetBaseDomain() to handle things like file URIs, IP address URIs, // etc. correctly. nsresult rv = aPrincipal->GetBaseDomain(aKey); if (NS_FAILED(rv)) { // We don't really know what to do here. But we should be conservative, // otherwise it would be possible to reorder two events incorrectly in the // future if we interrupt at the DocGroup level, so to be safe, use an // empty string to classify all such documents as belonging to the same // DocGroup. aKey.Truncate(); } return rv; } void DocGroup::RemoveDocument(nsIDocument* aDocument) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mDocuments.Contains(aDocument)); mDocuments.RemoveElement(aDocument); } DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey) : mKey(aKey), mTabGroup(aTabGroup) { // This method does not add itself to mTabGroup->mDocGroups as the caller does it for us. if (mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()) { mPerformanceCounter = new mozilla::PerformanceCounter(aKey); } } DocGroup::~DocGroup() { MOZ_ASSERT(mDocuments.IsEmpty()); if (!NS_IsMainThread()) { nsIEventTarget* target = EventTargetFor(TaskCategory::Other); NS_ProxyRelease("DocGroup::mReactionsStack", target, mReactionsStack.forget()); } mTabGroup->mDocGroups.RemoveEntry(mKey); } PerformanceInfo DocGroup::ReportPerformanceInfo() { AssertIsOnMainThread(); MOZ_ASSERT(mPerformanceCounter); #if defined(XP_WIN) uint32_t pid = GetCurrentProcessId(); #else uint32_t pid = getpid(); #endif uint64_t wid = 0; uint64_t pwid = 0; uint16_t count = 0; uint64_t duration = 0; nsCString host = NS_LITERAL_CSTRING("None"); for (const auto& document : *this) { // grabbing the host name of the first document nsCOMPtr doc = do_QueryInterface(document); MOZ_ASSERT(doc); nsCOMPtr docURI = doc->GetDocumentURI(); if (!docURI) { continue; } docURI->GetHost(host); wid = doc->OuterWindowID(); // getting the top window id - if not possible // pwid gets the same value than wid pwid = wid; nsPIDOMWindowInner* win = doc->GetInnerWindow(); if (win) { nsPIDOMWindowOuter* outer = win->GetOuterWindow(); if (outer) { nsCOMPtr top = outer->GetTop(); if (top) { pwid = top->WindowID(); } } } } duration = mPerformanceCounter->GetExecutionDuration(); FallibleTArray items; // now that we have the host and window ids, let's look at the perf counters for (uint32_t index = 0; index < (uint32_t)TaskCategory::Count; index++) { TaskCategory category = static_cast(index); count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category)); CategoryDispatch item = CategoryDispatch(index, count); if (!items.AppendElement(item, fallible)) { NS_ERROR("Could not complete the operation"); return PerformanceInfo(host, pid, wid, pwid, duration, false, items); } } // setting back all counters to zero mPerformanceCounter->ResetPerformanceCounters(); return PerformanceInfo(host, pid, wid, pwid, duration, false, items); } nsresult DocGroup::Dispatch(TaskCategory aCategory, already_AddRefed&& aRunnable) { if (mPerformanceCounter) { mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory)); } return mTabGroup->DispatchWithDocGroup(aCategory, Move(aRunnable), this); } nsISerialEventTarget* DocGroup::EventTargetFor(TaskCategory aCategory) const { return mTabGroup->EventTargetFor(aCategory); } AbstractThread* DocGroup::AbstractMainThreadFor(TaskCategory aCategory) { MOZ_RELEASE_ASSERT(NS_IsMainThread()); return mTabGroup->AbstractMainThreadFor(aCategory); } bool* DocGroup::GetValidAccessPtr() { return mTabGroup->GetValidAccessPtr(); } void DocGroup::SignalSlotChange(const HTMLSlotElement* aSlot) { if (mSignalSlotList.Contains(aSlot)) { return; } mSignalSlotList.AppendElement(const_cast(aSlot)); if (!sPendingDocGroups) { // Queue a mutation observer compound microtask. nsDOMMutationObserver::QueueMutationObserverMicroTask(); sPendingDocGroups = new AutoTArray, 2>; } sPendingDocGroups->AppendElement(this); } } }