mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 21:54:47 +08:00
Running Lighthouse on DevTools lead to these changes: * Label our <select> toolbar items * Don't use tabIndex=1 for our canvas elements * Don't use li items in the network panel * add lang=en to our html tags Change-Id: I9fef3d731dbbcbb56c3f73d81fd5669d65fa9827 Reviewed-on: https://chromium-review.googlesource.com/1093399 Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Commit-Queue: Joel Einbinder <einbinder@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#566195} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 237b755cd007b58b585ad34137353e11e2bc3ccd
354 lines
12 KiB
JavaScript
354 lines
12 KiB
JavaScript
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
/**
|
|
* @implements {SDK.SDKModelObserver<!SDK.RuntimeModel>}
|
|
* @implements {UI.SoftDropDown.Delegate<!SDK.ExecutionContext>}
|
|
*/
|
|
Console.ConsoleContextSelector = class {
|
|
constructor() {
|
|
/** @type {!UI.ListModel<!SDK.ExecutionContext>} */
|
|
this._items = new UI.ListModel();
|
|
/** @type {!UI.SoftDropDown<!SDK.ExecutionContext>} */
|
|
this._dropDown = new UI.SoftDropDown(this._items, this);
|
|
this._dropDown.setRowHeight(36);
|
|
this._toolbarItem = new UI.ToolbarItem(this._dropDown.element);
|
|
this._toolbarItem.setEnabled(false);
|
|
this._toolbarItem.setTitle(ls`JavaScript contexts`);
|
|
this._items.addEventListener(
|
|
UI.ListModel.Events.ItemsReplaced, () => this._toolbarItem.setEnabled(!!this._items.length));
|
|
|
|
/** @type {!Map<!SDK.ExecutionContext, !ProductRegistry.BadgePool>} */
|
|
this._badgePoolForExecutionContext = new Map();
|
|
|
|
this._toolbarItem.element.classList.add('toolbar-has-dropdown');
|
|
|
|
SDK.targetManager.addModelListener(
|
|
SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
|
|
SDK.targetManager.addModelListener(
|
|
SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextChanged, this._onExecutionContextChanged, this);
|
|
SDK.targetManager.addModelListener(
|
|
SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
|
|
SDK.targetManager.addModelListener(
|
|
SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated, this._frameNavigated, this);
|
|
|
|
UI.context.addFlavorChangeListener(SDK.ExecutionContext, this._executionContextChangedExternally, this);
|
|
UI.context.addFlavorChangeListener(SDK.DebuggerModel.CallFrame, this._callFrameSelectedInUI, this);
|
|
SDK.targetManager.observeModels(SDK.RuntimeModel, this);
|
|
SDK.targetManager.addModelListener(
|
|
SDK.DebuggerModel, SDK.DebuggerModel.Events.CallFrameSelected, this._callFrameSelectedInModel, this);
|
|
}
|
|
|
|
/**
|
|
* @return {!UI.ToolbarItem}
|
|
*/
|
|
toolbarItem() {
|
|
return this._toolbarItem;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {?SDK.ExecutionContext} from
|
|
* @param {?SDK.ExecutionContext} to
|
|
* @param {?Element} fromElement
|
|
* @param {?Element} toElement
|
|
*/
|
|
highlightedItemChanged(from, to, fromElement, toElement) {
|
|
SDK.OverlayModel.hideDOMNodeHighlight();
|
|
if (to && to.frameId) {
|
|
const overlayModel = to.target().model(SDK.OverlayModel);
|
|
if (overlayModel)
|
|
overlayModel.highlightFrame(to.frameId);
|
|
}
|
|
if (fromElement)
|
|
fromElement.classList.remove('highlighted');
|
|
if (toElement)
|
|
toElement.classList.add('highlighted');
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
* @return {string}
|
|
*/
|
|
titleFor(executionContext) {
|
|
const target = executionContext.target();
|
|
let label = executionContext.label() ? target.decorateLabel(executionContext.label()) : '';
|
|
if (executionContext.frameId) {
|
|
const resourceTreeModel = target.model(SDK.ResourceTreeModel);
|
|
const frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
if (frame)
|
|
label = label || frame.displayName();
|
|
}
|
|
label = label || executionContext.origin;
|
|
|
|
return label;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
* @return {number}
|
|
*/
|
|
_depthFor(executionContext) {
|
|
let target = executionContext.target();
|
|
let depth = 0;
|
|
if (!executionContext.isDefault)
|
|
depth++;
|
|
if (executionContext.frameId) {
|
|
const resourceTreeModel = target.model(SDK.ResourceTreeModel);
|
|
let frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
while (frame && frame.parentFrame) {
|
|
depth++;
|
|
frame = frame.parentFrame;
|
|
}
|
|
}
|
|
let targetDepth = 0;
|
|
while (target.parentTarget()) {
|
|
if (target.parentTarget().hasJSCapability()) {
|
|
targetDepth++;
|
|
} else {
|
|
// Special casing service workers to be top-level.
|
|
targetDepth = 0;
|
|
break;
|
|
}
|
|
target = target.parentTarget();
|
|
}
|
|
depth += targetDepth;
|
|
return depth;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
* @return {?Element}
|
|
*/
|
|
_badgeFor(executionContext) {
|
|
if (!executionContext.frameId || !executionContext.isDefault)
|
|
return null;
|
|
const resourceTreeModel = executionContext.target().model(SDK.ResourceTreeModel);
|
|
const frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
if (!frame)
|
|
return null;
|
|
const badgePool = new ProductRegistry.BadgePool();
|
|
this._badgePoolForExecutionContext.set(executionContext, badgePool);
|
|
return badgePool.badgeForFrame(frame);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
*/
|
|
_disposeExecutionContextBadge(executionContext) {
|
|
const badgePool = this._badgePoolForExecutionContext.get(executionContext);
|
|
if (!badgePool)
|
|
return;
|
|
badgePool.reset();
|
|
this._badgePoolForExecutionContext.delete(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
*/
|
|
_executionContextCreated(executionContext) {
|
|
// FIXME(413886): We never want to show execution context for the main thread of shadow page in service/shared worker frontend.
|
|
// This check could be removed once we do not send this context to frontend.
|
|
if (!executionContext.target().hasJSCapability())
|
|
return;
|
|
|
|
this._items.insertWithComparator(executionContext, executionContext.runtimeModel.executionContextComparator());
|
|
|
|
if (executionContext === UI.context.flavor(SDK.ExecutionContext))
|
|
this._dropDown.selectItem(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_onExecutionContextCreated(event) {
|
|
const executionContext = /** @type {!SDK.ExecutionContext} */ (event.data);
|
|
this._executionContextCreated(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_onExecutionContextChanged(event) {
|
|
const executionContext = /** @type {!SDK.ExecutionContext} */ (event.data);
|
|
if (this._items.indexOf(executionContext) === -1)
|
|
return;
|
|
this._executionContextDestroyed(executionContext);
|
|
this._executionContextCreated(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
*/
|
|
_executionContextDestroyed(executionContext) {
|
|
const index = this._items.indexOf(executionContext);
|
|
if (index === -1)
|
|
return;
|
|
this._disposeExecutionContextBadge(executionContext);
|
|
this._items.remove(index);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_onExecutionContextDestroyed(event) {
|
|
const executionContext = /** @type {!SDK.ExecutionContext} */ (event.data);
|
|
this._executionContextDestroyed(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_executionContextChangedExternally(event) {
|
|
const executionContext = /** @type {?SDK.ExecutionContext} */ (event.data);
|
|
this._dropDown.selectItem(executionContext);
|
|
}
|
|
|
|
/**
|
|
* @param {?SDK.ExecutionContext} executionContext
|
|
* @return {boolean}
|
|
*/
|
|
_isTopContext(executionContext) {
|
|
if (!executionContext || !executionContext.isDefault)
|
|
return false;
|
|
const resourceTreeModel = executionContext.target().model(SDK.ResourceTreeModel);
|
|
const frame =
|
|
executionContext.frameId && resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
if (!frame)
|
|
return false;
|
|
return frame.isTopFrame();
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
_hasTopContext() {
|
|
return this._items.some(executionContext => this._isTopContext(executionContext));
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.RuntimeModel} runtimeModel
|
|
*/
|
|
modelAdded(runtimeModel) {
|
|
runtimeModel.executionContexts().forEach(this._executionContextCreated, this);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.RuntimeModel} runtimeModel
|
|
*/
|
|
modelRemoved(runtimeModel) {
|
|
for (let i = this._items.length - 1; i >= 0; i--) {
|
|
if (this._items.at(i).runtimeModel === runtimeModel)
|
|
this._executionContextDestroyed(this._items.at(i));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.ExecutionContext} item
|
|
* @return {!Element}
|
|
*/
|
|
createElementForItem(item) {
|
|
const element = createElementWithClass('div');
|
|
const shadowRoot = UI.createShadowRootWithCoreStyles(element, 'console/consoleContextSelector.css');
|
|
const title = shadowRoot.createChild('div', 'title');
|
|
title.createTextChild(this.titleFor(item).trimEnd(100));
|
|
const subTitle = shadowRoot.createChild('div', 'subtitle');
|
|
const badgeElement = this._badgeFor(item);
|
|
if (badgeElement) {
|
|
badgeElement.classList.add('badge');
|
|
subTitle.appendChild(badgeElement);
|
|
}
|
|
subTitle.createTextChild(this._subtitleFor(item));
|
|
element.style.paddingLeft = (8 + this._depthFor(item) * 15) + 'px';
|
|
return element;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.ExecutionContext} executionContext
|
|
* @return {string}
|
|
*/
|
|
_subtitleFor(executionContext) {
|
|
const target = executionContext.target();
|
|
let frame;
|
|
if (executionContext.frameId) {
|
|
const resourceTreeModel = target.model(SDK.ResourceTreeModel);
|
|
frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
}
|
|
if (executionContext.origin.startsWith('chrome-extension://'))
|
|
return Common.UIString('Extension');
|
|
if (!frame || !frame.parentFrame || frame.parentFrame.securityOrigin !== executionContext.origin) {
|
|
const url = executionContext.origin.asParsedURL();
|
|
if (url)
|
|
return url.domain();
|
|
}
|
|
|
|
if (frame) {
|
|
const callFrame = frame.findCreationCallFrame(callFrame => !!callFrame.url);
|
|
if (callFrame)
|
|
return new Common.ParsedURL(callFrame.url).domain();
|
|
return Common.UIString('IFrame');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.ExecutionContext} item
|
|
* @return {boolean}
|
|
*/
|
|
isItemSelectable(item) {
|
|
const callFrame = item.debuggerModel.selectedCallFrame();
|
|
const callFrameContext = callFrame && callFrame.script.executionContext();
|
|
return !callFrameContext || item === callFrameContext;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {?SDK.ExecutionContext} item
|
|
*/
|
|
itemSelected(item) {
|
|
this._toolbarItem.element.classList.toggle('warning', !this._isTopContext(item) && this._hasTopContext());
|
|
UI.context.setFlavor(SDK.ExecutionContext, item);
|
|
}
|
|
|
|
_callFrameSelectedInUI() {
|
|
const callFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame);
|
|
const callFrameContext = callFrame && callFrame.script.executionContext();
|
|
if (callFrameContext)
|
|
UI.context.setFlavor(SDK.ExecutionContext, callFrameContext);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_callFrameSelectedInModel(event) {
|
|
const debuggerModel = /** @type {!SDK.DebuggerModel} */ (event.data);
|
|
for (const executionContext of this._items) {
|
|
if (executionContext.debuggerModel === debuggerModel) {
|
|
this._disposeExecutionContextBadge(executionContext);
|
|
this._dropDown.refreshItem(executionContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_frameNavigated(event) {
|
|
const frame = /** @type {!SDK.ResourceTreeFrame} */ (event.data);
|
|
const runtimeModel = frame.resourceTreeModel().target().model(SDK.RuntimeModel);
|
|
if (!runtimeModel)
|
|
return;
|
|
for (const executionContext of runtimeModel.executionContexts()) {
|
|
if (frame.id === executionContext.frameId) {
|
|
this._disposeExecutionContextBadge(executionContext);
|
|
this._dropDown.refreshItem(executionContext);
|
|
}
|
|
}
|
|
}
|
|
};
|