mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-06 20:11:38 +08:00
To suppress the races, we mark these invocations with `void` per the instructions of `no-floating-promises`. This explicitly marks these statements as side-effect promises. Engineers can later analyze these void statements and determine whether the promise races are intentional or accidental. R=jacktfranklin@chromium.org Bug: 1239617 Change-Id: I6b7a24cf21029d6ff73b112b3e8255ebab8c60ec Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3366467 Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> Commit-Queue: Tim Van der Lippe <tvanderlippe@chromium.org> Auto-Submit: Tim Van der Lippe <tvanderlippe@chromium.org>
294 lines
12 KiB
TypeScript
294 lines
12 KiB
TypeScript
// 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.
|
|
|
|
import * as Common from '../../core/common/common.js';
|
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
import * as Platform from '../../core/platform/platform.js';
|
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
|
|
import consoleContextSelectorStyles from './consoleContextSelector.css.js';
|
|
|
|
const UIStrings = {
|
|
/**
|
|
*@description Title of toolbar item in console context selector of the console panel
|
|
*/
|
|
javascriptContextNotSelected: 'JavaScript context: Not selected',
|
|
/**
|
|
*@description Text in Console Context Selector of the Console panel
|
|
*/
|
|
extension: 'Extension',
|
|
/**
|
|
*@description Text in Console Context Selector of the Console panel
|
|
*@example {top} PH1
|
|
*/
|
|
javascriptContextS: 'JavaScript context: {PH1}',
|
|
};
|
|
const str_ = i18n.i18n.registerUIStrings('panels/console/ConsoleContextSelector.ts', UIStrings);
|
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
export class ConsoleContextSelector implements SDK.TargetManager.SDKModelObserver<SDK.RuntimeModel.RuntimeModel>,
|
|
UI.SoftDropDown.Delegate<SDK.RuntimeModel.ExecutionContext> {
|
|
private readonly items: UI.ListModel.ListModel<SDK.RuntimeModel.ExecutionContext>;
|
|
private readonly dropDown: UI.SoftDropDown.SoftDropDown<SDK.RuntimeModel.ExecutionContext>;
|
|
private readonly toolbarItemInternal: UI.Toolbar.ToolbarItem;
|
|
|
|
constructor() {
|
|
this.items = new UI.ListModel.ListModel();
|
|
this.dropDown = new UI.SoftDropDown.SoftDropDown(this.items, this);
|
|
this.dropDown.setRowHeight(36);
|
|
this.toolbarItemInternal = new UI.Toolbar.ToolbarItem(this.dropDown.element);
|
|
this.toolbarItemInternal.setEnabled(false);
|
|
this.toolbarItemInternal.setTitle(i18nString(UIStrings.javascriptContextNotSelected));
|
|
this.items.addEventListener(
|
|
UI.ListModel.Events.ItemsReplaced, () => this.toolbarItemInternal.setEnabled(Boolean(this.items.length)));
|
|
|
|
this.toolbarItemInternal.element.classList.add('toolbar-has-dropdown');
|
|
|
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextCreated, this.onExecutionContextCreated,
|
|
this);
|
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextChanged, this.onExecutionContextChanged,
|
|
this);
|
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextDestroyed,
|
|
this.onExecutionContextDestroyed, this);
|
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated, this.frameNavigated,
|
|
this);
|
|
|
|
UI.Context.Context.instance().addFlavorChangeListener(
|
|
SDK.RuntimeModel.ExecutionContext, this.executionContextChangedExternally, this);
|
|
UI.Context.Context.instance().addFlavorChangeListener(
|
|
SDK.DebuggerModel.CallFrame, this.callFrameSelectedInUI, this);
|
|
SDK.TargetManager.TargetManager.instance().observeModels(SDK.RuntimeModel.RuntimeModel, this);
|
|
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.CallFrameSelected, this.callFrameSelectedInModel,
|
|
this);
|
|
}
|
|
|
|
toolbarItem(): UI.Toolbar.ToolbarItem {
|
|
return this.toolbarItemInternal;
|
|
}
|
|
|
|
highlightedItemChanged(
|
|
from: SDK.RuntimeModel.ExecutionContext|null, to: SDK.RuntimeModel.ExecutionContext|null,
|
|
fromElement: Element|null, toElement: Element|null): void {
|
|
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
|
|
if (to && to.frameId) {
|
|
const frame = SDK.FrameManager.FrameManager.instance().getFrame(to.frameId);
|
|
if (frame && !frame.isTopFrame()) {
|
|
void frame.highlight();
|
|
}
|
|
}
|
|
if (fromElement) {
|
|
fromElement.classList.remove('highlighted');
|
|
}
|
|
if (toElement) {
|
|
toElement.classList.add('highlighted');
|
|
}
|
|
}
|
|
|
|
titleFor(executionContext: SDK.RuntimeModel.ExecutionContext): string {
|
|
const target = executionContext.target();
|
|
const maybeLabel = executionContext.label();
|
|
let label: string = maybeLabel ? target.decorateLabel(maybeLabel) : '';
|
|
if (executionContext.frameId) {
|
|
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
const frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
if (frame) {
|
|
label = label || frame.displayName();
|
|
}
|
|
}
|
|
label = label || executionContext.origin;
|
|
|
|
return label;
|
|
}
|
|
|
|
private depthFor(executionContext: SDK.RuntimeModel.ExecutionContext): number {
|
|
let target = executionContext.target();
|
|
let depth = 0;
|
|
if (!executionContext.isDefault) {
|
|
depth++;
|
|
}
|
|
if (executionContext.frameId) {
|
|
let frame = SDK.FrameManager.FrameManager.instance().getFrame(executionContext.frameId);
|
|
while (frame) {
|
|
frame = frame.parentFrame();
|
|
if (frame) {
|
|
depth++;
|
|
target = frame.resourceTreeModel().target();
|
|
}
|
|
}
|
|
}
|
|
let targetDepth = 0;
|
|
let parentTarget = target.parentTarget();
|
|
// Special casing service workers to be top-level.
|
|
while (parentTarget && target.type() !== SDK.Target.Type.ServiceWorker) {
|
|
targetDepth++;
|
|
target = parentTarget;
|
|
parentTarget = target.parentTarget();
|
|
}
|
|
depth += targetDepth;
|
|
return depth;
|
|
}
|
|
|
|
private executionContextCreated(executionContext: SDK.RuntimeModel.ExecutionContext): void {
|
|
this.items.insertWithComparator(executionContext, executionContext.runtimeModel.executionContextComparator());
|
|
|
|
if (executionContext === UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext)) {
|
|
this.dropDown.selectItem(executionContext);
|
|
}
|
|
}
|
|
|
|
private onExecutionContextCreated(event: Common.EventTarget.EventTargetEvent<SDK.RuntimeModel.ExecutionContext>):
|
|
void {
|
|
const executionContext = event.data;
|
|
this.executionContextCreated(executionContext);
|
|
}
|
|
|
|
private onExecutionContextChanged(event: Common.EventTarget.EventTargetEvent<SDK.RuntimeModel.ExecutionContext>):
|
|
void {
|
|
const executionContext = event.data;
|
|
if (this.items.indexOf(executionContext) === -1) {
|
|
return;
|
|
}
|
|
this.executionContextDestroyed(executionContext);
|
|
this.executionContextCreated(executionContext);
|
|
}
|
|
|
|
private executionContextDestroyed(executionContext: SDK.RuntimeModel.ExecutionContext): void {
|
|
const index = this.items.indexOf(executionContext);
|
|
if (index === -1) {
|
|
return;
|
|
}
|
|
this.items.remove(index);
|
|
}
|
|
|
|
private onExecutionContextDestroyed(event: Common.EventTarget.EventTargetEvent<SDK.RuntimeModel.ExecutionContext>):
|
|
void {
|
|
const executionContext = event.data;
|
|
this.executionContextDestroyed(executionContext);
|
|
}
|
|
|
|
private executionContextChangedExternally({
|
|
data: executionContext,
|
|
}: Common.EventTarget.EventTargetEvent<SDK.RuntimeModel.ExecutionContext|null>): void {
|
|
this.dropDown.selectItem(executionContext);
|
|
}
|
|
|
|
private isTopContext(executionContext: SDK.RuntimeModel.ExecutionContext|null): boolean {
|
|
if (!executionContext || !executionContext.isDefault) {
|
|
return false;
|
|
}
|
|
const resourceTreeModel = executionContext.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
const frame =
|
|
executionContext.frameId && resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
if (!frame) {
|
|
return false;
|
|
}
|
|
return frame.isTopFrame();
|
|
}
|
|
|
|
private hasTopContext(): boolean {
|
|
return this.items.some(executionContext => this.isTopContext(executionContext));
|
|
}
|
|
|
|
modelAdded(runtimeModel: SDK.RuntimeModel.RuntimeModel): void {
|
|
runtimeModel.executionContexts().forEach(this.executionContextCreated, this);
|
|
}
|
|
|
|
modelRemoved(runtimeModel: SDK.RuntimeModel.RuntimeModel): void {
|
|
for (let i = this.items.length - 1; i >= 0; i--) {
|
|
if (this.items.at(i).runtimeModel === runtimeModel) {
|
|
this.executionContextDestroyed(this.items.at(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
createElementForItem(item: SDK.RuntimeModel.ExecutionContext): Element {
|
|
const element = document.createElement('div');
|
|
const shadowRoot = UI.Utils.createShadowRootWithCoreStyles(
|
|
element, {cssFile: [consoleContextSelectorStyles], delegatesFocus: undefined});
|
|
const title = shadowRoot.createChild('div', 'title');
|
|
UI.UIUtils.createTextChild(title, Platform.StringUtilities.trimEndWithMaxLength(this.titleFor(item), 100));
|
|
const subTitle = shadowRoot.createChild('div', 'subtitle');
|
|
UI.UIUtils.createTextChild(subTitle, this.subtitleFor(item));
|
|
element.style.paddingLeft = (8 + this.depthFor(item) * 15) + 'px';
|
|
return element;
|
|
}
|
|
|
|
private subtitleFor(executionContext: SDK.RuntimeModel.ExecutionContext): string {
|
|
const target = executionContext.target();
|
|
let frame: SDK.ResourceTreeModel.ResourceTreeFrame|null = null;
|
|
if (executionContext.frameId) {
|
|
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId);
|
|
}
|
|
if (executionContext.origin.startsWith('chrome-extension://')) {
|
|
return i18nString(UIStrings.extension);
|
|
}
|
|
const sameTargetParentFrame = frame && frame.sameTargetParentFrame();
|
|
// TODO(crbug.com/1159332): Understand why condition involves the sameTargetParentFrame.
|
|
if (!frame || !sameTargetParentFrame || sameTargetParentFrame.securityOrigin !== executionContext.origin) {
|
|
const url = Common.ParsedURL.ParsedURL.fromString(executionContext.origin);
|
|
if (url) {
|
|
return url.domain();
|
|
}
|
|
}
|
|
|
|
if (frame && frame.securityOrigin) {
|
|
const domain = new Common.ParsedURL.ParsedURL(frame.securityOrigin).domain();
|
|
if (domain) {
|
|
return domain;
|
|
}
|
|
}
|
|
return 'IFrame';
|
|
}
|
|
|
|
isItemSelectable(item: SDK.RuntimeModel.ExecutionContext): boolean {
|
|
const callFrame = item.debuggerModel.selectedCallFrame();
|
|
const callFrameContext = callFrame && callFrame.script.executionContext();
|
|
return !callFrameContext || item === callFrameContext;
|
|
}
|
|
|
|
itemSelected(item: SDK.RuntimeModel.ExecutionContext|null): void {
|
|
this.toolbarItemInternal.element.classList.toggle('highlight', !this.isTopContext(item) && this.hasTopContext());
|
|
const title = item ? i18nString(UIStrings.javascriptContextS, {PH1: this.titleFor(item)}) :
|
|
i18nString(UIStrings.javascriptContextNotSelected);
|
|
this.toolbarItemInternal.setTitle(title);
|
|
UI.Context.Context.instance().setFlavor(SDK.RuntimeModel.ExecutionContext, item);
|
|
}
|
|
|
|
private callFrameSelectedInUI(): void {
|
|
const callFrame = UI.Context.Context.instance().flavor(SDK.DebuggerModel.CallFrame);
|
|
const callFrameContext = callFrame && callFrame.script.executionContext();
|
|
if (callFrameContext) {
|
|
UI.Context.Context.instance().setFlavor(SDK.RuntimeModel.ExecutionContext, callFrameContext);
|
|
}
|
|
}
|
|
|
|
private callFrameSelectedInModel(event: Common.EventTarget.EventTargetEvent<SDK.DebuggerModel.DebuggerModel>): void {
|
|
const debuggerModel = event.data;
|
|
for (const executionContext of this.items) {
|
|
if (executionContext.debuggerModel === debuggerModel) {
|
|
this.dropDown.refreshItem(executionContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
private frameNavigated(event: Common.EventTarget.EventTargetEvent<SDK.ResourceTreeModel.ResourceTreeFrame>): void {
|
|
const frame = event.data;
|
|
const runtimeModel = frame.resourceTreeModel().target().model(SDK.RuntimeModel.RuntimeModel);
|
|
if (!runtimeModel) {
|
|
return;
|
|
}
|
|
for (const executionContext of runtimeModel.executionContexts()) {
|
|
if (frame.id === executionContext.frameId) {
|
|
this.dropDown.refreshItem(executionContext);
|
|
}
|
|
}
|
|
}
|
|
}
|