| /* |
| * Copyright (C) 2013, 2014 Apple 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. AND ITS CONTRIBUTORS ``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 ITS 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. |
| */ |
| |
| WebInspector.DebuggerManager = function() |
| { |
| // FIXME: Convert this to a WebInspector.Object subclass, and call super(). |
| // WebInspector.Object.call(this); |
| |
| if (window.DebuggerAgent) |
| DebuggerAgent.enable(); |
| |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this); |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointDisabledStateDidChange, this); |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ConditionDidChange, this._breakpointEditablePropertyDidChange, this); |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this); |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ActionsDidChange, this._breakpointEditablePropertyDidChange, this); |
| |
| window.addEventListener("pagehide", this._inspectorClosing.bind(this)); |
| |
| this._allExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-exceptions", false); |
| this._allUncaughtExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-uncaught-exceptions", false); |
| |
| var specialBreakpointLocation = new WebInspector.SourceCodeLocation(null, Infinity, Infinity); |
| |
| this._allExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allExceptionsBreakpointEnabledSetting.value); |
| this._allExceptionsBreakpoint.resolved = true; |
| |
| this._allUncaughtExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allUncaughtExceptionsBreakpointEnabledSetting.value); |
| |
| this._breakpoints = []; |
| this._breakpointURLMap = {}; |
| this._breakpointScriptIdentifierMap = {}; |
| this._breakpointIdMap = {}; |
| |
| this._nextBreakpointActionIdentifier = 1; |
| |
| this._paused = false; |
| this._pauseReason = null; |
| this._pauseData = null; |
| |
| this._scriptIdMap = {}; |
| this._scriptURLMap = {}; |
| |
| this._breakpointsSetting = new WebInspector.Setting("breakpoints", []); |
| this._breakpointsEnabledSetting = new WebInspector.Setting("breakpoints-enabled", true); |
| |
| if (window.DebuggerAgent) |
| DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value); |
| |
| this._updateBreakOnExceptionsState(); |
| |
| function restoreBreakpointsSoon() { |
| this._restoringBreakpoints = true; |
| for (var cookie of this._breakpointsSetting.value) |
| this.addBreakpoint(new WebInspector.Breakpoint(cookie)); |
| delete this._restoringBreakpoints; |
| } |
| |
| // Ensure that all managers learn about restored breakpoints, |
| // regardless of their initialization order. |
| setTimeout(restoreBreakpointsSoon.bind(this), 0); |
| }; |
| |
| WebInspector.DebuggerManager.Event = { |
| BreakpointAdded: "debugger-manager-breakpoint-added", |
| BreakpointRemoved: "debugger-manager-breakpoint-removed", |
| BreakpointMoved: "debugger-manager-breakpoint-moved", |
| Paused: "debugger-manager-paused", |
| Resumed: "debugger-manager-resumed", |
| CallFramesDidChange: "debugger-manager-call-frames-did-change", |
| ActiveCallFrameDidChange: "debugger-manager-active-call-frame-did-change", |
| ScriptAdded: "debugger-manager-script-added", |
| ScriptsCleared: "debugger-manager-scripts-cleared", |
| BreakpointsEnabledDidChange: "debugger-manager-breakpoints-enabled-did-change" |
| }; |
| |
| WebInspector.DebuggerManager.PauseReason = { |
| Assertion: "assertion", |
| Breakpoint: "breakpoint", |
| CSPViolation: "CSP-violation", |
| DebuggerStatement: "debugger-statement", |
| Exception: "exception", |
| PauseOnNextStatement: "pause-on-next-statement", |
| Other: "other", |
| } |
| |
| WebInspector.DebuggerManager.prototype = { |
| constructor: WebInspector.DebuggerManager, |
| |
| // Public |
| |
| get breakpointsEnabled() |
| { |
| return this._breakpointsEnabledSetting.value; |
| }, |
| |
| set breakpointsEnabled(enabled) |
| { |
| if (this._breakpointsEnabledSetting.value === enabled) |
| return; |
| |
| this._breakpointsEnabledSetting.value = enabled; |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange); |
| |
| DebuggerAgent.setBreakpointsActive(enabled); |
| |
| this._updateBreakOnExceptionsState(); |
| }, |
| |
| get paused() |
| { |
| return this._paused; |
| }, |
| |
| get pauseReason() |
| { |
| return this._pauseReason; |
| }, |
| |
| get pauseData() |
| { |
| return this._pauseData; |
| }, |
| |
| get callFrames() |
| { |
| return this._callFrames; |
| }, |
| |
| get activeCallFrame() |
| { |
| return this._activeCallFrame; |
| }, |
| |
| set activeCallFrame(callFrame) |
| { |
| if (callFrame === this._activeCallFrame) |
| return; |
| |
| this._activeCallFrame = callFrame || null; |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange); |
| }, |
| |
| pause: function() |
| { |
| if (this._paused) |
| return Promise.resolve(); |
| |
| var listener = new WebInspector.EventListener(this, true); |
| |
| var managerResult = new Promise(function(resolve, reject) { |
| listener.connect(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.Paused, resolve); |
| }); |
| |
| var protocolResult = DebuggerAgent.pause() |
| .catch(function(error) { |
| listener.disconnect(); |
| console.error("DebuggerManager.pause failed: ", error); |
| throw error; |
| }); |
| |
| return Promise.all([managerResult, protocolResult]); |
| }, |
| |
| resume: function() |
| { |
| if (!this._paused) |
| return Promise.resolve(); |
| |
| var listener = new WebInspector.EventListener(this, true); |
| |
| var managerResult = new Promise(function(resolve, reject) { |
| listener.connect(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.Resumed, resolve); |
| }); |
| |
| var protocolResult = DebuggerAgent.resume() |
| .catch(function(error) { |
| listener.disconnect(); |
| console.error("DebuggerManager.resume failed: ", error); |
| throw error; |
| }); |
| |
| return Promise.all([managerResult, protocolResult]); |
| }, |
| |
| stepOver: function() |
| { |
| if (!this._paused) |
| return Promise.reject(new Error("Cannot step over because debugger is not paused.")); |
| |
| var listener = new WebInspector.EventListener(this, true); |
| |
| var managerResult = new Promise(function(resolve, reject) { |
| listener.connect(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, resolve); |
| }); |
| |
| var protocolResult = DebuggerAgent.stepOver() |
| .catch(function(error) { |
| listener.disconnect(); |
| console.error("DebuggerManager.stepOver failed: ", error); |
| throw error; |
| }); |
| |
| return Promise.all([managerResult, protocolResult]); |
| }, |
| |
| stepInto: function() |
| { |
| if (!this._paused) |
| return Promise.reject(new Error("Cannot step into because debugger is not paused.")); |
| |
| var listener = new WebInspector.EventListener(this, true); |
| |
| var managerResult = new Promise(function(resolve, reject) { |
| listener.connect(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, resolve); |
| }); |
| |
| var protocolResult = DebuggerAgent.stepInto() |
| .catch(function(error) { |
| listener.disconnect(); |
| console.error("DebuggerManager.stepInto failed: ", error); |
| throw error; |
| }); |
| |
| return Promise.all([managerResult, protocolResult]); |
| }, |
| |
| stepOut: function() |
| { |
| if (!this._paused) |
| return Promise.reject(new Error("Cannot step out because debugger is not paused.")); |
| |
| var listener = new WebInspector.EventListener(this, true); |
| |
| var managerResult = new Promise(function(resolve, reject) { |
| listener.connect(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, resolve); |
| }); |
| |
| var protocolResult = DebuggerAgent.stepOut() |
| .catch(function(error) { |
| listener.disconnect(); |
| console.error("DebuggerManager.stepOut failed: ", error); |
| throw error; |
| }); |
| |
| return Promise.all([managerResult, protocolResult]); |
| }, |
| |
| get allExceptionsBreakpoint() |
| { |
| return this._allExceptionsBreakpoint; |
| }, |
| |
| get allUncaughtExceptionsBreakpoint() |
| { |
| return this._allUncaughtExceptionsBreakpoint; |
| }, |
| |
| get breakpoints() |
| { |
| return this._breakpoints; |
| }, |
| |
| breakpointsForSourceCode: function(sourceCode) |
| { |
| console.assert(sourceCode instanceof WebInspector.Resource || sourceCode instanceof WebInspector.Script); |
| |
| if (sourceCode instanceof WebInspector.SourceMapResource) { |
| var mappedResourceBreakpoints = []; |
| var originalSourceCodeBreakpoints = this.breakpointsForSourceCode(sourceCode.sourceMap.originalSourceCode); |
| return originalSourceCodeBreakpoints.filter(function(breakpoint) { |
| return breakpoint.sourceCodeLocation.displaySourceCode === sourceCode; |
| }); |
| } |
| |
| if (sourceCode.url in this._breakpointURLMap) { |
| var urlBreakpoint = this._breakpointURLMap[sourceCode.url] || []; |
| this._associateBreakpointsWithSourceCode(urlBreakpoint, sourceCode); |
| return urlBreakpoint; |
| } |
| |
| if (sourceCode instanceof WebInspector.Script && sourceCode.id in this._breakpointScriptIdentifierMap) { |
| var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[sourceCode.id] || []; |
| this._associateBreakpointsWithSourceCode(scriptIdentifierBreakpoints, sourceCode); |
| return scriptIdentifierBreakpoints; |
| } |
| |
| return []; |
| }, |
| |
| breakpointForIdentifier: function(id) |
| { |
| return this._breakpointIdMap[id]; |
| }, |
| |
| scriptForIdentifier: function(id) |
| { |
| return this._scriptIdMap[id] || null; |
| }, |
| |
| scriptsForURL: function(url) |
| { |
| // FIXME: This may not be safe. A Resource's URL may differ from a Script's URL. |
| return this._scriptURLMap[url] || []; |
| }, |
| |
| continueToLocation: function(scriptIdentifier, lineNumber, columnNumber) |
| { |
| DebuggerAgent.continueToLocation({scriptId: scriptIdentifier, lineNumber, columnNumber}); |
| }, |
| |
| addBreakpoint: function(breakpoint, skipEventDispatch, shouldSpeculativelyResolve) |
| { |
| console.assert(breakpoint instanceof WebInspector.Breakpoint, "Bad argument to DebuggerManger.addBreakpoint: ", breakpoint); |
| if (!breakpoint) |
| return; |
| |
| if (breakpoint.url) { |
| var urlBreakpoints = this._breakpointURLMap[breakpoint.url]; |
| if (!urlBreakpoints) |
| urlBreakpoints = this._breakpointURLMap[breakpoint.url] = []; |
| urlBreakpoints.push(breakpoint); |
| } |
| |
| if (breakpoint.scriptIdentifier) { |
| var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier]; |
| if (!scriptIdentifierBreakpoints) |
| scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier] = []; |
| scriptIdentifierBreakpoints.push(breakpoint); |
| } |
| |
| this._breakpoints.push(breakpoint); |
| |
| function speculativelyResolveBreakpoint(breakpoint) { |
| breakpoint.resolved = true; |
| } |
| |
| if (!breakpoint.disabled) |
| this._setBreakpoint(breakpoint, shouldSpeculativelyResolve ? speculativelyResolveBreakpoint.bind(null, breakpoint) : null); |
| |
| if (!skipEventDispatch) |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointAdded, {breakpoint}); |
| }, |
| |
| removeBreakpoint: function(breakpoint) |
| { |
| console.assert(breakpoint); |
| if (!breakpoint) |
| return; |
| |
| console.assert(this.isBreakpointRemovable(breakpoint)); |
| if (!this.isBreakpointRemovable(breakpoint)) |
| return; |
| |
| this._breakpoints.remove(breakpoint); |
| |
| if (breakpoint.identifier) |
| this._removeBreakpoint(breakpoint); |
| |
| if (breakpoint.url) { |
| var urlBreakpoints = this._breakpointURLMap[breakpoint.url]; |
| if (urlBreakpoints) { |
| urlBreakpoints.remove(breakpoint); |
| if (!urlBreakpoints.length) |
| delete this._breakpointURLMap[breakpoint.url]; |
| } |
| } |
| |
| if (breakpoint.scriptIdentifier) { |
| var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier]; |
| if (scriptIdentifierBreakpoints) { |
| scriptIdentifierBreakpoints.remove(breakpoint); |
| if (!scriptIdentifierBreakpoints.length) |
| delete this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier]; |
| } |
| } |
| |
| // Disable the breakpoint first, so removing actions doesn't re-add the breakpoint. |
| breakpoint.disabled = true; |
| breakpoint.clearActions(); |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointRemoved, {breakpoint}); |
| }, |
| |
| breakpointResolved: function(breakpointIdentifier, location) |
| { |
| // Called from WebInspector.DebuggerObserver. |
| |
| var breakpoint = this._breakpointIdMap[breakpointIdentifier]; |
| console.assert(breakpoint); |
| if (!breakpoint) |
| return; |
| |
| console.assert(breakpoint.identifier === breakpointIdentifier); |
| |
| if (!breakpoint.sourceCodeLocation.sourceCode) { |
| var sourceCodeLocation = this._sourceCodeLocationFromPayload(location); |
| breakpoint.sourceCodeLocation.sourceCode = sourceCodeLocation.sourceCode; |
| } |
| |
| breakpoint.resolved = true; |
| }, |
| |
| reset: function() |
| { |
| // Called from WebInspector.DebuggerObserver. |
| |
| var wasPaused = this._paused; |
| |
| WebInspector.Script.resetUniqueDisplayNameNumbers(); |
| |
| this._paused = false; |
| this._pauseReason = null; |
| this._pauseData = null; |
| |
| this._scriptIdMap = {}; |
| this._scriptURLMap = {}; |
| |
| this._ignoreBreakpointDisplayLocationDidChangeEvent = true; |
| |
| // Mark all the breakpoints as unresolved. They will be reported as resolved when |
| // breakpointResolved is called as the page loads. |
| for (var i = 0; i < this._breakpoints.length; ++i) { |
| var breakpoint = this._breakpoints[i]; |
| breakpoint.resolved = false; |
| if (breakpoint.sourceCodeLocation.sourceCode) |
| breakpoint.sourceCodeLocation.sourceCode = null; |
| } |
| |
| delete this._ignoreBreakpointDisplayLocationDidChangeEvent; |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptsCleared); |
| |
| if (wasPaused) |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed); |
| }, |
| |
| debuggerDidPause: function(callFramesPayload, reason, data) |
| { |
| // Called from WebInspector.DebuggerObserver. |
| |
| if (this._delayedResumeTimeout) { |
| clearTimeout(this._delayedResumeTimeout); |
| delete this._delayedResumeTimeout; |
| } |
| |
| var wasStillPaused = this._paused; |
| |
| this._paused = true; |
| this._callFrames = []; |
| |
| this._pauseReason = this._pauseReasonFromPayload(reason); |
| this._pauseData = data || null; |
| |
| for (var i = 0; i < callFramesPayload.length; ++i) { |
| var callFramePayload = callFramesPayload[i]; |
| var sourceCodeLocation = this._sourceCodeLocationFromPayload(callFramePayload.location); |
| // FIXME: There may be useful call frames without a source code location (native callframes), should we include them? |
| if (!sourceCodeLocation) |
| continue; |
| if (!sourceCodeLocation.sourceCode) |
| continue; |
| // Exclude the case where the call frame is in the inspector code. |
| if (sourceCodeLocation.sourceCode.url && sourceCodeLocation.sourceCode.url.startsWith("__WebInspector")) |
| continue; |
| var thisObject = WebInspector.RemoteObject.fromPayload(callFramePayload.this); |
| var scopeChain = this._scopeChainFromPayload(callFramePayload.scopeChain); |
| var callFrame = new WebInspector.CallFrame(callFramePayload.callFrameId, sourceCodeLocation, callFramePayload.functionName, thisObject, scopeChain); |
| this._callFrames.push(callFrame); |
| } |
| |
| this._activeCallFrame = this._callFrames[0]; |
| |
| if (!wasStillPaused) |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Paused); |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange); |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange); |
| }, |
| |
| debuggerDidResume: function() |
| { |
| // Called from WebInspector.DebuggerObserver. |
| |
| function delayedWork() |
| { |
| delete this._delayedResumeTimeout; |
| |
| this._paused = false; |
| this._callFrames = null; |
| this._activeCallFrame = null; |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed); |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange); |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange); |
| } |
| |
| // We delay clearing the state and firing events so the user interface does not flash |
| // between brief steps or successive breakpoints. |
| this._delayedResumeTimeout = setTimeout(delayedWork.bind(this), 50); |
| }, |
| |
| playBreakpointActionSound: function(breakpointActionIdentifier) |
| { |
| InspectorFrontendHost.beep(); |
| }, |
| |
| scriptDidParse: function(scriptIdentifier, url, isContentScript, startLine, startColumn, endLine, endColumn, sourceMapURL) |
| { |
| // Don't add the script again if it is already known. |
| if (this._scriptIdMap[scriptIdentifier]) { |
| console.assert(this._scriptIdMap[scriptIdentifier].url === (url || null)); |
| console.assert(this._scriptIdMap[scriptIdentifier].range.startLine === startLine); |
| console.assert(this._scriptIdMap[scriptIdentifier].range.startColumn === startColumn); |
| console.assert(this._scriptIdMap[scriptIdentifier].range.endLine === endLine); |
| console.assert(this._scriptIdMap[scriptIdentifier].range.endColumn === endColumn); |
| return; |
| } |
| |
| var script = new WebInspector.Script(scriptIdentifier, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn), url, isContentScript, sourceMapURL); |
| |
| this._scriptIdMap[scriptIdentifier] = script; |
| |
| if (script.url) { |
| var scripts = this._scriptURLMap[script.url]; |
| if (!scripts) |
| scripts = this._scriptURLMap[script.url] = []; |
| scripts.push(script); |
| } |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptAdded, {script}); |
| }, |
| |
| isBreakpointRemovable: function(breakpoint) |
| { |
| return breakpoint !== this._allExceptionsBreakpoint && breakpoint !== this._allUncaughtExceptionsBreakpoint; |
| }, |
| |
| isBreakpointEditable: function(breakpoint) |
| { |
| return this.isBreakpointRemovable(breakpoint); |
| }, |
| |
| get nextBreakpointActionIdentifier() |
| { |
| return this._nextBreakpointActionIdentifier++; |
| }, |
| |
| // Private |
| |
| _sourceCodeLocationFromPayload: function(payload) |
| { |
| var script = this._scriptIdMap[payload.scriptId]; |
| console.assert(script); |
| if (!script) |
| return null; |
| |
| return script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber); |
| }, |
| |
| _scopeChainFromPayload: function(payload) |
| { |
| var scopeChain = []; |
| for (var i = 0; i < payload.length; ++i) |
| scopeChain.push(this._scopeChainNodeFromPayload(payload[i])); |
| return scopeChain; |
| }, |
| |
| _scopeChainNodeFromPayload: function(payload) |
| { |
| var type = null; |
| switch (payload.type) { |
| case "local": |
| type = WebInspector.ScopeChainNode.Type.Local; |
| break; |
| case "global": |
| type = WebInspector.ScopeChainNode.Type.Global; |
| break; |
| case "with": |
| type = WebInspector.ScopeChainNode.Type.With; |
| break; |
| case "closure": |
| type = WebInspector.ScopeChainNode.Type.Closure; |
| break; |
| case "catch": |
| type = WebInspector.ScopeChainNode.Type.Catch; |
| break; |
| case "functionName": |
| type = WebInspector.ScopeChainNode.Type.FunctionName; |
| break; |
| default: |
| console.error("Unknown type: " + payload.type); |
| } |
| |
| var object = WebInspector.RemoteObject.fromPayload(payload.object); |
| return new WebInspector.ScopeChainNode(type, object); |
| }, |
| |
| _pauseReasonFromPayload: function(payload) |
| { |
| // FIXME: Handle other backend pause seasons. |
| switch (payload) { |
| case DebuggerAgent.PausedReason.Assert: |
| return WebInspector.DebuggerManager.PauseReason.Assertion; |
| case DebuggerAgent.PausedReason.Breakpoint: |
| return WebInspector.DebuggerManager.PauseReason.Breakpoint; |
| case DebuggerAgent.PausedReason.CSPViolation: |
| return WebInspector.DebuggerManager.PauseReason.CSPViolation; |
| case DebuggerAgent.PausedReason.DebuggerStatement: |
| return WebInspector.DebuggerManager.PauseReason.DebuggerStatement; |
| case DebuggerAgent.PausedReason.Exception: |
| return WebInspector.DebuggerManager.PauseReason.Exception; |
| case DebuggerAgent.PausedReason.PauseOnNextStatement: |
| return WebInspector.DebuggerManager.PauseReason.PauseOnNextStatement; |
| default: |
| return WebInspector.DebuggerManager.PauseReason.Other; |
| } |
| }, |
| |
| _debuggerBreakpointActionType: function(type) |
| { |
| switch (type) { |
| case WebInspector.BreakpointAction.Type.Log: |
| return DebuggerAgent.BreakpointActionType.Log; |
| case WebInspector.BreakpointAction.Type.Evaluate: |
| return DebuggerAgent.BreakpointActionType.Evaluate; |
| case WebInspector.BreakpointAction.Type.Sound: |
| return DebuggerAgent.BreakpointActionType.Sound; |
| case WebInspector.BreakpointAction.Type.Probe: |
| return DebuggerAgent.BreakpointActionType.Probe; |
| default: |
| console.assert(false); |
| return DebuggerAgent.BreakpointActionType.Log; |
| } |
| }, |
| |
| _setBreakpoint: function(breakpoint, callback) |
| { |
| console.assert(!breakpoint.identifier); |
| console.assert(!breakpoint.disabled); |
| |
| if (breakpoint.identifier || breakpoint.disabled) |
| return; |
| |
| if (!this._restoringBreakpoints) { |
| // Enable breakpoints since a breakpoint is being set. This eliminates |
| // a multi-step process for the user that can be confusing. |
| this.breakpointsEnabled = true; |
| } |
| |
| function didSetBreakpoint(error, breakpointIdentifier, locations) |
| { |
| if (error) |
| return; |
| |
| this._breakpointIdMap[breakpointIdentifier] = breakpoint; |
| |
| breakpoint.identifier = breakpointIdentifier; |
| |
| // Debugger.setBreakpoint returns a single location. |
| if (!(locations instanceof Array)) |
| locations = [locations]; |
| |
| for (var location of locations) |
| this.breakpointResolved(breakpointIdentifier, location); |
| |
| if (typeof callback === "function") |
| callback(); |
| } |
| |
| // The breakpoint will be resolved again by calling DebuggerAgent, so mark it as unresolved. |
| // If something goes wrong it will stay unresolved and show up as such in the user interface. |
| breakpoint.resolved = false; |
| |
| // Convert BreakpointAction types to DebuggerAgent protocol types. |
| // NOTE: Breakpoint.options returns new objects each time, so it is safe to modify. |
| var options; |
| if (DebuggerAgent.BreakpointActionType) { |
| options = breakpoint.options; |
| if (options.actions.length) { |
| for (var i = 0; i < options.actions.length; ++i) |
| options.actions[i].type = this._debuggerBreakpointActionType(options.actions[i].type); |
| } |
| } |
| |
| // COMPATIBILITY (iOS 7): iOS 7 and earlier, DebuggerAgent.setBreakpoint* took a "condition" string argument. |
| // This has been replaced with an "options" BreakpointOptions object. |
| if (breakpoint.url) { |
| DebuggerAgent.setBreakpointByUrl.invoke({ |
| lineNumber: breakpoint.sourceCodeLocation.lineNumber, |
| url: breakpoint.url, |
| urlRegex: undefined, |
| columnNumber: breakpoint.sourceCodeLocation.columnNumber, |
| condition: breakpoint.condition, |
| options |
| }, didSetBreakpoint.bind(this)); |
| } else if (breakpoint.scriptIdentifier) { |
| DebuggerAgent.setBreakpoint.invoke({ |
| location: {scriptId: breakpoint.scriptIdentifier, lineNumber: breakpoint.sourceCodeLocation.lineNumber, columnNumber: breakpoint.sourceCodeLocation.columnNumber}, |
| condition: breakpoint.condition, |
| options |
| }, didSetBreakpoint.bind(this)); |
| } |
| }, |
| |
| _removeBreakpoint: function(breakpoint, callback) |
| { |
| if (!breakpoint.identifier) |
| return; |
| |
| function didRemoveBreakpoint(error) |
| { |
| if (error) |
| console.error(error); |
| |
| delete this._breakpointIdMap[breakpoint.identifier]; |
| |
| breakpoint.identifier = null; |
| |
| // Don't reset resolved here since we want to keep disabled breakpoints looking like they |
| // are resolved in the user interface. They will get marked as unresolved in reset. |
| |
| if (typeof callback === "function") |
| callback(); |
| } |
| |
| DebuggerAgent.removeBreakpoint(breakpoint.identifier, didRemoveBreakpoint.bind(this)); |
| }, |
| |
| _breakpointDisplayLocationDidChange: function(event) |
| { |
| if (this._ignoreBreakpointDisplayLocationDidChangeEvent) |
| return; |
| |
| var breakpoint = event.target; |
| if (!breakpoint.identifier || breakpoint.disabled) |
| return; |
| |
| // Remove the breakpoint with its old id. |
| this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this)); |
| |
| function breakpointRemoved() |
| { |
| // Add the breakpoint at its new lineNumber and get a new id. |
| this._setBreakpoint(breakpoint); |
| |
| this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointMoved, {breakpoint}); |
| } |
| }, |
| |
| _breakpointDisabledStateDidChange: function(event) |
| { |
| var breakpoint = event.target; |
| |
| if (breakpoint === this._allExceptionsBreakpoint) { |
| if (!breakpoint.disabled) |
| this.breakpointsEnabled = true; |
| this._allExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled; |
| this._updateBreakOnExceptionsState(); |
| return; |
| } |
| |
| if (breakpoint === this._allUncaughtExceptionsBreakpoint) { |
| if (!breakpoint.disabled) |
| this.breakpointsEnabled = true; |
| this._allUncaughtExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled; |
| this._updateBreakOnExceptionsState(); |
| return; |
| } |
| |
| if (breakpoint.disabled) |
| this._removeBreakpoint(breakpoint); |
| else |
| this._setBreakpoint(breakpoint); |
| }, |
| |
| _breakpointEditablePropertyDidChange: function(event) |
| { |
| var breakpoint = event.target; |
| if (breakpoint.disabled) |
| return; |
| |
| console.assert(this.isBreakpointEditable(breakpoint)); |
| if (!this.isBreakpointEditable(breakpoint)) |
| return; |
| |
| // Remove the breakpoint with its old id. |
| this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this)); |
| |
| function breakpointRemoved() |
| { |
| // Add the breakpoint with its new condition and get a new id. |
| this._setBreakpoint(breakpoint); |
| } |
| }, |
| |
| _updateBreakOnExceptionsState: function() |
| { |
| var state = "none"; |
| |
| if (this._breakpointsEnabledSetting.value) { |
| if (!this._allExceptionsBreakpoint.disabled) |
| state = "all"; |
| else if (!this._allUncaughtExceptionsBreakpoint.disabled) |
| state = "uncaught"; |
| } |
| |
| switch (state) { |
| case "all": |
| // Mark the uncaught breakpoint as unresolved since "all" includes "uncaught". |
| // That way it is clear in the user interface that the breakpoint is ignored. |
| this._allUncaughtExceptionsBreakpoint.resolved = false; |
| break; |
| case "uncaught": |
| case "none": |
| // Mark the uncaught breakpoint as resolved again. |
| this._allUncaughtExceptionsBreakpoint.resolved = true; |
| break; |
| } |
| |
| DebuggerAgent.setPauseOnExceptions(state); |
| }, |
| |
| _inspectorClosing: function(event) |
| { |
| this._saveBreakpoints(); |
| }, |
| |
| _saveBreakpoints: function() |
| { |
| var savedBreakpoints = []; |
| |
| for (var i = 0; i < this._breakpoints.length; ++i) { |
| var breakpoint = this._breakpoints[i]; |
| |
| // Only breakpoints with URLs can be saved. Breakpoints for transient scripts can't. |
| if (!breakpoint.url) |
| continue; |
| |
| savedBreakpoints.push(breakpoint.info); |
| } |
| |
| this._breakpointsSetting.value = savedBreakpoints; |
| }, |
| |
| _associateBreakpointsWithSourceCode: function(breakpoints, sourceCode) |
| { |
| this._ignoreBreakpointDisplayLocationDidChangeEvent = true; |
| |
| for (var i = 0; i < breakpoints.length; ++i) { |
| var breakpoint = breakpoints[i]; |
| if (breakpoint.sourceCodeLocation.sourceCode === null) |
| breakpoint.sourceCodeLocation.sourceCode = sourceCode; |
| // SourceCodes can be unequal if the SourceCodeLocation is associated with a Script and we are looking at the Resource. |
| console.assert(breakpoint.sourceCodeLocation.sourceCode === sourceCode || breakpoint.sourceCodeLocation.sourceCode.url === sourceCode.url); |
| } |
| |
| delete this._ignoreBreakpointDisplayLocationDidChangeEvent; |
| } |
| }; |
| |
| WebInspector.DebuggerManager.prototype.__proto__ = WebInspector.Object.prototype; |