Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
jlongster committed Mar 23, 2016
0 parents commit 2d2100e
Show file tree
Hide file tree
Showing 29 changed files with 1,543 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
build
node_modules
12 changes: 12 additions & 0 deletions index.html
@@ -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>
24 changes: 24 additions & 0 deletions js/DevToolsUtils.js
@@ -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 };
182 changes: 182 additions & 0 deletions js/actions/breakpoints.js
@@ -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
}
116 changes: 116 additions & 0 deletions js/actions/event-listeners.js
@@ -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 };
5 changes: 5 additions & 0 deletions js/actions/index.js
@@ -0,0 +1,5 @@
const breakpoints = require("breakpoints");
const eventListeners = require("eventListeners");
const sources = require("sources");

module.exports = { breakpoints, eventListeners, sources };
10 changes: 10 additions & 0 deletions js/actions/moz.build
@@ -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'
)

0 comments on commit 2d2100e

Please sign in to comment.