mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-09 04:25:38 +00:00
Bug 1549999 - Use notification infrastructure to implement DOM event breakpoints. r=davidwalsh
Differential Revision: https://phabricator.services.mozilla.com/D31002 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
9bedd32999
commit
bf92dc5ae0
@ -24,7 +24,7 @@ async function updateBreakpoints(dispatch, client, newEvents: string[]) {
|
||||
active: newEvents,
|
||||
};
|
||||
|
||||
client.setEventListenerBreakpoints(newEvents);
|
||||
await client.setEventListenerBreakpoints(newEvents);
|
||||
}
|
||||
|
||||
async function updateExpanded(dispatch, newExpanded: string[]) {
|
||||
@ -46,7 +46,7 @@ export function addEventListenerBreakpoints(eventsToAdd: string[]) {
|
||||
|
||||
const newEvents = uniq([...eventsToAdd, ...activeListenerBreakpoints]);
|
||||
|
||||
updateBreakpoints(dispatch, client, newEvents);
|
||||
await updateBreakpoints(dispatch, client, newEvents);
|
||||
};
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ export function removeEventListenerBreakpoints(eventsToRemove: string[]) {
|
||||
event => !eventsToRemove.includes(event)
|
||||
);
|
||||
|
||||
updateBreakpoints(dispatch, client, newEvents);
|
||||
await updateBreakpoints(dispatch, client, newEvents);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,11 @@ export async function onConnect(connection: any, actions: Object) {
|
||||
});
|
||||
|
||||
// Retrieve possible event listener breakpoints
|
||||
actions.getEventListenerBreakpointTypes();
|
||||
actions.getEventListenerBreakpointTypes().catch(e => console.error(e));
|
||||
|
||||
// Initialize the event breakpoints on the thread up front so that
|
||||
// they are active once attached.
|
||||
actions.addEventListenerBreakpoints([]).catch(e => console.error(e));
|
||||
|
||||
// In Firefox, we need to initially request all of the sources. This
|
||||
// usually fires off individual `newSource` notifications as the
|
||||
|
@ -35,7 +35,10 @@ import type {
|
||||
SourcesPacket,
|
||||
} from "./types";
|
||||
|
||||
import type { EventListenerCategoryList } from "../../actions/types";
|
||||
import type {
|
||||
EventListenerCategoryList,
|
||||
EventListenerActiveList,
|
||||
} from "../../actions/types";
|
||||
|
||||
let workerClients: Object;
|
||||
let threadClient: ThreadClient;
|
||||
@ -43,6 +46,7 @@ let tabTarget: TabTarget;
|
||||
let debuggerClient: DebuggerClient;
|
||||
let sourceActors: { [ActorId]: SourceId };
|
||||
let breakpoints: { [string]: Object };
|
||||
let eventBreakpoints: ?EventListenerActiveList;
|
||||
let supportsWasm: boolean;
|
||||
|
||||
let shouldWaitForWorkers = false;
|
||||
@ -363,6 +367,8 @@ function interrupt(thread: string): Promise<*> {
|
||||
}
|
||||
|
||||
async function setEventListenerBreakpoints(ids: string[]) {
|
||||
eventBreakpoints = ids;
|
||||
|
||||
await threadClient.setActiveEventBreakpoints(ids);
|
||||
await forEachWorkerThread(thread => thread.setActiveEventBreakpoints(ids));
|
||||
}
|
||||
@ -371,8 +377,7 @@ async function setEventListenerBreakpoints(ids: string[]) {
|
||||
async function getEventListenerBreakpointTypes(): Promise<
|
||||
EventListenerCategoryList
|
||||
> {
|
||||
const { value } = await threadClient.getAvailableEventBreakpoints();
|
||||
return value;
|
||||
return threadClient.getAvailableEventBreakpoints();
|
||||
}
|
||||
|
||||
function pauseGrip(thread: string, func: Function): ObjectClient {
|
||||
@ -406,6 +411,7 @@ async function fetchWorkers(): Promise<Worker[]> {
|
||||
if (features.windowlessWorkers) {
|
||||
const options = {
|
||||
breakpoints,
|
||||
eventBreakpoints,
|
||||
observeAsmJS: true,
|
||||
};
|
||||
|
||||
|
@ -368,9 +368,7 @@ export type ThreadClient = {
|
||||
request: (payload: Object) => Promise<*>,
|
||||
url: string,
|
||||
setActiveEventBreakpoints: (string[]) => void,
|
||||
getAvailableEventBreakpoints: () => Promise<{|
|
||||
value: EventListenerCategoryList,
|
||||
|}>,
|
||||
getAvailableEventBreakpoints: () => Promise<EventListenerCategoryList>,
|
||||
skipBreakpoints: boolean => Promise<{| skip: boolean |}>,
|
||||
};
|
||||
|
||||
|
@ -91,7 +91,14 @@ class EventListeners extends Component<Props> {
|
||||
<input
|
||||
type="checkbox"
|
||||
value={category.name}
|
||||
onChange={e => this.onCategoryClick(category, e.target.checked)}
|
||||
onChange={e => {
|
||||
this.onCategoryClick(
|
||||
category,
|
||||
// Clicking an indeterminate checkbox should always have the
|
||||
// effect of disabling any selected items.
|
||||
indeterminate ? false : e.target.checked
|
||||
);
|
||||
}}
|
||||
checked={checked}
|
||||
ref={el => el && (el.indeterminate = indeterminate)}
|
||||
/>
|
||||
|
@ -46,19 +46,19 @@ function update(
|
||||
}
|
||||
|
||||
export function getActiveEventListeners(state: State): EventListenerActiveList {
|
||||
return state.eventListenerBreakpoints.active;
|
||||
return state.eventListenerBreakpoints.active || [];
|
||||
}
|
||||
|
||||
export function getEventListenerBreakpointTypes(
|
||||
state: State
|
||||
): EventListenerCategoryList {
|
||||
return state.eventListenerBreakpoints.categories;
|
||||
return state.eventListenerBreakpoints.categories || [];
|
||||
}
|
||||
|
||||
export function getEventListenerExpanded(
|
||||
state: State
|
||||
): EventListenerExpandedList {
|
||||
return state.eventListenerBreakpoints.expanded;
|
||||
return state.eventListenerBreakpoints.expanded || [];
|
||||
}
|
||||
|
||||
export default update;
|
||||
|
@ -16,6 +16,7 @@ const reasons = {
|
||||
exception: "whyPaused.exception",
|
||||
resumeLimit: "whyPaused.resumeLimit",
|
||||
breakpointConditionThrown: "whyPaused.breakpointConditionThrown",
|
||||
eventBreakpoint: "whyPaused.eventBreakpoint",
|
||||
|
||||
// V8
|
||||
DOM: "whyPaused.breakpoint",
|
||||
|
@ -593,6 +593,8 @@ support-files =
|
||||
examples/html-breakpoints-slow.js
|
||||
examples/sjs_slow-load.sjs
|
||||
examples/fetch.js
|
||||
examples/doc-event-breakpoints.html
|
||||
examples/event-breakpoints.js
|
||||
examples/doc-xhr.html
|
||||
examples/doc-xhr-run-to-completion.html
|
||||
examples/doc-scroll-run-to-completion.html
|
||||
@ -806,6 +808,7 @@ skip-if = true
|
||||
[browser_dbg-worker-scopes.js]
|
||||
skip-if = (os == 'linux' && debug) || ccov #Bug 1456013
|
||||
[browser_dbg-event-handler.js]
|
||||
[browser_dbg-event-breakpoints.js]
|
||||
[browser_dbg-eval-throw.js]
|
||||
[browser_dbg-sourceURL-breakpoint.js]
|
||||
[browser_dbg-old-breakpoint.js]
|
||||
|
@ -0,0 +1,48 @@
|
||||
/* 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/>. */
|
||||
|
||||
function assertPauseLocation(dbg, line) {
|
||||
const { location } = dbg.selectors.getVisibleSelectedFrame();
|
||||
|
||||
const source = findSource(dbg, "event-breakpoints.js");
|
||||
|
||||
is(location.sourceId, source.id, `correct sourceId`);
|
||||
is(location.line, line, `correct line`);
|
||||
|
||||
assertPausedLocation(dbg);
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
await pushPref("devtools.debugger.features.event-listeners-breakpoints", true);
|
||||
|
||||
const dbg = await initDebugger("doc-event-breakpoints.html", "event-breakpoints");
|
||||
await selectSource(dbg, "event-breakpoints");
|
||||
await waitForSelectedSource(dbg, "event-breakpoints");
|
||||
|
||||
await dbg.actions.addEventListenerBreakpoints([
|
||||
"event.mouse.click",
|
||||
"event.xhr.load",
|
||||
"timer.timeout.set",
|
||||
"timer.timeout.fire",
|
||||
]);
|
||||
|
||||
invokeInTab("clickHandler");
|
||||
await waitForPaused(dbg);
|
||||
assertPauseLocation(dbg, 12);
|
||||
await resume(dbg);
|
||||
|
||||
invokeInTab("xhrHandler");
|
||||
await waitForPaused(dbg);
|
||||
assertPauseLocation(dbg, 20);
|
||||
await resume(dbg);
|
||||
|
||||
invokeInTab("timerHandler");
|
||||
await waitForPaused(dbg);
|
||||
assertPauseLocation(dbg, 27);
|
||||
await resume(dbg);
|
||||
|
||||
await waitForPaused(dbg);
|
||||
assertPauseLocation(dbg, 29);
|
||||
await resume(dbg);
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
<!-- 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/. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger event bp test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button id="click-button">Run Click Handler</button>
|
||||
<button id="xhr-button">Run XHR Handler</button>
|
||||
<button id="timer-button">Run Timer Handler</button>
|
||||
<div id="click-target" style="margin: 50px; background-color: green;">
|
||||
Click Target
|
||||
</div>
|
||||
<script src="event-breakpoints.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,34 @@
|
||||
/* 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/>. */
|
||||
|
||||
document.getElementById("click-button").onmousedown = clickHandler;
|
||||
function clickHandler() {
|
||||
document.getElementById("click-target").click();
|
||||
}
|
||||
|
||||
document.getElementById("click-target").onclick = clickTargetClicked;
|
||||
function clickTargetClicked() {
|
||||
console.log("clicked");
|
||||
}
|
||||
|
||||
document.getElementById("xhr-button").onmousedown = xhrHandler;
|
||||
function xhrHandler() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "doc-event-breakpoints.html", true);
|
||||
xhr.onload = function(){
|
||||
console.log("xhr load");
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
document.getElementById("timer-button").onmousedown = timerHandler;
|
||||
function timerHandler() {
|
||||
setTimeout(
|
||||
() => {
|
||||
console.log("timer callback");
|
||||
},
|
||||
50
|
||||
);
|
||||
console.log("timer set");
|
||||
}
|
@ -983,6 +983,11 @@ whyPaused.debuggerStatement=Paused on debugger statement
|
||||
# in a info block explaining how the debugger is currently paused on a breakpoint
|
||||
whyPaused.breakpoint=Paused on breakpoint
|
||||
|
||||
# LOCALIZATION NOTE (whyPaused.eventBreakpoint): The text that is displayed
|
||||
# in a info block explaining how the debugger is currently paused on an event
|
||||
# breakpoint.
|
||||
whyPaused.eventBreakpoint=Paused on event breakpoint
|
||||
|
||||
# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed
|
||||
# in a info block explaining how the debugger is currently paused on an exception
|
||||
whyPaused.exception=Paused on exception
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const DebuggerNotificationObserver = require("DebuggerNotificationObserver");
|
||||
const Services = require("Services");
|
||||
const { Cr, Ci } = require("chrome");
|
||||
const { ActorPool } = require("devtools/server/actors/common");
|
||||
@ -16,6 +17,8 @@ const { assert, dumpn } = DevToolsUtils;
|
||||
const { threadSpec } = require("devtools/shared/specs/thread");
|
||||
const {
|
||||
getAvailableEventBreakpoints,
|
||||
eventBreakpointForNotification,
|
||||
makeEventBreakpointMessage,
|
||||
} = require("devtools/server/actors/utils/event-breakpoints");
|
||||
|
||||
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
|
||||
@ -61,7 +64,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this._scripts = null;
|
||||
this._xhrBreakpoints = [];
|
||||
this._observingNetwork = false;
|
||||
this._eventBreakpoints = [];
|
||||
this._activeEventBreakpoints = new Set();
|
||||
this._activeEventPause = null;
|
||||
|
||||
this._priorPause = null;
|
||||
|
||||
@ -94,6 +98,10 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this.objectGrip = this.objectGrip.bind(this);
|
||||
this.pauseObjectGrip = this.pauseObjectGrip.bind(this);
|
||||
this._onOpeningRequest = this._onOpeningRequest.bind(this);
|
||||
this._onNewDebuggee = this._onNewDebuggee.bind(this);
|
||||
this._eventBreakpointListener = this._eventBreakpointListener.bind(this);
|
||||
|
||||
this._debuggerNotificationObserver = new DebuggerNotificationObserver();
|
||||
|
||||
if (Services.obs) {
|
||||
// Set a wrappedJSObject property so |this| can be sent via the observer svc
|
||||
@ -112,6 +120,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this._dbg.uncaughtExceptionHook = this.uncaughtExceptionHook;
|
||||
this._dbg.onDebuggerStatement = this.onDebuggerStatement;
|
||||
this._dbg.onNewScript = this.onNewScript;
|
||||
this._dbg.onNewDebuggee = this._onNewDebuggee;
|
||||
if (this._dbg.replaying) {
|
||||
this._dbg.replayingOnForcedPause = this.replayingOnForcedPause.bind(this);
|
||||
const sendProgress = throttle((recording, executionPoint) => {
|
||||
@ -223,6 +232,16 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this._xhrBreakpoints = [];
|
||||
this._updateNetworkObserver();
|
||||
|
||||
this._activeEventBreakpoints = new Set();
|
||||
this._debuggerNotificationObserver.removeListener(
|
||||
this._eventBreakpointListener);
|
||||
|
||||
for (const global of this.dbg.getDebuggees()) {
|
||||
try {
|
||||
this._debuggerNotificationObserver.disconnect(global);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
this.sources.off("newSource", this.onNewSourceEvent);
|
||||
this.clearDebuggees();
|
||||
this.conn.removeActorPool(this._threadLifetimePool);
|
||||
@ -285,6 +304,9 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
if (options.breakpoints) {
|
||||
this._setBreakpointsOnAttach(options.breakpoints);
|
||||
}
|
||||
if (options.eventBreakpoints) {
|
||||
this.setActiveEventBreakpoints(options.eventBreakpoints);
|
||||
}
|
||||
|
||||
this.dbg.addDebuggees();
|
||||
this.dbg.enabled = true;
|
||||
@ -400,10 +422,24 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
return getAvailableEventBreakpoints();
|
||||
},
|
||||
getActiveEventBreakpoints: function() {
|
||||
return this._eventBreakpoints;
|
||||
return Array.from(this._activeEventBreakpoints);
|
||||
},
|
||||
setActiveEventBreakpoints: function(ids) {
|
||||
this._eventBreakpoints = ids;
|
||||
this._activeEventBreakpoints = new Set(ids);
|
||||
|
||||
if (this._activeEventBreakpoints.size === 0) {
|
||||
this._debuggerNotificationObserver.removeListener(
|
||||
this._eventBreakpointListener);
|
||||
} else {
|
||||
this._debuggerNotificationObserver.addListener(
|
||||
this._eventBreakpointListener);
|
||||
}
|
||||
},
|
||||
|
||||
_onNewDebuggee(global) {
|
||||
try {
|
||||
this._debuggerNotificationObserver.connect(global);
|
||||
} catch (e) { }
|
||||
},
|
||||
|
||||
_updateNetworkObserver() {
|
||||
@ -513,6 +549,81 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
return {};
|
||||
},
|
||||
|
||||
_eventBreakpointListener(notification) {
|
||||
if (this._state === "paused" || this._state === "detached") {
|
||||
return;
|
||||
}
|
||||
|
||||
const eventBreakpoint =
|
||||
eventBreakpointForNotification(this.dbg, notification);
|
||||
|
||||
if (!this._activeEventBreakpoints.has(eventBreakpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (notification.phase === "pre" && !this._activeEventPause) {
|
||||
this._activeEventPause = this._captureDebuggerHooks();
|
||||
|
||||
this.dbg.onEnterFrame =
|
||||
this._makeEventBreakpointEnterFrame(eventBreakpoint);
|
||||
} else if (notification.phase === "post" && this._activeEventPause) {
|
||||
this._restoreDebuggerHooks(this._activeEventPause);
|
||||
this._activeEventPause = null;
|
||||
} else if (!notification.phase && !this._activeEventPause) {
|
||||
const frame = this.dbg.getNewestFrame();
|
||||
if (frame) {
|
||||
const { sourceActor } = this.sources.getFrameLocation(frame);
|
||||
const url = sourceActor.url;
|
||||
if (this.sources.isBlackBoxed(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pauseAndRespondEventBreakpoint(frame, eventBreakpoint);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_makeEventBreakpointEnterFrame(eventBreakpoint) {
|
||||
return frame => {
|
||||
const { sourceActor } = this.sources.getFrameLocation(frame);
|
||||
const url = sourceActor.url;
|
||||
if (this.sources.isBlackBoxed(url)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this._restoreDebuggerHooks(this._activeEventPause);
|
||||
this._activeEventPause = null;
|
||||
|
||||
return this._pauseAndRespondEventBreakpoint(frame, eventBreakpoint);
|
||||
};
|
||||
},
|
||||
|
||||
_pauseAndRespondEventBreakpoint(frame, eventBreakpoint) {
|
||||
if (this.skipBreakpoints) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this._pauseAndRespond(frame, {
|
||||
type: "eventBreakpoint",
|
||||
breakpoint: eventBreakpoint,
|
||||
message: makeEventBreakpointMessage(eventBreakpoint),
|
||||
});
|
||||
},
|
||||
|
||||
_captureDebuggerHooks() {
|
||||
return {
|
||||
onEnterFrame: this.dbg.onEnterFrame,
|
||||
onStep: this.dbg.onStep,
|
||||
onPop: this.dbg.onPop,
|
||||
};
|
||||
},
|
||||
|
||||
_restoreDebuggerHooks(hooks) {
|
||||
this.dbg.onEnterFrame = hooks.onEnterFrame;
|
||||
this.dbg.onStep = hooks.onStep;
|
||||
this.dbg.onPop = hooks.onPop;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pause the debuggee, by entering a nested event loop, and return a 'paused'
|
||||
* packet to the client.
|
||||
@ -575,7 +686,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
return this._parentClosed ? null : undefined;
|
||||
},
|
||||
|
||||
_makeOnEnterFrame: function({ thread, pauseAndRespond }) {
|
||||
_makeOnEnterFrame: function({ pauseAndRespond }) {
|
||||
return frame => {
|
||||
const { sourceActor } = this.sources.getFrameLocation(frame);
|
||||
|
||||
@ -590,13 +701,13 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
}
|
||||
|
||||
// Continue forward until we get to a valid step target.
|
||||
const { onStep, onPop } = thread._makeSteppingHooks(
|
||||
const { onStep, onPop } = this._makeSteppingHooks(
|
||||
null, "next", false, null
|
||||
);
|
||||
|
||||
if (thread.dbg.replaying) {
|
||||
if (this.dbg.replaying) {
|
||||
const offsets =
|
||||
thread._findReplayingStepOffsets(null, frame,
|
||||
this._findReplayingStepOffsets(null, frame,
|
||||
/* rewinding = */ false);
|
||||
frame.setReplayingOnStep(onStep, offsets);
|
||||
} else {
|
||||
@ -608,7 +719,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
};
|
||||
},
|
||||
|
||||
_makeOnPop: function({ thread, pauseAndRespond, startLocation, steppingType }) {
|
||||
_makeOnPop: function({ pauseAndRespond, startLocation, steppingType }) {
|
||||
const thread = this;
|
||||
const result = function(completion) {
|
||||
// onPop is called with 'this' set to the current frame.
|
||||
const location = thread.sources.getFrameLocation(this);
|
||||
@ -728,8 +840,9 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
return script.getOffsetMetadata(offset).isStepStart;
|
||||
},
|
||||
|
||||
_makeOnStep: function({ thread, pauseAndRespond, startFrame,
|
||||
_makeOnStep: function({ pauseAndRespond, startFrame,
|
||||
startLocation, steppingType, completion, rewinding }) {
|
||||
const thread = this;
|
||||
return function() {
|
||||
// onStep is called with 'this' set to the current frame.
|
||||
|
||||
@ -828,7 +941,6 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
{ type: "resumeLimit" },
|
||||
onPacket
|
||||
),
|
||||
thread: this,
|
||||
startFrame: this.youngestFrame,
|
||||
startLocation: startLocation,
|
||||
steppingType: steppingType,
|
||||
|
@ -6,6 +6,401 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
function generalEvent(groupID, eventType) {
|
||||
return {
|
||||
id: `event.${groupID}.${eventType}`,
|
||||
type: "event",
|
||||
name: eventType,
|
||||
message: `DOM '${eventType}' event`,
|
||||
eventType,
|
||||
filter: "general",
|
||||
};
|
||||
}
|
||||
function nodeEvent(groupID, eventType) {
|
||||
return {
|
||||
...generalEvent(groupID, eventType),
|
||||
filter: "node",
|
||||
};
|
||||
}
|
||||
function mediaNodeEvent(groupID, eventType) {
|
||||
return {
|
||||
...generalEvent(groupID, eventType),
|
||||
filter: "media",
|
||||
};
|
||||
}
|
||||
function globalEvent(groupID, eventType) {
|
||||
return {
|
||||
...generalEvent(groupID, eventType),
|
||||
message: `Global '${eventType}' event`,
|
||||
filter: "global",
|
||||
};
|
||||
}
|
||||
function xhrEvent(groupID, eventType) {
|
||||
return {
|
||||
...generalEvent(groupID, eventType),
|
||||
message: `XHR '${eventType}' event`,
|
||||
filter: "xhr",
|
||||
};
|
||||
}
|
||||
function workerEvent(eventType) {
|
||||
return {
|
||||
...generalEvent("worker", eventType),
|
||||
message: `Worker '${eventType}' event`,
|
||||
filter: "worker",
|
||||
};
|
||||
}
|
||||
|
||||
function timerEvent(type, operation, name, notificationType) {
|
||||
return {
|
||||
id: `timer.${type}.${operation}`,
|
||||
type: "simple",
|
||||
name,
|
||||
message: name,
|
||||
notificationType,
|
||||
};
|
||||
}
|
||||
|
||||
function animationEvent(operation, name, notificationType) {
|
||||
return {
|
||||
id: `animationframe.${operation}`,
|
||||
type: "simple",
|
||||
name,
|
||||
message: name,
|
||||
notificationType,
|
||||
};
|
||||
}
|
||||
|
||||
const AVAILABLE_BREAKPOINTS = [
|
||||
{
|
||||
name: "Animation",
|
||||
items: [
|
||||
animationEvent(
|
||||
"request",
|
||||
"Request Animation Frame",
|
||||
"requestAnimationFrame"
|
||||
),
|
||||
animationEvent(
|
||||
"cancel",
|
||||
"Cancel Animation Frame",
|
||||
"cancelAnimationFrame"
|
||||
),
|
||||
animationEvent(
|
||||
"fire",
|
||||
"Animation Frame fired",
|
||||
"requestAnimationFrameCallback"
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Clipboard",
|
||||
items: [
|
||||
generalEvent("clipboard", "copy"),
|
||||
generalEvent("clipboard", "cut"),
|
||||
generalEvent("clipboard", "paste"),
|
||||
generalEvent("clipboard", "beforecopy"),
|
||||
generalEvent("clipboard", "beforecut"),
|
||||
generalEvent("clipboard", "beforepaste"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Control",
|
||||
items: [
|
||||
generalEvent("control", "resize"),
|
||||
generalEvent("control", "scroll"),
|
||||
generalEvent("control", "zoom"),
|
||||
generalEvent("control", "focus"),
|
||||
generalEvent("control", "blur"),
|
||||
generalEvent("control", "select"),
|
||||
generalEvent("control", "change"),
|
||||
generalEvent("control", "submit"),
|
||||
generalEvent("control", "reset"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "DOM Mutation",
|
||||
items: [
|
||||
// Deprecated DOM events.
|
||||
nodeEvent("dom-mutation", "DOMActivate"),
|
||||
nodeEvent("dom-mutation", "DOMFocusIn"),
|
||||
nodeEvent("dom-mutation", "DOMFocusOut"),
|
||||
|
||||
// Standard DOM mutation events.
|
||||
nodeEvent("dom-mutation", "DOMAttrModified"),
|
||||
nodeEvent("dom-mutation", "DOMCharacterDataModified"),
|
||||
nodeEvent("dom-mutation", "DOMNodeInserted"),
|
||||
nodeEvent("dom-mutation", "DOMNodeInsertedIntoDocument"),
|
||||
nodeEvent("dom-mutation", "DOMNodeRemoved"),
|
||||
nodeEvent("dom-mutation", "DOMNodeRemovedIntoDocument"),
|
||||
nodeEvent("dom-mutation", "DOMSubtreeModified"),
|
||||
|
||||
// DOM load events.
|
||||
nodeEvent("dom-mutation", "DOMContentLoaded"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Device",
|
||||
items: [
|
||||
globalEvent("device", "deviceorientation"),
|
||||
globalEvent("device", "devicemotion"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Drag and Drop",
|
||||
items: [
|
||||
generalEvent("drag-and-drop", "drag"),
|
||||
generalEvent("drag-and-drop", "dragstart"),
|
||||
generalEvent("drag-and-drop", "dragend"),
|
||||
generalEvent("drag-and-drop", "dragenter"),
|
||||
generalEvent("drag-and-drop", "dragover"),
|
||||
generalEvent("drag-and-drop", "dragleave"),
|
||||
generalEvent("drag-and-drop", "drop"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Keyboard",
|
||||
items: [
|
||||
generalEvent("keyboard", "keydown"),
|
||||
generalEvent("keyboard", "keyup"),
|
||||
generalEvent("keyboard", "keypress"),
|
||||
generalEvent("keyboard", "input"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Load",
|
||||
items: [
|
||||
globalEvent("load", "load"),
|
||||
globalEvent("load", "beforeunload"),
|
||||
globalEvent("load", "unload"),
|
||||
globalEvent("load", "abort"),
|
||||
globalEvent("load", "error"),
|
||||
globalEvent("load", "hashchange"),
|
||||
globalEvent("load", "popstate"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Media",
|
||||
items: [
|
||||
mediaNodeEvent("media", "play"),
|
||||
mediaNodeEvent("media", "pause"),
|
||||
mediaNodeEvent("media", "playing"),
|
||||
mediaNodeEvent("media", "canplay"),
|
||||
mediaNodeEvent("media", "canplaythrough"),
|
||||
mediaNodeEvent("media", "seeking"),
|
||||
mediaNodeEvent("media", "seeked"),
|
||||
mediaNodeEvent("media", "timeupdate"),
|
||||
mediaNodeEvent("media", "ended"),
|
||||
mediaNodeEvent("media", "ratechange"),
|
||||
mediaNodeEvent("media", "durationchange"),
|
||||
mediaNodeEvent("media", "volumechange"),
|
||||
mediaNodeEvent("media", "loadstart"),
|
||||
mediaNodeEvent("media", "progress"),
|
||||
mediaNodeEvent("media", "suspend"),
|
||||
mediaNodeEvent("media", "abort"),
|
||||
mediaNodeEvent("media", "error"),
|
||||
mediaNodeEvent("media", "emptied"),
|
||||
mediaNodeEvent("media", "stalled"),
|
||||
mediaNodeEvent("media", "loadedmetadata"),
|
||||
mediaNodeEvent("media", "loadeddata"),
|
||||
mediaNodeEvent("media", "waiting"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Mouse",
|
||||
items: [
|
||||
generalEvent("mouse", "auxclick"),
|
||||
generalEvent("mouse", "click"),
|
||||
generalEvent("mouse", "dblclick"),
|
||||
generalEvent("mouse", "mousedown"),
|
||||
generalEvent("mouse", "mouseup"),
|
||||
generalEvent("mouse", "mouseover"),
|
||||
generalEvent("mouse", "mousemove"),
|
||||
generalEvent("mouse", "mouseout"),
|
||||
generalEvent("mouse", "mouseenter"),
|
||||
generalEvent("mouse", "mouseleave"),
|
||||
generalEvent("mouse", "mousewheel"),
|
||||
generalEvent("mouse", "wheel"),
|
||||
generalEvent("mouse", "contextmenu"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Pointer",
|
||||
items: [
|
||||
generalEvent("pointer", "pointerover"),
|
||||
generalEvent("pointer", "pointerout"),
|
||||
generalEvent("pointer", "pointerenter"),
|
||||
generalEvent("pointer", "pointerleave"),
|
||||
generalEvent("pointer", "pointerdown"),
|
||||
generalEvent("pointer", "pointerup"),
|
||||
generalEvent("pointer", "pointermove"),
|
||||
generalEvent("pointer", "pointercancel"),
|
||||
generalEvent("pointer", "gotpointercapture"),
|
||||
generalEvent("pointer", "lostpointercapture"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Timer",
|
||||
items: [
|
||||
timerEvent("timeout", "set", "setTimeout", "setTimeout"),
|
||||
timerEvent("timeout", "clear", "clearTimeout", "clearTimeout"),
|
||||
timerEvent("timeout", "fire", "setTimeout fired", "setTimeoutCallback"),
|
||||
timerEvent("interval", "set", "setInterval", "setInterval"),
|
||||
timerEvent("interval", "clear", "clearInterval", "clearInterval"),
|
||||
timerEvent(
|
||||
"interval",
|
||||
"fire",
|
||||
"setInterval fired",
|
||||
"setIntervalCallback"
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Touch",
|
||||
items: [
|
||||
generalEvent("touch", "touchstart"),
|
||||
generalEvent("touch", "touchmove"),
|
||||
generalEvent("touch", "touchend"),
|
||||
generalEvent("touch", "touchcancel"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Worker",
|
||||
items: [
|
||||
workerEvent("message"),
|
||||
workerEvent("messageerror"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XHR",
|
||||
items: [
|
||||
xhrEvent("xhr", "readystatechange"),
|
||||
xhrEvent("xhr", "load"),
|
||||
xhrEvent("xhr", "loadstart"),
|
||||
xhrEvent("xhr", "loadend"),
|
||||
xhrEvent("xhr", "abort"),
|
||||
xhrEvent("xhr", "error"),
|
||||
xhrEvent("xhr", "progress"),
|
||||
xhrEvent("xhr", "timeout"),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const FLAT_EVENTS = [];
|
||||
for (const category of AVAILABLE_BREAKPOINTS) {
|
||||
for (const event of category.items) {
|
||||
FLAT_EVENTS.push(event);
|
||||
}
|
||||
}
|
||||
const EVENTS_BY_ID = {};
|
||||
for (const event of FLAT_EVENTS) {
|
||||
if (EVENTS_BY_ID[event.id]) {
|
||||
throw new Error("Duplicate event ID detected: " + event.id);
|
||||
}
|
||||
EVENTS_BY_ID[event.id] = event;
|
||||
}
|
||||
|
||||
const SIMPLE_EVENTS = {};
|
||||
const DOM_EVENTS = {};
|
||||
for (const eventBP of FLAT_EVENTS) {
|
||||
if (eventBP.type === "simple") {
|
||||
const { notificationType } = eventBP;
|
||||
if (SIMPLE_EVENTS[notificationType]) {
|
||||
throw new Error("Duplicate simple event");
|
||||
}
|
||||
SIMPLE_EVENTS[notificationType] = eventBP.id;
|
||||
} else if (eventBP.type === "event") {
|
||||
const { eventType, filter } = eventBP;
|
||||
|
||||
let targetTypes;
|
||||
if (filter === "global") {
|
||||
targetTypes = ["global"];
|
||||
} else if (filter === "xhr") {
|
||||
targetTypes = ["xhr"];
|
||||
} else if (filter === "worker") {
|
||||
targetTypes = ["worker"];
|
||||
} else if (filter === "general") {
|
||||
targetTypes = ["global", "node"];
|
||||
} else if (filter === "node" || filter === "media") {
|
||||
targetTypes = ["node"];
|
||||
} else {
|
||||
throw new Error("Unexpected filter type");
|
||||
}
|
||||
|
||||
for (const targetType of targetTypes) {
|
||||
let byEventType = DOM_EVENTS[targetType];
|
||||
if (!byEventType) {
|
||||
byEventType = {};
|
||||
DOM_EVENTS[targetType] = byEventType;
|
||||
}
|
||||
|
||||
if (byEventType[eventType]) {
|
||||
throw new Error("Duplicate dom event: " + eventType);
|
||||
}
|
||||
byEventType[eventType] = eventBP.id;
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unknown type: " + eventBP.type);
|
||||
}
|
||||
}
|
||||
|
||||
exports.eventBreakpointForNotification = eventBreakpointForNotification;
|
||||
function eventBreakpointForNotification(dbg, notification) {
|
||||
const notificationType = notification.type;
|
||||
|
||||
if (notification.type === "domEvent") {
|
||||
const domEventNotification = DOM_EVENTS[notification.targetType];
|
||||
if (!domEventNotification) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The 'event' value is a cross-compartment wrapper for the DOM Event object.
|
||||
// While we could use that directly in the main thread as an Xray wrapper,
|
||||
// when debugging workers we can't, because it is an opaque wrapper.
|
||||
// To make things work, we have to always interact with the Event object via
|
||||
// the Debugger.Object interface.
|
||||
const evt = dbg
|
||||
.makeGlobalObjectReference(notification.global)
|
||||
.makeDebuggeeValue(notification.event);
|
||||
|
||||
const eventType = evt.getProperty("type").return;
|
||||
const id = domEventNotification[eventType];
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
const eventBreakpoint = EVENTS_BY_ID[id];
|
||||
|
||||
if (eventBreakpoint.filter === "media") {
|
||||
const currentTarget = evt.getProperty("currentTarget").return;
|
||||
if (!currentTarget) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeType = currentTarget.getProperty("nodeType").return;
|
||||
const namespaceURI = currentTarget.getProperty("namespaceURI").return;
|
||||
if (
|
||||
nodeType !== 1 /* ELEMENT_NODE */ ||
|
||||
namespaceURI !== "http://www.w3.org/1999/xhtml"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeName =
|
||||
currentTarget.getProperty("nodeName").return.toLowerCase();
|
||||
if (nodeName !== "audio" && nodeName !== "video") {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
return SIMPLE_EVENTS[notificationType] || null;
|
||||
}
|
||||
|
||||
exports.makeEventBreakpointMessage = makeEventBreakpointMessage;
|
||||
function makeEventBreakpointMessage(id) {
|
||||
return EVENTS_BY_ID[id].message;
|
||||
}
|
||||
|
||||
exports.getAvailableEventBreakpoints = getAvailableEventBreakpoints;
|
||||
function getAvailableEventBreakpoints() {
|
||||
const available = [];
|
||||
@ -14,196 +409,9 @@ function getAvailableEventBreakpoints() {
|
||||
name,
|
||||
events: items.map(item => ({
|
||||
id: item.id,
|
||||
name: item.eventType,
|
||||
name: item.name,
|
||||
})),
|
||||
});
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
function event(groupID, name, filter = "content") {
|
||||
return {
|
||||
id: `${groupID}.event.${name}`,
|
||||
type: "event",
|
||||
eventType: name,
|
||||
filter,
|
||||
};
|
||||
}
|
||||
|
||||
const AVAILABLE_BREAKPOINTS = [
|
||||
{
|
||||
name: "Clipboard",
|
||||
items: [
|
||||
event("clipboard", "copy"),
|
||||
event("clipboard", "cut"),
|
||||
event("clipboard", "paste"),
|
||||
event("clipboard", "beforecopy"),
|
||||
event("clipboard", "beforecut"),
|
||||
event("clipboard", "beforepaste"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Control",
|
||||
items: [
|
||||
event("control", "resize"),
|
||||
event("control", "scroll"),
|
||||
event("control", "zoom"),
|
||||
event("control", "focus"),
|
||||
event("control", "blur"),
|
||||
event("control", "select"),
|
||||
event("control", "change"),
|
||||
event("control", "submit"),
|
||||
event("control", "reset"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "DOM Mutation",
|
||||
items: [
|
||||
// Deprecated DOM events.
|
||||
event("dom-mutation", "DOMActivate"),
|
||||
event("dom-mutation", "DOMFocusIn"),
|
||||
event("dom-mutation", "DOMFocusOut"),
|
||||
|
||||
// Standard DOM mutation events.
|
||||
event("dom-mutation", "DOMAttrModified"),
|
||||
event("dom-mutation", "DOMCharacterDataModified"),
|
||||
event("dom-mutation", "DOMNodeInserted"),
|
||||
event("dom-mutation", "DOMNodeInsertedIntoDocument"),
|
||||
event("dom-mutation", "DOMNodeRemoved"),
|
||||
event("dom-mutation", "DOMNodeRemovedIntoDocument"),
|
||||
event("dom-mutation", "DOMSubtreeModified"),
|
||||
|
||||
// DOM load events.
|
||||
event("dom-mutation", "DOMContentLoaded"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Device",
|
||||
items: [
|
||||
event("device", "deviceorientation"),
|
||||
event("device", "devicemotion"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Drag and Drop",
|
||||
items: [
|
||||
event("drag-and-drop", "drag"),
|
||||
event("drag-and-drop", "dragstart"),
|
||||
event("drag-and-drop", "dragend"),
|
||||
event("drag-and-drop", "dragenter"),
|
||||
event("drag-and-drop", "dragover"),
|
||||
event("drag-and-drop", "dragleave"),
|
||||
event("drag-and-drop", "drop"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Keyboard",
|
||||
items: [
|
||||
event("keyboard", "keydown"),
|
||||
event("keyboard", "keyup"),
|
||||
event("keyboard", "keypress"),
|
||||
event("keyboard", "input"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Load",
|
||||
items: [
|
||||
event("load", "load", "global"),
|
||||
event("load", "beforeunload", "global"),
|
||||
event("load", "unload", "global"),
|
||||
event("load", "abort", "global"),
|
||||
event("load", "error", "global"),
|
||||
event("load", "hashchange", "global"),
|
||||
event("load", "popstate", "global"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Media",
|
||||
items: [
|
||||
event("media", "play", "media"),
|
||||
event("media", "pause", "media"),
|
||||
event("media", "playing", "media"),
|
||||
event("media", "canplay", "media"),
|
||||
event("media", "canplaythrough", "media"),
|
||||
event("media", "seeking", "media"),
|
||||
event("media", "seeked", "media"),
|
||||
event("media", "timeupdate", "media"),
|
||||
event("media", "ended", "media"),
|
||||
event("media", "ratechange", "media"),
|
||||
event("media", "durationchange", "media"),
|
||||
event("media", "volumechange", "media"),
|
||||
event("media", "loadstart", "media"),
|
||||
event("media", "progress", "media"),
|
||||
event("media", "suspend", "media"),
|
||||
event("media", "abort", "media"),
|
||||
event("media", "error", "media"),
|
||||
event("media", "emptied", "media"),
|
||||
event("media", "stalled", "media"),
|
||||
event("media", "loadedmetadata", "media"),
|
||||
event("media", "loadeddata", "media"),
|
||||
event("media", "waiting", "media"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Mouse",
|
||||
items: [
|
||||
event("mouse", "auxclick"),
|
||||
event("mouse", "click"),
|
||||
event("mouse", "dblclick"),
|
||||
event("mouse", "mousedown"),
|
||||
event("mouse", "mouseup"),
|
||||
event("mouse", "mouseover"),
|
||||
event("mouse", "mousemove"),
|
||||
event("mouse", "mouseout"),
|
||||
event("mouse", "mouseenter"),
|
||||
event("mouse", "mouseleave"),
|
||||
event("mouse", "mousewheel"),
|
||||
event("mouse", "wheel"),
|
||||
event("mouse", "contextmenu"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Pointer",
|
||||
items: [
|
||||
event("pointer", "pointerover"),
|
||||
event("pointer", "pointerout"),
|
||||
event("pointer", "pointerenter"),
|
||||
event("pointer", "pointerleave"),
|
||||
event("pointer", "pointerdown"),
|
||||
event("pointer", "pointerup"),
|
||||
event("pointer", "pointermove"),
|
||||
event("pointer", "pointercancel"),
|
||||
event("pointer", "gotpointercapture"),
|
||||
event("pointer", "lostpointercapture"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Touch",
|
||||
items: [
|
||||
event("touch", "touchstart"),
|
||||
event("touch", "touchmove"),
|
||||
event("touch", "touchend"),
|
||||
event("touch", "touchcancel"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Worker",
|
||||
items: [
|
||||
event("worker", "message", "global"),
|
||||
event("worker", "messageerror", "global"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XHR",
|
||||
items: [
|
||||
event("xhr", "readystatechange", "xhr"),
|
||||
event("xhr", "load", "xhr"),
|
||||
event("xhr", "loadstart", "xhr"),
|
||||
event("xhr", "loadend", "xhr"),
|
||||
event("xhr", "abort", "xhr"),
|
||||
event("xhr", "error", "xhr"),
|
||||
event("xhr", "progress", "xhr"),
|
||||
event("xhr", "timeout", "xhr"),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@ -64,15 +64,23 @@ module.exports = function makeDebugger({ findDebuggees, shouldAddNewGlobalAsDebu
|
||||
dbg.allowUnobservedAsmJS = true;
|
||||
dbg.uncaughtExceptionHook = reportDebuggerHookException;
|
||||
|
||||
function onNewDebuggee(global) {
|
||||
if (dbg.onNewDebuggee) {
|
||||
dbg.onNewDebuggee(global);
|
||||
}
|
||||
}
|
||||
|
||||
dbg.onNewGlobalObject = function(global) {
|
||||
if (shouldAddNewGlobalAsDebuggee(global)) {
|
||||
safeAddDebuggee(this, global);
|
||||
onNewDebuggee(global);
|
||||
}
|
||||
};
|
||||
|
||||
dbg.addDebuggees = function() {
|
||||
for (const global of findDebuggees(this)) {
|
||||
safeAddDebuggee(this, global);
|
||||
onNewDebuggee(global);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -23,6 +23,7 @@ const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
// Steal various globals only available in JSM scope (and not Sandbox one)
|
||||
const {
|
||||
console,
|
||||
DebuggerNotificationObserver,
|
||||
DOMPoint,
|
||||
DOMQuad,
|
||||
DOMRect,
|
||||
@ -229,6 +230,7 @@ function lazyRequireGetter(obj, property, module, destructure) {
|
||||
// List of pseudo modules exposed to all devtools modules.
|
||||
exports.modules = {
|
||||
ChromeUtils,
|
||||
DebuggerNotificationObserver,
|
||||
HeapSnapshot,
|
||||
promise,
|
||||
// Expose "chrome" Promise, which aren't related to any document
|
||||
|
@ -44,6 +44,7 @@ const UnsolicitedPauses = {
|
||||
breakpoint: "breakpoint",
|
||||
DOMEvent: "DOMEvent",
|
||||
watchpoint: "watchpoint",
|
||||
eventBreakpoint: "eventBreakpoint",
|
||||
exception: "exception",
|
||||
replayForcedPause: "replayForcedPause",
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global worker */
|
||||
/* global worker, DebuggerNotificationObserver */
|
||||
|
||||
// A CommonJS module loader that is designed to run inside a worker debugger.
|
||||
// We can't simply use the SDK module loader, because it relies heavily on
|
||||
@ -576,6 +576,7 @@ this.worker = new WorkerDebuggerLoader({
|
||||
"chrome": chrome,
|
||||
"xpcInspector": xpcInspector,
|
||||
"ChromeUtils": ChromeUtils,
|
||||
"DebuggerNotificationObserver": DebuggerNotificationObserver,
|
||||
},
|
||||
paths: {
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
|
Loading…
x
Reference in New Issue
Block a user