This repository has been archived by the owner on Jan 11, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2d2100e
Showing
29 changed files
with
1,543 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
build | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf8" /> | ||
<link rel="stylesheet" href="styles.css" type="text/css" /> | ||
</head> | ||
|
||
<body> | ||
<div id="mount"></div> | ||
<script src="build/bundle.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
function assert(x) { | ||
return x; | ||
} | ||
|
||
function entries(obj) { | ||
return Object.keys(obj).map(k => [k, obj[k]]); | ||
}; | ||
|
||
function toObject(arr) { | ||
const obj = {}; | ||
for (let [k, v] of arr) { | ||
obj[k] = v; | ||
} | ||
return obj; | ||
}; | ||
|
||
function executeSoon(fn) { | ||
// TODO: Use something faster. | ||
setTimeout(() => { | ||
fn(); | ||
}, 0); | ||
} | ||
|
||
module.exports = { assert, entries, toObject, executeSoon }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/* 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'); | ||
|
||
// 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); | ||
|
||
return dispatch({ | ||
type: constants.SET_BREAKPOINT_CONDITION, | ||
breakpoint: bp, | ||
condition: condition, | ||
[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 }; | ||
}) | ||
}); | ||
}; | ||
} | ||
|
||
module.exports = { | ||
enableBreakpoint, | ||
addBreakpoint, | ||
disableBreakpoint, | ||
removeBreakpoint, | ||
removeAllBreakpoints, | ||
setBreakpointCondition | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* 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 { asPaused } = require("../utils"); | ||
const { reportException } = require("devtools/shared/DevToolsUtils"); | ||
|
||
const FETCH_EVENT_LISTENERS_DELAY = 200; // ms | ||
|
||
function fetchEventListeners() { | ||
return (dispatch, getState) => { | ||
// Make sure we"re not sending a batch of closely repeated requests. | ||
// This can easily happen whenever new sources are fetched. | ||
setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => { | ||
// In case there is still a request of listeners going on (it | ||
// takes several RDP round trips right now), make sure we wait | ||
// on a currently running request | ||
if (getState().eventListeners.fetchingListeners) { | ||
dispatch({ | ||
type: services.WAIT_UNTIL, | ||
predicate: action => ( | ||
action.type === constants.FETCH_EVENT_LISTENERS && | ||
action.status === "done" | ||
), | ||
run: dispatch => dispatch(fetchEventListeners()) | ||
}); | ||
return; | ||
} | ||
|
||
dispatch({ | ||
type: constants.FETCH_EVENT_LISTENERS, | ||
status: "begin" | ||
}); | ||
|
||
asPaused(gThreadClient, _getListeners).then(listeners => { | ||
// Notify that event listeners were fetched and shown in the view, | ||
// and callback to resume the active thread if necessary. | ||
window.emit(EVENTS.EVENT_LISTENERS_FETCHED); | ||
|
||
dispatch({ | ||
type: constants.FETCH_EVENT_LISTENERS, | ||
status: "done", | ||
listeners: listeners | ||
}); | ||
}); | ||
}); | ||
}; | ||
} | ||
|
||
const _getListeners = Task.async(function*() { | ||
const response = yield gThreadClient.eventListeners(); | ||
|
||
// Make sure all the listeners are sorted by the event type, since | ||
// they"re not guaranteed to be clustered together. | ||
response.listeners.sort((a, b) => a.type > b.type ? 1 : -1); | ||
|
||
// Add all the listeners in the debugger view event linsteners container. | ||
let fetchedDefinitions = new Map(); | ||
let listeners = []; | ||
for (let listener of response.listeners) { | ||
let definitionSite; | ||
if (fetchedDefinitions.has(listener.function.actor)) { | ||
definitionSite = fetchedDefinitions.get(listener.function.actor); | ||
} else if (listener.function.class == "Function") { | ||
definitionSite = yield _getDefinitionSite(listener.function); | ||
if (!definitionSite) { | ||
// We don"t know where this listener comes from so don"t show it in | ||
// the UI as breaking on it doesn"t work (bug 942899). | ||
continue; | ||
} | ||
|
||
fetchedDefinitions.set(listener.function.actor, definitionSite); | ||
} | ||
listener.function.url = definitionSite; | ||
listeners.push(listener); | ||
} | ||
fetchedDefinitions.clear(); | ||
|
||
return listeners; | ||
}); | ||
|
||
const _getDefinitionSite = Task.async(function*(aFunction) { | ||
const grip = gThreadClient.pauseGrip(aFunction); | ||
let response; | ||
|
||
try { | ||
response = yield grip.getDefinitionSite(); | ||
} | ||
catch(e) { | ||
// Don't make this error fatal, because it would break the entire events pane. | ||
reportException("_getDefinitionSite", e); | ||
return null; | ||
} | ||
|
||
return response.source.url; | ||
}); | ||
|
||
function updateEventBreakpoints(eventNames) { | ||
return dispatch => { | ||
setNamedTimeout("event-breakpoints-update", 0, () => { | ||
gThreadClient.pauseOnDOMEvents(eventNames, function() { | ||
// Notify that event breakpoints were added/removed on the server. | ||
window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED); | ||
|
||
dispatch({ | ||
type: constants.UPDATE_EVENT_BREAKPOINTS, | ||
eventNames: eventNames | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
module.exports = { updateEventBreakpoints, fetchEventListeners }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const breakpoints = require("breakpoints"); | ||
const eventListeners = require("eventListeners"); | ||
const sources = require("sources"); | ||
|
||
module.exports = { breakpoints, eventListeners, sources }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# vim: set filetype=python: | ||
# 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/. | ||
|
||
DevToolsModules( | ||
'breakpoints.js', | ||
'event-listeners.js', | ||
'sources.js' | ||
) |
Oops, something went wrong.