mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
f2b43fd60f
The purpose of this patch is to know if two different global objects are in the same agent cluster. To achieve this, the plan to to add an id for each DocGroup in this patch. And, have an id on each workers. The final goal is to pass these ids to ClientInfo so that we can check if two different global are in the same agent cluster group by checking the AgentClusterId on their ClientInfos. Differential Revision: https://phabricator.services.mozilla.com/D43239 --HG-- extra : moz-landing-system : lando
269 lines
8.8 KiB
C++
269 lines
8.8 KiB
C++
/* -*- 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/DOMTypes.h"
|
|
#include "mozilla/dom/TabGroup.h"
|
|
#include "mozilla/AbstractThread.h"
|
|
#include "mozilla/PerformanceUtils.h"
|
|
#include "mozilla/ThrottledEventQueue.h"
|
|
#include "mozilla/StaticPrefs_dom.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsDOMMutationObserver.h"
|
|
#include "nsProxyRelease.h"
|
|
#if defined(XP_WIN)
|
|
# include <processthreadsapi.h> // for GetCurrentProcessId()
|
|
#else
|
|
# include <unistd.h> // for getpid()
|
|
#endif // defined(XP_WIN)
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
AutoTArray<RefPtr<DocGroup>, 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(Document* aDocument) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mDocuments.Contains(aDocument));
|
|
mDocuments.RemoveElement(aDocument);
|
|
}
|
|
|
|
DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey,
|
|
const nsID& aAgentClusterId)
|
|
: mKey(aKey), mTabGroup(aTabGroup), mAgentClusterId(aAgentClusterId) {
|
|
// This method does not add itself to mTabGroup->mDocGroups as the caller does
|
|
// it for us.
|
|
mPerformanceCounter =
|
|
new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + 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);
|
|
|
|
if (mIframePostMessageQueue) {
|
|
FlushIframePostMessageQueue();
|
|
}
|
|
}
|
|
|
|
RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(mPerformanceCounter);
|
|
#if defined(XP_WIN)
|
|
uint32_t pid = GetCurrentProcessId();
|
|
#else
|
|
uint32_t pid = getpid();
|
|
#endif
|
|
uint64_t windowID = 0;
|
|
uint16_t count = 0;
|
|
uint64_t duration = 0;
|
|
bool isTopLevel = false;
|
|
nsCString host;
|
|
nsCOMPtr<nsPIDOMWindowOuter> top;
|
|
RefPtr<AbstractThread> mainThread;
|
|
|
|
// iterating on documents until we find the top window
|
|
for (const auto& document : *this) {
|
|
nsCOMPtr<Document> doc = document;
|
|
MOZ_ASSERT(doc);
|
|
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
|
|
if (!docURI) {
|
|
continue;
|
|
}
|
|
docURI->GetHost(host);
|
|
// If the host is empty, using the url
|
|
if (host.IsEmpty()) {
|
|
host = docURI->GetSpecOrDefault();
|
|
}
|
|
// looking for the top level document URI
|
|
nsPIDOMWindowOuter* win = doc->GetWindow();
|
|
if (!win) {
|
|
continue;
|
|
}
|
|
top = win->GetInProcessTop();
|
|
if (!top) {
|
|
continue;
|
|
}
|
|
windowID = top->WindowID();
|
|
isTopLevel = win->IsTopLevelWindow();
|
|
mainThread = AbstractMainThreadFor(TaskCategory::Performance);
|
|
break;
|
|
}
|
|
|
|
MOZ_ASSERT(!host.IsEmpty());
|
|
duration = mPerformanceCounter->GetExecutionDuration();
|
|
FallibleTArray<CategoryDispatch> 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<TaskCategory>(index);
|
|
count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category));
|
|
CategoryDispatch item = CategoryDispatch(index, count);
|
|
if (!items.AppendElement(item, fallible)) {
|
|
NS_ERROR("Could not complete the operation");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isTopLevel) {
|
|
return PerformanceInfoPromise::CreateAndResolve(
|
|
PerformanceInfo(host, pid, windowID, duration,
|
|
mPerformanceCounter->GetID(), false, isTopLevel,
|
|
PerformanceMemoryInfo(), // Empty memory info
|
|
items),
|
|
__func__);
|
|
}
|
|
|
|
MOZ_ASSERT(mainThread);
|
|
RefPtr<DocGroup> self = this;
|
|
|
|
return CollectMemoryInfo(top, mainThread)
|
|
->Then(
|
|
mainThread, __func__,
|
|
[self, host, pid, windowID, duration, isTopLevel,
|
|
items](const PerformanceMemoryInfo& aMemoryInfo) {
|
|
PerformanceInfo info =
|
|
PerformanceInfo(host, pid, windowID, duration,
|
|
self->mPerformanceCounter->GetID(), false,
|
|
isTopLevel, aMemoryInfo, items);
|
|
|
|
return PerformanceInfoPromise::CreateAndResolve(std::move(info),
|
|
__func__);
|
|
},
|
|
[self](const nsresult rv) {
|
|
return PerformanceInfoPromise::CreateAndReject(rv, __func__);
|
|
});
|
|
}
|
|
|
|
nsresult DocGroup::Dispatch(TaskCategory aCategory,
|
|
already_AddRefed<nsIRunnable>&& aRunnable) {
|
|
if (mPerformanceCounter) {
|
|
mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory));
|
|
}
|
|
return mTabGroup->DispatchWithDocGroup(aCategory, std::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(HTMLSlotElement& aSlot) {
|
|
MOZ_ASSERT(!mSignalSlotList.Contains(&aSlot));
|
|
mSignalSlotList.AppendElement(&aSlot);
|
|
|
|
if (!sPendingDocGroups) {
|
|
// Queue a mutation observer compound microtask.
|
|
nsDOMMutationObserver::QueueMutationObserverMicroTask();
|
|
sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>;
|
|
}
|
|
|
|
sPendingDocGroups->AppendElement(this);
|
|
}
|
|
|
|
bool DocGroup::TryToLoadIframesInBackground() {
|
|
return StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
|
|
StaticPrefs::dom_cross_origin_iframes_loaded_in_background();
|
|
}
|
|
|
|
nsresult DocGroup::QueueIframePostMessages(
|
|
already_AddRefed<nsIRunnable>&& aRunnable, uint64_t aWindowId) {
|
|
if (DocGroup::TryToLoadIframesInBackground()) {
|
|
if (!mIframePostMessageQueue) {
|
|
nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
|
|
mIframePostMessageQueue = ThrottledEventQueue::Create(
|
|
target, "Background Loading Iframe PostMessage Queue",
|
|
nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
|
|
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
|
MOZ_ALWAYS_SUCCEEDS(rv);
|
|
}
|
|
|
|
// Ensure the queue is disabled. Unlike the postMessageEvent queue in
|
|
// TabGroup, this postMessage queue should always be paused, because if
|
|
// we leave it open, the postMessage may get dispatched to an unloaded
|
|
// iframe
|
|
MOZ_ASSERT(mIframePostMessageQueue);
|
|
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
|
|
|
mIframesUsedPostMessageQueue.PutEntry(aWindowId);
|
|
|
|
mIframePostMessageQueue->Dispatch(std::move(aRunnable), NS_DISPATCH_NORMAL);
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
void DocGroup::TryFlushIframePostMessages(uint64_t aWindowId) {
|
|
if (DocGroup::TryToLoadIframesInBackground()) {
|
|
mIframesUsedPostMessageQueue.RemoveEntry(aWindowId);
|
|
if (mIframePostMessageQueue && mIframesUsedPostMessageQueue.IsEmpty()) {
|
|
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
|
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
|
MOZ_ALWAYS_SUCCEEDS(rv);
|
|
FlushIframePostMessageQueue();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DocGroup::FlushIframePostMessageQueue() {
|
|
nsCOMPtr<nsIRunnable> event;
|
|
while ((event = mIframePostMessageQueue->GetEvent())) {
|
|
Dispatch(TaskCategory::Other, event.forget());
|
|
}
|
|
}
|
|
|
|
void DocGroup::MoveSignalSlotListTo(nsTArray<RefPtr<HTMLSlotElement>>& aDest) {
|
|
aDest.SetCapacity(aDest.Length() + mSignalSlotList.Length());
|
|
for (RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
|
|
slot->RemovedFromSignalSlotList();
|
|
aDest.AppendElement(std::move(slot));
|
|
}
|
|
mSignalSlotList.Clear();
|
|
}
|
|
|
|
bool DocGroup::IsActive() const {
|
|
for (Document* doc : mDocuments) {
|
|
if (doc->IsCurrentActiveDocument()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|