| /* | |
| * Copyright (C) 2008 Apple Inc. All Rights Reserved. | |
| * Copyright (C) 2011 Google Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /** | |
| * @implements {UI.ContextFlavorListener} | |
| * @unrestricted | |
| */ | |
| Sources.ScopeChainSidebarPane = class extends UI.VBox { | |
| constructor() { | |
| super(true); | |
| this.registerRequiredCSS('sources/scopeChainSidebarPane.css'); | |
| this._treeOutline = new ObjectUI.ObjectPropertiesSectionsTreeOutline(); | |
| this._treeOutline.registerRequiredCSS('sources/scopeChainSidebarPane.css'); | |
| this._treeOutline.setShowSelectionOnKeyboardFocus(/* show */ true); | |
| this._expandController = new ObjectUI.ObjectPropertiesSectionsTreeExpandController(this._treeOutline); | |
| this._linkifier = new Components.Linkifier(); | |
| this._infoElement = createElement('div'); | |
| this._infoElement.className = 'gray-info-message'; | |
| this._infoElement.textContent = ls`Not paused`; | |
| this._infoElement.tabIndex = -1; | |
| this._update(); | |
| } | |
| /** | |
| * @override | |
| * @param {?Object} object | |
| */ | |
| flavorChanged(object) { | |
| this._update(); | |
| } | |
| /** | |
| * @override | |
| */ | |
| focus() { | |
| if (this.hasFocus()) { | |
| return; | |
| } | |
| if (UI.context.flavor(SDK.DebuggerPausedDetails)) { | |
| this._treeOutline.forceSelect(); | |
| } | |
| } | |
| _update() { | |
| const callFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); | |
| const details = UI.context.flavor(SDK.DebuggerPausedDetails); | |
| this._linkifier.reset(); | |
| Sources.SourceMapNamesResolver.resolveThisObject(callFrame).then(this._innerUpdate.bind(this, details, callFrame)); | |
| } | |
| /** | |
| * @param {?SDK.DebuggerPausedDetails} details | |
| * @param {?SDK.DebuggerModel.CallFrame} callFrame | |
| * @param {?SDK.RemoteObject} thisObject | |
| */ | |
| _innerUpdate(details, callFrame, thisObject) { | |
| this._treeOutline.removeChildren(); | |
| this.contentElement.removeChildren(); | |
| if (!details || !callFrame) { | |
| this.contentElement.appendChild(this._infoElement); | |
| return; | |
| } | |
| this.contentElement.appendChild(this._treeOutline.element); | |
| let foundLocalScope = false; | |
| const scopeChain = callFrame.scopeChain(); | |
| for (let i = 0; i < scopeChain.length; ++i) { | |
| const scope = scopeChain[i]; | |
| const extraProperties = this._extraPropertiesForScope(scope, details, callFrame, thisObject, i === 0); | |
| if (scope.type() === Protocol.Debugger.ScopeType.Local) { | |
| foundLocalScope = true; | |
| } | |
| const section = this._createScopeSectionTreeElement(scope, extraProperties); | |
| if (scope.type() === Protocol.Debugger.ScopeType.Global) { | |
| section.collapse(); | |
| } else if (!foundLocalScope || scope.type() === Protocol.Debugger.ScopeType.Local) { | |
| section.expand(); | |
| } | |
| this._treeOutline.appendChild(section); | |
| if (i === 0) { | |
| section.select(/* omitFocus */ true); | |
| } | |
| } | |
| this._sidebarPaneUpdatedForTest(); | |
| } | |
| /** | |
| * @param {!SDK.DebuggerModel.Scope} scope | |
| * @param {!Array.<!SDK.RemoteObjectProperty>} extraProperties | |
| * @return {!ObjectUI.ObjectPropertiesSection.RootElement} | |
| */ | |
| _createScopeSectionTreeElement(scope, extraProperties) { | |
| let emptyPlaceholder = null; | |
| if (scope.type() === Protocol.Debugger.ScopeType.Local || Protocol.Debugger.ScopeType.Closure) { | |
| emptyPlaceholder = ls`No variables`; | |
| } | |
| let title = scope.typeName(); | |
| if (scope.type() === Protocol.Debugger.ScopeType.Closure) { | |
| const scopeName = scope.name(); | |
| if (scopeName) { | |
| title = ls`Closure (${UI.beautifyFunctionName(scopeName)})`; | |
| } else { | |
| title = ls`Closure`; | |
| } | |
| } | |
| let subtitle = scope.description(); | |
| if (!title || title === subtitle) { | |
| subtitle = undefined; | |
| } | |
| const titleElement = createElementWithClass('div', 'scope-chain-sidebar-pane-section-header tree-element-title'); | |
| titleElement.createChild('div', 'scope-chain-sidebar-pane-section-subtitle').textContent = subtitle; | |
| titleElement.createChild('div', 'scope-chain-sidebar-pane-section-title').textContent = title; | |
| const section = new ObjectUI.ObjectPropertiesSection.RootElement( | |
| Sources.SourceMapNamesResolver.resolveScopeInObject(scope), this._linkifier, emptyPlaceholder, | |
| /* ignoreHasOwnProperty */ true, extraProperties); | |
| section.title = titleElement; | |
| section.listItemElement.classList.add('scope-chain-sidebar-pane-section'); | |
| this._expandController.watchSection(title + (subtitle ? ':' + subtitle : ''), section); | |
| return section; | |
| } | |
| /** | |
| * @param {!SDK.DebuggerModel.Scope} scope | |
| * @param {?SDK.DebuggerPausedDetails} details | |
| * @param {?SDK.DebuggerModel.CallFrame} callFrame | |
| * @param {?SDK.RemoteObject} thisObject | |
| * @param {boolean} isFirstScope | |
| * @return {!Array.<!SDK.RemoteObjectProperty>} | |
| */ | |
| _extraPropertiesForScope(scope, details, callFrame, thisObject, isFirstScope) { | |
| if (scope.type() !== Protocol.Debugger.ScopeType.Local) { | |
| return []; | |
| } | |
| const extraProperties = []; | |
| if (thisObject) { | |
| extraProperties.push(new SDK.RemoteObjectProperty('this', thisObject)); | |
| } | |
| if (isFirstScope) { | |
| const exception = details.exception(); | |
| if (exception) { | |
| extraProperties.push(new SDK.RemoteObjectProperty( | |
| Common.UIString('Exception'), exception, undefined, undefined, undefined, undefined, undefined, | |
| /* synthetic */ true)); | |
| } | |
| const returnValue = callFrame.returnValue(); | |
| if (returnValue) { | |
| extraProperties.push(new SDK.RemoteObjectProperty( | |
| Common.UIString('Return value'), returnValue, undefined, undefined, undefined, undefined, undefined, | |
| /* synthetic */ true, callFrame.setReturnValue.bind(callFrame))); | |
| } | |
| } | |
| return extraProperties; | |
| } | |
| _sidebarPaneUpdatedForTest() { | |
| } | |
| }; | |
| Sources.ScopeChainSidebarPane._pathSymbol = Symbol('path'); |