diff options
Diffstat (limited to 'devtools/client/debugger/content/actions/breakpoints.js')
-rw-r--r-- | devtools/client/debugger/content/actions/breakpoints.js | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/devtools/client/debugger/content/actions/breakpoints.js b/devtools/client/debugger/content/actions/breakpoints.js new file mode 100644 index 000000000..5c5552d78 --- /dev/null +++ b/devtools/client/debugger/content/actions/breakpoints.js @@ -0,0 +1,191 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const constants = require("../constants"); +const promise = require("promise"); +const { asPaused } = require("../utils"); +const { PROMISE } = require("devtools/client/shared/redux/middleware/promise"); +const { + getSource, getBreakpoint, getBreakpoints, makeLocationId +} = require("../queries"); +const { Task } = require("devtools/shared/task"); + +// Because breakpoints are just simple data structures, we still need +// a way to lookup the actual client instance to talk to the server. +// We keep an internal database of clients based off of actor ID. +const BREAKPOINT_CLIENT_STORE = new Map(); + +function setBreakpointClient(actor, client) { + BREAKPOINT_CLIENT_STORE.set(actor, client); +} + +function getBreakpointClient(actor) { + return BREAKPOINT_CLIENT_STORE.get(actor); +} + +function enableBreakpoint(location) { + // Enabling is exactly the same as adding. It will use the existing + // breakpoint that still stored. + return addBreakpoint(location); +} + +function _breakpointExists(state, location) { + const currentBp = getBreakpoint(state, location); + return currentBp && !currentBp.disabled; +} + +function _getOrCreateBreakpoint(state, location, condition) { + return getBreakpoint(state, location) || { location, condition }; +} + +function addBreakpoint(location, condition) { + return (dispatch, getState) => { + if (_breakpointExists(getState(), location)) { + return; + } + + const bp = _getOrCreateBreakpoint(getState(), location, condition); + + return dispatch({ + type: constants.ADD_BREAKPOINT, + breakpoint: bp, + condition: condition, + [PROMISE]: Task.spawn(function* () { + const sourceClient = gThreadClient.source( + getSource(getState(), bp.location.actor) + ); + const [response, bpClient] = yield sourceClient.setBreakpoint({ + line: bp.location.line, + column: bp.location.column, + condition: bp.condition + }); + const { isPending, actualLocation } = response; + + // Save the client instance + setBreakpointClient(bpClient.actor, bpClient); + + return { + text: DebuggerView.editor.getText( + (actualLocation ? actualLocation.line : bp.location.line) - 1 + ).trim(), + + // If the breakpoint response has an "actualLocation" attached, then + // the original requested placement for the breakpoint wasn't + // accepted. + actualLocation: isPending ? null : actualLocation, + actor: bpClient.actor + }; + }) + }); + }; +} + +function disableBreakpoint(location) { + return _removeOrDisableBreakpoint(location, true); +} + +function removeBreakpoint(location) { + return _removeOrDisableBreakpoint(location); +} + +function _removeOrDisableBreakpoint(location, isDisabled) { + return (dispatch, getState) => { + let bp = getBreakpoint(getState(), location); + if (!bp) { + throw new Error("attempt to remove breakpoint that does not exist"); + } + if (bp.loading) { + // TODO(jwl): make this wait until the breakpoint is saved if it + // is still loading + throw new Error("attempt to remove unsaved breakpoint"); + } + + const bpClient = getBreakpointClient(bp.actor); + const action = { + type: constants.REMOVE_BREAKPOINT, + breakpoint: bp, + disabled: isDisabled + }; + + // If the breakpoint is already disabled, we don't need to remove + // it from the server. We just need to dispatch an action + // simulating a successful server request to remove it, and it + // will be removed completely from the state. + if (!bp.disabled) { + return dispatch(Object.assign({}, action, { + [PROMISE]: bpClient.remove() + })); + } else { + return dispatch(Object.assign({}, action, { status: "done" })); + } + }; +} + +function removeAllBreakpoints() { + return (dispatch, getState) => { + const breakpoints = getBreakpoints(getState()); + const activeBreakpoints = breakpoints.filter(bp => !bp.disabled); + activeBreakpoints.forEach(bp => removeBreakpoint(bp.location)); + }; +} + +/** + * Update the condition of a breakpoint. + * + * @param object aLocation + * @see DebuggerController.Breakpoints.addBreakpoint + * @param string aClients + * The condition to set on the breakpoint + * @return object + * A promise that will be resolved with the breakpoint client + */ +function setBreakpointCondition(location, condition) { + return (dispatch, getState) => { + const bp = getBreakpoint(getState(), location); + if (!bp) { + throw new Error("Breakpoint does not exist at the specified location"); + } + if (bp.loading) { + // TODO(jwl): when this function is called, make sure the action + // creator waits for the breakpoint to exist + throw new Error("breakpoint must be saved"); + } + + const bpClient = getBreakpointClient(bp.actor); + const action = { + type: constants.SET_BREAKPOINT_CONDITION, + breakpoint: bp, + condition: condition + }; + + // If it's not disabled, we need to update the condition on the + // server. Otherwise, just dispatch a non-remote action that + // updates the condition locally. + if (!bp.disabled) { + return dispatch(Object.assign({}, action, { + [PROMISE]: Task.spawn(function* () { + const newClient = yield bpClient.setCondition(gThreadClient, condition); + + // Remove the old instance and save the new one + setBreakpointClient(bpClient.actor, null); + setBreakpointClient(newClient.actor, newClient); + + return { actor: newClient.actor }; + }) + })); + } else { + return dispatch(action); + } + }; +} + +module.exports = { + enableBreakpoint, + addBreakpoint, + disableBreakpoint, + removeBreakpoint, + removeAllBreakpoints, + setBreakpointCondition +}; |