diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index ecccccec5cd1..24f7d8f08631 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -31,11 +31,14 @@ #include "mozilla/dom/JSActorService.h" #include "mozilla/dom/MediaMetadata.h" #include "mozilla/dom/MediaSessionBinding.h" +#include "mozilla/dom/PBrowserParent.h" +#include "mozilla/dom/PWindowGlobalParent.h" #include "mozilla/dom/Performance.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ReportingHeader.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options +#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/ipc/GeckoChildProcessHost.h" @@ -810,7 +813,8 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, requests.EmplaceBack( /* aPid = */ base::GetCurrentProcId(), /* aProcessType = */ ProcType::Browser, - /* aOrigin = */ ""_ns); + /* aOrigin = */ ""_ns, + /* aWindowInfo = */ nsTArray()); mozilla::ipc::GeckoChildProcessHost::GetAll( [&requests, @@ -825,6 +829,8 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, base::ProcessId childPid = base::GetProcId(handle); int32_t childId = 0; mozilla::ProcType type = mozilla::ProcType::Unknown; + nsTArray windows; + switch (aGeckoProcess->GetProcessType()) { case GeckoProcessType::GeckoProcessType_Content: { ContentParent* contentParent = nullptr; @@ -841,6 +847,35 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, // FIXME: When can this happen? return; } + + // Attach DOM window information to the process. + for (const auto& browserParentWrapper : + contentParent->ManagedPBrowserParent()) { + for (const auto& windowGlobalParentWrapper : + browserParentWrapper.GetKey() + ->ManagedPWindowGlobalParent()) { + // WindowGlobalParent is the only immediate subclass of + // PWindowGlobalParent. + auto* windowGlobalParent = static_cast( + windowGlobalParentWrapper.GetKey()); + + nsString documentTitle; + windowGlobalParent->GetDocumentTitle(documentTitle); + WindowInfo* window = windows.EmplaceBack( + fallible, + /* aOuterWindowId = */ windowGlobalParent->OuterWindowId(), + /* aDocumentURI = */ windowGlobalParent->GetDocumentURI(), + /* aDocumentTitle = */ std::move(documentTitle), + /* aIsProcessRoot = */ windowGlobalParent->IsProcessRoot(), + /* aIsInProcess = */ windowGlobalParent->IsInProcess()); + if (!window) { + // That's bad sign, but we don't have a good place to return + // an OOM error from. + return; + } + } + } + // Converting the remoteType into a ProcType. // Ideally, the remoteType should be strongly typed // upstream, this would make the conversion less brittle. @@ -922,6 +957,7 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, /* aPid = */ childPid, /* aProcessType = */ type, /* aOrigin = */ origin, + /* aWindowInfo = */ std::move(windows), /* aChild = */ childId #ifdef XP_MACOSX , @@ -976,6 +1012,19 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, childInfo->mChildID = sysProcInfo.childId; childInfo->mOrigin = sysProcInfo.origin; childInfo->mType = ProcTypeToWebIDL(sysProcInfo.type); + + for (const auto& source : sysProcInfo.windows) { + auto* dest = childInfo->mWindows.AppendElement(fallible); + if (!dest) { + domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); + return; + } + dest->mOuterWindowId = source.outerWindowId; + dest->mDocumentURI = source.documentURI; + dest->mDocumentTitle = source.documentTitle; + dest->mIsProcessRoot = source.isProcessRoot; + dest->mIsInProcess = source.isInProcess; + } } } diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl index f611363a58f8..ee97b0981b9d 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -530,6 +530,24 @@ dictionary ThreadInfoDictionary { unsigned long long cpuKernel = 0; }; +dictionary WindowInfoDictionary { + // Window ID, as known to the parent process. + unsigned long long outerWindowId = 0; + + // URI of the document loaded in the window. + URI? documentURI = null; + + // Title of the document loaded in the window. + // Commonly empty for subframes. + DOMString documentTitle = ""; + + // `true` if this window is the root for the process. + boolean isProcessRoot = false; + + // `true` if this is loaded in the same process as the parent, `false` otherwise. + boolean isInProcess = false; +}; + /** * Information on a child process. * @@ -579,6 +597,9 @@ dictionary ChildProcInfoDictionary { // Type of this child process. WebIDLProcType type = "web"; + + // The windows implemented by this process. + sequence windows = []; }; /** diff --git a/widget/ProcInfo.h b/widget/ProcInfo.h index d083f15854f5..fb7a77c5c8f7 100644 --- a/widget/ProcInfo.h +++ b/widget/ProcInfo.h @@ -61,6 +61,38 @@ struct ThreadInfo { uint64_t cpuKernel = 0; }; +// Info on a DOM window. +struct WindowInfo { + explicit WindowInfo() + : outerWindowId(0), + documentURI(nullptr), + documentTitle(u""_ns), + isProcessRoot(false), + isInProcess(false) {} + WindowInfo(uint64_t aOuterWindowId, nsIURI* aDocumentURI, + nsAString&& aDocumentTitle, bool aIsProcessRoot, bool aIsInProcess) + : outerWindowId(aOuterWindowId), + documentURI(aDocumentURI), + documentTitle(std::move(aDocumentTitle)), + isProcessRoot(aIsProcessRoot), + isInProcess(aIsInProcess) {} + + // Internal window id. + const uint64_t outerWindowId; + + // URI of the document. + const nsCOMPtr documentURI; + + // Title of the document. + const nsString documentTitle; + + // True if this is the toplevel window of the process. + // Note that this may be an iframe from another process. + const bool isProcessRoot; + + const bool isInProcess; +}; + struct ProcInfo { // Process Id base::ProcessId pid = 0; @@ -82,6 +114,8 @@ struct ProcInfo { uint64_t cpuKernel = 0; // Threads owned by this process. CopyableTArray threads; + // DOM windows represented by this process. + CopyableTArray windows; }; typedef MozPromise, nsresult, true> @@ -99,7 +133,8 @@ typedef MozPromise, nsresult, true> */ struct ProcInfoRequest { ProcInfoRequest(base::ProcessId aPid, ProcType aProcessType, - const nsACString& aOrigin, uint32_t aChildId = 0 + const nsACString& aOrigin, nsTArray&& aWindowInfo, + uint32_t aChildId = 0 #ifdef XP_MACOSX , mach_port_t aChildTask = 0 @@ -108,6 +143,7 @@ struct ProcInfoRequest { : pid(aPid), processType(aProcessType), origin(aOrigin), + windowInfo(std::move(aWindowInfo)), childId(aChildId) #ifdef XP_MACOSX , @@ -118,6 +154,7 @@ struct ProcInfoRequest { const base::ProcessId pid; const ProcType processType; const nsCString origin; + const nsTArray windowInfo; // If the process is a child, its child id, otherwise `0`. const int32_t childId; #ifdef XP_MACOSX diff --git a/widget/cocoa/ProcInfo.mm b/widget/cocoa/ProcInfo.mm index cce0014797be..077692d9e468 100644 --- a/widget/cocoa/ProcInfo.mm +++ b/widget/cocoa/ProcInfo.mm @@ -45,6 +45,7 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { info.childId = request.childId; info.type = request.processType; info.origin = std::move(request.origin); + info.windows = std::move(request.windowInfo); struct proc_bsdinfo proc; if ((unsigned long)proc_pidinfo(request.pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE) < PROC_PIDTBSDINFO_SIZE) { diff --git a/widget/gtk/ProcInfo.cpp b/widget/gtk/ProcInfo.cpp index 3bf7144f4df8..cb7e785bc022 100644 --- a/widget/gtk/ProcInfo.cpp +++ b/widget/gtk/ProcInfo.cpp @@ -248,6 +248,7 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { info.childId = request.childId; info.type = request.processType; info.origin = request.origin; + info.windows = std::move(request.windowInfo); // Let's look at the threads nsCString taskPath; diff --git a/widget/tests/browser/browser_test_procinfo.js b/widget/tests/browser/browser_test_procinfo.js index 87cc25e44092..43051351ab91 100644 --- a/widget/tests/browser/browser_test_procinfo.js +++ b/widget/tests/browser/browser_test_procinfo.js @@ -16,7 +16,16 @@ const isFissionEnabled = Services.prefs.getBoolPref("fission.autostart"); const SAMPLE_SIZE = 10; add_task(async function test_proc_info() { - waitForExplicitFinish(); + console.log("YORIC", "Test starts"); + // Open a few `about:home` tabs, they'll end up in `privilegedabout`. + let tabsAboutHome = []; + for (let i = 0; i < 5; ++i) { + let tab = BrowserTestUtils.addTab(gBrowser, "about:home"); + tabsAboutHome.push(tab); + gBrowser.selectedTab = tab; + await BrowserTestUtils.browserLoaded(tab.linkedBrowser); + } + await BrowserTestUtils.withNewTab( { gBrowser, url: DUMMY_URL }, async function(browser) { @@ -98,16 +107,15 @@ add_task(async function test_proc_info() { } // We only check other properties on the `privilegedabout` subprocess, which - // as of this writing is hosting the page we test, so should be active and - // available. If we ever move `about:processes` to another process type, we'll - // need to update this test. - var hasSocketProcess = false; + // as of this writing is always active and available. + var hasPrivilegedAbout = false; + var numberOfAboutTabs = 0; for (i = 0; i < parentProc.children.length; i++) { let childProc = parentProc.children[i]; if (childProc.type != "privilegedabout") { continue; } - hasSocketProcess = true; + hasPrivilegedAbout = true; Assert.ok( childProc.residentUniqueSize > 0, "Resident-unique-size was set" @@ -117,17 +125,45 @@ add_task(async function test_proc_info() { `Resident-unique-size should be bounded by resident-set-size ${childProc.residentUniqueSize} <= ${childProc.residentSetSize}` ); - // Once we have found the socket process, bailout. + for (var win of childProc.windows) { + if (win.documentURI.spec != "about:home") { + // We're only interested in about:home for this test. + continue; + } + numberOfAboutTabs++; + Assert.ok( + win.outerWindowId > 0, + `ContentParentID should be > 0 ${win.outerWindowId}` + ); + if (win.documentTitle) { + // Unfortunately, we sometimes reach this point before the document is fully loaded, so + // `win.documentTitle` may still be empty. + Assert.equal(win.documentTitle, "New Tab"); + } + } + Assert.ok( + numberOfAboutTabs >= tabsAboutHome.length, + "We have found at least as many about:home tabs as we opened" + ); + + // Once we have verified the privileged about process, bailout. break; } - Assert.ok(hasSocketProcess, "We have found the socket process"); + Assert.ok( + hasPrivilegedAbout, + "We have found the privileged about process" + ); } // see https://bugzilla.mozilla.org/show_bug.cgi?id=1529023 if (!MAC) { Assert.greater(cpuThreads, 0, "Got some cpu time in the threads"); } Assert.greater(cpuUser, 0, "Got some cpu time"); + + for (let tab of tabsAboutHome) { + BrowserTestUtils.removeTab(tab); + } } ); }); diff --git a/widget/windows/ProcInfo.cpp b/widget/windows/ProcInfo.cpp index dbe2404d340b..e55ed25b33d8 100644 --- a/widget/windows/ProcInfo.cpp +++ b/widget/windows/ProcInfo.cpp @@ -85,6 +85,7 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { info.childId = request.childId; info.type = request.processType; info.origin = request.origin; + info.windows = std::move(request.windowInfo); info.filename.Assign(filename); info.cpuKernel = ToNanoSeconds(kernelTime); info.cpuUser = ToNanoSeconds(userTime);