Backed out 9 changesets (bug 1620280) for failure at browser_ext_devtools_inspectedWindow_targetSwitch.js. CLOSED TREE

Backed out changeset 8dda1f048067 (bug 1620280)
Backed out changeset 309c0be48745 (bug 1620280)
Backed out changeset 78a06cd336c6 (bug 1620280)
Backed out changeset 74aba83895ce (bug 1620280)
Backed out changeset 6a8126ded6ec (bug 1620280)
Backed out changeset 47a2d6b77270 (bug 1620280)
Backed out changeset 526c067da0c1 (bug 1620280)
Backed out changeset 488f645884ba (bug 1620280)
Backed out changeset 9cffe1c5fb6d (bug 1620280)
This commit is contained in:
Butkovits Atila 2020-10-01 23:32:57 +03:00
Родитель 4f41db1dfa
Коммит d89b519adc
30 изменённых файлов: 164 добавлений и 497 удалений

Просмотреть файл

@ -42,10 +42,5 @@ add_task(async function() {
const markupViewElement = inspector.panelDoc.getElementById("markup-box"); const markupViewElement = inspector.panelDoc.getElementById("markup-box");
ok(markupViewElement, "Inspector is still rendered"); ok(markupViewElement, "Inspector is still rendered");
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
info("Destroy the opened toolbox");
await toolbox.destroy();
await removeTab(tab); await removeTab(tab);
}); });

Просмотреть файл

@ -51,7 +51,6 @@ class DebuggerPanel {
client, client,
} = await this.panelWin.Debugger.bootstrap({ } = await this.panelWin.Debugger.bootstrap({
targetList: this.toolbox.targetList, targetList: this.toolbox.targetList,
resourceWatcher: this.toolbox.resourceWatcher,
devToolsClient: this.toolbox.target.client, devToolsClient: this.toolbox.target.client,
workers: { workers: {
sourceMaps: this.toolbox.sourceMapService, sourceMaps: this.toolbox.sourceMapService,

Просмотреть файл

@ -47,7 +47,9 @@ import { validateNavigateContext, ContextError } from "../../utils/context";
import type { import type {
Source, Source,
SourceActorId,
SourceId, SourceId,
ThreadId,
Context, Context,
OriginalSourceData, OriginalSourceData,
GeneratedSourceData, GeneratedSourceData,
@ -398,3 +400,18 @@ function checkNewSources(cx: Context, sources: Source[]) {
return sources; return sources;
}; };
} }
export function ensureSourceActor(
thread: ThreadId,
sourceActor: SourceActorId
) {
return async function({ dispatch, getState, client }: ThunkArgs) {
await sourceQueue.flush();
if (hasSourceActor(getState(), sourceActor)) {
return Promise.resolve();
}
const sources = await client.fetchThreadSources(thread);
await dispatch(newGeneratedSources(sources));
};
}

Просмотреть файл

@ -7,6 +7,7 @@
import type { Target } from "../client/firefox/types"; import type { Target } from "../client/firefox/types";
import type { Action, ThunkArgs } from "./types"; import type { Action, ThunkArgs } from "./types";
import { removeSourceActors } from "./source-actors"; import { removeSourceActors } from "./source-actors";
import { newGeneratedSources } from "./sources";
import { validateContext } from "../utils/context"; import { validateContext } from "../utils/context";
import { getContext, getThread, getSourceActorsForThread } from "../selectors"; import { getContext, getThread, getSourceActorsForThread } from "../selectors";
@ -19,6 +20,19 @@ export function addTarget(targetFront: Target) {
validateContext(getState(), cx); validateContext(getState(), cx);
dispatch(({ type: "INSERT_THREAD", cx, newThread: thread }: Action)); dispatch(({ type: "INSERT_THREAD", cx, newThread: thread }: Action));
// Fetch the sources and install breakpoints on any new workers.
try {
const sources = await client.fetchThreadSources(thread.actor);
validateContext(getState(), cx);
await dispatch(newGeneratedSources(sources));
} catch (e) {
// NOTE: This fails quietly because it is pretty easy for sources to
// throw during the fetch if their thread shuts down,
// which would cause test failures.
console.error(e);
}
}; };
} }

Просмотреть файл

@ -7,28 +7,20 @@
import { setupCommands, clientCommands } from "./firefox/commands"; import { setupCommands, clientCommands } from "./firefox/commands";
import { setupEvents, clientEvents } from "./firefox/events"; import { setupEvents, clientEvents } from "./firefox/events";
import { features, prefs } from "../utils/prefs"; import { features, prefs } from "../utils/prefs";
import { prepareSourcePayload } from "./firefox/create";
let actions; let actions;
let targetList; let targetList;
let resourceWatcher;
export async function onConnect( export async function onConnect(
connection: any, connection: any,
_actions: Object, _actions: Object
store: any
): Promise<void> { ): Promise<void> {
const { const { devToolsClient, targetList: _targetList } = connection;
devToolsClient,
targetList: _targetList,
resourceWatcher: _resourceWatcher,
} = connection;
actions = _actions; actions = _actions;
targetList = _targetList; targetList = _targetList;
resourceWatcher = _resourceWatcher;
setupCommands({ devToolsClient, targetList }); setupCommands({ devToolsClient, targetList });
setupEvents({ actions, devToolsClient, store, resourceWatcher }); setupEvents({ actions, devToolsClient });
const { targetFront } = targetList; const { targetFront } = targetList;
if (targetFront.isBrowsingContext || targetFront.isParentProcess) { if (targetFront.isBrowsingContext || targetFront.isParentProcess) {
targetList.listenForWorkers = true; targetList.listenForWorkers = true;
@ -44,21 +36,6 @@ export async function onConnect(
onTargetAvailable, onTargetAvailable,
onTargetDestroyed onTargetDestroyed
); );
await resourceWatcher.watchResources([resourceWatcher.TYPES.SOURCE], {
onAvailable: onSourceAvailable,
});
}
export function onDisconnect() {
targetList.unwatchTargets(
targetList.ALL_TYPES,
onTargetAvailable,
onTargetDestroyed
);
resourceWatcher.unwatchResources([resourceWatcher.TYPES.SOURCE], {
onAvailable: onSourceAvailable,
});
} }
async function onTargetAvailable({ async function onTargetAvailable({
@ -135,15 +112,4 @@ function onTargetDestroyed({ targetFront }): void {
actions.removeTarget(targetFront); actions.removeTarget(targetFront);
} }
async function onSourceAvailable(sources) {
const frontendSources = await Promise.all(
sources.map(async source => {
const threadFront = await source.targetFront.getFront("thread");
const frontendSource = prepareSourcePayload(threadFront, source);
return frontendSource;
})
);
await actions.newGeneratedSources(frontendSources);
}
export { clientCommands, clientEvents }; export { clientCommands, clientEvents };

Просмотреть файл

@ -4,12 +4,11 @@
// @flow // @flow
import { createThread, createFrame } from "./create"; import { prepareSourcePayload, createThread, createFrame } from "./create";
import { import {
addThreadEventListeners, addThreadEventListeners,
clientEvents, clientEvents,
removeThreadEventListeners, removeThreadEventListeners,
ensureSourceActor,
} from "./events"; } from "./events";
import { makePendingLocationId } from "../../utils/breakpoint"; import { makePendingLocationId } from "../../utils/breakpoint";
@ -23,6 +22,7 @@ import type {
PendingLocation, PendingLocation,
Frame, Frame,
FrameId, FrameId,
GeneratedSourceData,
Script, Script,
SourceId, SourceId,
SourceActor, SourceActor,
@ -39,6 +39,7 @@ import type {
ThreadFront, ThreadFront,
ObjectFront, ObjectFront,
ExpressionResult, ExpressionResult,
SourcesPacket,
} from "./types"; } from "./types";
import type { EventListenerCategoryList } from "../../actions/types"; import type { EventListenerCategoryList } from "../../actions/types";
@ -317,13 +318,6 @@ function getProperties(thread: string, grip: Grip): Promise<*> {
async function getFrames(thread: string) { async function getFrames(thread: string) {
const threadFront = lookupThreadFront(thread); const threadFront = lookupThreadFront(thread);
const response = await threadFront.getFrames(0, CALL_STACK_PAGE_SIZE); const response = await threadFront.getFrames(0, CALL_STACK_PAGE_SIZE);
// 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 => ensureSourceActor(frame.where.actor))
);
return response.frames.map<?Frame>((frame, i) => return response.frames.map<?Frame>((frame, i) =>
createFrame(thread, frame, i) createFrame(thread, frame, i)
); );
@ -402,6 +396,14 @@ function registerSourceActor(sourceActorId: string, sourceId: SourceId) {
sourceActors[sourceActorId] = sourceId; sourceActors[sourceActorId] = sourceId;
} }
async function getSources(
client: ThreadFront
): Promise<Array<GeneratedSourceData>> {
const { sources }: SourcesPacket = await client.getSources();
return sources.map(source => prepareSourcePayload(client, source));
}
async function toggleEventLogging(logEventBreakpoints: boolean) { async function toggleEventLogging(logEventBreakpoints: boolean) {
return forEachThread(thread => return forEachThread(thread =>
thread.toggleEventLogging(logEventBreakpoints) thread.toggleEventLogging(logEventBreakpoints)
@ -416,6 +418,21 @@ function getAllThreadFronts(): ThreadFront[] {
return fronts; return fronts;
} }
// Fetch the sources for all the targets
async function fetchSources(): Promise<Array<GeneratedSourceData>> {
let sources = [];
for (const threadFront of getAllThreadFronts()) {
sources = sources.concat(await getSources(threadFront));
}
return sources;
}
async function fetchThreadSources(
thread: string
): Promise<Array<GeneratedSourceData>> {
return getSources(lookupThreadFront(thread));
}
// Check if any of the targets were paused before we opened // Check if any of the targets were paused before we opened
// the debugger. If one is paused. Fake a `pause` RDP event // the debugger. If one is paused. Fake a `pause` RDP event
// by directly calling the client event listener. // by directly calling the client event listener.
@ -538,6 +555,8 @@ const clientCommands = {
getFrames, getFrames,
pauseOnExceptions, pauseOnExceptions,
toggleEventLogging, toggleEventLogging,
fetchSources,
fetchThreadSources,
checkIfAlreadyPaused, checkIfAlreadyPaused,
registerSourceActor, registerSourceActor,
addThread, addThread,

Просмотреть файл

@ -5,6 +5,7 @@
// @flow // @flow
import type { import type {
SourcePacket,
PausedPacket, PausedPacket,
ThreadFront, ThreadFront,
Target, Target,
@ -13,23 +14,19 @@ import type {
import Actions from "../../actions"; import Actions from "../../actions";
import { createPause } from "./create"; import { createPause, prepareSourcePayload } from "./create";
import sourceQueue from "../../utils/source-queue"; import sourceQueue from "../../utils/source-queue";
import { recordEvent } from "../../utils/telemetry"; import { recordEvent } from "../../utils/telemetry";
import { prefs } from "../../utils/prefs"; import { prefs } from "../../utils/prefs";
import { hasSourceActor } from "../../selectors";
import { stringToSourceActorId } from "../../reducers/source-actors";
type Dependencies = { type Dependencies = {
actions: typeof Actions, actions: typeof Actions,
devToolsClient: DevToolsClient, devToolsClient: DevToolsClient,
store: any,
}; };
let actions: typeof Actions; let actions: typeof Actions;
let isInterrupted: boolean; let isInterrupted: boolean;
let threadFrontListeners: WeakMap<ThreadFront, Array<Function>>; let threadFrontListeners: WeakMap<ThreadFront, Array<Function>>;
let store: any;
function addThreadEventListeners(thread: ThreadFront): void { function addThreadEventListeners(thread: ThreadFront): void {
const removeListeners = []; const removeListeners = [];
@ -56,7 +53,6 @@ function attachAllTargets(currentTarget: Target): boolean {
function setupEvents(dependencies: Dependencies): void { function setupEvents(dependencies: Dependencies): void {
actions = dependencies.actions; actions = dependencies.actions;
sourceQueue.initialize(actions); sourceQueue.initialize(actions);
store = dependencies.store;
threadFrontListeners = new WeakMap(); threadFrontListeners = new WeakMap();
} }
@ -82,7 +78,10 @@ async function paused(
if (packet.frame) { if (packet.frame) {
// When reloading we might receive a pause event before the // When reloading we might receive a pause event before the
// top frame's source has arrived. // top frame's source has arrived.
await ensureSourceActor(packet.frame.where.actor); await actions.ensureSourceActor(
threadFront.actorID,
packet.frame.where.actor
);
} }
const pause = createPause(threadFront.actor, packet); const pause = createPause(threadFront.actor, packet);
@ -103,36 +102,17 @@ function resumed(threadFront: ThreadFront): void {
actions.resumed(threadFront.actorID); actions.resumed(threadFront.actorID);
} }
/** function newSource(threadFront: ThreadFront, { source }: SourcePacket): void {
* This method wait for the given source is registered in Redux store. sourceQueue.queue({
* type: "generated",
* @param {String} sourceActor data: prepareSourcePayload(threadFront, source),
* Actor ID of the source to be waiting for.
*/
async function ensureSourceActor(sourceActor: string) {
const sourceActorId = stringToSourceActorId(sourceActor);
if (!hasSourceActor(store.getState(), sourceActorId)) {
await new Promise(resolve => {
const unsubscribe = store.subscribe(check);
let currentState = null;
function check() {
const previousState = currentState;
currentState = store.getState().sourceActors.values;
if (previousState == currentState) {
return;
}
if (hasSourceActor(store.getState(), sourceActorId)) {
unsubscribe();
resolve();
}
}
}); });
}
} }
const clientEvents = { const clientEvents = {
paused, paused,
resumed, resumed,
newSource,
}; };
export { export {
@ -141,5 +121,4 @@ export {
addThreadEventListeners, addThreadEventListeners,
removeThreadEventListeners, removeThreadEventListeners,
attachAllTargets, attachAllTargets,
ensureSourceActor,
}; };

Просмотреть файл

@ -58,6 +58,10 @@ async function loadInitialState() {
}; };
} }
function getClient(connection: any) {
return firefox;
}
export async function onConnect( export async function onConnect(
connection: Object, connection: Object,
panelWorkers: Object, panelWorkers: Object,
@ -70,7 +74,8 @@ export async function onConnect(
verifyPrefSchema(); verifyPrefSchema();
const commands = firefox.clientCommands; const client = getClient(connection);
const commands = client.clientCommands;
const initialState = await loadInitialState(); const initialState = await loadInitialState();
const workers = bootstrapWorkers(panelWorkers); const workers = bootstrapWorkers(panelWorkers);
@ -82,7 +87,7 @@ export async function onConnect(
initialState initialState
); );
const connected = firefox.onConnect(connection, actions, store); const connected = client.onConnect(connection, actions);
await syncBreakpoints(); await syncBreakpoints();
syncXHRBreakpoints(); syncXHRBreakpoints();
@ -92,14 +97,10 @@ export async function onConnect(
selectors, selectors,
workers, workers,
connection, connection,
client: firefox.clientCommands, client: client.clientCommands,
}); });
bootstrapApp(store, panel); bootstrapApp(store, panel);
await connected; await connected;
return { store, actions, selectors, client: commands }; return { store, actions, selectors, client: commands };
} }
export function onDisconnect() {
return firefox.onDisconnect();
}

Просмотреть файл

@ -5,7 +5,7 @@
// @flow // @flow
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { onConnect, onDisconnect } from "./client"; import { onConnect } from "./client";
import { teardownWorkers } from "./utils/bootstrap"; import { teardownWorkers } from "./utils/bootstrap";
import sourceQueue from "./utils/source-queue"; import sourceQueue from "./utils/source-queue";
@ -15,25 +15,17 @@ function unmountRoot() {
} }
module.exports = { module.exports = {
bootstrap: ({ bootstrap: ({ targetList, devToolsClient, workers, panel }: any) =>
targetList,
resourceWatcher,
devToolsClient,
workers,
panel,
}: any) =>
onConnect( onConnect(
{ {
tab: { clientType: "firefox" }, tab: { clientType: "firefox" },
targetList, targetList,
resourceWatcher,
devToolsClient, devToolsClient,
}, },
workers, workers,
panel panel
), ),
destroy: () => { destroy: () => {
onDisconnect();
unmountRoot(); unmountRoot();
sourceQueue.clear(); sourceQueue.clear();
teardownWorkers(); teardownWorkers();

Просмотреть файл

@ -25,10 +25,7 @@ export type PendingBreakpointsState = { [string]: PendingBreakpoint };
function update(state: PendingBreakpointsState = {}, action: Action) { function update(state: PendingBreakpointsState = {}, action: Action) {
switch (action.type) { switch (action.type) {
case "SET_BREAKPOINT": case "SET_BREAKPOINT":
if (action.status === "start") {
return setBreakpoint(state, action); return setBreakpoint(state, action);
}
return state;
case "REMOVE_BREAKPOINT": case "REMOVE_BREAKPOINT":
if (action.status === "start") { if (action.status === "start") {

Просмотреть файл

@ -47,11 +47,7 @@ export function insertResources<R: ResourceBound>(
for (const resource of resources) { for (const resource of resources) {
const { id } = resource; const { id } = resource;
if (state.identity[id]) { if (state.identity[id]) {
throw new Error( throw new Error(`Resource "${id}" already exists, cannot insert`);
`Resource "${id}" already exists, cannot insert ${JSON.stringify(
resource
)}`
);
} }
if (state.values[id]) { if (state.values[id]) {
throw new Error( throw new Error(

Просмотреть файл

@ -7,7 +7,6 @@
import { throttle } from "lodash"; import { throttle } from "lodash";
import type { QueuedSourceData } from "../types"; import type { QueuedSourceData } from "../types";
// This SourceQueue module is now only used for source mapped sources
let newQueuedSources; let newQueuedSources;
let queuedSources; let queuedSources;
let currentWork; let currentWork;

Просмотреть файл

@ -15,8 +15,6 @@ add_task(async function() {
// Navigate to a content process URL and check that the sources tree updates // Navigate to a content process URL and check that the sources tree updates
await navigate(dbg, EXAMPLE_URL + "doc-scripts.html", "simple1.js"); await navigate(dbg, EXAMPLE_URL + "doc-scripts.html", "simple1.js");
info("Wait for all sources to be in the store");
await waitFor(() => dbg.selectors.getSourceCount() == 5);
is(dbg.selectors.getSourceCount(), 5, "5 sources are loaded."); is(dbg.selectors.getSourceCount(), 5, "5 sources are loaded.");
// Check that you can still break after target switching. // Check that you can still break after target switching.

Просмотреть файл

@ -28,14 +28,15 @@ class SourceMapURLService {
this._urlToIDMap = new Map(); this._urlToIDMap = new Map();
this._mapsById = new Map(); this._mapsById = new Map();
this._sourcesLoading = null; this._sourcesLoading = null;
this._onResourceAvailable = this._onResourceAvailable.bind(this);
this._runningCallback = false; this._runningCallback = false;
this._syncPrevValue = this._syncPrevValue.bind(this); this._syncPrevValue = this._syncPrevValue.bind(this);
this._onSourceUpdatedEvent = this._onSourceUpdatedEvent.bind(this);
this._clearAllState = this._clearAllState.bind(this); this._clearAllState = this._clearAllState.bind(this);
this._onNewStyleSheet = this._onNewStyleSheet.bind(this);
this._target.on("will-navigate", this._clearAllState); this._target.on("will-navigate", this._clearAllState);
this._target.on("source-updated", this._onSourceUpdatedEvent);
Services.prefs.addObserver(SOURCE_MAP_PREF, this._syncPrevValue); Services.prefs.addObserver(SOURCE_MAP_PREF, this._syncPrevValue);
} }
@ -46,6 +47,7 @@ class SourceMapURLService {
destroy() { destroy() {
this._clearAllState(); this._clearAllState();
this._target.off("will-navigate", this._clearAllState); this._target.off("will-navigate", this._clearAllState);
this._target.off("source-updated", this._onSourceUpdatedEvent);
Services.prefs.removeObserver(SOURCE_MAP_PREF, this._syncPrevValue); Services.prefs.removeObserver(SOURCE_MAP_PREF, this._syncPrevValue);
} }
@ -202,10 +204,9 @@ class SourceMapURLService {
this._pendingURLSubscriptions.clear(); this._pendingURLSubscriptions.clear();
this._urlToIDMap.clear(); this._urlToIDMap.clear();
const { resourceWatcher } = this._toolbox;
try { try {
resourceWatcher.unwatchResources( this._toolbox.resourceWatcher.unwatchResources(
[resourceWatcher.TYPES.STYLESHEET, resourceWatcher.TYPES.SOURCE], [this._toolbox.resourceWatcher.TYPES.STYLESHEET],
{ onAvailable: this._onResourceAvailable } { onAvailable: this._onResourceAvailable }
); );
} catch (e) { } catch (e) {
@ -216,6 +217,10 @@ class SourceMapURLService {
this._sourcesLoading = null; this._sourcesLoading = null;
} }
_onSourceUpdatedEvent(sourceEvent) {
this._onNewJavascript(sourceEvent.source);
}
_onNewJavascript(source) { _onNewJavascript(source) {
const { url, actor: id, sourceMapBaseURL, sourceMapURL } = source; const { url, actor: id, sourceMapBaseURL, sourceMapURL } = source;
@ -416,42 +421,54 @@ class SourceMapURLService {
if (!this._prefValue) { if (!this._prefValue) {
return null; return null;
} }
if (!this._sourcesLoading) {
const sourcesLoading = (async () => {
if (this._target.isWorkerTarget) { if (this._target.isWorkerTarget) {
return; return;
} }
if (!this._sourcesLoading) { this._onResourceAvailable = async resources => {
const { resourceWatcher } = this._toolbox; if (this._sourcesLoading === sourcesLoading) {
const { STYLESHEET, SOURCE } = resourceWatcher.TYPES;
this._sourcesLoading = resourceWatcher.watchResources(
[STYLESHEET, SOURCE],
{
onAvailable: this._onResourceAvailable,
}
);
}
return this._sourcesLoading;
}
waitForSourcesLoading() {
if (this._sourcesLoading) {
return this._sourcesLoading;
}
return Promise.resolve();
}
_onResourceAvailable(resources) {
const { resourceWatcher } = this._toolbox;
const { STYLESHEET, SOURCE } = resourceWatcher.TYPES;
for (const resource of resources) { for (const resource of resources) {
if (resource.resourceType == STYLESHEET) {
this._onNewStyleSheet(resource); this._onNewStyleSheet(resource);
} else if (resource.resourceType == SOURCE) {
this._onNewJavascript(resource);
} }
} }
};
await Promise.all([
this._toolbox.resourceWatcher.watchResources(
[this._toolbox.resourceWatcher.TYPES.STYLESHEET],
{ onAvailable: this._onResourceAvailable }
),
(async () => {
const { threadFront } = this._toolbox;
if (!threadFront) {
console.warn(
"sourcemap url service cannot query for sources, no threadFront found"
);
return;
}
try {
const { sources } = await threadFront.getSources();
if (this._sourcesLoading === sourcesLoading) {
// If we've cleared the state since starting this request,
// we don't want to populate these.
for (const source of sources) {
this._onNewJavascript(source);
}
}
} catch (err) {
// Also ignore any protocol-based errors.
}
})(),
]);
})();
this._sourcesLoading = sourcesLoading;
}
return this._sourcesLoading;
} }
_convertPendingURLSubscriptionsToID(url, id) { _convertPendingURLSubscriptionsToID(url, id) {

Просмотреть файл

@ -119,21 +119,16 @@ function createScript(url) {
function waitForSourceLoad(toolbox, url) { function waitForSourceLoad(toolbox, url) {
info(`Waiting for source ${url} to be available...`); info(`Waiting for source ${url} to be available...`);
return new Promise(resolve => { return new Promise(resolve => {
const { resourceWatcher } = toolbox; const target = toolbox.target;
function onAvailable(sources) { function sourceHandler(sourceEvent) {
for (const source of sources) { if (sourceEvent && sourceEvent.source && sourceEvent.source.url === url) {
if (source.url === url) {
resourceWatcher.unwatchResources([resourceWatcher.TYPES.SOURCE], {
onAvailable,
});
resolve(); resolve();
target.off("source-updated", sourceHandler);
} }
} }
}
resourceWatcher.watchResources([resourceWatcher.TYPES.SOURCE], { target.on("source-updated", sourceHandler);
onAvailable,
});
}); });
} }

Просмотреть файл

@ -3586,6 +3586,11 @@ Toolbox.prototype = {
this._lastFocusedElement = null; this._lastFocusedElement = null;
this._pausedThreads = null; this._pausedThreads = null;
if (this._sourceMapURLService) {
this._sourceMapURLService.destroy();
this._sourceMapURLService = null;
}
if (this._sourceMapService) { if (this._sourceMapService) {
this._sourceMapService.stopSourceMapWorker(); this._sourceMapService.stopSourceMapWorker();
this._sourceMapService = null; this._sourceMapService = null;
@ -3690,12 +3695,6 @@ Toolbox.prototype = {
this._netMonitorAPI = null; this._netMonitorAPI = null;
} }
if (this._sourceMapURLService) {
await this._sourceMapURLService.waitForSourcesLoading();
this._sourceMapURLService.destroy();
this._sourceMapURLService = null;
}
this._removeWindowListeners(); this._removeWindowListeners();
this._removeChromeEventHandlerEvents(); this._removeChromeEventHandlerEvents();

Просмотреть файл

@ -42,6 +42,7 @@ function TargetMixin(parentClass) {
this._forceChrome = false; this._forceChrome = false;
this.destroy = this.destroy.bind(this); this.destroy = this.destroy.bind(this);
this._onNewSource = this._onNewSource.bind(this);
this.threadFront = null; this.threadFront = null;
@ -565,9 +566,16 @@ function TargetMixin(parentClass) {
await this.threadFront.attach(options); await this.threadFront.attach(options);
this.threadFront.on("newSource", this._onNewSource);
return this.threadFront; return this.threadFront;
} }
// Listener for "newSource" event fired by the thread actor
_onNewSource(packet) {
this.emit("source-updated", packet);
}
/** /**
* Setup listeners for remote debugging, updating existing ones as necessary. * Setup listeners for remote debugging, updating existing ones as necessary.
*/ */
@ -587,6 +595,11 @@ function TargetMixin(parentClass) {
} }
this.off("tabDetached", this.destroy); this.off("tabDetached", this.destroy);
// Remove listeners set in attachThread
if (this.threadFront) {
this.threadFront.off("newSource", this._onNewSource);
}
// Remove listeners set in attachConsole // Remove listeners set in attachConsole
if (this.removeOnInspectObjectListener) { if (this.removeOnInspectObjectListener) {
this.removeOnInspectObjectListener(); this.removeOnInspectObjectListener();

Просмотреть файл

@ -233,11 +233,6 @@ BoxModel.prototype = {
* Hides the box-model highlighter on the currently selected element. * Hides the box-model highlighter on the currently selected element.
*/ */
onHideBoxModelHighlighter() { onHideBoxModelHighlighter() {
// As React components aren't destroyed when the panel closes,
// this function may still be called and throw because of destroyed fronts.
if (!this.inspector) {
return;
}
this.inspector.highlighters.hideHighlighterType( this.inspector.highlighters.hideHighlighterType(
this.inspector.highlighters.TYPES.BOXMODEL this.inspector.highlighters.TYPES.BOXMODEL
); );

Просмотреть файл

@ -1927,11 +1927,6 @@ Inspector.prototype = {
* Options passed to the highlighter actor. * Options passed to the highlighter actor.
*/ */
onShowBoxModelHighlighterForNode(nodeFront, options) { onShowBoxModelHighlighterForNode(nodeFront, options) {
// As React components aren't destroyed when the panel closes,
// this function may still be called and throw because of destroyed fronts.
if (this._destroyed) {
return;
}
this.highlighters.showHighlighterTypeForNode( this.highlighters.showHighlighterTypeForNode(
this.highlighters.TYPES.BOXMODEL, this.highlighters.TYPES.BOXMODEL,
nodeFront, nodeFront,

Просмотреть файл

@ -94,9 +94,6 @@ add_task(async function testForeignCookieBlockedMessage() {
message, message,
getStorageErrorUrl("CookieBlockedForeign") getStorageErrorUrl("CookieBlockedForeign")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
}); });
@ -127,9 +124,6 @@ add_task(async function testLimitForeignCookieBlockedMessage() {
message, message,
getStorageErrorUrl("CookieBlockedForeign") getStorageErrorUrl("CookieBlockedForeign")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
}); });
@ -150,9 +144,6 @@ add_task(async function testAllCookieBlockedMessage() {
message, message,
getStorageErrorUrl("CookieBlockedAll") getStorageErrorUrl("CookieBlockedAll")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
}); });
@ -173,9 +164,6 @@ add_task(async function testTrackerCookieBlockedMessage() {
message, message,
getStorageErrorUrl("CookieBlockedTracker") getStorageErrorUrl("CookieBlockedTracker")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
}); });
@ -196,9 +184,6 @@ add_task(async function testForeignCookiePartitionedMessage() {
message, message,
getStorageErrorUrl("CookiePartitionedForeign") getStorageErrorUrl("CookiePartitionedForeign")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
}); });
@ -227,9 +212,6 @@ add_task(async function testCookieBlockedByPermissionMessage() {
message, message,
getStorageErrorUrl("CookieBlockedByPermission") getStorageErrorUrl("CookieBlockedByPermission")
); );
// We explicitely destroy the toolbox in order to ensure waiting for its full destruction
// and avoid leak / pending requests
await hud.toolbox.destroy();
win.close(); win.close();
// Remove the custom permission. // Remove the custom permission.

Просмотреть файл

@ -144,11 +144,6 @@ add_task(async function testInvalidSameSiteMessage() {
`| ${message2}`, `| ${message2}`,
]); ]);
// Source map are being resolved in background and we might have
// pending request related to this service if we close the window
// immeditely. So just wait for these request to finish before proceeding.
await hud.toolbox.sourceMapURLService.waitForSourcesLoading();
await win.close(); await win.close();
}); });

Просмотреть файл

@ -144,11 +144,8 @@ class Front extends Pool {
} }
// Call listeners registered via `watchFronts` method // Call listeners registered via `watchFronts` method
// (ignore if this front has been destroyed)
if (this._frontCreationListeners) {
this._frontCreationListeners.emit(front.typeName, front); this._frontCreationListeners.emit(front.typeName, front);
} }
}
async unmanage(front) { async unmanage(front) {
super.unmanage(front); super.unmanage(front);

Просмотреть файл

@ -11,7 +11,6 @@ DevToolsModules(
'network-events.js', 'network-events.js',
'platform-messages.js', 'platform-messages.js',
'root-node.js', 'root-node.js',
'source.js',
'stylesheet.js', 'stylesheet.js',
'websocket.js', 'websocket.js',
) )

Просмотреть файл

@ -1,81 +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");
/**
* Emit SOURCE resources, which represents a Javascript source and has the following attributes set on "available":
*
* - introductionType {null|String}: A string indicating how this source code was introduced into the system.
* This will typically be set to "scriptElement", "eval", ...
* But this may have many other values:
* https://searchfox.org/mozilla-central/rev/ac142717cc067d875e83e4b1316f004f6e063a46/dom/script/ScriptLoader.cpp#2628-2639
* https://searchfox.org/mozilla-central/search?q=symbol:_ZN2JS14CompileOptions19setIntroductionTypeEPKc&redirect=false
* https://searchfox.org/mozilla-central/rev/ac142717cc067d875e83e4b1316f004f6e063a46/devtools/server/actors/source.js#160-169
* - sourceMapBaseURL {String}: Base URL where to look for a source map.
* This isn't the source map URL.
* - sourceMapURL {null|String}: URL of the source map, if there is one.
* - url {null|String}: URL of the source, if it relates to a particular URL.
* Evaled sources won't have any related URL.
* - isBlackBoxed {Boolean}: Specifying whether the source actor's 'black-boxed' flag is set.
* - extensionName {null|String}: If the source comes from an add-on, the add-on name.
*/
module.exports = async function({
targetList,
targetFront,
isFissionEnabledOnContentToolbox,
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;
}
const threadFront = await targetFront.getFront("thread");
// Use a list of all notified SourceFront as we don't have a newSource event for all sources
// but we sometime get sources notified both via newSource event *and* sources() method...
// We store actor ID instead of SourceFront as it appears that multiple SourceFront for the same
// actor are created...
const sourcesActorIDCache = new Set();
// Forward new sources (but also existing ones, see next comment)
threadFront.on("newSource", ({ source }) => {
if (sourcesActorIDCache.has(source.actor)) {
return;
}
sourcesActorIDCache.add(source.actor);
// source is a SourceActor's form, add the resourceType attribute on it
source.resourceType = ResourceWatcher.TYPES.SOURCE;
onAvailable([source]);
});
// Forward already existing sources
// Note that calling `sources()` will end up emitting `newSource` event for all existing sources.
// But not in some cases, for example, when the thread is already paused.
// (And yes, it means that already existing sources can be transfered twice over the wire)
let { sources } = await threadFront.sources();
// Note that `sources()` doesn't encapsulate SourceFront into a `source` attribute
// while `newSource` event does.
sources = sources.filter(source => {
return !sourcesActorIDCache.has(source.actor);
});
for (const source of sources) {
sourcesActorIDCache.add(source.actor);
// source is a SourceActor's form, add the resourceType attribute on it
source.resourceType = ResourceWatcher.TYPES.SOURCE;
}
onAvailable(sources);
};

Просмотреть файл

@ -666,9 +666,6 @@ class ResourceWatcher {
const onAvailable = this._onResourceAvailable.bind(this, { targetFront }); const onAvailable = this._onResourceAvailable.bind(this, { targetFront });
const onDestroyed = this._onResourceDestroyed.bind(this, { targetFront }); const onDestroyed = this._onResourceDestroyed.bind(this, { targetFront });
const onUpdated = this._onResourceUpdated.bind(this, { targetFront }); const onUpdated = this._onResourceUpdated.bind(this, { targetFront });
if (!(resourceType in LegacyListeners)) {
throw new Error(`Missing legacy listener for ${resourceType}`);
}
return LegacyListeners[resourceType]({ return LegacyListeners[resourceType]({
targetList: this.targetList, targetList: this.targetList,
targetFront, targetFront,
@ -750,7 +747,6 @@ ResourceWatcher.TYPES = ResourceWatcher.prototype.TYPES = {
NETWORK_EVENT: "network-event", NETWORK_EVENT: "network-event",
WEBSOCKET: "websocket", WEBSOCKET: "websocket",
NETWORK_EVENT_STACKTRACE: "network-event-stacktrace", NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
SOURCE: "source",
}; };
module.exports = { ResourceWatcher }; module.exports = { ResourceWatcher };
@ -795,8 +791,6 @@ const LegacyListeners = {
.WEBSOCKET]: require("devtools/shared/resources/legacy-listeners/websocket"), .WEBSOCKET]: require("devtools/shared/resources/legacy-listeners/websocket"),
[ResourceWatcher.TYPES [ResourceWatcher.TYPES
.NETWORK_EVENT_STACKTRACE]: require("devtools/shared/resources/legacy-listeners/network-event-stacktraces"), .NETWORK_EVENT_STACKTRACE]: require("devtools/shared/resources/legacy-listeners/network-event-stacktraces"),
[ResourceWatcher.TYPES
.SOURCE]: require("devtools/shared/resources/legacy-listeners/source"),
}; };
// Optional transformers for each type of resource. // Optional transformers for each type of resource.

Просмотреть файл

@ -9,8 +9,6 @@ support-files =
network_document.html network_document.html
fission_document.html fission_document.html
fission_iframe.html fission_iframe.html
sources.html
sources.js
style_document.css style_document.css
style_document.html style_document.html
style_iframe.css style_iframe.css
@ -21,7 +19,6 @@ support-files =
test_worker.js test_worker.js
websocket_backend_wsh.py websocket_backend_wsh.py
websocket_frontend.html websocket_frontend.html
worker-sources.js
[browser_resources_client_caching.js] [browser_resources_client_caching.js]
[browser_resources_console_messages.js] [browser_resources_console_messages.js]
@ -36,7 +33,6 @@ skip-if = os == "linux" #Bug 1655183
[browser_resources_platform_messages.js] [browser_resources_platform_messages.js]
[browser_resources_root_node.js] [browser_resources_root_node.js]
[browser_resources_several_resources.js] [browser_resources_several_resources.js]
[browser_resources_sources.js]
[browser_resources_stylesheets.js] [browser_resources_stylesheets.js]
[browser_resources_target_destroy.js] [browser_resources_target_destroy.js]
[browser_resources_target_resources_race.js] [browser_resources_target_resources_race.js]

Просмотреть файл

@ -1,176 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the ResourceWatcher API around SOURCE.
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const TEST_URL = URL_ROOT + "sources.html";
add_task(async function() {
const tab = await addTab(TEST_URL);
const htmlRequest = await fetch(TEST_URL);
const htmlContent = await htmlRequest.text();
const {
client,
resourceWatcher,
targetList,
} = await initResourceWatcherAndTarget(tab);
// Force the target list to cover workers
targetList.listenForWorkers = true;
await targetList.startListening();
const targets = [];
await targetList.watchTargets(targetList.ALL_TYPES, async function({
targetFront,
}) {
targets.push(targetFront);
});
is(targets.length, 2, "Got expected number of targets");
info("Check already available resources");
const availableResources = [];
await resourceWatcher.watchResources([ResourceWatcher.TYPES.SOURCE], {
onAvailable: resources => availableResources.push(...resources),
});
const expectedExistingResources = [
{
description: "independent js file",
sourceForm: {
introductionType: "scriptElement",
sourceMapBaseURL:
"http://example.com/browser/devtools/shared/resources/tests/sources.js",
url:
"http://example.com/browser/devtools/shared/resources/tests/sources.js",
isBlackBoxed: false,
sourceMapURL: null,
extensionName: null,
},
sourceContent: {
contentType: "text/javascript",
source: "/* eslint-disable */\nfunction scriptSource() {}\n",
},
},
{
description: "eval",
sourceForm: {
introductionType: "eval",
sourceMapBaseURL:
"http://example.com/browser/devtools/shared/resources/tests/sources.html",
url: null,
isBlackBoxed: false,
sourceMapURL: null,
extensionName: null,
},
sourceContent: {
contentType: "text/javascript",
source: "this.global = function evalFunction() {}",
},
},
{
description: "inline JS",
sourceForm: {
introductionType: "scriptElement",
sourceMapBaseURL:
"http://example.com/browser/devtools/shared/resources/tests/sources.html",
url:
"http://example.com/browser/devtools/shared/resources/tests/sources.html",
isBlackBoxed: false,
sourceMapURL: null,
extensionName: null,
},
sourceContent: {
contentType: "text/html",
source: htmlContent,
},
},
{
description: "worker script",
sourceForm: {
introductionType: undefined,
sourceMapBaseURL:
"http://example.com/browser/devtools/shared/resources/tests/worker-sources.js",
url:
"http://example.com/browser/devtools/shared/resources/tests/worker-sources.js",
isBlackBoxed: false,
sourceMapURL: null,
extensionName: null,
},
sourceContent: {
contentType: "text/javascript",
source: "/* eslint-disable */\nfunction workerSource() {}\n",
},
},
];
await assertResources(availableResources, expectedExistingResources);
await targetList.stopListening();
await client.close();
});
async function assertResources(resources, expected) {
is(
resources.length,
expected.length,
"Length of existing resources is correct at initial"
);
for (let i = 0; i < resources.length; i++) {
await assertResource(resources[i], expected);
}
}
async function assertResource(source, expected) {
info(`Checking resource "#${expected.description}"`);
is(
source.resourceType,
ResourceWatcher.TYPES.SOURCE,
"Resource type is correct"
);
const threadFront = await source.targetFront.getFront("thread");
// `source` is SourceActor's form()
// so try to instantiate the related SourceFront:
const sourceFront = threadFront.source(source);
// then fetch source content
const sourceContent = await sourceFront.source();
// Order of sources is random, so we have to find the best expected resource.
// The only unique attribute is the JS Source text content.
const matchingExpected = expected.find(res => {
return res.sourceContent.source == sourceContent.source;
});
ok(
matchingExpected,
`This source was expected with source content being "${sourceContent.source}"`
);
assertObject(
sourceContent,
matchingExpected.sourceContent,
matchingExpected.description
);
assertObject(
source,
matchingExpected.sourceForm,
matchingExpected.description
);
}
function assertObject(object, expected, description) {
for (const field in expected) {
is(
object[field],
expected[field],
`The value of ${field} is correct for "#${description}"`
);
}
}

Просмотреть файл

@ -1,21 +0,0 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype HTML>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<script type="text/javascript">
"use strict";
/* eslint-disable */
function inlineSource() {}
// Assign it to a global in order to avoid it being GCed
eval("this.global = function evalFunction() {}");
// Assign the worker to a global variable in order to avoid
// having it be GCed.
this.worker = new Worker("worker-sources.js");
</script>
<script src="sources.js"></script>
</body>
</html>

Просмотреть файл

@ -1,2 +0,0 @@
/* eslint-disable */
function scriptSource() {}

Просмотреть файл

@ -1,2 +0,0 @@
/* eslint-disable */
function workerSource() {}