зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland a=merge on a CLOSED TREE
This commit is contained in:
Коммит
1abc23fe8d
|
@ -5,14 +5,9 @@
|
|||
// @flow
|
||||
|
||||
import { setupCommands, clientCommands } from "./firefox/commands";
|
||||
import {
|
||||
setupEvents,
|
||||
waitForSourceActorToBeRegisteredInStore,
|
||||
} from "./firefox/events";
|
||||
import { createPause, prepareSourcePayload } from "./firefox/create";
|
||||
import { setupEvents, clientEvents } from "./firefox/events";
|
||||
import { features, prefs } from "../utils/prefs";
|
||||
|
||||
import { recordEvent } from "../utils/telemetry";
|
||||
import { prepareSourcePayload } from "./firefox/create";
|
||||
|
||||
let actions;
|
||||
let targetList;
|
||||
|
@ -50,14 +45,9 @@ export async function onConnect(
|
|||
onTargetDestroyed
|
||||
);
|
||||
|
||||
// Use independant listeners for SOURCE and BREAKPOINT in order to ease
|
||||
// doing batching and notify about a set of SOURCE's in one redux action.
|
||||
await resourceWatcher.watchResources([resourceWatcher.TYPES.SOURCE], {
|
||||
onAvailable: onSourceAvailable,
|
||||
});
|
||||
await resourceWatcher.watchResources([resourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: onBreakpointAvailable,
|
||||
});
|
||||
}
|
||||
|
||||
export function onDisconnect() {
|
||||
|
@ -69,9 +59,6 @@ export function onDisconnect() {
|
|||
resourceWatcher.unwatchResources([resourceWatcher.TYPES.SOURCE], {
|
||||
onAvailable: onSourceAvailable,
|
||||
});
|
||||
resourceWatcher.unwatchResources([resourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: onBreakpointAvailable,
|
||||
});
|
||||
}
|
||||
|
||||
async function onTargetAvailable({
|
||||
|
@ -136,6 +123,7 @@ async function onTargetAvailable({
|
|||
targetFront.isWebExtension
|
||||
);
|
||||
|
||||
await clientCommands.checkIfAlreadyPaused();
|
||||
await actions.addTarget(targetFront);
|
||||
}
|
||||
|
||||
|
@ -158,25 +146,4 @@ async function onSourceAvailable(sources) {
|
|||
await actions.newGeneratedSources(frontendSources);
|
||||
}
|
||||
|
||||
async function onBreakpointAvailable(breakpoints) {
|
||||
for (const resource of breakpoints) {
|
||||
const threadFront = await resource.targetFront.getFront("thread");
|
||||
if (resource.state == "paused") {
|
||||
if (resource.frame) {
|
||||
// When reloading we might receive a pause event before the
|
||||
// top frame's source has arrived.
|
||||
await waitForSourceActorToBeRegisteredInStore(
|
||||
resource.frame.where.actor
|
||||
);
|
||||
}
|
||||
|
||||
const pause = createPause(threadFront.actor, resource);
|
||||
actions.paused(pause);
|
||||
recordEvent("pause", { reason: resource.why.type });
|
||||
} else if (resource.state == "resumed") {
|
||||
actions.resumed(threadFront.actorID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { clientCommands };
|
||||
export { clientCommands, clientEvents };
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
// @flow
|
||||
|
||||
import { createThread, createFrame } from "./create";
|
||||
import { waitForSourceActorToBeRegisteredInStore } from "./events";
|
||||
import {
|
||||
addThreadEventListeners,
|
||||
clientEvents,
|
||||
removeThreadEventListeners,
|
||||
ensureSourceActor,
|
||||
} from "./events";
|
||||
import { makePendingLocationId } from "../../utils/breakpoint";
|
||||
|
||||
// $FlowIgnore
|
||||
|
@ -347,9 +352,7 @@ async function getFrames(thread: string) {
|
|||
// Ensure that each frame has its source already available.
|
||||
// Because of throttling, the source may be available a bit late.
|
||||
await Promise.all(
|
||||
response.frames.map(frame =>
|
||||
waitForSourceActorToBeRegisteredInStore(frame.where.actor)
|
||||
)
|
||||
response.frames.map(frame => ensureSourceActor(frame.where.actor))
|
||||
);
|
||||
|
||||
return response.frames.map<?Frame>((frame, i) =>
|
||||
|
@ -436,6 +439,26 @@ async function toggleEventLogging(logEventBreakpoints: boolean) {
|
|||
);
|
||||
}
|
||||
|
||||
function getAllThreadFronts(): ThreadFront[] {
|
||||
const fronts = [currentThreadFront()];
|
||||
for (const { threadFront } of (Object.values(targets): any)) {
|
||||
fronts.push(threadFront);
|
||||
}
|
||||
return fronts;
|
||||
}
|
||||
|
||||
// Check if any of the targets were paused before we opened
|
||||
// the debugger. If one is paused. Fake a `pause` RDP event
|
||||
// by directly calling the client event listener.
|
||||
async function checkIfAlreadyPaused() {
|
||||
for (const threadFront of getAllThreadFronts()) {
|
||||
const pausedPacket = threadFront.getLastPausePacket();
|
||||
if (pausedPacket) {
|
||||
clientEvents.paused(threadFront, pausedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceForActor(actor: ActorId) {
|
||||
if (!sourceActors[actor]) {
|
||||
throw new Error(`Unknown source actor: ${actor}`);
|
||||
|
@ -447,11 +470,19 @@ async function addThread(targetFront: Target) {
|
|||
const threadActorID = targetFront.targetForm.threadActor;
|
||||
if (!targets[threadActorID]) {
|
||||
targets[threadActorID] = targetFront;
|
||||
addThreadEventListeners(targetFront.threadFront);
|
||||
}
|
||||
return createThread(threadActorID, targetFront);
|
||||
}
|
||||
|
||||
function removeThread(thread: Thread) {
|
||||
const targetFront = targets[thread.actor];
|
||||
if (targetFront) {
|
||||
// Note that if the target is already fully destroyed, threadFront will be
|
||||
// null, but event listeners will already have been removed.
|
||||
removeThreadEventListeners(targetFront.threadFront);
|
||||
}
|
||||
|
||||
delete targets[thread.actor];
|
||||
}
|
||||
|
||||
|
@ -538,6 +569,7 @@ const clientCommands = {
|
|||
getFrames,
|
||||
pauseOnExceptions,
|
||||
toggleEventLogging,
|
||||
checkIfAlreadyPaused,
|
||||
registerSourceActor,
|
||||
addThread,
|
||||
removeThread,
|
||||
|
|
|
@ -4,23 +4,103 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import type {
|
||||
PausedPacket,
|
||||
ThreadFront,
|
||||
Target,
|
||||
DevToolsClient,
|
||||
} from "./types";
|
||||
|
||||
import Actions from "../../actions";
|
||||
|
||||
import { createPause } from "./create";
|
||||
import sourceQueue from "../../utils/source-queue";
|
||||
import { recordEvent } from "../../utils/telemetry";
|
||||
import { prefs } from "../../utils/prefs";
|
||||
import { hasSourceActor } from "../../selectors";
|
||||
import { stringToSourceActorId } from "../../reducers/source-actors";
|
||||
|
||||
type Dependencies = {
|
||||
actions: typeof Actions,
|
||||
devToolsClient: DevToolsClient,
|
||||
store: any,
|
||||
};
|
||||
|
||||
let actions: typeof Actions;
|
||||
let isInterrupted: boolean;
|
||||
let threadFrontListeners: WeakMap<ThreadFront, Array<Function>>;
|
||||
let store: any;
|
||||
|
||||
function addThreadEventListeners(thread: ThreadFront): void {
|
||||
const removeListeners = [];
|
||||
Object.keys(clientEvents).forEach(eventName => {
|
||||
// EventEmitter.on returns a function that removes the event listener.
|
||||
removeListeners.push(
|
||||
thread.on(eventName, clientEvents[eventName].bind(null, thread))
|
||||
);
|
||||
});
|
||||
threadFrontListeners.set(thread, removeListeners);
|
||||
}
|
||||
|
||||
function removeThreadEventListeners(thread: ThreadFront): void {
|
||||
const removeListeners = threadFrontListeners.get(thread) || [];
|
||||
for (const removeListener of removeListeners) {
|
||||
removeListener();
|
||||
}
|
||||
}
|
||||
|
||||
function attachAllTargets(currentTarget: Target): boolean {
|
||||
return prefs.fission && currentTarget.isParentProcess;
|
||||
}
|
||||
|
||||
function setupEvents(dependencies: Dependencies): void {
|
||||
const actions = dependencies.actions;
|
||||
actions = dependencies.actions;
|
||||
sourceQueue.initialize(actions);
|
||||
store = dependencies.store;
|
||||
|
||||
threadFrontListeners = new WeakMap();
|
||||
}
|
||||
|
||||
async function paused(
|
||||
threadFront: ThreadFront,
|
||||
packet: PausedPacket
|
||||
): Promise<*> {
|
||||
// If paused by an explicit interrupt, which are generated by the
|
||||
// slow script dialog and internal events such as setting
|
||||
// breakpoints, ignore the event.
|
||||
const { why } = packet;
|
||||
if (why.type === "interrupted" && !packet.why.onNext) {
|
||||
isInterrupted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore attached events because they are not useful to the user.
|
||||
if (why.type == "alreadyPaused" || why.type == "attached") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.frame) {
|
||||
// When reloading we might receive a pause event before the
|
||||
// top frame's source has arrived.
|
||||
await ensureSourceActor(packet.frame.where.actor);
|
||||
}
|
||||
|
||||
const pause = createPause(threadFront.actor, packet);
|
||||
|
||||
actions.paused(pause);
|
||||
recordEvent("pause", { reason: why.type });
|
||||
}
|
||||
|
||||
function resumed(threadFront: ThreadFront): void {
|
||||
// NOTE: the client suppresses resumed events while interrupted
|
||||
// to prevent unintentional behavior.
|
||||
// see [client docs](../README.md#interrupted) for more information.
|
||||
if (isInterrupted) {
|
||||
isInterrupted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
actions.resumed(threadFront.actorID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +109,7 @@ function setupEvents(dependencies: Dependencies): void {
|
|||
* @param {String} sourceActor
|
||||
* Actor ID of the source to be waiting for.
|
||||
*/
|
||||
async function waitForSourceActorToBeRegisteredInStore(sourceActor: string) {
|
||||
async function ensureSourceActor(sourceActor: string) {
|
||||
const sourceActorId = stringToSourceActorId(sourceActor);
|
||||
if (!hasSourceActor(store.getState(), sourceActorId)) {
|
||||
await new Promise(resolve => {
|
||||
|
@ -50,4 +130,16 @@ async function waitForSourceActorToBeRegisteredInStore(sourceActor: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export { setupEvents, waitForSourceActorToBeRegisteredInStore };
|
||||
const clientEvents = {
|
||||
paused,
|
||||
resumed,
|
||||
};
|
||||
|
||||
export {
|
||||
setupEvents,
|
||||
clientEvents,
|
||||
addThreadEventListeners,
|
||||
removeThreadEventListeners,
|
||||
attachAllTargets,
|
||||
ensureSourceActor,
|
||||
};
|
||||
|
|
|
@ -33,23 +33,13 @@ add_task(async function() {
|
|||
// can expand an expression
|
||||
await toggleExpressionNode(dbg, 2);
|
||||
|
||||
is(findAllElements(dbg, "expressionNodes").length, 35);
|
||||
is(dbg.selectors.getExpressions(dbg.store.getState()).length, 2);
|
||||
|
||||
await deleteExpression(dbg, "foo");
|
||||
await deleteExpression(dbg, "location");
|
||||
is(findAllElements(dbg, "expressionNodes").length, 0);
|
||||
is(dbg.selectors.getExpressions(dbg.store.getState()).length, 0);
|
||||
|
||||
// Test expanding properties when the debuggee is active
|
||||
// Wait for full evaluation of the expressions in order to avoid having
|
||||
// mixed up code between the location being removed and the one being re-added
|
||||
const evaluated = waitForDispatch(dbg, "EVALUATE_EXPRESSIONS");
|
||||
await resume(dbg);
|
||||
await evaluated;
|
||||
|
||||
await addExpression(dbg, "location");
|
||||
is(dbg.selectors.getExpressions(dbg.store.getState()).length, 1);
|
||||
|
||||
is(findAllElements(dbg, "expressionNodes").length, 1);
|
||||
|
||||
|
|
|
@ -1851,9 +1851,9 @@ async function addExpression(dbg, input) {
|
|||
}
|
||||
findElementWithSelector(dbg, selectors.expressionInput).focus();
|
||||
type(dbg, input);
|
||||
const evaluated = waitForDispatch(dbg, "EVALUATE_EXPRESSION");
|
||||
pressKey(dbg, "Enter");
|
||||
await evaluated;
|
||||
|
||||
await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
|
||||
}
|
||||
|
||||
async function editExpression(dbg, input) {
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/* 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 {
|
||||
ResourceWatcher,
|
||||
} = require("devtools/shared/resources/resource-watcher");
|
||||
|
||||
module.exports = async function({ targetList, targetFront, onAvailable }) {
|
||||
const isBrowserToolbox = targetList.targetFront.isParentProcess;
|
||||
const isNonTopLevelFrameTarget =
|
||||
!targetFront.isTopLevel &&
|
||||
targetFront.targetType === targetList.TYPES.FRAME;
|
||||
|
||||
if (isBrowserToolbox && isNonTopLevelFrameTarget) {
|
||||
// In the BrowserToolbox, non-top-level frame targets are already
|
||||
// debugged via content-process targets.
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the thread actor to be attached, otherwise getFront(thread) will throw for worker targets
|
||||
// This is because worker target are still kind of descriptors and are only resolved into real target
|
||||
// after being attached. And the thread actor ID is only retrieved and available after being attached.
|
||||
await targetFront.onThreadAttached;
|
||||
|
||||
if (targetFront.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
const threadFront = await targetFront.getFront("thread");
|
||||
|
||||
let isInterrupted = false;
|
||||
const onPausedPacket = packet => {
|
||||
// If paused by an explicit interrupt, which are generated by the
|
||||
// slow script dialog and internal events such as setting
|
||||
// breakpoints, ignore the event.
|
||||
const { why } = packet;
|
||||
if (why.type === "interrupted" && !why.onNext) {
|
||||
isInterrupted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore attached events because they are not useful to the user.
|
||||
if (why.type == "alreadyPaused" || why.type == "attached") {
|
||||
return;
|
||||
}
|
||||
|
||||
onAvailable([
|
||||
{
|
||||
resourceType: ResourceWatcher.TYPES.BREAKPOINT,
|
||||
state: "paused",
|
||||
why,
|
||||
frame: packet.frame,
|
||||
},
|
||||
]);
|
||||
};
|
||||
threadFront.on("paused", onPausedPacket);
|
||||
|
||||
threadFront.on("resumed", packet => {
|
||||
// NOTE: the client suppresses resumed events while interrupted
|
||||
// to prevent unintentional behavior.
|
||||
// see [client docs](devtools/client/debugger/src/client/README.md#interrupted) for more information.
|
||||
if (isInterrupted) {
|
||||
isInterrupted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
onAvailable([
|
||||
{
|
||||
resourceType: ResourceWatcher.TYPES.BREAKPOINT,
|
||||
state: "resumed",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Notify about already paused thread
|
||||
const pausedPacket = threadFront.getLastPausePacket();
|
||||
if (pausedPacket) {
|
||||
onPausedPacket(pausedPacket);
|
||||
}
|
||||
};
|
|
@ -3,7 +3,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
"breakpoint.js",
|
||||
"cache-storage.js",
|
||||
"console-messages.js",
|
||||
"cookie.js",
|
||||
|
|
|
@ -847,7 +847,6 @@ ResourceWatcher.TYPES = ResourceWatcher.prototype.TYPES = {
|
|||
INDEXED_DB: "indexed-db",
|
||||
NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
|
||||
SOURCE: "source",
|
||||
BREAKPOINT: "breakpoint",
|
||||
};
|
||||
module.exports = { ResourceWatcher, TYPES: ResourceWatcher.TYPES };
|
||||
|
||||
|
@ -906,8 +905,6 @@ const LegacyListeners = {
|
|||
.NETWORK_EVENT_STACKTRACE]: require("devtools/shared/resources/legacy-listeners/network-event-stacktraces"),
|
||||
[ResourceWatcher.TYPES
|
||||
.SOURCE]: require("devtools/shared/resources/legacy-listeners/source"),
|
||||
[ResourceWatcher.TYPES
|
||||
.BREAKPOINT]: require("devtools/shared/resources/legacy-listeners/breakpoint"),
|
||||
};
|
||||
|
||||
// Optional transformers for each type of resource.
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test breakpoint document</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
"use strict";
|
||||
/* eslint-disable */
|
||||
function testFunction() {
|
||||
console.log("test Function ran");
|
||||
}
|
||||
function runDebuggerStatement() {
|
||||
debugger;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -6,7 +6,6 @@ support-files =
|
|||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||
!/devtools/client/shared/test/test-actor.js
|
||||
head.js
|
||||
breakpoint_document.html
|
||||
network_document.html
|
||||
early_console_document.html
|
||||
fission_document.html
|
||||
|
@ -27,7 +26,6 @@ support-files =
|
|||
worker-sources.js
|
||||
|
||||
[browser_browser_resources_console_messages.js]
|
||||
[browser_resources_breakpoints.js]
|
||||
[browser_resources_client_caching.js]
|
||||
[browser_resources_console_messages.js]
|
||||
[browser_resources_console_messages_workers.js]
|
||||
|
|
|
@ -1,371 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test the ResourceWatcher API around BREAKPOINT
|
||||
|
||||
const {
|
||||
ResourceWatcher,
|
||||
} = require("devtools/shared/resources/resource-watcher");
|
||||
|
||||
const BREAKPOINT_TEST_URL = URL_ROOT_SSL + "breakpoint_document.html";
|
||||
|
||||
add_task(async function() {
|
||||
await checkBreakpointBeforeWatchResources();
|
||||
|
||||
await checkBreakpointAfterWatchResources();
|
||||
|
||||
await checkRealBreakpoint();
|
||||
|
||||
await checkPauseOnException();
|
||||
});
|
||||
|
||||
async function checkBreakpointBeforeWatchResources() {
|
||||
info(
|
||||
"Check whether ResourceWatcher gets existing breakpoint, being hit before calling watchResources"
|
||||
);
|
||||
|
||||
const tab = await addTab(BREAKPOINT_TEST_URL);
|
||||
|
||||
const { client, resourceWatcher, targetList } = await initResourceWatcher(
|
||||
tab
|
||||
);
|
||||
|
||||
// Attach the thread actor before running the debugger statement,
|
||||
// so that it is correctly catched by the thread actor.
|
||||
info("Attach the top level target");
|
||||
await targetList.targetFront.attach();
|
||||
info("Attach the top level thread actor");
|
||||
const threadFront = await targetList.targetFront.attachThread();
|
||||
|
||||
info("Run the 'debugger' statement");
|
||||
// Note that we do not wait for the resolution of spawn as it will be paused
|
||||
ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.window.wrappedJSObject.runDebuggerStatement();
|
||||
});
|
||||
|
||||
info("Call watchResources");
|
||||
const availableResources = [];
|
||||
await resourceWatcher.watchResources([ResourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: resources => availableResources.push(...resources),
|
||||
});
|
||||
|
||||
is(
|
||||
availableResources.length,
|
||||
1,
|
||||
"Got the breakpoint related to the debugger statement"
|
||||
);
|
||||
const breakpoint = availableResources.pop();
|
||||
|
||||
assertPausedResource(breakpoint, {
|
||||
state: "paused",
|
||||
why: {
|
||||
type: "debuggerStatement",
|
||||
},
|
||||
frame: {
|
||||
type: "call",
|
||||
asyncCause: null,
|
||||
state: "on-stack",
|
||||
// this: object actor's form referring to `this` variable
|
||||
displayName: "runDebuggerStatement",
|
||||
// arguments: []
|
||||
where: {
|
||||
line: 17,
|
||||
column: 6,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await threadFront.resume();
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Wait until we receive the resumed event"
|
||||
);
|
||||
|
||||
const resumed = availableResources.pop();
|
||||
|
||||
assertResumedResource(resumed);
|
||||
|
||||
targetList.destroy();
|
||||
await client.close();
|
||||
}
|
||||
|
||||
async function checkBreakpointAfterWatchResources() {
|
||||
info(
|
||||
"Check whether ResourceWatcher gets breakpoint hit after calling watchResources"
|
||||
);
|
||||
|
||||
const tab = await addTab(BREAKPOINT_TEST_URL);
|
||||
|
||||
const { client, resourceWatcher, targetList } = await initResourceWatcher(
|
||||
tab
|
||||
);
|
||||
|
||||
info("Call watchResources");
|
||||
const availableResources = [];
|
||||
await resourceWatcher.watchResources([ResourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: resources => availableResources.push(...resources),
|
||||
});
|
||||
|
||||
is(
|
||||
availableResources.length,
|
||||
0,
|
||||
"Got no breakpoint when calling watchResources"
|
||||
);
|
||||
|
||||
info("Run the 'debugger' statement");
|
||||
// Note that we do not wait for the resolution of spawn as it will be paused
|
||||
ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.window.wrappedJSObject.runDebuggerStatement();
|
||||
});
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Got the breakpoint related to the debugger statement"
|
||||
);
|
||||
const breakpoint = availableResources.pop();
|
||||
|
||||
assertPausedResource(breakpoint, {
|
||||
state: "paused",
|
||||
why: {
|
||||
type: "debuggerStatement",
|
||||
},
|
||||
frame: {
|
||||
type: "call",
|
||||
asyncCause: null,
|
||||
state: "on-stack",
|
||||
// this: object actor's form referring to `this` variable
|
||||
displayName: "runDebuggerStatement",
|
||||
// arguments: []
|
||||
where: {
|
||||
line: 17,
|
||||
column: 6,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// treadFront is created and attached while calling watchResources
|
||||
const { threadFront } = targetList.targetFront;
|
||||
|
||||
await threadFront.resume();
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Wait until we receive the resumed event"
|
||||
);
|
||||
|
||||
const resumed = availableResources.pop();
|
||||
|
||||
assertResumedResource(resumed);
|
||||
|
||||
targetList.destroy();
|
||||
await client.close();
|
||||
}
|
||||
|
||||
async function checkRealBreakpoint() {
|
||||
info(
|
||||
"Check whether ResourceWatcher gets breakpoint set via the thread Front (instead of just debugger statements)"
|
||||
);
|
||||
|
||||
const tab = await addTab(BREAKPOINT_TEST_URL);
|
||||
|
||||
const { client, resourceWatcher, targetList } = await initResourceWatcher(
|
||||
tab
|
||||
);
|
||||
|
||||
info("Call watchResources");
|
||||
const availableResources = [];
|
||||
await resourceWatcher.watchResources([ResourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: resources => availableResources.push(...resources),
|
||||
});
|
||||
|
||||
is(
|
||||
availableResources.length,
|
||||
0,
|
||||
"Got no breakpoint when calling watchResources"
|
||||
);
|
||||
|
||||
// treadFront is created and attached while calling watchResources
|
||||
const { threadFront } = targetList.targetFront;
|
||||
|
||||
// We have to call `sources` request, otherwise the Thread Actor
|
||||
// doesn't start watching for sources, and ignore the setBreakpoint call
|
||||
// as it doesn't have any source registered.
|
||||
await threadFront.getSources();
|
||||
|
||||
await threadFront.setBreakpoint(
|
||||
{ sourceUrl: BREAKPOINT_TEST_URL, line: 14 },
|
||||
{}
|
||||
);
|
||||
|
||||
info("Run the test function where we set a breakpoint");
|
||||
// Note that we do not wait for the resolution of spawn as it will be paused
|
||||
ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.window.wrappedJSObject.testFunction();
|
||||
});
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Got the breakpoint related to the debugger statement"
|
||||
);
|
||||
const breakpoint = availableResources.pop();
|
||||
|
||||
assertPausedResource(breakpoint, {
|
||||
state: "paused",
|
||||
why: {
|
||||
type: "breakpoint",
|
||||
},
|
||||
frame: {
|
||||
type: "call",
|
||||
asyncCause: null,
|
||||
state: "on-stack",
|
||||
// this: object actor's form referring to `this` variable
|
||||
displayName: "testFunction",
|
||||
// arguments: []
|
||||
where: {
|
||||
line: 14,
|
||||
column: 6,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await threadFront.resume();
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Wait until we receive the resumed event"
|
||||
);
|
||||
|
||||
const resumed = availableResources.pop();
|
||||
|
||||
assertResumedResource(resumed);
|
||||
|
||||
targetList.destroy();
|
||||
await client.close();
|
||||
}
|
||||
|
||||
async function checkPauseOnException() {
|
||||
info(
|
||||
"Check whether ResourceWatcher gets breakpoint for exception (when explicitly requested)"
|
||||
);
|
||||
|
||||
const tab = await addTab(
|
||||
"data:text/html,<meta charset=utf8><script>a.b.c.d</script>"
|
||||
);
|
||||
|
||||
const { client, resourceWatcher, targetList } = await initResourceWatcher(
|
||||
tab
|
||||
);
|
||||
|
||||
info("Call watchResources");
|
||||
const availableResources = [];
|
||||
await resourceWatcher.watchResources([ResourceWatcher.TYPES.BREAKPOINT], {
|
||||
onAvailable: resources => availableResources.push(...resources),
|
||||
});
|
||||
|
||||
is(
|
||||
availableResources.length,
|
||||
0,
|
||||
"Got no breakpoint when calling watchResources"
|
||||
);
|
||||
|
||||
// treadFront is created and attached while calling watchResources
|
||||
const { threadFront } = targetList.targetFront;
|
||||
await threadFront.reconfigure({ pauseOnExceptions: true });
|
||||
|
||||
info("Reload the page, in order to trigger exception on load");
|
||||
const reloaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Got the breakpoint related to the debugger statement"
|
||||
);
|
||||
const breakpoint = availableResources.pop();
|
||||
|
||||
assertPausedResource(breakpoint, {
|
||||
state: "paused",
|
||||
why: {
|
||||
type: "exception",
|
||||
},
|
||||
frame: {
|
||||
type: "global",
|
||||
asyncCause: null,
|
||||
state: "on-stack",
|
||||
// this: object actor's form referring to `this` variable
|
||||
displayName: "(global)",
|
||||
// arguments: []
|
||||
where: {
|
||||
line: 1,
|
||||
column: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await threadFront.resume();
|
||||
info("Wait for page to finish reloading after resume");
|
||||
await reloaded;
|
||||
|
||||
await waitFor(
|
||||
() => availableResources.length == 1,
|
||||
"Wait until we receive the resumed event"
|
||||
);
|
||||
|
||||
const resumed = availableResources.pop();
|
||||
|
||||
assertResumedResource(resumed);
|
||||
|
||||
targetList.destroy();
|
||||
await client.close();
|
||||
}
|
||||
|
||||
async function assertPausedResource(resource, expected) {
|
||||
is(
|
||||
resource.resourceType,
|
||||
ResourceWatcher.TYPES.BREAKPOINT,
|
||||
"Resource type is correct"
|
||||
);
|
||||
is(resource.state, "paused", "state attribute is correct");
|
||||
is(resource.why.type, expected.why.type, "why.type attribute is correct");
|
||||
is(
|
||||
resource.frame.type,
|
||||
expected.frame.type,
|
||||
"frame.type attribute is correct"
|
||||
);
|
||||
is(
|
||||
resource.frame.asyncCause,
|
||||
expected.frame.asyncCause,
|
||||
"frame.asyncCause attribute is correct"
|
||||
);
|
||||
is(
|
||||
resource.frame.state,
|
||||
expected.frame.state,
|
||||
"frame.state attribute is correct"
|
||||
);
|
||||
is(
|
||||
resource.frame.displayName,
|
||||
expected.frame.displayName,
|
||||
"frame.displayName attribute is correct"
|
||||
);
|
||||
is(
|
||||
resource.frame.where.line,
|
||||
expected.frame.where.line,
|
||||
"frame.where.line attribute is correct"
|
||||
);
|
||||
is(
|
||||
resource.frame.where.column,
|
||||
expected.frame.where.column,
|
||||
"frame.where.column attribute is correct"
|
||||
);
|
||||
}
|
||||
|
||||
async function assertResumedResource(resource) {
|
||||
is(
|
||||
resource.resourceType,
|
||||
ResourceWatcher.TYPES.BREAKPOINT,
|
||||
"Resource type is correct"
|
||||
);
|
||||
is(resource.state, "resumed", "state attribute is correct");
|
||||
}
|
|
@ -269,45 +269,19 @@ WorkerDebugger::GetWindow(mozIDOMWindow** aResult) {
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = DedicatedWorkerWindow();
|
||||
window.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::GetWindowIDs(nsTArray<uint64_t>& aResult) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mWorkerPrivate) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mWorkerPrivate->IsDedicatedWorker()) {
|
||||
const auto window = DedicatedWorkerWindow();
|
||||
aResult.AppendElement(window->WindowID());
|
||||
} else if (mWorkerPrivate->IsSharedWorker()) {
|
||||
const RemoteWorkerChild* const controller =
|
||||
mWorkerPrivate->GetRemoteWorkerController();
|
||||
MOZ_ASSERT(controller);
|
||||
aResult = controller->WindowIDs().Clone();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> WorkerDebugger::DedicatedWorkerWindow() {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
|
||||
WorkerPrivate* worker = mWorkerPrivate;
|
||||
while (worker->GetParent()) {
|
||||
worker = worker->GetParent();
|
||||
}
|
||||
|
||||
if (!worker->IsDedicatedWorker()) {
|
||||
return nullptr;
|
||||
*aResult = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return worker->GetWindow();
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = worker->GetWindow();
|
||||
window.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
class mozIDOMWindow;
|
||||
class nsIPrincipal;
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -57,8 +56,6 @@ class WorkerDebugger : public nsIWorkerDebugger {
|
|||
void ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
|
||||
uint32_t aLineno,
|
||||
const nsAString& aMessage);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> DedicatedWorkerWindow();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -37,8 +37,6 @@ interface nsIWorkerDebugger : nsISupports
|
|||
// nested workers) its top-level ancestral worker is associated with.
|
||||
readonly attribute mozIDOMWindow window;
|
||||
|
||||
readonly attribute Array<uint64_t> windowIDs;
|
||||
|
||||
readonly attribute nsIPrincipal principal;
|
||||
|
||||
readonly attribute unsigned long serviceWorkerID;
|
||||
|
|
|
@ -66,8 +66,6 @@ class RemoteWorkerChild final
|
|||
|
||||
RefPtr<GenericPromise> MaybeSendSetServiceWorkerSkipWaitingFlag();
|
||||
|
||||
const nsTArray<uint64_t>& WindowIDs() const { return mWindowIDs; }
|
||||
|
||||
private:
|
||||
class InitializeWorkerRunnable;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче