зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
4f41db1dfa
Коммит
d89b519adc
|
@ -42,10 +42,5 @@ add_task(async function() {
|
|||
const markupViewElement = inspector.panelDoc.getElementById("markup-box");
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -51,7 +51,6 @@ class DebuggerPanel {
|
|||
client,
|
||||
} = await this.panelWin.Debugger.bootstrap({
|
||||
targetList: this.toolbox.targetList,
|
||||
resourceWatcher: this.toolbox.resourceWatcher,
|
||||
devToolsClient: this.toolbox.target.client,
|
||||
workers: {
|
||||
sourceMaps: this.toolbox.sourceMapService,
|
||||
|
|
|
@ -47,7 +47,9 @@ import { validateNavigateContext, ContextError } from "../../utils/context";
|
|||
|
||||
import type {
|
||||
Source,
|
||||
SourceActorId,
|
||||
SourceId,
|
||||
ThreadId,
|
||||
Context,
|
||||
OriginalSourceData,
|
||||
GeneratedSourceData,
|
||||
|
@ -398,3 +400,18 @@ function checkNewSources(cx: Context, sources: Source[]) {
|
|||
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 { Action, ThunkArgs } from "./types";
|
||||
import { removeSourceActors } from "./source-actors";
|
||||
import { newGeneratedSources } from "./sources";
|
||||
import { validateContext } from "../utils/context";
|
||||
|
||||
import { getContext, getThread, getSourceActorsForThread } from "../selectors";
|
||||
|
@ -19,6 +20,19 @@ export function addTarget(targetFront: Target) {
|
|||
validateContext(getState(), cx);
|
||||
|
||||
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 { setupEvents, clientEvents } from "./firefox/events";
|
||||
import { features, prefs } from "../utils/prefs";
|
||||
import { prepareSourcePayload } from "./firefox/create";
|
||||
|
||||
let actions;
|
||||
let targetList;
|
||||
let resourceWatcher;
|
||||
|
||||
export async function onConnect(
|
||||
connection: any,
|
||||
_actions: Object,
|
||||
store: any
|
||||
_actions: Object
|
||||
): Promise<void> {
|
||||
const {
|
||||
devToolsClient,
|
||||
targetList: _targetList,
|
||||
resourceWatcher: _resourceWatcher,
|
||||
} = connection;
|
||||
const { devToolsClient, targetList: _targetList } = connection;
|
||||
actions = _actions;
|
||||
targetList = _targetList;
|
||||
resourceWatcher = _resourceWatcher;
|
||||
|
||||
setupCommands({ devToolsClient, targetList });
|
||||
setupEvents({ actions, devToolsClient, store, resourceWatcher });
|
||||
setupEvents({ actions, devToolsClient });
|
||||
const { targetFront } = targetList;
|
||||
if (targetFront.isBrowsingContext || targetFront.isParentProcess) {
|
||||
targetList.listenForWorkers = true;
|
||||
|
@ -44,21 +36,6 @@ export async function onConnect(
|
|||
onTargetAvailable,
|
||||
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({
|
||||
|
@ -135,15 +112,4 @@ function onTargetDestroyed({ targetFront }): void {
|
|||
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 };
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { createThread, createFrame } from "./create";
|
||||
import { prepareSourcePayload, createThread, createFrame } from "./create";
|
||||
import {
|
||||
addThreadEventListeners,
|
||||
clientEvents,
|
||||
removeThreadEventListeners,
|
||||
ensureSourceActor,
|
||||
} from "./events";
|
||||
import { makePendingLocationId } from "../../utils/breakpoint";
|
||||
|
||||
|
@ -23,6 +22,7 @@ import type {
|
|||
PendingLocation,
|
||||
Frame,
|
||||
FrameId,
|
||||
GeneratedSourceData,
|
||||
Script,
|
||||
SourceId,
|
||||
SourceActor,
|
||||
|
@ -39,6 +39,7 @@ import type {
|
|||
ThreadFront,
|
||||
ObjectFront,
|
||||
ExpressionResult,
|
||||
SourcesPacket,
|
||||
} from "./types";
|
||||
|
||||
import type { EventListenerCategoryList } from "../../actions/types";
|
||||
|
@ -317,13 +318,6 @@ function getProperties(thread: string, grip: Grip): Promise<*> {
|
|||
async function getFrames(thread: string) {
|
||||
const threadFront = lookupThreadFront(thread);
|
||||
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) =>
|
||||
createFrame(thread, frame, i)
|
||||
);
|
||||
|
@ -402,6 +396,14 @@ function registerSourceActor(sourceActorId: string, sourceId: 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) {
|
||||
return forEachThread(thread =>
|
||||
thread.toggleEventLogging(logEventBreakpoints)
|
||||
|
@ -416,6 +418,21 @@ function getAllThreadFronts(): ThreadFront[] {
|
|||
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
|
||||
// the debugger. If one is paused. Fake a `pause` RDP event
|
||||
// by directly calling the client event listener.
|
||||
|
@ -538,6 +555,8 @@ const clientCommands = {
|
|||
getFrames,
|
||||
pauseOnExceptions,
|
||||
toggleEventLogging,
|
||||
fetchSources,
|
||||
fetchThreadSources,
|
||||
checkIfAlreadyPaused,
|
||||
registerSourceActor,
|
||||
addThread,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// @flow
|
||||
|
||||
import type {
|
||||
SourcePacket,
|
||||
PausedPacket,
|
||||
ThreadFront,
|
||||
Target,
|
||||
|
@ -13,23 +14,19 @@ import type {
|
|||
|
||||
import Actions from "../../actions";
|
||||
|
||||
import { createPause } from "./create";
|
||||
import { createPause, prepareSourcePayload } 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 = [];
|
||||
|
@ -56,7 +53,6 @@ function attachAllTargets(currentTarget: Target): boolean {
|
|||
function setupEvents(dependencies: Dependencies): void {
|
||||
actions = dependencies.actions;
|
||||
sourceQueue.initialize(actions);
|
||||
store = dependencies.store;
|
||||
|
||||
threadFrontListeners = new WeakMap();
|
||||
}
|
||||
|
@ -82,7 +78,10 @@ async function paused(
|
|||
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);
|
||||
await actions.ensureSourceActor(
|
||||
threadFront.actorID,
|
||||
packet.frame.where.actor
|
||||
);
|
||||
}
|
||||
|
||||
const pause = createPause(threadFront.actor, packet);
|
||||
|
@ -103,36 +102,17 @@ function resumed(threadFront: ThreadFront): void {
|
|||
actions.resumed(threadFront.actorID);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method wait for the given source is registered in Redux store.
|
||||
*
|
||||
* @param {String} sourceActor
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function newSource(threadFront: ThreadFront, { source }: SourcePacket): void {
|
||||
sourceQueue.queue({
|
||||
type: "generated",
|
||||
data: prepareSourcePayload(threadFront, source),
|
||||
});
|
||||
}
|
||||
|
||||
const clientEvents = {
|
||||
paused,
|
||||
resumed,
|
||||
newSource,
|
||||
};
|
||||
|
||||
export {
|
||||
|
@ -141,5 +121,4 @@ export {
|
|||
addThreadEventListeners,
|
||||
removeThreadEventListeners,
|
||||
attachAllTargets,
|
||||
ensureSourceActor,
|
||||
};
|
||||
|
|
|
@ -58,6 +58,10 @@ async function loadInitialState() {
|
|||
};
|
||||
}
|
||||
|
||||
function getClient(connection: any) {
|
||||
return firefox;
|
||||
}
|
||||
|
||||
export async function onConnect(
|
||||
connection: Object,
|
||||
panelWorkers: Object,
|
||||
|
@ -70,7 +74,8 @@ export async function onConnect(
|
|||
|
||||
verifyPrefSchema();
|
||||
|
||||
const commands = firefox.clientCommands;
|
||||
const client = getClient(connection);
|
||||
const commands = client.clientCommands;
|
||||
|
||||
const initialState = await loadInitialState();
|
||||
const workers = bootstrapWorkers(panelWorkers);
|
||||
|
@ -82,7 +87,7 @@ export async function onConnect(
|
|||
initialState
|
||||
);
|
||||
|
||||
const connected = firefox.onConnect(connection, actions, store);
|
||||
const connected = client.onConnect(connection, actions);
|
||||
|
||||
await syncBreakpoints();
|
||||
syncXHRBreakpoints();
|
||||
|
@ -92,14 +97,10 @@ export async function onConnect(
|
|||
selectors,
|
||||
workers,
|
||||
connection,
|
||||
client: firefox.clientCommands,
|
||||
client: client.clientCommands,
|
||||
});
|
||||
|
||||
bootstrapApp(store, panel);
|
||||
await connected;
|
||||
return { store, actions, selectors, client: commands };
|
||||
}
|
||||
|
||||
export function onDisconnect() {
|
||||
return firefox.onDisconnect();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// @flow
|
||||
|
||||
import ReactDOM from "react-dom";
|
||||
import { onConnect, onDisconnect } from "./client";
|
||||
import { onConnect } from "./client";
|
||||
import { teardownWorkers } from "./utils/bootstrap";
|
||||
import sourceQueue from "./utils/source-queue";
|
||||
|
||||
|
@ -15,25 +15,17 @@ function unmountRoot() {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
bootstrap: ({
|
||||
targetList,
|
||||
resourceWatcher,
|
||||
devToolsClient,
|
||||
workers,
|
||||
panel,
|
||||
}: any) =>
|
||||
bootstrap: ({ targetList, devToolsClient, workers, panel }: any) =>
|
||||
onConnect(
|
||||
{
|
||||
tab: { clientType: "firefox" },
|
||||
targetList,
|
||||
resourceWatcher,
|
||||
devToolsClient,
|
||||
},
|
||||
workers,
|
||||
panel
|
||||
),
|
||||
destroy: () => {
|
||||
onDisconnect();
|
||||
unmountRoot();
|
||||
sourceQueue.clear();
|
||||
teardownWorkers();
|
||||
|
|
|
@ -25,10 +25,7 @@ export type PendingBreakpointsState = { [string]: PendingBreakpoint };
|
|||
function update(state: PendingBreakpointsState = {}, action: Action) {
|
||||
switch (action.type) {
|
||||
case "SET_BREAKPOINT":
|
||||
if (action.status === "start") {
|
||||
return setBreakpoint(state, action);
|
||||
}
|
||||
return state;
|
||||
return setBreakpoint(state, action);
|
||||
|
||||
case "REMOVE_BREAKPOINT":
|
||||
if (action.status === "start") {
|
||||
|
|
|
@ -47,11 +47,7 @@ export function insertResources<R: ResourceBound>(
|
|||
for (const resource of resources) {
|
||||
const { id } = resource;
|
||||
if (state.identity[id]) {
|
||||
throw new Error(
|
||||
`Resource "${id}" already exists, cannot insert ${JSON.stringify(
|
||||
resource
|
||||
)}`
|
||||
);
|
||||
throw new Error(`Resource "${id}" already exists, cannot insert`);
|
||||
}
|
||||
if (state.values[id]) {
|
||||
throw new Error(
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { throttle } from "lodash";
|
||||
import type { QueuedSourceData } from "../types";
|
||||
|
||||
// This SourceQueue module is now only used for source mapped sources
|
||||
let newQueuedSources;
|
||||
let queuedSources;
|
||||
let currentWork;
|
||||
|
|
|
@ -15,8 +15,6 @@ add_task(async function() {
|
|||
|
||||
// Navigate to a content process URL and check that the sources tree updates
|
||||
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.");
|
||||
|
||||
// Check that you can still break after target switching.
|
||||
|
|
|
@ -28,14 +28,15 @@ class SourceMapURLService {
|
|||
this._urlToIDMap = new Map();
|
||||
this._mapsById = new Map();
|
||||
this._sourcesLoading = null;
|
||||
this._onResourceAvailable = this._onResourceAvailable.bind(this);
|
||||
this._runningCallback = false;
|
||||
|
||||
this._syncPrevValue = this._syncPrevValue.bind(this);
|
||||
this._onSourceUpdatedEvent = this._onSourceUpdatedEvent.bind(this);
|
||||
this._clearAllState = this._clearAllState.bind(this);
|
||||
this._onNewStyleSheet = this._onNewStyleSheet.bind(this);
|
||||
|
||||
this._target.on("will-navigate", this._clearAllState);
|
||||
|
||||
this._target.on("source-updated", this._onSourceUpdatedEvent);
|
||||
Services.prefs.addObserver(SOURCE_MAP_PREF, this._syncPrevValue);
|
||||
}
|
||||
|
||||
|
@ -46,6 +47,7 @@ class SourceMapURLService {
|
|||
destroy() {
|
||||
this._clearAllState();
|
||||
this._target.off("will-navigate", this._clearAllState);
|
||||
this._target.off("source-updated", this._onSourceUpdatedEvent);
|
||||
Services.prefs.removeObserver(SOURCE_MAP_PREF, this._syncPrevValue);
|
||||
}
|
||||
|
||||
|
@ -202,10 +204,9 @@ class SourceMapURLService {
|
|||
this._pendingURLSubscriptions.clear();
|
||||
this._urlToIDMap.clear();
|
||||
|
||||
const { resourceWatcher } = this._toolbox;
|
||||
try {
|
||||
resourceWatcher.unwatchResources(
|
||||
[resourceWatcher.TYPES.STYLESHEET, resourceWatcher.TYPES.SOURCE],
|
||||
this._toolbox.resourceWatcher.unwatchResources(
|
||||
[this._toolbox.resourceWatcher.TYPES.STYLESHEET],
|
||||
{ onAvailable: this._onResourceAvailable }
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -216,6 +217,10 @@ class SourceMapURLService {
|
|||
this._sourcesLoading = null;
|
||||
}
|
||||
|
||||
_onSourceUpdatedEvent(sourceEvent) {
|
||||
this._onNewJavascript(sourceEvent.source);
|
||||
}
|
||||
|
||||
_onNewJavascript(source) {
|
||||
const { url, actor: id, sourceMapBaseURL, sourceMapURL } = source;
|
||||
|
||||
|
@ -416,44 +421,56 @@ class SourceMapURLService {
|
|||
if (!this._prefValue) {
|
||||
return null;
|
||||
}
|
||||
if (this._target.isWorkerTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._sourcesLoading) {
|
||||
const { resourceWatcher } = this._toolbox;
|
||||
const { STYLESHEET, SOURCE } = resourceWatcher.TYPES;
|
||||
|
||||
this._sourcesLoading = resourceWatcher.watchResources(
|
||||
[STYLESHEET, SOURCE],
|
||||
{
|
||||
onAvailable: this._onResourceAvailable,
|
||||
const sourcesLoading = (async () => {
|
||||
if (this._target.isWorkerTarget) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
this._onResourceAvailable = async resources => {
|
||||
if (this._sourcesLoading === sourcesLoading) {
|
||||
for (const resource of resources) {
|
||||
this._onNewStyleSheet(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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (resource.resourceType == STYLESHEET) {
|
||||
this._onNewStyleSheet(resource);
|
||||
} else if (resource.resourceType == SOURCE) {
|
||||
this._onNewJavascript(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_convertPendingURLSubscriptionsToID(url, id) {
|
||||
const urlSubscriptions = this._pendingURLSubscriptions.get(url);
|
||||
if (!urlSubscriptions) {
|
||||
|
|
|
@ -119,21 +119,16 @@ function createScript(url) {
|
|||
function waitForSourceLoad(toolbox, url) {
|
||||
info(`Waiting for source ${url} to be available...`);
|
||||
return new Promise(resolve => {
|
||||
const { resourceWatcher } = toolbox;
|
||||
const target = toolbox.target;
|
||||
|
||||
function onAvailable(sources) {
|
||||
for (const source of sources) {
|
||||
if (source.url === url) {
|
||||
resourceWatcher.unwatchResources([resourceWatcher.TYPES.SOURCE], {
|
||||
onAvailable,
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
function sourceHandler(sourceEvent) {
|
||||
if (sourceEvent && sourceEvent.source && sourceEvent.source.url === url) {
|
||||
resolve();
|
||||
target.off("source-updated", sourceHandler);
|
||||
}
|
||||
}
|
||||
resourceWatcher.watchResources([resourceWatcher.TYPES.SOURCE], {
|
||||
onAvailable,
|
||||
});
|
||||
|
||||
target.on("source-updated", sourceHandler);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3586,6 +3586,11 @@ Toolbox.prototype = {
|
|||
this._lastFocusedElement = null;
|
||||
this._pausedThreads = null;
|
||||
|
||||
if (this._sourceMapURLService) {
|
||||
this._sourceMapURLService.destroy();
|
||||
this._sourceMapURLService = null;
|
||||
}
|
||||
|
||||
if (this._sourceMapService) {
|
||||
this._sourceMapService.stopSourceMapWorker();
|
||||
this._sourceMapService = null;
|
||||
|
@ -3690,12 +3695,6 @@ Toolbox.prototype = {
|
|||
this._netMonitorAPI = null;
|
||||
}
|
||||
|
||||
if (this._sourceMapURLService) {
|
||||
await this._sourceMapURLService.waitForSourcesLoading();
|
||||
this._sourceMapURLService.destroy();
|
||||
this._sourceMapURLService = null;
|
||||
}
|
||||
|
||||
this._removeWindowListeners();
|
||||
this._removeChromeEventHandlerEvents();
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ function TargetMixin(parentClass) {
|
|||
this._forceChrome = false;
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this._onNewSource = this._onNewSource.bind(this);
|
||||
|
||||
this.threadFront = null;
|
||||
|
||||
|
@ -565,9 +566,16 @@ function TargetMixin(parentClass) {
|
|||
|
||||
await this.threadFront.attach(options);
|
||||
|
||||
this.threadFront.on("newSource", this._onNewSource);
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -587,6 +595,11 @@ function TargetMixin(parentClass) {
|
|||
}
|
||||
this.off("tabDetached", this.destroy);
|
||||
|
||||
// Remove listeners set in attachThread
|
||||
if (this.threadFront) {
|
||||
this.threadFront.off("newSource", this._onNewSource);
|
||||
}
|
||||
|
||||
// Remove listeners set in attachConsole
|
||||
if (this.removeOnInspectObjectListener) {
|
||||
this.removeOnInspectObjectListener();
|
||||
|
|
|
@ -233,11 +233,6 @@ BoxModel.prototype = {
|
|||
* Hides the box-model highlighter on the currently selected element.
|
||||
*/
|
||||
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.TYPES.BOXMODEL
|
||||
);
|
||||
|
|
|
@ -1927,11 +1927,6 @@ Inspector.prototype = {
|
|||
* Options passed to the highlighter actor.
|
||||
*/
|
||||
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.TYPES.BOXMODEL,
|
||||
nodeFront,
|
||||
|
|
|
@ -94,9 +94,6 @@ add_task(async function testForeignCookieBlockedMessage() {
|
|||
message,
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -127,9 +124,6 @@ add_task(async function testLimitForeignCookieBlockedMessage() {
|
|||
message,
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -150,9 +144,6 @@ add_task(async function testAllCookieBlockedMessage() {
|
|||
message,
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -173,9 +164,6 @@ add_task(async function testTrackerCookieBlockedMessage() {
|
|||
message,
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -196,9 +184,6 @@ add_task(async function testForeignCookiePartitionedMessage() {
|
|||
message,
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -227,9 +212,6 @@ add_task(async function testCookieBlockedByPermissionMessage() {
|
|||
message,
|
||||
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();
|
||||
|
||||
// Remove the custom permission.
|
||||
|
|
|
@ -144,11 +144,6 @@ add_task(async function testInvalidSameSiteMessage() {
|
|||
`| ${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();
|
||||
});
|
||||
|
||||
|
|
|
@ -144,10 +144,7 @@ class Front extends Pool {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
@ -11,7 +11,6 @@ DevToolsModules(
|
|||
'network-events.js',
|
||||
'platform-messages.js',
|
||||
'root-node.js',
|
||||
'source.js',
|
||||
'stylesheet.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 onDestroyed = this._onResourceDestroyed.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]({
|
||||
targetList: this.targetList,
|
||||
targetFront,
|
||||
|
@ -750,7 +747,6 @@ ResourceWatcher.TYPES = ResourceWatcher.prototype.TYPES = {
|
|||
NETWORK_EVENT: "network-event",
|
||||
WEBSOCKET: "websocket",
|
||||
NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
|
||||
SOURCE: "source",
|
||||
};
|
||||
module.exports = { ResourceWatcher };
|
||||
|
||||
|
@ -795,8 +791,6 @@ const LegacyListeners = {
|
|||
.WEBSOCKET]: require("devtools/shared/resources/legacy-listeners/websocket"),
|
||||
[ResourceWatcher.TYPES
|
||||
.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.
|
||||
|
|
|
@ -9,8 +9,6 @@ support-files =
|
|||
network_document.html
|
||||
fission_document.html
|
||||
fission_iframe.html
|
||||
sources.html
|
||||
sources.js
|
||||
style_document.css
|
||||
style_document.html
|
||||
style_iframe.css
|
||||
|
@ -21,7 +19,6 @@ support-files =
|
|||
test_worker.js
|
||||
websocket_backend_wsh.py
|
||||
websocket_frontend.html
|
||||
worker-sources.js
|
||||
|
||||
[browser_resources_client_caching.js]
|
||||
[browser_resources_console_messages.js]
|
||||
|
@ -36,7 +33,6 @@ skip-if = os == "linux" #Bug 1655183
|
|||
[browser_resources_platform_messages.js]
|
||||
[browser_resources_root_node.js]
|
||||
[browser_resources_several_resources.js]
|
||||
[browser_resources_sources.js]
|
||||
[browser_resources_stylesheets.js]
|
||||
[browser_resources_target_destroy.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() {}
|
Загрузка…
Ссылка в новой задаче