mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
191
devtools/client/debugger/content/actions/breakpoints.js
Normal file
191
devtools/client/debugger/content/actions/breakpoints.js
Normal file
|
|
@ -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
|
||||
};
|
||||
118
devtools/client/debugger/content/actions/event-listeners.js
Normal file
118
devtools/client/debugger/content/actions/event-listeners.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* 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 { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
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 };
|
||||
10
devtools/client/debugger/content/actions/moz.build
Normal file
10
devtools/client/debugger/content/actions/moz.build
Normal file
|
|
@ -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'
|
||||
)
|
||||
280
devtools/client/debugger/content/actions/sources.js
Normal file
280
devtools/client/debugger/content/actions/sources.js
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
/* 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 Services = require("Services");
|
||||
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||
const { PROMISE, HISTOGRAM_ID } = require("devtools/client/shared/redux/middleware/promise");
|
||||
const { getSource, getSourceText } = require("../queries");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
|
||||
const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
|
||||
|
||||
function getSourceClient(source) {
|
||||
return gThreadClient.source(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the debugger client's unsolicited newSource notification.
|
||||
*/
|
||||
function newSource(source) {
|
||||
return dispatch => {
|
||||
// Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
|
||||
if (NEW_SOURCE_IGNORED_URLS.indexOf(source.url) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal that a new source has been added.
|
||||
window.emit(EVENTS.NEW_SOURCE);
|
||||
|
||||
return dispatch({
|
||||
type: constants.ADD_SOURCE,
|
||||
source: source
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function selectSource(source, opts) {
|
||||
return (dispatch, getState) => {
|
||||
if (!gThreadClient) {
|
||||
// No connection, do nothing. This happens when the debugger is
|
||||
// shut down too fast and it tries to display a default source.
|
||||
return;
|
||||
}
|
||||
|
||||
source = getSource(getState(), source.actor);
|
||||
|
||||
// Make sure to start a request to load the source text.
|
||||
dispatch(loadSourceText(source));
|
||||
|
||||
dispatch({
|
||||
type: constants.SELECT_SOURCE,
|
||||
source: source,
|
||||
opts: opts
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function loadSources() {
|
||||
return {
|
||||
type: constants.LOAD_SOURCES,
|
||||
[PROMISE]: Task.spawn(function* () {
|
||||
const response = yield gThreadClient.getSources();
|
||||
|
||||
// Top-level breakpoints may pause the entire loading process
|
||||
// because scripts are executed as they are loaded, so the
|
||||
// engine may pause in the middle of loading all the sources.
|
||||
// This is relatively harmless, as individual `newSource`
|
||||
// notifications are fired for each script and they will be
|
||||
// added to the UI through that.
|
||||
if (!response.sources) {
|
||||
dumpn(
|
||||
"Error getting sources, probably because a top-level " +
|
||||
"breakpoint was hit while executing them"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
|
||||
return response.sources.filter(source => {
|
||||
return NEW_SOURCE_IGNORED_URLS.indexOf(source.url) === -1;
|
||||
});
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the black boxed status of the given source.
|
||||
*
|
||||
* @param Object aSource
|
||||
* The source form.
|
||||
* @param bool aBlackBoxFlag
|
||||
* True to black box the source, false to un-black box it.
|
||||
* @returns Promise
|
||||
* A promize that resolves to [aSource, isBlackBoxed] or rejects to
|
||||
* [aSource, error].
|
||||
*/
|
||||
function blackbox(source, shouldBlackBox) {
|
||||
const client = getSourceClient(source);
|
||||
|
||||
return {
|
||||
type: constants.BLACKBOX,
|
||||
source: source,
|
||||
[PROMISE]: Task.spawn(function* () {
|
||||
yield shouldBlackBox ? client.blackBox() : client.unblackBox();
|
||||
return {
|
||||
isBlackBoxed: shouldBlackBox
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the pretty printing of a source's text. All subsequent calls to
|
||||
* |getText| will return the pretty-toggled text. Nothing will happen for
|
||||
* non-javascript files.
|
||||
*
|
||||
* @param Object aSource
|
||||
* The source form from the RDP.
|
||||
* @returns Promise
|
||||
* A promise that resolves to [aSource, prettyText] or rejects to
|
||||
* [aSource, error].
|
||||
*/
|
||||
function togglePrettyPrint(source) {
|
||||
return (dispatch, getState) => {
|
||||
const sourceClient = getSourceClient(source);
|
||||
const wantPretty = !source.isPrettyPrinted;
|
||||
|
||||
return dispatch({
|
||||
type: constants.TOGGLE_PRETTY_PRINT,
|
||||
source: source,
|
||||
[PROMISE]: Task.spawn(function* () {
|
||||
let response;
|
||||
|
||||
// Only attempt to pretty print JavaScript sources.
|
||||
const sourceText = getSourceText(getState(), source.actor);
|
||||
const contentType = sourceText ? sourceText.contentType : null;
|
||||
if (!SourceUtils.isJavaScript(source.url, contentType)) {
|
||||
throw new Error("Can't prettify non-javascript files.");
|
||||
}
|
||||
|
||||
if (wantPretty) {
|
||||
response = yield sourceClient.prettyPrint(Prefs.editorTabSize);
|
||||
}
|
||||
else {
|
||||
response = yield sourceClient.disablePrettyPrint();
|
||||
}
|
||||
|
||||
// Remove the cached source AST from the Parser, to avoid getting
|
||||
// wrong locations when searching for functions.
|
||||
DebuggerController.Parser.clearSource(source.url);
|
||||
|
||||
return {
|
||||
isPrettyPrinted: wantPretty,
|
||||
text: response.source,
|
||||
contentType: response.contentType
|
||||
};
|
||||
})
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function loadSourceText(source) {
|
||||
return (dispatch, getState) => {
|
||||
// Fetch the source text only once.
|
||||
let textInfo = getSourceText(getState(), source.actor);
|
||||
if (textInfo) {
|
||||
// It's already loaded or is loading
|
||||
return promise.resolve(textInfo);
|
||||
}
|
||||
|
||||
const sourceClient = getSourceClient(source);
|
||||
|
||||
return dispatch({
|
||||
type: constants.LOAD_SOURCE_TEXT,
|
||||
source: source,
|
||||
[PROMISE]: Task.spawn(function* () {
|
||||
let transportType = gClient.localTransport ? "_LOCAL" : "_REMOTE";
|
||||
let histogramId = "DEVTOOLS_DEBUGGER_DISPLAY_SOURCE" + transportType + "_MS";
|
||||
let histogram = Services.telemetry.getHistogramById(histogramId);
|
||||
let startTime = Date.now();
|
||||
|
||||
const response = yield sourceClient.source();
|
||||
|
||||
histogram.add(Date.now() - startTime);
|
||||
|
||||
// Automatically pretty print if enabled and the test is
|
||||
// detected to be "minified"
|
||||
if (Prefs.autoPrettyPrint &&
|
||||
!source.isPrettyPrinted &&
|
||||
SourceUtils.isMinified(source.actor, response.source)) {
|
||||
dispatch(togglePrettyPrint(source));
|
||||
}
|
||||
|
||||
return { text: response.source,
|
||||
contentType: response.contentType };
|
||||
})
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts fetching all the sources, silently.
|
||||
*
|
||||
* @param array aUrls
|
||||
* The urls for the sources to fetch. If fetching a source's text
|
||||
* takes too long, it will be discarded.
|
||||
* @return object
|
||||
* A promise that is resolved after source texts have been fetched.
|
||||
*/
|
||||
function getTextForSources(actors) {
|
||||
return (dispatch, getState) => {
|
||||
let deferred = promise.defer();
|
||||
let pending = new Set(actors);
|
||||
let fetched = [];
|
||||
|
||||
// Can't use promise.all, because if one fetch operation is rejected, then
|
||||
// everything is considered rejected, thus no other subsequent source will
|
||||
// be getting fetched. We don't want that. Something like Q's allSettled
|
||||
// would work like a charm here.
|
||||
|
||||
// Try to fetch as many sources as possible.
|
||||
for (let actor of actors) {
|
||||
let source = getSource(getState(), actor);
|
||||
dispatch(loadSourceText(source)).then(({ text, contentType }) => {
|
||||
onFetch([source, text, contentType]);
|
||||
}, err => {
|
||||
onError(source, err);
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(onTimeout, FETCH_SOURCE_RESPONSE_DELAY);
|
||||
|
||||
/* Called if fetching a source takes too long. */
|
||||
function onTimeout() {
|
||||
pending = new Set();
|
||||
maybeFinish();
|
||||
}
|
||||
|
||||
/* Called if fetching a source finishes successfully. */
|
||||
function onFetch([aSource, aText, aContentType]) {
|
||||
// If fetching the source has previously timed out, discard it this time.
|
||||
if (!pending.has(aSource.actor)) {
|
||||
return;
|
||||
}
|
||||
pending.delete(aSource.actor);
|
||||
fetched.push([aSource.actor, aText, aContentType]);
|
||||
maybeFinish();
|
||||
}
|
||||
|
||||
/* Called if fetching a source failed because of an error. */
|
||||
function onError([aSource, aError]) {
|
||||
pending.delete(aSource.actor);
|
||||
maybeFinish();
|
||||
}
|
||||
|
||||
/* Called every time something interesting happens while fetching sources. */
|
||||
function maybeFinish() {
|
||||
if (pending.size == 0) {
|
||||
// Sort the fetched sources alphabetically by their url.
|
||||
deferred.resolve(fetched.sort(([aFirst], [aSecond]) => aFirst > aSecond));
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
newSource,
|
||||
selectSource,
|
||||
loadSources,
|
||||
blackbox,
|
||||
togglePrettyPrint,
|
||||
loadSourceText,
|
||||
getTextForSources
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue