blob: 43f6b3dd29c8a91f994caa90788a2bce3a3398a5 [file] [log] [blame]
// 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>}
*/
export default class ConsoleContextSelector {
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 context: Not selected`);
this._items.addEventListener(
UI.ListModel.Events.ItemsReplaced, () => this._toolbarItem.setEnabled(!!this._items.length));
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 = frame.parentFrame || frame.crossTargetParentFrame();
if (frame) {
depth++;
target = frame.resourceTreeModel().target();
}
}
}
let targetDepth = 0;
// Special casing service workers to be top-level.
while (target.parentTarget() && target.type() !== SDK.Target.Type.ServiceWorker) {
targetDepth++;
target = target.parentTarget();
}
depth += targetDepth;
return depth;
}
/**
* @param {!SDK.ExecutionContext} executionContext
*/
_executionContextCreated(executionContext) {
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._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).trimEndWithMaxLength(100));
const subTitle = shadowRoot.createChild('div', 'subtitle');
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 = Common.ParsedURL.fromString(executionContext.origin);
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());
const title = item ? ls`JavaScript context: ${this.titleFor(item)}` : ls`JavaScript context: Not selected`;
this._toolbarItem.setTitle(title);
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._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._dropDown.refreshItem(executionContext);
}
}
}
}
/* Legacy exported object */
self.Console = self.Console || {};
/* Legacy exported object */
Console = Console || {};
/**
* @constructor
*/
Console.ConsoleContextSelector = ConsoleContextSelector;