| /* |
| * 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.ContextMenu.Provider} |
| * @implements {SDK.TargetManager.Observer} |
| * @implements {UI.ViewLocationResolver} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel = class extends UI.Panel { |
| constructor() { |
| super('sources'); |
| Sources.SourcesPanel._instance = this; |
| this.registerRequiredCSS('sources/sourcesPanel.css'); |
| new UI.DropTarget( |
| this.element, [UI.DropTarget.Type.Folder], Common.UIString('Drop workspace folder here'), |
| this._handleDrop.bind(this)); |
| |
| this._workspace = Workspace.workspace; |
| |
| this._togglePauseAction = |
| /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.toggle-pause')); |
| this._stepOverAction = |
| /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.step-over')); |
| this._stepIntoAction = |
| /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.step-into')); |
| this._stepOutAction = /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.step-out')); |
| this._stepAction = |
| /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.step')); |
| this._toggleBreakpointsActiveAction = |
| /** @type {!UI.Action }*/ (UI.actionRegistry.action('debugger.toggle-breakpoints-active')); |
| |
| this._debugToolbar = this._createDebugToolbar(); |
| this._debugToolbarDrawer = this._createDebugToolbarDrawer(); |
| this._debuggerPausedMessage = new Sources.DebuggerPausedMessage(); |
| |
| const initialDebugSidebarWidth = 225; |
| this._splitWidget = new UI.SplitWidget(true, true, 'sourcesPanelSplitViewState', initialDebugSidebarWidth); |
| this._splitWidget.enableShowModeSaving(); |
| this._splitWidget.show(this.element); |
| |
| // Create scripts navigator |
| const initialNavigatorWidth = 225; |
| this.editorView = new UI.SplitWidget(true, false, 'sourcesPanelNavigatorSplitViewState', initialNavigatorWidth); |
| this.editorView.enableShowModeSaving(); |
| this._splitWidget.setMainWidget(this.editorView); |
| |
| // Create navigator tabbed pane with toolbar. |
| this._navigatorTabbedLocation = |
| UI.viewManager.createTabbedLocation(this._revealNavigatorSidebar.bind(this), 'navigator-view', true); |
| const tabbedPane = this._navigatorTabbedLocation.tabbedPane(); |
| tabbedPane.setMinimumSize(100, 25); |
| tabbedPane.element.classList.add('navigator-tabbed-pane'); |
| const navigatorMenuButton = new UI.ToolbarMenuButton(this._populateNavigatorMenu.bind(this), true); |
| navigatorMenuButton.setTitle(Common.UIString('More options')); |
| tabbedPane.rightToolbar().appendToolbarItem(navigatorMenuButton); |
| |
| if (UI.viewManager.hasViewsForLocation('run-view-sidebar')) { |
| const navigatorSplitWidget = new UI.SplitWidget(false, true, 'sourcePanelNavigatorSidebarSplitViewState'); |
| navigatorSplitWidget.setMainWidget(tabbedPane); |
| const runViewTabbedPane = |
| UI.viewManager.createTabbedLocation(this._revealNavigatorSidebar.bind(this), 'run-view-sidebar').tabbedPane(); |
| navigatorSplitWidget.setSidebarWidget(runViewTabbedPane); |
| navigatorSplitWidget.installResizer(runViewTabbedPane.headerElement()); |
| this.editorView.setSidebarWidget(navigatorSplitWidget); |
| } else { |
| this.editorView.setSidebarWidget(tabbedPane); |
| } |
| |
| this._sourcesView = new Sources.SourcesView(); |
| this._sourcesView.addEventListener(Sources.SourcesView.Events.EditorSelected, this._editorSelected.bind(this)); |
| |
| this._toggleNavigatorSidebarButton = this.editorView.createShowHideSidebarButton(ls`navigator`); |
| this._toggleDebuggerSidebarButton = this._splitWidget.createShowHideSidebarButton(ls`debugger`); |
| this.editorView.setMainWidget(this._sourcesView); |
| |
| this._threadsSidebarPane = null; |
| this._watchSidebarPane = /** @type {!UI.View} */ (UI.viewManager.view('sources.watch')); |
| this._callstackPane = self.runtime.sharedInstance(Sources.CallStackSidebarPane); |
| |
| Common.moduleSetting('sidebarPosition').addChangeListener(this._updateSidebarPosition.bind(this)); |
| this._updateSidebarPosition(); |
| |
| this._updateDebuggerButtonsAndStatus(); |
| this._pauseOnExceptionEnabledChanged(); |
| Common.moduleSetting('pauseOnExceptionEnabled').addChangeListener(this._pauseOnExceptionEnabledChanged, this); |
| |
| this._liveLocationPool = new Bindings.LiveLocationPool(); |
| |
| this._setTarget(UI.context.flavor(SDK.Target)); |
| Common.moduleSetting('breakpointsActive').addChangeListener(this._breakpointsActiveStateChanged, this); |
| UI.context.addFlavorChangeListener(SDK.Target, this._onCurrentTargetChanged, this); |
| UI.context.addFlavorChangeListener(SDK.DebuggerModel.CallFrame, this._callFrameChanged, this); |
| SDK.targetManager.addModelListener( |
| SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this); |
| SDK.targetManager.addModelListener( |
| SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this); |
| SDK.targetManager.addModelListener( |
| SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, |
| event => this._debuggerResumed(/** @type {!SDK.DebuggerModel} */ (event.data))); |
| SDK.targetManager.addModelListener( |
| SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, |
| event => this._debuggerResumed(/** @type {!SDK.DebuggerModel} */ (event.data))); |
| Extensions.extensionServer.addEventListener( |
| Extensions.ExtensionServer.Events.SidebarPaneAdded, this._extensionSidebarPaneAdded, this); |
| SDK.targetManager.observeTargets(this); |
| } |
| |
| /** |
| * @return {!Sources.SourcesPanel} |
| */ |
| static instance() { |
| if (Sources.SourcesPanel._instance) { |
| return Sources.SourcesPanel._instance; |
| } |
| return /** @type {!Sources.SourcesPanel} */ (self.runtime.sharedInstance(Sources.SourcesPanel)); |
| } |
| |
| /** |
| * @param {!Sources.SourcesPanel} panel |
| */ |
| static updateResizerAndSidebarButtons(panel) { |
| panel._sourcesView.leftToolbar().removeToolbarItems(); |
| panel._sourcesView.rightToolbar().removeToolbarItems(); |
| panel._sourcesView.bottomToolbar().removeToolbarItems(); |
| const isInWrapper = Sources.SourcesPanel.WrapperView.isShowing() && !UI.inspectorView.isDrawerMinimized(); |
| if (panel._splitWidget.isVertical() || isInWrapper) { |
| panel._splitWidget.uninstallResizer(panel._sourcesView.toolbarContainerElement()); |
| } else { |
| panel._splitWidget.installResizer(panel._sourcesView.toolbarContainerElement()); |
| } |
| if (!isInWrapper) { |
| panel._sourcesView.leftToolbar().appendToolbarItem(panel._toggleNavigatorSidebarButton); |
| if (panel._splitWidget.isVertical()) { |
| panel._sourcesView.rightToolbar().appendToolbarItem(panel._toggleDebuggerSidebarButton); |
| } else { |
| panel._sourcesView.bottomToolbar().appendToolbarItem(panel._toggleDebuggerSidebarButton); |
| } |
| } |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.Target} target |
| */ |
| targetAdded(target) { |
| this._showThreadsIfNeeded(); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.Target} target |
| */ |
| targetRemoved(target) { |
| } |
| |
| _showThreadsIfNeeded() { |
| if (Sources.ThreadsSidebarPane.shouldBeShown() && !this._threadsSidebarPane) { |
| this._threadsSidebarPane = /** @type {!UI.View} */ (UI.viewManager.view('sources.threads')); |
| if (this._sidebarPaneStack && this._threadsSidebarPane) { |
| this._sidebarPaneStack.showView( |
| this._threadsSidebarPane, this._splitWidget.isVertical() ? this._watchSidebarPane : this._callstackPane); |
| } |
| } |
| } |
| |
| /** |
| * @param {?SDK.Target} target |
| */ |
| _setTarget(target) { |
| if (!target) { |
| return; |
| } |
| const debuggerModel = target.model(SDK.DebuggerModel); |
| if (!debuggerModel) { |
| return; |
| } |
| |
| if (debuggerModel.isPaused()) { |
| this._showDebuggerPausedDetails( |
| /** @type {!SDK.DebuggerPausedDetails} */ (debuggerModel.debuggerPausedDetails())); |
| } else { |
| this._paused = false; |
| this._clearInterface(); |
| this._toggleDebuggerSidebarButton.setEnabled(true); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onCurrentTargetChanged(event) { |
| const target = /** @type {?SDK.Target} */ (event.data); |
| this._setTarget(target); |
| } |
| /** |
| * @return {boolean} |
| */ |
| paused() { |
| return this._paused; |
| } |
| |
| /** |
| * @override |
| */ |
| wasShown() { |
| UI.context.setFlavor(Sources.SourcesPanel, this); |
| super.wasShown(); |
| const wrapper = Sources.SourcesPanel.WrapperView._instance; |
| if (wrapper && wrapper.isShowing()) { |
| UI.inspectorView.setDrawerMinimized(true); |
| Sources.SourcesPanel.updateResizerAndSidebarButtons(this); |
| } |
| this.editorView.setMainWidget(this._sourcesView); |
| } |
| |
| /** |
| * @override |
| */ |
| willHide() { |
| super.willHide(); |
| UI.context.setFlavor(Sources.SourcesPanel, null); |
| if (Sources.SourcesPanel.WrapperView.isShowing()) { |
| Sources.SourcesPanel.WrapperView._instance._showViewInWrapper(); |
| UI.inspectorView.setDrawerMinimized(false); |
| Sources.SourcesPanel.updateResizerAndSidebarButtons(this); |
| } |
| } |
| |
| /** |
| * @override |
| * @param {string} locationName |
| * @return {?UI.ViewLocation} |
| */ |
| resolveLocation(locationName) { |
| if (locationName === 'sources.sidebar-top' || locationName === 'sources.sidebar-bottom' || |
| locationName === 'sources.sidebar-tabs') { |
| return this._sidebarPaneStack; |
| } else { |
| return this._navigatorTabbedLocation; |
| } |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _ensureSourcesViewVisible() { |
| if (Sources.SourcesPanel.WrapperView.isShowing()) { |
| return true; |
| } |
| if (!UI.inspectorView.canSelectPanel('sources')) { |
| return false; |
| } |
| UI.viewManager.showView('sources'); |
| return true; |
| } |
| |
| /** |
| * @override |
| */ |
| onResize() { |
| if (Common.moduleSetting('sidebarPosition').get() === 'auto') { |
| this.element.window().requestAnimationFrame(this._updateSidebarPosition.bind(this)); |
| } // Do not force layout. |
| } |
| |
| /** |
| * @override |
| * @return {!UI.SearchableView} |
| */ |
| searchableView() { |
| return this._sourcesView.searchableView(); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _debuggerPaused(event) { |
| const debuggerModel = /** @type {!SDK.DebuggerModel} */ (event.data); |
| const details = debuggerModel.debuggerPausedDetails(); |
| if (!this._paused) { |
| this._setAsCurrentPanel(); |
| } |
| |
| if (UI.context.flavor(SDK.Target) === debuggerModel.target()) { |
| this._showDebuggerPausedDetails(/** @type {!SDK.DebuggerPausedDetails} */ (details)); |
| } else if (!this._paused) { |
| UI.context.setFlavor(SDK.Target, debuggerModel.target()); |
| } |
| } |
| |
| /** |
| * @param {!SDK.DebuggerPausedDetails} details |
| */ |
| _showDebuggerPausedDetails(details) { |
| this._paused = true; |
| this._updateDebuggerButtonsAndStatus(); |
| UI.context.setFlavor(SDK.DebuggerPausedDetails, details); |
| this._toggleDebuggerSidebarButton.setEnabled(false); |
| this._revealDebuggerSidebar(); |
| window.focus(); |
| Host.InspectorFrontendHost.bringToFront(); |
| } |
| |
| /** |
| * @param {!SDK.DebuggerModel} debuggerModel |
| */ |
| _debuggerResumed(debuggerModel) { |
| const target = debuggerModel.target(); |
| if (UI.context.flavor(SDK.Target) !== target) { |
| return; |
| } |
| this._paused = false; |
| this._clearInterface(); |
| this._toggleDebuggerSidebarButton.setEnabled(true); |
| this._switchToPausedTargetTimeout = setTimeout(this._switchToPausedTarget.bind(this, debuggerModel), 500); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _debuggerWasEnabled(event) { |
| const debuggerModel = /** @type {!SDK.DebuggerModel} */ (event.data); |
| if (UI.context.flavor(SDK.Target) !== debuggerModel.target()) { |
| return; |
| } |
| |
| this._updateDebuggerButtonsAndStatus(); |
| } |
| |
| /** |
| * @return {?UI.Widget} |
| */ |
| get visibleView() { |
| return this._sourcesView.visibleView(); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @param {number=} lineNumber 0-based |
| * @param {number=} columnNumber |
| * @param {boolean=} omitFocus |
| */ |
| showUISourceCode(uiSourceCode, lineNumber, columnNumber, omitFocus) { |
| if (omitFocus) { |
| const wrapperShowing = |
| Sources.SourcesPanel.WrapperView._instance && Sources.SourcesPanel.WrapperView._instance.isShowing(); |
| if (!this.isShowing() && !wrapperShowing) { |
| return; |
| } |
| } else { |
| this._showEditor(); |
| } |
| this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber, omitFocus); |
| } |
| |
| _showEditor() { |
| if (Sources.SourcesPanel.WrapperView._instance && Sources.SourcesPanel.WrapperView._instance.isShowing()) { |
| return; |
| } |
| this._setAsCurrentPanel(); |
| } |
| |
| /** |
| * @param {!Workspace.UILocation} uiLocation |
| * @param {boolean=} omitFocus |
| */ |
| showUILocation(uiLocation, omitFocus) { |
| this.showUISourceCode(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, omitFocus); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @param {boolean=} skipReveal |
| */ |
| _revealInNavigator(uiSourceCode, skipReveal) { |
| const extensions = self.runtime.extensions(Sources.NavigatorView); |
| Promise.all(extensions.map(extension => extension.instance())).then(filterNavigators.bind(this)); |
| |
| /** |
| * @this {Sources.SourcesPanel} |
| * @param {!Array.<!Object>} objects |
| */ |
| function filterNavigators(objects) { |
| for (let i = 0; i < objects.length; ++i) { |
| const navigatorView = /** @type {!Sources.NavigatorView} */ (objects[i]); |
| const viewId = extensions[i].descriptor()['viewId']; |
| if (navigatorView.acceptProject(uiSourceCode.project())) { |
| navigatorView.revealUISourceCode(uiSourceCode, true); |
| if (skipReveal) { |
| this._navigatorTabbedLocation.tabbedPane().selectTab(viewId); |
| } else { |
| UI.viewManager.showView(viewId); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| */ |
| _populateNavigatorMenu(contextMenu) { |
| const groupByFolderSetting = Common.moduleSetting('navigatorGroupByFolder'); |
| contextMenu.appendItemsAtLocation('navigatorMenu'); |
| contextMenu.viewSection().appendCheckboxItem( |
| Common.UIString('Group by folder'), () => groupByFolderSetting.set(!groupByFolderSetting.get()), |
| groupByFolderSetting.get()); |
| } |
| |
| /** |
| * @param {boolean} ignoreExecutionLineEvents |
| */ |
| setIgnoreExecutionLineEvents(ignoreExecutionLineEvents) { |
| this._ignoreExecutionLineEvents = ignoreExecutionLineEvents; |
| } |
| |
| updateLastModificationTime() { |
| this._lastModificationTime = window.performance.now(); |
| } |
| |
| /** |
| * @param {!Bindings.LiveLocation} liveLocation |
| */ |
| _executionLineChanged(liveLocation) { |
| const uiLocation = liveLocation.uiLocation(); |
| if (!uiLocation) { |
| return; |
| } |
| if (window.performance.now() - this._lastModificationTime < Sources.SourcesPanel._lastModificationTimeout) { |
| return; |
| } |
| this._sourcesView.showSourceLocation( |
| uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, undefined, true); |
| } |
| |
| _lastModificationTimeoutPassedForTest() { |
| Sources.SourcesPanel._lastModificationTimeout = Number.MIN_VALUE; |
| } |
| |
| _updateLastModificationTimeForTest() { |
| Sources.SourcesPanel._lastModificationTimeout = Number.MAX_VALUE; |
| } |
| |
| _callFrameChanged() { |
| const callFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); |
| if (!callFrame) { |
| return; |
| } |
| if (this._executionLineLocation) { |
| this._executionLineLocation.dispose(); |
| } |
| this._executionLineLocation = Bindings.debuggerWorkspaceBinding.createCallFrameLiveLocation( |
| callFrame.location(), this._executionLineChanged.bind(this), this._liveLocationPool); |
| } |
| |
| _pauseOnExceptionEnabledChanged() { |
| const enabled = Common.moduleSetting('pauseOnExceptionEnabled').get(); |
| this._pauseOnExceptionButton.setToggled(enabled); |
| this._pauseOnExceptionButton.setTitle(enabled ? ls`Don't pause on exceptions` : ls`Pause on exceptions`); |
| this._debugToolbarDrawer.classList.toggle('expanded', enabled); |
| } |
| |
| async _updateDebuggerButtonsAndStatus() { |
| const currentTarget = UI.context.flavor(SDK.Target); |
| const currentDebuggerModel = currentTarget ? currentTarget.model(SDK.DebuggerModel) : null; |
| if (!currentDebuggerModel) { |
| this._togglePauseAction.setEnabled(false); |
| this._stepOverAction.setEnabled(false); |
| this._stepIntoAction.setEnabled(false); |
| this._stepOutAction.setEnabled(false); |
| this._stepAction.setEnabled(false); |
| } else if (this._paused) { |
| this._togglePauseAction.setToggled(true); |
| this._togglePauseAction.setEnabled(true); |
| this._stepOverAction.setEnabled(true); |
| this._stepIntoAction.setEnabled(true); |
| this._stepOutAction.setEnabled(true); |
| this._stepAction.setEnabled(true); |
| } else { |
| this._togglePauseAction.setToggled(false); |
| this._togglePauseAction.setEnabled(!currentDebuggerModel.isPausing()); |
| this._stepOverAction.setEnabled(false); |
| this._stepIntoAction.setEnabled(false); |
| this._stepOutAction.setEnabled(false); |
| this._stepAction.setEnabled(false); |
| } |
| |
| const details = currentDebuggerModel ? currentDebuggerModel.debuggerPausedDetails() : null; |
| await this._debuggerPausedMessage.render(details, Bindings.debuggerWorkspaceBinding, Bindings.breakpointManager); |
| if (details) { |
| this._updateDebuggerButtonsAndStatusForTest(); |
| } |
| } |
| |
| _updateDebuggerButtonsAndStatusForTest() { |
| } |
| |
| _clearInterface() { |
| this._updateDebuggerButtonsAndStatus(); |
| UI.context.setFlavor(SDK.DebuggerPausedDetails, null); |
| |
| if (this._switchToPausedTargetTimeout) { |
| clearTimeout(this._switchToPausedTargetTimeout); |
| } |
| this._liveLocationPool.disposeAll(); |
| } |
| |
| /** |
| * @param {!SDK.DebuggerModel} debuggerModel |
| */ |
| _switchToPausedTarget(debuggerModel) { |
| delete this._switchToPausedTargetTimeout; |
| if (this._paused) { |
| return; |
| } |
| if (debuggerModel.isPaused()) { |
| return; |
| } |
| const debuggerModels = SDK.targetManager.models(SDK.DebuggerModel); |
| for (let i = 0; i < debuggerModels.length; ++i) { |
| if (debuggerModels[i].isPaused()) { |
| UI.context.setFlavor(SDK.Target, debuggerModels[i].target()); |
| break; |
| } |
| } |
| } |
| |
| _togglePauseOnExceptions() { |
| Common.moduleSetting('pauseOnExceptionEnabled').set(!this._pauseOnExceptionButton.toggled()); |
| } |
| |
| _runSnippet() { |
| const uiSourceCode = this._sourcesView.currentUISourceCode(); |
| if (!uiSourceCode) { |
| return; |
| } |
| Snippets.evaluateScriptSnippet(uiSourceCode); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _editorSelected(event) { |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data); |
| if (this.editorView.mainWidget() && Common.moduleSetting('autoRevealInNavigator').get()) { |
| this._revealInNavigator(uiSourceCode, true); |
| } |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _togglePause() { |
| const target = UI.context.flavor(SDK.Target); |
| if (!target) { |
| return true; |
| } |
| const debuggerModel = target.model(SDK.DebuggerModel); |
| if (!debuggerModel) { |
| return true; |
| } |
| |
| if (this._paused) { |
| this._paused = false; |
| debuggerModel.resume(); |
| } else { |
| // Make sure pauses didn't stick skipped. |
| debuggerModel.pause(); |
| } |
| |
| this._clearInterface(); |
| return true; |
| } |
| |
| /** |
| * @return {?SDK.DebuggerModel} |
| */ |
| _prepareToResume() { |
| if (!this._paused) { |
| return null; |
| } |
| |
| this._paused = false; |
| |
| this._clearInterface(); |
| const target = UI.context.flavor(SDK.Target); |
| return target ? target.model(SDK.DebuggerModel) : null; |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _longResume(event) { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return; |
| } |
| |
| debuggerModel.skipAllPausesUntilReloadOrTimeout(500); |
| debuggerModel.resume(); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _terminateExecution(event) { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return; |
| } |
| debuggerModel.runtimeModel().terminateExecution(); |
| debuggerModel.resume(); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _stepOver() { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return true; |
| } |
| |
| debuggerModel.stepOver(); |
| return true; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _stepInto() { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return true; |
| } |
| |
| debuggerModel.stepInto(); |
| return true; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _stepIntoAsync() { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return true; |
| } |
| debuggerModel.scheduleStepIntoAsync(); |
| return true; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _stepOut() { |
| const debuggerModel = this._prepareToResume(); |
| if (!debuggerModel) { |
| return true; |
| } |
| |
| debuggerModel.stepOut(); |
| return true; |
| } |
| |
| /** |
| * @param {!Workspace.UILocation} uiLocation |
| */ |
| _continueToLocation(uiLocation) { |
| const executionContext = UI.context.flavor(SDK.ExecutionContext); |
| if (!executionContext) { |
| return; |
| } |
| // Always use 0 column. |
| const rawLocations = |
| Bindings.debuggerWorkspaceBinding.uiLocationToRawLocations(uiLocation.uiSourceCode, uiLocation.lineNumber, 0); |
| const rawLocation = rawLocations.find(location => location.debuggerModel === executionContext.debuggerModel); |
| if (!rawLocation) { |
| return; |
| } |
| if (!this._prepareToResume()) { |
| return; |
| } |
| |
| rawLocation.continueToLocation(); |
| } |
| |
| _toggleBreakpointsActive() { |
| Common.moduleSetting('breakpointsActive').set(!Common.moduleSetting('breakpointsActive').get()); |
| } |
| |
| _breakpointsActiveStateChanged() { |
| const active = Common.moduleSetting('breakpointsActive').get(); |
| this._toggleBreakpointsActiveAction.setToggled(!active); |
| this._sourcesView.toggleBreakpointsActiveState(active); |
| } |
| |
| /** |
| * @return {!UI.Toolbar} |
| */ |
| _createDebugToolbar() { |
| const debugToolbar = new UI.Toolbar('scripts-debug-toolbar'); |
| |
| const longResumeButton = |
| new UI.ToolbarButton(Common.UIString('Resume with all pauses blocked for 500 ms'), 'largeicon-play'); |
| longResumeButton.addEventListener(UI.ToolbarButton.Events.Click, this._longResume, this); |
| const terminateExecutionButton = |
| new UI.ToolbarButton(ls`Terminate current JavaScript call`, 'largeicon-terminate-execution'); |
| terminateExecutionButton.addEventListener(UI.ToolbarButton.Events.Click, this._terminateExecution, this); |
| debugToolbar.appendToolbarItem(UI.Toolbar.createLongPressActionButton( |
| this._togglePauseAction, [terminateExecutionButton, longResumeButton], [])); |
| |
| debugToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._stepOverAction)); |
| debugToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._stepIntoAction)); |
| debugToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._stepOutAction)); |
| debugToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._stepAction)); |
| |
| debugToolbar.appendSeparator(); |
| debugToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._toggleBreakpointsActiveAction)); |
| |
| this._pauseOnExceptionButton = new UI.ToolbarToggle('', 'largeicon-pause-on-exceptions'); |
| this._pauseOnExceptionButton.addEventListener(UI.ToolbarButton.Events.Click, this._togglePauseOnExceptions, this); |
| debugToolbar.appendToolbarItem(this._pauseOnExceptionButton); |
| |
| return debugToolbar; |
| } |
| |
| _createDebugToolbarDrawer() { |
| const debugToolbarDrawer = createElementWithClass('div', 'scripts-debug-toolbar-drawer'); |
| |
| const label = Common.UIString('Pause on caught exceptions'); |
| const setting = Common.moduleSetting('pauseOnCaughtException'); |
| debugToolbarDrawer.appendChild(UI.SettingsUI.createSettingCheckbox(label, setting, true)); |
| |
| return debugToolbarDrawer; |
| } |
| |
| /** |
| * @override |
| * @param {!Event} event |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} target |
| */ |
| appendApplicableItems(event, contextMenu, target) { |
| this._appendUISourceCodeItems(event, contextMenu, target); |
| this._appendUISourceCodeFrameItems(event, contextMenu, target); |
| this.appendUILocationItems(contextMenu, target); |
| this._appendRemoteObjectItems(contextMenu, target); |
| this._appendNetworkRequestItems(contextMenu, target); |
| } |
| |
| /** |
| * @param {!Event} event |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} target |
| */ |
| _appendUISourceCodeItems(event, contextMenu, target) { |
| if (!(target instanceof Workspace.UISourceCode)) { |
| return; |
| } |
| |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (target); |
| if (!uiSourceCode.project().isServiceProject() && |
| !event.target.isSelfOrDescendant(this._navigatorTabbedLocation.widget().element)) { |
| contextMenu.revealSection().appendItem( |
| Common.UIString('Reveal in sidebar'), this._handleContextMenuReveal.bind(this, uiSourceCode)); |
| } |
| } |
| |
| /** |
| * @param {!Event} event |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} target |
| */ |
| _appendUISourceCodeFrameItems(event, contextMenu, target) { |
| if (!(target instanceof Sources.UISourceCodeFrame)) { |
| return; |
| } |
| if (target.uiSourceCode().contentType().isFromSourceMap() || target.textEditor.selection().isEmpty()) { |
| return; |
| } |
| contextMenu.debugSection().appendAction('debugger.evaluate-selection'); |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} object |
| */ |
| appendUILocationItems(contextMenu, object) { |
| if (!(object instanceof Workspace.UILocation)) { |
| return; |
| } |
| const uiLocation = /** @type {!Workspace.UILocation} */ (object); |
| const uiSourceCode = uiLocation.uiSourceCode; |
| |
| const contentType = uiSourceCode.contentType(); |
| if (contentType.hasScripts()) { |
| const target = UI.context.flavor(SDK.Target); |
| const debuggerModel = target ? target.model(SDK.DebuggerModel) : null; |
| if (debuggerModel && debuggerModel.isPaused()) { |
| contextMenu.debugSection().appendItem( |
| Common.UIString('Continue to here'), this._continueToLocation.bind(this, uiLocation)); |
| } |
| |
| this._callstackPane.appendBlackboxURLContextMenuItems(contextMenu, uiSourceCode); |
| } |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| _handleContextMenuReveal(uiSourceCode) { |
| this.editorView.showBoth(); |
| this._revealInNavigator(uiSourceCode); |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} target |
| */ |
| _appendRemoteObjectItems(contextMenu, target) { |
| if (!(target instanceof SDK.RemoteObject)) { |
| return; |
| } |
| const remoteObject = /** @type {!SDK.RemoteObject} */ (target); |
| const executionContext = UI.context.flavor(SDK.ExecutionContext); |
| contextMenu.debugSection().appendItem( |
| ls`Store as global variable`, () => SDK.consoleModel.saveToTempVariable(executionContext, remoteObject)); |
| if (remoteObject.type === 'function') { |
| contextMenu.debugSection().appendItem( |
| ls`Show function definition`, this._showFunctionDefinition.bind(this, remoteObject)); |
| } |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {!Object} target |
| */ |
| _appendNetworkRequestItems(contextMenu, target) { |
| if (!(target instanceof SDK.NetworkRequest)) { |
| return; |
| } |
| const request = /** @type {!SDK.NetworkRequest} */ (target); |
| const uiSourceCode = this._workspace.uiSourceCodeForURL(request.url()); |
| if (!uiSourceCode) { |
| return; |
| } |
| const openText = Common.UIString('Open in Sources panel'); |
| contextMenu.revealSection().appendItem(openText, this.showUILocation.bind(this, uiSourceCode.uiLocation(0, 0))); |
| } |
| |
| /** |
| * @param {!SDK.RemoteObject} remoteObject |
| */ |
| _showFunctionDefinition(remoteObject) { |
| remoteObject.debuggerModel().functionDetailsPromise(remoteObject).then(this._didGetFunctionDetails.bind(this)); |
| } |
| |
| /** |
| * @param {?{location: ?SDK.DebuggerModel.Location}} response |
| */ |
| _didGetFunctionDetails(response) { |
| if (!response || !response.location) { |
| return; |
| } |
| |
| const location = response.location; |
| if (!location) { |
| return; |
| } |
| |
| const uiLocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(location); |
| if (uiLocation) { |
| this.showUILocation(uiLocation); |
| } |
| } |
| |
| _revealNavigatorSidebar() { |
| this._setAsCurrentPanel(); |
| this.editorView.showBoth(true); |
| } |
| |
| _revealDebuggerSidebar() { |
| this._setAsCurrentPanel(); |
| this._splitWidget.showBoth(true); |
| } |
| |
| _updateSidebarPosition() { |
| let vertically; |
| const position = Common.moduleSetting('sidebarPosition').get(); |
| if (position === 'right') { |
| vertically = false; |
| } else if (position === 'bottom') { |
| vertically = true; |
| } else { |
| vertically = UI.inspectorView.element.offsetWidth < 680; |
| } |
| |
| if (this.sidebarPaneView && vertically === !this._splitWidget.isVertical()) { |
| return; |
| } |
| |
| if (this.sidebarPaneView && this.sidebarPaneView.shouldHideOnDetach()) { |
| return; |
| } // We can't reparent extension iframes. |
| |
| if (this.sidebarPaneView) { |
| this.sidebarPaneView.detach(); |
| } |
| |
| this._splitWidget.setVertical(!vertically); |
| this._splitWidget.element.classList.toggle('sources-split-view-vertical', vertically); |
| |
| Sources.SourcesPanel.updateResizerAndSidebarButtons(this); |
| |
| // Create vertical box with stack. |
| const vbox = new UI.VBox(); |
| vbox.element.appendChild(this._debugToolbar.element); |
| vbox.element.appendChild(this._debugToolbarDrawer); |
| |
| vbox.setMinimumAndPreferredSizes( |
| Sources.SourcesPanel.minToolbarWidth, 25, Sources.SourcesPanel.minToolbarWidth, 100); |
| this._sidebarPaneStack = UI.viewManager.createStackLocation(this._revealDebuggerSidebar.bind(this)); |
| this._sidebarPaneStack.widget().element.classList.add('overflow-auto'); |
| this._sidebarPaneStack.widget().show(vbox.element); |
| this._sidebarPaneStack.widget().element.appendChild(this._debuggerPausedMessage.element()); |
| this._sidebarPaneStack.appendApplicableItems('sources.sidebar-top'); |
| |
| if (this._threadsSidebarPane) { |
| this._sidebarPaneStack.showView(this._threadsSidebarPane); |
| } |
| |
| if (!vertically) { |
| this._sidebarPaneStack.appendView(this._watchSidebarPane); |
| } |
| |
| this._sidebarPaneStack.showView(this._callstackPane); |
| const jsBreakpoints = /** @type {!UI.View} */ (UI.viewManager.view('sources.jsBreakpoints')); |
| const scopeChainView = /** @type {!UI.View} */ (UI.viewManager.view('sources.scopeChain')); |
| |
| if (this._tabbedLocationHeader) { |
| this._splitWidget.uninstallResizer(this._tabbedLocationHeader); |
| this._tabbedLocationHeader = null; |
| } |
| |
| if (!vertically) { |
| // Populate the rest of the stack. |
| this._sidebarPaneStack.showView(scopeChainView); |
| this._sidebarPaneStack.showView(jsBreakpoints); |
| this._extensionSidebarPanesContainer = this._sidebarPaneStack; |
| this.sidebarPaneView = vbox; |
| this._splitWidget.uninstallResizer(this._debugToolbar.gripElementForResize()); |
| } else { |
| const splitWidget = new UI.SplitWidget(true, true, 'sourcesPanelDebuggerSidebarSplitViewState', 0.5); |
| splitWidget.setMainWidget(vbox); |
| |
| // Populate the left stack. |
| this._sidebarPaneStack.showView(jsBreakpoints); |
| |
| const tabbedLocation = UI.viewManager.createTabbedLocation(this._revealDebuggerSidebar.bind(this)); |
| splitWidget.setSidebarWidget(tabbedLocation.tabbedPane()); |
| this._tabbedLocationHeader = tabbedLocation.tabbedPane().headerElement(); |
| this._splitWidget.installResizer(this._tabbedLocationHeader); |
| this._splitWidget.installResizer(this._debugToolbar.gripElementForResize()); |
| tabbedLocation.appendView(scopeChainView); |
| tabbedLocation.appendView(this._watchSidebarPane); |
| tabbedLocation.appendApplicableItems('sources.sidebar-tabs'); |
| this._extensionSidebarPanesContainer = tabbedLocation; |
| this.sidebarPaneView = splitWidget; |
| } |
| |
| this._sidebarPaneStack.appendApplicableItems('sources.sidebar-bottom'); |
| const extensionSidebarPanes = Extensions.extensionServer.sidebarPanes(); |
| for (let i = 0; i < extensionSidebarPanes.length; ++i) { |
| this._addExtensionSidebarPane(extensionSidebarPanes[i]); |
| } |
| |
| this._splitWidget.setSidebarWidget(this.sidebarPaneView); |
| } |
| |
| /** |
| * @return {!Promise} |
| */ |
| _setAsCurrentPanel() { |
| return UI.viewManager.showView('sources'); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _extensionSidebarPaneAdded(event) { |
| const pane = /** @type {!Extensions.ExtensionSidebarPane} */ (event.data); |
| this._addExtensionSidebarPane(pane); |
| } |
| |
| /** |
| * @param {!Extensions.ExtensionSidebarPane} pane |
| */ |
| _addExtensionSidebarPane(pane) { |
| if (pane.panelName() === this.name) { |
| this._extensionSidebarPanesContainer.appendView(pane); |
| } |
| } |
| |
| /** |
| * @return {!Sources.SourcesView} |
| */ |
| sourcesView() { |
| return this._sourcesView; |
| } |
| |
| /** |
| * @param {!DataTransfer} dataTransfer |
| */ |
| _handleDrop(dataTransfer) { |
| const items = dataTransfer.items; |
| if (!items.length) { |
| return; |
| } |
| const entry = items[0].webkitGetAsEntry(); |
| if (!entry.isDirectory) { |
| return; |
| } |
| Host.InspectorFrontendHost.upgradeDraggedFileSystemPermissions(entry.filesystem); |
| } |
| }; |
| |
| Sources.SourcesPanel._lastModificationTimeout = 200; |
| |
| Sources.SourcesPanel.minToolbarWidth = 215; |
| |
| /** |
| * @implements {Common.Revealer} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.UILocationRevealer = class { |
| /** |
| * @override |
| * @param {!Object} uiLocation |
| * @param {boolean=} omitFocus |
| * @return {!Promise} |
| */ |
| reveal(uiLocation, omitFocus) { |
| if (!(uiLocation instanceof Workspace.UILocation)) { |
| return Promise.reject(new Error('Internal error: not a ui location')); |
| } |
| Sources.SourcesPanel.instance().showUILocation(uiLocation, omitFocus); |
| return Promise.resolve(); |
| } |
| }; |
| |
| /** |
| * @implements {Common.Revealer} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.DebuggerLocationRevealer = class { |
| /** |
| * @override |
| * @param {!Object} rawLocation |
| * @param {boolean=} omitFocus |
| * @return {!Promise} |
| */ |
| reveal(rawLocation, omitFocus) { |
| if (!(rawLocation instanceof SDK.DebuggerModel.Location)) { |
| return Promise.reject(new Error('Internal error: not a debugger location')); |
| } |
| const uiLocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation); |
| if (!uiLocation) { |
| return Promise.resolve(); |
| } |
| Sources.SourcesPanel.instance().showUILocation(uiLocation, omitFocus); |
| return Promise.resolve(); |
| } |
| }; |
| |
| /** |
| * @implements {Common.Revealer} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.UISourceCodeRevealer = class { |
| /** |
| * @override |
| * @param {!Object} uiSourceCode |
| * @param {boolean=} omitFocus |
| * @return {!Promise} |
| */ |
| reveal(uiSourceCode, omitFocus) { |
| if (!(uiSourceCode instanceof Workspace.UISourceCode)) { |
| return Promise.reject(new Error('Internal error: not a ui source code')); |
| } |
| Sources.SourcesPanel.instance().showUISourceCode(uiSourceCode, undefined, undefined, omitFocus); |
| return Promise.resolve(); |
| } |
| }; |
| |
| /** |
| * @implements {Common.Revealer} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.DebuggerPausedDetailsRevealer = class { |
| /** |
| * @override |
| * @param {!Object} object |
| * @return {!Promise} |
| */ |
| reveal(object) { |
| return Sources.SourcesPanel.instance()._setAsCurrentPanel(); |
| } |
| }; |
| |
| /** |
| * @implements {UI.ActionDelegate} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.RevealingActionDelegate = class { |
| /** |
| * @override |
| * @param {!UI.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction(context, actionId) { |
| const panel = Sources.SourcesPanel.instance(); |
| if (!panel._ensureSourcesViewVisible()) { |
| return false; |
| } |
| switch (actionId) { |
| case 'debugger.toggle-pause': |
| panel._togglePause(); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| /** |
| * @implements {UI.ActionDelegate} |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.DebuggingActionDelegate = class { |
| /** |
| * @override |
| * @param {!UI.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction(context, actionId) { |
| const panel = Sources.SourcesPanel.instance(); |
| switch (actionId) { |
| case 'debugger.step-over': |
| panel._stepOver(); |
| return true; |
| case 'debugger.step-into': |
| panel._stepIntoAsync(); |
| return true; |
| case 'debugger.step': |
| panel._stepInto(); |
| return true; |
| case 'debugger.step-out': |
| panel._stepOut(); |
| return true; |
| case 'debugger.run-snippet': |
| panel._runSnippet(); |
| return true; |
| case 'debugger.toggle-breakpoints-active': |
| panel._toggleBreakpointsActive(); |
| return true; |
| case 'debugger.evaluate-selection': |
| const frame = UI.context.flavor(Sources.UISourceCodeFrame); |
| if (frame) { |
| let text = frame.textEditor.text(frame.textEditor.selection()); |
| const executionContext = UI.context.flavor(SDK.ExecutionContext); |
| if (executionContext) { |
| const message = SDK.consoleModel.addCommandMessage(executionContext, text); |
| text = ObjectUI.JavaScriptREPL.wrapObjectLiteral(text); |
| SDK.consoleModel.evaluateCommandInConsole( |
| executionContext, message, text, /* useCommandLineAPI */ true, /* awaitPromise */ false); |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| |
| /** |
| * @unrestricted |
| */ |
| Sources.SourcesPanel.WrapperView = class extends UI.VBox { |
| constructor() { |
| super(); |
| this.element.classList.add('sources-view-wrapper'); |
| Sources.SourcesPanel.WrapperView._instance = this; |
| this._view = Sources.SourcesPanel.instance()._sourcesView; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| static isShowing() { |
| return !!Sources.SourcesPanel.WrapperView._instance && Sources.SourcesPanel.WrapperView._instance.isShowing(); |
| } |
| |
| /** |
| * @override |
| */ |
| wasShown() { |
| if (!Sources.SourcesPanel.instance().isShowing()) { |
| this._showViewInWrapper(); |
| } else { |
| UI.inspectorView.setDrawerMinimized(true); |
| } |
| Sources.SourcesPanel.updateResizerAndSidebarButtons(Sources.SourcesPanel.instance()); |
| } |
| |
| /** |
| * @override |
| */ |
| willHide() { |
| UI.inspectorView.setDrawerMinimized(false); |
| setImmediate(() => Sources.SourcesPanel.updateResizerAndSidebarButtons(Sources.SourcesPanel.instance())); |
| } |
| |
| _showViewInWrapper() { |
| this._view.show(this.element); |
| } |
| }; |