Add information about all WebContents in a profile to process-internals.
This CL adds basic information for all WebContents in the current browser profile. For each one, the frame tree is displayed in a tree UI. Bug: 850087 Change-Id: I91e88f88ad97931fb708f97e44e12d676648c507 Reviewed-on: https://chromium-review.googlesource.com/c/1265022 Commit-Queue: Nasko Oskov <nasko@chromium.org> Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org> Reviewed-by: Charlie Reis <creis@chromium.org> Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Cr-Commit-Position: refs/heads/master@{#597397}
This commit is contained in:
@ -8,4 +8,8 @@ mojom("mojo_bindings") {
|
||||
sources = [
|
||||
"process_internals.mojom",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//url/mojom:url_mojom_gurl",
|
||||
]
|
||||
}
|
||||
|
@ -4,6 +4,37 @@
|
||||
|
||||
module mojom;
|
||||
|
||||
import "url/mojom/url.mojom";
|
||||
|
||||
// Basic information describing a SiteInstance.
|
||||
struct SiteInstanceInfo {
|
||||
int32 id;
|
||||
|
||||
// Boolean indicating whether the SiteInstance is locked to a specific URL.
|
||||
// It does not indicate the granularity of the lock URL.
|
||||
bool locked;
|
||||
|
||||
url.mojom.Url? site_url;
|
||||
};
|
||||
|
||||
// Basic information describing a frame and all of its subframes.
|
||||
struct FrameInfo {
|
||||
int32 routing_id;
|
||||
int32 process_id;
|
||||
|
||||
SiteInstanceInfo site_instance;
|
||||
url.mojom.Url? last_committed_url;
|
||||
|
||||
array<FrameInfo> subframes;
|
||||
};
|
||||
|
||||
// Basic information describing a WebContents object and all frames that are
|
||||
// in it.
|
||||
struct WebContentsInfo {
|
||||
string title;
|
||||
FrameInfo root_frame;
|
||||
};
|
||||
|
||||
// Interface used by chrome://process-internals to query data from the
|
||||
// browser process.
|
||||
interface ProcessInternalsHandler {
|
||||
@ -13,4 +44,8 @@ interface ProcessInternalsHandler {
|
||||
|
||||
// Returns the number of isolated origins.
|
||||
GetIsolatedOriginsSize() => (uint32 size);
|
||||
|
||||
// Returns an array of WebContentsInfo structs for all WebContents
|
||||
// associated with the profile in which this call is made.
|
||||
GetAllWebContentsInfo() => (array<WebContentsInfo> infos);
|
||||
};
|
||||
|
@ -9,14 +9,48 @@
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "content/browser/process_internals/process_internals.mojom.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/public/browser/site_isolation_policy.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
::mojom::FrameInfoPtr FrameTreeNodeToFrameInfo(FrameTreeNode* ftn) {
|
||||
RenderFrameHost* frame = ftn->current_frame_host();
|
||||
auto frame_info = ::mojom::FrameInfo::New();
|
||||
|
||||
frame_info->routing_id = frame->GetRoutingID();
|
||||
frame_info->process_id = frame->GetProcess()->GetID();
|
||||
frame_info->last_committed_url =
|
||||
frame->GetLastCommittedURL().is_valid()
|
||||
? base::make_optional(frame->GetLastCommittedURL())
|
||||
: base::nullopt;
|
||||
|
||||
SiteInstanceImpl* site_instance =
|
||||
static_cast<SiteInstanceImpl*>(frame->GetSiteInstance());
|
||||
frame_info->site_instance = ::mojom::SiteInstanceInfo::New();
|
||||
frame_info->site_instance->id = site_instance->GetId();
|
||||
frame_info->site_instance->locked = site_instance->lock_url().is_valid();
|
||||
frame_info->site_instance->site_url =
|
||||
site_instance->HasSite()
|
||||
? base::make_optional(site_instance->GetSiteURL())
|
||||
: base::nullopt;
|
||||
|
||||
for (size_t i = 0; i < ftn->child_count(); ++i) {
|
||||
frame_info->subframes.push_back(FrameTreeNodeToFrameInfo(ftn->child_at(i)));
|
||||
}
|
||||
|
||||
return frame_info;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProcessInternalsHandlerImpl::ProcessInternalsHandlerImpl(
|
||||
BrowserContext* browser_context,
|
||||
mojo::InterfaceRequest<::mojom::ProcessInternalsHandler> request)
|
||||
: binding_(this, std::move(request)) {}
|
||||
: browser_context_(browser_context), binding_(this, std::move(request)) {}
|
||||
|
||||
ProcessInternalsHandlerImpl::~ProcessInternalsHandlerImpl() = default;
|
||||
|
||||
@ -38,4 +72,26 @@ void ProcessInternalsHandlerImpl::GetIsolatedOriginsSize(
|
||||
std::move(callback).Run(size);
|
||||
}
|
||||
|
||||
void ProcessInternalsHandlerImpl::GetAllWebContentsInfo(
|
||||
GetAllWebContentsInfoCallback callback) {
|
||||
std::vector<::mojom::WebContentsInfoPtr> infos;
|
||||
std::vector<WebContentsImpl*> all_contents =
|
||||
WebContentsImpl::GetAllWebContents();
|
||||
|
||||
for (WebContentsImpl* web_contents : all_contents) {
|
||||
// Do not return WebContents that don't belong to the current
|
||||
// BrowserContext to avoid leaking data between contexts.
|
||||
if (web_contents->GetBrowserContext() != browser_context_)
|
||||
continue;
|
||||
|
||||
auto info = ::mojom::WebContentsInfo::New();
|
||||
info->title = base::UTF16ToUTF8(web_contents->GetTitle());
|
||||
info->root_frame =
|
||||
FrameTreeNodeToFrameInfo(web_contents->GetFrameTree()->root());
|
||||
infos.push_back(std::move(info));
|
||||
}
|
||||
|
||||
std::move(callback).Run(std::move(infos));
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -17,14 +17,17 @@ namespace content {
|
||||
class ProcessInternalsHandlerImpl : public ::mojom::ProcessInternalsHandler {
|
||||
public:
|
||||
ProcessInternalsHandlerImpl(
|
||||
BrowserContext* browser_context,
|
||||
mojo::InterfaceRequest<::mojom::ProcessInternalsHandler> request);
|
||||
~ProcessInternalsHandlerImpl() override;
|
||||
|
||||
// mojom::ProcessInternalsHandler overrides:
|
||||
void GetIsolationMode(GetIsolationModeCallback callback) override;
|
||||
void GetIsolatedOriginsSize(GetIsolatedOriginsSizeCallback callback) override;
|
||||
void GetAllWebContentsInfo(GetAllWebContentsInfoCallback callback) override;
|
||||
|
||||
private:
|
||||
BrowserContext* browser_context_;
|
||||
mojo::Binding<::mojom::ProcessInternalsHandler> binding_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessInternalsHandlerImpl);
|
||||
|
@ -34,6 +34,7 @@ ProcessInternalsUI::ProcessInternalsUI(WebUI* web_ui)
|
||||
WebUIDataSource* source =
|
||||
WebUIDataSource::Create(kChromeUIProcessInternalsHost);
|
||||
|
||||
source->AddResourcePath("url.mojom.js", IDR_URL_MOJO_JS);
|
||||
source->AddResourcePath("process_internals.js", IDR_PROCESS_INTERNALS_JS);
|
||||
source->AddResourcePath("process_internals.css", IDR_PROCESS_INTERNALS_CSS);
|
||||
source->AddResourcePath("process_internals.mojom.js",
|
||||
@ -51,13 +52,15 @@ ProcessInternalsUI::ProcessInternalsUI(WebUI* web_ui)
|
||||
ProcessInternalsUI::~ProcessInternalsUI() = default;
|
||||
|
||||
void ProcessInternalsUI::BindProcessInternalsHandler(
|
||||
::mojom::ProcessInternalsHandlerRequest request) {
|
||||
ui_handler_ =
|
||||
std::make_unique<ProcessInternalsHandlerImpl>(std::move(request));
|
||||
::mojom::ProcessInternalsHandlerRequest request,
|
||||
RenderFrameHost* render_frame_host) {
|
||||
ui_handler_ = std::make_unique<ProcessInternalsHandlerImpl>(
|
||||
render_frame_host->GetSiteInstance()->GetBrowserContext(),
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
void ProcessInternalsUI::OnInterfaceRequestFromFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
RenderFrameHost* render_frame_host,
|
||||
const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle* interface_pipe) {
|
||||
// This should not be requested by subframes, so terminate the renderer if
|
||||
@ -68,7 +71,7 @@ void ProcessInternalsUI::OnInterfaceRequestFromFrame(
|
||||
return;
|
||||
}
|
||||
|
||||
registry_.TryBindInterface(interface_name, interface_pipe);
|
||||
registry_.TryBindInterface(interface_name, interface_pipe, render_frame_host);
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -32,11 +32,12 @@ class ProcessInternalsUI : public WebUIController, public WebContentsObserver {
|
||||
registry_.AddInterface(std::move(binder));
|
||||
}
|
||||
void BindProcessInternalsHandler(
|
||||
::mojom::ProcessInternalsHandlerRequest request);
|
||||
::mojom::ProcessInternalsHandlerRequest request,
|
||||
RenderFrameHost* render_frame_host);
|
||||
|
||||
private:
|
||||
std::unique_ptr<::mojom::ProcessInternalsHandler> ui_handler_;
|
||||
service_manager::BinderRegistry registry_;
|
||||
service_manager::BinderRegistryWithArgs<content::RenderFrameHost*> registry_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessInternalsUI);
|
||||
};
|
||||
|
@ -43,12 +43,12 @@ body {
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
-webkit-border-start: 6px solid transparent;
|
||||
border-inline-start: 6px solid transparent;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.tab-header.selected {
|
||||
-webkit-border-start-color: rgb(78, 87, 100);
|
||||
border-inline-start-color: rgb(78, 87, 100);
|
||||
}
|
||||
|
||||
.tab-header > button {
|
||||
@ -80,3 +80,7 @@ body {
|
||||
padding: 20px 0 10px 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
user-select: text;
|
||||
}
|
||||
|
@ -6,9 +6,16 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
|
||||
<link rel="stylesheet" href="process_internals.css">
|
||||
<script src="chrome://resources/js/mojo_bindings.js"></script>
|
||||
<script src="chrome://resources/js/util.js"></script>
|
||||
<script src="chrome://resources/js/cr.js"></script>
|
||||
<script src="chrome://resources/js/mojo_bindings.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="chrome://resources/css/tree.css">
|
||||
<script src="chrome://resources/js/cr/ui.js"></script>
|
||||
<script src="chrome://resources/js/cr/ui/tree.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="process_internals.css">
|
||||
<script src="url.mojom.js"></script>
|
||||
<script src="process_internals.mojom.js"></script>
|
||||
<script src='process_internals.js'></script>
|
||||
<title>Process Model Internals</title>
|
||||
@ -28,8 +35,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="web-contents">
|
||||
<div class="content-header">WebContents</div>
|
||||
<div class="content-header">Frame Trees</div>
|
||||
<div id="wc-list" class="list pages"></div>
|
||||
<div id="tree-view-container">
|
||||
<button id="refresh-button">Refresh</button>
|
||||
<div>Legend:</div>
|
||||
<div>Frame[<i>process_id</i>:<i>routing_id</i>]:
|
||||
SI:<i>site_instance_id</i>, <i>whether process is locked to a
|
||||
site</i>, site: <i>site_url</i> | url:
|
||||
<i>last_committed_url</i>
|
||||
</div>
|
||||
<tree id="tree-view"></tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -57,6 +57,114 @@ function setupTabs() {
|
||||
onHashChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Root of the WebContents tree.
|
||||
* @type {cr.ui.Tree|null}
|
||||
*/
|
||||
let treeViewRoot = null;
|
||||
|
||||
/**
|
||||
* Initialize and return |treeViewRoot|.
|
||||
* @return {cr.ui.Tree} Initialized |treeViewRoot|.
|
||||
*/
|
||||
function getTreeViewRoot() {
|
||||
if (!treeViewRoot) {
|
||||
cr.ui.decorate('#tree-view', cr.ui.Tree);
|
||||
|
||||
treeViewRoot = /** @type {cr.ui.Tree} */ ($('tree-view'));
|
||||
treeViewRoot.detail = {payload: {}, children: {}};
|
||||
}
|
||||
return treeViewRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and return a tree item representing a FrameInfo object and
|
||||
* recursively creates its subframe objects.
|
||||
* @param {mojom.FrameInfo} frame
|
||||
* @return {Array}
|
||||
*/
|
||||
function frameToTreeItem(frame) {
|
||||
// Compose the string which will appear in the entry for this frame.
|
||||
let itemLabel = `Frame[${frame.processId}:${frame.routingId}]:`;
|
||||
itemLabel += ` SI:${frame.siteInstance.id}`;
|
||||
if (frame.siteInstance.locked)
|
||||
itemLabel += ', locked';
|
||||
if (frame.siteInstance.siteUrl)
|
||||
itemLabel += `, site:${frame.siteInstance.siteUrl.url}`;
|
||||
if (frame.lastCommittedUrl)
|
||||
itemLabel += ` | url: ${frame.lastCommittedUrl.url}`;
|
||||
|
||||
let item = new cr.ui.TreeItem(
|
||||
{label: itemLabel, detail: {payload: {}, children: {}}});
|
||||
item.mayHaveChildren_ = true;
|
||||
item.expanded = true;
|
||||
item.icon = '';
|
||||
|
||||
let frameCount = 1;
|
||||
for (const subframe of frame.subframes) {
|
||||
let result = frameToTreeItem(subframe);
|
||||
const subItem = result[0];
|
||||
const count = result[1];
|
||||
|
||||
frameCount += count;
|
||||
item.add(subItem);
|
||||
}
|
||||
|
||||
return [item, frameCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and return a tree item representing the WebContentsInfo object
|
||||
* and contains all frames in it as a subtree.
|
||||
* @param {mojom.WebContentsInfo} webContents
|
||||
* @return {cr.ui.TreeItem}
|
||||
*/
|
||||
function webContentsToTreeItem(webContents) {
|
||||
let itemLabel = 'WebContents: ';
|
||||
if (webContents.title.length > 0)
|
||||
itemLabel += webContents.title + ', ';
|
||||
|
||||
let item = new cr.ui.TreeItem(
|
||||
{label: itemLabel, detail: {payload: {}, children: {}}});
|
||||
item.mayHaveChildren_ = true;
|
||||
item.expanded = true;
|
||||
item.icon = '';
|
||||
|
||||
let result = frameToTreeItem(webContents.rootFrame);
|
||||
const rootItem = result[0];
|
||||
const count = result[1];
|
||||
|
||||
itemLabel += `${count} frame` + (count > 1 ? 's.' : '.');
|
||||
item.label = itemLabel;
|
||||
|
||||
item.add(rootItem);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a callback which is invoked when the data for WebContents
|
||||
* associated with the browser profile is received from the browser process.
|
||||
* @param {mojom.ProcessInternalsHandler_GetAllWebContentsInfo_ResponseParams} input
|
||||
*/
|
||||
function populateWebContentsTab(input) {
|
||||
let tree = getTreeViewRoot();
|
||||
|
||||
// Clear the tree first before populating it with the new content.
|
||||
tree.innerText = '';
|
||||
|
||||
for (const webContents of input.infos) {
|
||||
const item = webContentsToTreeItem(webContents);
|
||||
tree.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function which retrieves the data for all WebContents associated with the
|
||||
* current browser profile. The result is passed to populateWebContentsTab.
|
||||
*/
|
||||
function loadWebContentsInfo() {
|
||||
uiHandler.getAllWebContentsInfo().then(populateWebContentsTab);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Setup Mojo interface to the backend.
|
||||
@ -67,14 +175,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Get the Site Isolation mode and populate it.
|
||||
uiHandler.getIsolationMode().then((response) => {
|
||||
document.getElementById('isolation-mode').innerText = response.mode;
|
||||
$('isolation-mode').innerText = response.mode;
|
||||
});
|
||||
uiHandler.getIsolatedOriginsSize().then((response) => {
|
||||
document.getElementById('isolated-origins').innerText = response.size;
|
||||
$('isolated-origins').innerText = response.size;
|
||||
});
|
||||
|
||||
// Setup the UI
|
||||
// Setup the tabbed UI
|
||||
setupTabs();
|
||||
|
||||
// Start loading the information about WebContents.
|
||||
loadWebContentsInfo();
|
||||
|
||||
$('refresh-button').addEventListener('click', loadWebContentsInfo);
|
||||
});
|
||||
|
||||
})();
|
||||
|
@ -43,6 +43,7 @@
|
||||
<include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
|
||||
<include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
|
||||
<include name="IDR_SERVICE_WORKER_INTERNALS_CSS" file="browser/resources/service_worker/serviceworker_internals.css" flattenhtml="true" compress="gzip" type="BINDATA" />
|
||||
<include name="IDR_URL_MOJO_JS" file="${root_gen_dir}/url/mojom/url.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
|
||||
<include name="IDR_WEBRTC_INTERNALS_HTML" file="browser/resources/media/webrtc_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
|
||||
<include name="IDR_WEBRTC_INTERNALS_JS" file="browser/resources/media/webrtc_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
|
||||
</includes>
|
||||
|
Reference in New Issue
Block a user