Backed out 4 changesets (bug 1611096) for dt failures on browser_animation_inspector_exists.js . CLOSED TREE

Backed out changeset 0fe3613ee36b (bug 1611096)
Backed out changeset c86433fe8cd0 (bug 1611096)
Backed out changeset a7242f506aa6 (bug 1611096)
Backed out changeset 3703fbfe567a (bug 1611096)
This commit is contained in:
Narcis Beleuzu 2020-05-08 20:34:54 +03:00
Родитель 217614b78d
Коммит 43ad94eeae
14 изменённых файлов: 201 добавлений и 448 удалений

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

@ -112,21 +112,18 @@ async function testReload(shortcut, toolbox) {
for (const { type } of mutations) {
if (type === "documentUnload") {
this._isDocumentUnloaded = true;
} else if (type === "newRoot") {
this._isNewRooted = true;
}
}
},
onNewRootNode() {
this._isNewRooted = true;
},
isReady() {
return this._isDocumentUnloaded && this._isNewRooted;
},
};
observer.onMutation = observer.onMutation.bind(observer);
observer.onNewRootNode = observer.onNewRootNode.bind(observer);
walker.on("mutations", observer.onMutation);
walker.watchRootNode(observer.onNewRootNode);
// If we have a jsdebugger panel, wait for it to complete its reload
const jsdebugger = toolbox.getPanel("jsdebugger");
@ -140,7 +137,6 @@ async function testReload(shortcut, toolbox) {
// Wait for the documentUnload and newRoot were fired.
await waitUntil(() => observer.isReady());
walker.off("mutations", observer.onMutation);
walker.unwatchRootNode(observer.onNewRootNode);
await onReloaded;
resolve();
};

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

@ -4,6 +4,7 @@
"use strict";
const defer = require("devtools/shared/defer");
const {
FrontClassWithSpec,
types,
@ -23,33 +24,21 @@ loader.lazyRequireGetter(
class WalkerFront extends FrontClassWithSpec(walkerSpec) {
constructor(client, targetFront, parentFront) {
super(client, targetFront, parentFront);
this._createRootNodePromise();
this._orphaned = new Set();
this._retainedOrphans = new Set();
// Set to true if cleanup should be requested after every mutation list.
this.autoCleanup = true;
this._rootNodeWatchers = 0;
this._onRootNodeAvailable = this._onRootNodeAvailable.bind(this);
this._onRootNodeDestroyed = this._onRootNodeDestroyed.bind(this);
this.before("new-mutations", this.onMutations.bind(this));
// Those events will only be emitted if we are watching root nodes on the
// actor. See `watchRootNode`.
this.on("root-available", this._onRootNodeAvailable);
this.on("root-destroyed", this._onRootNodeDestroyed);
}
// Update the object given a form representation off the wire.
form(json) {
this.actorID = json.actor;
// The rootNode property should usually be provided via watchRootNode.
// However tests are currently using the walker front without explicitly
// calling watchRootNode, so we keep this assignment as a fallback.
this.rootNode = types.getType("domnode").read(json.root, this);
this._rootNodeDeferred.resolve(this.rootNode);
// FF42+ the actor starts exposing traits
this.traits = json.traits || {};
}
@ -60,27 +49,18 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
* method returns a promise that will resolve to the root node when it is
* set.
*/
async getRootNode() {
// We automatically start and stop watching when getRootNode is called so
// that consumers using getRootNode without watchRootNode can still get a
// correct rootNode. Otherwise they might receive an outdated node.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1633348.
this._rootNodeWatchers++;
if (this.traits.watchRootNode && this._rootNodeWatchers === 1) {
await super.watchRootNode();
}
getRootNode() {
return this._rootNodeDeferred.promise;
}
let rootNode = this.rootNode;
if (!rootNode) {
rootNode = await this.once("root-available");
}
this._rootNodeWatchers--;
if (this.traits.watchRootNode && this._rootNodeWatchers === 0) {
super.unwatchRootNode();
}
return rootNode;
/**
* Create the root node promise, triggering the "new-root" notification
* on resolution.
*/
async _createRootNodePromise() {
this._rootNodeDeferred = defer();
await this._rootNodeDeferred.promise;
this.emit("new-root");
}
/**
@ -262,18 +242,24 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
const mutations = await super.getMutations(options);
const emitMutations = [];
for (const change of mutations) {
// Backward compatibility. FF77 or older will send "new root" information
// via mutations. Newer servers use the new-root-available event.
if (change.type === "newRoot") {
const rootNode = types.getType("domnode").read(change.target, this);
this.emit("root-available", rootNode);
// Don't process this as a regular mutation.
continue;
}
// The target is only an actorID, get the associated front.
const targetID = change.target;
const targetFront = this.get(targetID);
let targetID;
let targetFront;
if (change.type === "newRoot") {
// We may receive a new root without receiving any documentUnload
// beforehand. Like when opening tools in middle of a document load.
if (this.rootNode) {
this._createRootNodePromise();
}
this.rootNode = types.getType("domnode").read(change.target, this);
this._rootNodeDeferred.resolve(this.rootNode);
targetID = this.rootNode.actorID;
targetFront = this.rootNode;
} else {
targetID = change.target;
targetFront = this.get(targetID);
}
if (!targetFront) {
console.warn(
@ -351,8 +337,8 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
}
}
} else if (change.type === "documentUnload") {
if (!this.traits.watchRootNode && targetFront === this.rootNode) {
this.emit("root-destroyed");
if (targetFront === this.rootNode) {
this._createRootNodePromise();
}
// We try to give fronts instead of actorIDs, but these fronts need
@ -559,43 +545,6 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
return querySelectors(nodeFront);
}
_onRootNodeAvailable(rootNode) {
this.rootNode = rootNode;
}
_onRootNodeDestroyed() {
this.rootNode = null;
}
async watchRootNode(onRootNodeAvailable) {
this.on("root-available", onRootNodeAvailable);
this._rootNodeWatchers++;
if (this.traits.watchRootNode && this._rootNodeWatchers === 1) {
await super.watchRootNode();
} else if (this.rootNode) {
// This else branch is here for 2 reasons:
// - subsequent calls to `watchRootNode`:
// When debugging recent servers if we skip `super.watchRootNode`,
// we should call `onRootNodeAvailable`` immediately, because the actor
// will not emit "root-available". And we should not emit it from the
// Front, because it would notify other consumers unnecessarily.
// - backward compatibility for FF77 or older:
// we assume that a node will already be available when calling
// `watchRootNode`, so we call `onRootNodeAvailable` immediately.
await onRootNodeAvailable(this.rootNode);
}
}
unwatchRootNode(onRootNodeAvailable) {
this.off("root-available", onRootNodeAvailable);
this._rootNodeWatchers--;
if (this.traits.watchRootNode && this._rootNodeWatchers === 0) {
super.unwatchRootNode();
}
}
}
exports.WalkerFront = WalkerFront;

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

@ -146,7 +146,6 @@ function Inspector(toolbox) {
this.panelWin.inspector = this;
this.telemetry = toolbox.telemetry;
this.store = createStore();
this.isReady = false;
// Map [panel id => panel instance]
// Stores all the instances of sidebar panels like rule view, computed view, ...
@ -166,7 +165,7 @@ function Inspector(toolbox) {
this.onHostChanged = this.onHostChanged.bind(this);
this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
this.onNewSelection = this.onNewSelection.bind(this);
this.onRootNodeAvailable = this.onRootNodeAvailable.bind(this);
this.onNewRoot = this.onNewRoot.bind(this);
this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
this.onShowBoxModelHighlighterForNode = this.onShowBoxModelHighlighterForNode.bind(
this
@ -189,11 +188,6 @@ Inspector.prototype = {
// Localize all the nodes containing a data-localization attribute.
localizeMarkup(this.panelDoc);
// The markup view will be initialized in onRootNodeAvailable, which will be
// called through watchTargets and _onTargetAvailable, when a root node is
// available for the top-level target.
this._onFirstMarkupLoaded = this.once("markuploaded");
await this.toolbox.targetList.watchTargets(
[this.toolbox.targetList.TYPES.FRAME],
this._onTargetAvailable,
@ -205,6 +199,8 @@ Inspector.prototype = {
this.previousURL = this.currentTarget.url;
this.styleChangeTracker = new InspectorStyleChangeTracker(this);
this._markupBox = this.panelDoc.getElementById("markup-box");
return this._deferredOpen();
},
@ -220,10 +216,23 @@ Inspector.prototype = {
await Promise.all([
this._getCssProperties(),
this._getDefaultSelection(),
this._getAccessibilityFront(),
]);
this.walker.watchRootNode(this.onRootNodeAvailable);
// When we navigate to another process and switch to a new
// target and the inspector is already ready, we want to
// update the markup view accordingly. So force a new-root event.
// For the initial panel startup, the initial top level target
// update the markup view from _deferredOpen.
// We might want to followup here in order to share the same
// codepath between the initial top level target and the next
// one we switch to. i.e. extract from deferredOpen code
// which has to be called only once on inspector startup.
// Then move the rest to onNewRoot and always call onNewRoot from here.
if (this.isReady) {
this.onNewRoot();
}
},
_onTargetDestroyed({ type, targetFront, isTopLevel }) {
@ -342,6 +351,18 @@ Inspector.prototype = {
},
_deferredOpen: async function() {
const onMarkupLoaded = this.once("markuploaded");
this._initMarkup();
this.isReady = false;
// Set the node front so that the markup and sidebar panels will have the selected
// nodeFront ready when they're initialized.
if (this._defaultNode) {
this.selection.setNodeFront(this._defaultNode, {
reason: "inspector-open",
});
}
// Setup the splitter before the sidebar is displayed so, we don't miss any events.
this.setupSplitter();
@ -354,7 +375,7 @@ Inspector.prototype = {
// Setup the sidebar panels.
this.setupSidebar();
await this._onFirstMarkupLoaded;
await onMarkupLoaded;
this.isReady = true;
// All the components are initialized. Take care of the remaining initialization
@ -362,9 +383,11 @@ Inspector.prototype = {
this.breadcrumbs = new HTMLBreadcrumbs(this);
this.setupExtensionSidebars();
this.setupSearchBox();
await this.setupToolbar();
this.onNewSelection();
this.walker.on("new-root", this.onNewRoot);
this.toolbox.on("host-changed", this.onHostChanged);
this.selection.on("new-node-front", this.onNewSelection);
this.selection.on("detached-front", this.onDetached);
@ -400,6 +423,14 @@ Inspector.prototype = {
return this.accessibilityFront;
},
_getDefaultSelection: function() {
// This may throw if the document is still loading and we are
// refering to a dead about:blank document
return this._getDefaultNodeForSelection().catch(
this._handleRejectionIfNotDestroyed
);
},
/**
* Return a promise that will resolve to the default node for selection.
*/
@ -1282,7 +1313,7 @@ Inspector.prototype = {
/**
* Reset the inspector on new root mutation.
*/
onRootNodeAvailable: function() {
onNewRoot: function() {
// Record new-root timing for telemetry
this._newRootStart = this.panelWin.performance.now();
@ -1297,10 +1328,9 @@ Inspector.prototype = {
return;
}
this._pendingSelection = null;
this.selection.setNodeFront(defaultNode, {
reason: "inspector-default-selection",
});
this.selection.setNodeFront(defaultNode, { reason: "navigateaway" });
this.once("markuploaded", this.onMarkupLoaded);
this._initMarkup();
// Setup the toolbar again, since its content may depend on the current document.
@ -1612,6 +1642,10 @@ Inspector.prototype = {
this.currentTarget.threadFront.off("paused", this.handleThreadPaused);
this.currentTarget.threadFront.off("resumed", this.handleThreadResumed);
if (this.walker) {
this.walker.off("new-root", this.onNewRoot);
}
this.cancelUpdate();
this.sidebar.destroy();
@ -1684,8 +1718,6 @@ Inspector.prototype = {
},
_initMarkup: function() {
this.once("markuploaded", this.onMarkupLoaded);
if (!this._markupFrame) {
this._markupFrame = this.panelDoc.createElement("iframe");
this._markupFrame.setAttribute(
@ -1696,7 +1728,6 @@ Inspector.prototype = {
// This is needed to enable tooltips inside the iframe document.
this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
this._markupBox = this.panelDoc.getElementById("markup-box");
this._markupBox.style.visibility = "hidden";
this._markupBox.appendChild(this._markupFrame);
@ -1729,9 +1760,7 @@ Inspector.prototype = {
destroyPromise = promise.resolve();
}
if (this._markupBox) {
this._markupBox.style.visibility = "hidden";
}
this._markupBox.style.visibility = "hidden";
return destroyPromise;
},
@ -1746,14 +1775,14 @@ Inspector.prototype = {
this.toolbox.tellRDMAboutPickerState(true, PICKER_TYPES.EYEDROPPER);
this.inspectorFront.once("color-pick-canceled", this.onEyeDropperDone);
this.inspectorFront.once("color-picked", this.onEyeDropperDone);
this.once("new-root", this.onEyeDropperDone);
this.walker.once("new-root", this.onEyeDropperDone);
},
stopEyeDropperListeners: function() {
this.toolbox.tellRDMAboutPickerState(false, PICKER_TYPES.EYEDROPPER);
this.inspectorFront.off("color-pick-canceled", this.onEyeDropperDone);
this.inspectorFront.off("color-picked", this.onEyeDropperDone);
this.off("new-root", this.onEyeDropperDone);
this.walker.off("new-root", this.onEyeDropperDone);
},
onEyeDropperDone: function() {

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

@ -889,8 +889,9 @@ MarkupView.prototype = {
* Given the known reason, should the current selection be briefly highlighted
* In a few cases, we don't want to highlight the node:
* - If the reason is null (used to reset the selection),
* - if it's "inspector-default-selection" (initial node selected, either when
* opening the inspector or after a navigation/reload)
* - if it's "inspector-open" (when the inspector opens up, let's not
* highlight the default node)
* - if it's "navigateaway" (since the page is being navigated away from)
* - if it's "test" (this is a special case for mochitest. In tests, we often
* need to select elements but don't necessarily want the highlighter to come
* and go after a delay as this might break test scenarios)
@ -901,7 +902,8 @@ MarkupView.prototype = {
_shouldNewSelectionBeHighlighted: function() {
const reason = this.inspector.selection.reason;
const unwantedReasons = [
"inspector-default-selection",
"inspector-open",
"navigateaway",
"nodeselected",
"test",
];

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

@ -73,7 +73,6 @@ skip-if = (os == 'win' && processor == 'aarch64') # bug 1533490
[browser_inspector_destroy-after-navigation.js]
[browser_inspector_destroy-before-ready.js]
[browser_inspector_expand-collapse.js]
[browser_inspector_fission_switch_target.js]
[browser_inspector_highlighter-01.js]
[browser_inspector_highlighter-02.js]
[browser_inspector_highlighter-03.js]

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

@ -1,33 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test target-switching with the inspector.
// This test should check both fission and non-fission target switching.
const PARENT_PROCESS_URI = "about:robots";
const EXAMPLE_COM_URI =
"http://example.com/document-builder.sjs?html=<div id=com>com";
const EXAMPLE_NET_URI =
"http://example.net/document-builder.sjs?html=<div id=net>net";
add_task(async function() {
await pushPref("devtools.target-switching.enabled", true);
const { inspector } = await openInspectorForURL(PARENT_PROCESS_URI);
const aboutRobotsNodeFront = await getNodeFront(".title-text", inspector);
ok(!!aboutRobotsNodeFront, "Can retrieve a node front from about:robots");
info("Navigate to " + EXAMPLE_COM_URI);
await navigateTo(EXAMPLE_COM_URI);
const comNodeFront = await getNodeFront("#com", inspector);
ok(!!comNodeFront, "Can retrieve a node front from example.com");
await navigateTo(EXAMPLE_NET_URI);
const netNodeFront = await getNodeFront("#net", inspector);
ok(!!netNodeFront, "Can retrieve a node front from example.net");
await navigateTo(PARENT_PROCESS_URI);
const aboutRobotsNodeFront2 = await getNodeFront(".title-text", inspector);
ok(!!aboutRobotsNodeFront2, "Can retrieve a node front from about:robots");
});

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

@ -27,6 +27,7 @@ add_task(async function() {
// Create/remove an extra one now, after the load event.
info("Creating and removing an iframe.");
const onMarkupLoaded = inspector.once("markuploaded");
testActor.eval(
"new " +
function() {
@ -41,6 +42,9 @@ add_task(async function() {
"The after-load iframe should have been removed."
);
info("Waiting for markup-view to load.");
await onMarkupLoaded;
// Assert that the markup-view is displayed and works
ok(!(await testActor.hasNode("iframe")), "Iframe has been removed.");
is(

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

@ -200,6 +200,7 @@ const MUTATIONS_THROTTLING_DELAY = 100;
const IMMEDIATE_MUTATIONS = [
"documentUnload",
"frameLoad",
"newRoot",
"pseudoClassLock",
// These should be delivered right away in order to be sure that the
@ -349,16 +350,6 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
// managed.
this.rootNode = this.document();
// By default the walker will not notify about new root nodes and waits for
// a consumer to explicitly ask to be notified about root nodes to start
// emitting related events.
this._isWatchingRootNode = false;
// XXX: Ideally the walker would also use a watch API on the target actor to
// know if "window-ready" has already been fired. Without such an API there
// is a risk that the walker will fire several new-root-available for the
// same node.
this._emittedRootNode = null;
this.layoutChangeObserver = getLayoutChangesObserver(this.targetActor);
this._onReflows = this._onReflows.bind(this);
this.layoutChangeObserver.on("reflows", this._onReflows);
@ -369,40 +360,6 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
eventListenerService.addListenerChangeListener(this._onEventListenerChange);
},
watchRootNode() {
if (this._isWatchingRootNode) {
throw new Error("WalkerActor::watchRootNode should only be called once");
}
this._isWatchingRootNode = true;
if (this.rootNode && this._isRootDocumentReady()) {
this._emitNewRoot();
}
},
unwatchRootNode() {
this._isWatchingRootNode = false;
this._emittedRootNode = null;
},
_emitNewRoot() {
if (!this._isWatchingRootNode || this._emittedRootNode === this.rootNode) {
return;
}
this._emittedRootNode = this.rootNode;
this.emit("root-available", this.rootNode);
},
_isRootDocumentReady() {
if (!this.rootDoc) {
return false;
}
const { readyState } = this.rootDoc;
return readyState == "interactive" || readyState == "complete";
},
/**
* Callback for eventListenerService.addListenerChangeListener
* @param nsISimpleEnumerator changesEnum
@ -429,10 +386,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
return {
actor: this.actorID,
root: this.rootNode.form(),
traits: {
// watch/unwatchRootNode are available starting with Fx77
watchRootNode: true,
},
traits: {},
};
},
@ -2566,7 +2520,10 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
this.rootWin = window;
this.rootDoc = window.document;
this.rootNode = this.document();
this._emitNewRoot();
this.queueMutation({
type: "newRoot",
target: this.rootNode.form(),
});
return;
}
const frame = getFrameElement(window);
@ -2642,9 +2599,6 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
if (this.rootDoc === doc) {
this.rootDoc = null;
if (this._isWatchingRootNode) {
this.emit("root-destroyed", this.rootNode);
}
this.rootNode = null;
}

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

@ -125,7 +125,6 @@ skip-if = true # Bug 1593562
[browser_inspector-shadow.js]
[browser_inspector-traversal.js]
[browser_inspector-utils.js]
[browser_inspector_walker_watch_root_node.js]
[browser_layout_getGrids.js]
[browser_layout_simple.js]
[browser_markers-cycle-collection.js]

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

@ -1,165 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from inspector-helpers.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/server/tests/browser/inspector-helpers.js",
this
);
/**
* Test the watch/unwatchRootNodeFront API available on the walker front.
* This first test checks that the callbacks are called the expected number of
* time.
*/
add_task(async function() {
const { walker } = await initInspectorFront(`data:text/html,<div>`);
const browser = gBrowser.selectedBrowser;
info("Call watchRootNode");
let onAvailableCounter1 = 0;
const onAvailable1 = () => onAvailableCounter1++;
await walker.watchRootNode(onAvailable1);
info("Wait until onAvailable1 has been called");
await waitUntil(() => onAvailableCounter1 === 1);
is(onAvailableCounter1, 1, "onAvailable1 has been called 1 time");
info("Reload the selected browser");
browser.reload();
info("Wait until the watchRootNode callback has been called");
await waitUntil(() => onAvailableCounter1 === 2);
is(onAvailableCounter1, 2, "onAvailable1 has been called 2 times");
info("Call watchNode with only the onAvailable callback");
let onAvailableCounter2 = 0;
const onAvailable2 = () => onAvailableCounter2++;
await walker.watchRootNode(onAvailable2);
info("Wait until onAvailable2 has been called");
await waitUntil(() => onAvailableCounter2 === 1);
is(onAvailableCounter2, 1, "onAvailable2 has been called 1 time");
info("Reload the selected browser");
browser.reload();
info("Wait until the watchRootNode callback has been called");
await waitUntil(() => {
return onAvailableCounter1 === 3 && onAvailableCounter2 === 2;
});
is(onAvailableCounter1, 3, "onAvailable1 has been called 3 times");
is(onAvailableCounter2, 2, "onAvailable2 has been called 2 times");
info("Call unwatchRootNode for the onAvailable2 callback");
walker.unwatchRootNode(onAvailable2);
info("Reload the selected browser");
browser.reload();
info("Wait until the watchRootNode callback has been called");
await waitUntil(() => onAvailableCounter1 === 4);
is(onAvailableCounter1, 4, "onAvailable1 has been called 4 times");
is(onAvailableCounter2, 2, "onAvailable2 was not called again");
info("Call unwatchRootNode for the onAvailable1 callback");
walker.unwatchRootNode(onAvailable1);
info("Reload the selected browser");
const reloaded = BrowserTestUtils.browserLoaded(browser);
browser.reload();
await reloaded;
is(onAvailableCounter1, 4, "onAvailable1 was not called again");
is(onAvailableCounter2, 2, "onAvailable2 was not called again");
// After removing all watchers, we watch again to check that the front is
// able to resume watching correctly.
info("Call watchRootNode for the onAvailable1 callback again");
await walker.watchRootNode(onAvailable1);
info("Reload the selected browser");
browser.reload();
await waitUntil(() => onAvailableCounter1 === 5);
is(onAvailableCounter1, 5, "onAvailable1 was called again once");
is(onAvailableCounter2, 2, "onAvailable2 was not called again");
// Cleanup.
walker.unwatchRootNode(onAvailable1);
});
/**
* Test that the actor will re-emit an already emitted root if we call unwatch
* and then watch again.
*/
add_task(async function testCallingWatchSuccessivelyWithoutReload() {
const { walker } = await initInspectorFront(`data:text/html,<div>`);
info("Call watchRootNode");
let onAvailableCounter = 0;
const onAvailable = () => onAvailableCounter++;
await walker.watchRootNode(onAvailable);
info("Wait until onAvailable has been called");
await waitUntil(() => onAvailableCounter === 1);
info("Unwatch and watch again");
walker.unwatchRootNode(onAvailable);
await walker.watchRootNode(onAvailable);
// The actor already notified about the current root node, but it should
// do it again since we called unwatch first.
info("Wait until the callback is called again");
await waitUntil(() => onAvailableCounter === 2);
// cleanup
walker.unwatchRootNode(onAvailable);
});
/**
* Test that the watchRootNode API provides the expected node fronts.
*/
add_task(async function testRootNodeFrontIsCorrect() {
const { walker } = await initInspectorFront(`data:text/html,<div id=div1>`);
const browser = gBrowser.selectedBrowser;
info("Call watchRootNode");
let rootNodeResolve;
let rootNodePromise = new Promise(r => (rootNodeResolve = r));
const onAvailable = rootNodeFront => rootNodeResolve(rootNodeFront);
await walker.watchRootNode(onAvailable);
info("Wait until onAvailable has been called");
const root1 = await rootNodePromise;
ok(!!root1, "onAvailable has been called with a valid argument");
info("Check we can query an expected node under the retrieved root");
const div1 = await walker.querySelector(root1, "div");
is(div1.getAttribute("id"), "div1", "Correct root node retrieved");
info("Reload the selected browser");
rootNodePromise = new Promise(r => (rootNodeResolve = r));
browser.reload();
const root2 = await rootNodePromise;
ok(
root1 !== root2,
"onAvailable has been called with a different node front after reload"
);
info("Navigate to another URL");
rootNodePromise = new Promise(r => (rootNodeResolve = r));
BrowserTestUtils.loadURI(browser, `data:text/html,<div id=div2>`);
const root3 = await rootNodePromise;
info("Check we can query an expected node under the retrieved root");
const div2 = await walker.querySelector(root3, "div");
is(div2.getAttribute("id"), "div2", "Correct root node retrieved");
walker.unwatchRootNode(onAvailable);
});

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

@ -1,6 +1,6 @@
/* exported attachURL, promiseDone,
promiseOnce,
addTest, addAsyncTest,
promiseOnce, isNewRoot,
waitForMutation, addTest, addAsyncTest,
runNextTest, _documentWalker */
"use strict";
@ -107,6 +107,32 @@ function promiseDone(currentPromise) {
});
}
// Mutation list testing
function isNewRoot(change) {
return change.type === "newRoot";
}
// Load mutations aren't predictable, so keep accumulating mutations until
// the one we're looking for shows up.
function waitForMutation(walker, test, mutations = []) {
return new Promise(resolve => {
for (const change of mutations) {
if (test(change)) {
resolve(mutations);
}
}
walker.once("mutations", newMutations => {
waitForMutation(walker, test, mutations.concat(newMutations)).then(
finalMutations => {
resolve(finalMutations);
}
);
});
});
}
var _tests = [];
function addTest(test) {
_tests.push(test);

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

@ -21,25 +21,12 @@ window.onload = function() {
let gWalker = null;
let gDoc = null;
async function reloadTarget() {
const onNewRootAvailable = gWalker.once("root-available");
gDoc.defaultView.location.reload();
await onNewRootAvailable;
}
addAsyncTest(async function() {
const url = document.getElementById("inspectorContent").href;
const { target, doc } = await attachURL(url);
gDoc = doc;
const inspector = await target.getFront("inspector");
gWalker = inspector.walker;
info("Start watching for root nodes and wait for the initial root node");
let watchRootNodeResolve;
const onInitialRootNodePromise = new Promise(r => (watchRootNodeResolve = r));
gWalker.watchRootNode(() => watchRootNodeResolve());
await onInitialRootNodePromise;
runNextTest();
});
@ -48,9 +35,10 @@ addAsyncTest(async function() {
"walker.children(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "body");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.children(nodeFront);
await newRoot;
ok(true, "The call to walker.children() didn't fail");
runNextTest();
@ -61,8 +49,10 @@ addAsyncTest(async function() {
"walker.nextSibling(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.nextSibling(nodeFront);
await newRoot;
ok(true, "The call to walker.nextSibling() didn't fail");
runNextTest();
@ -73,8 +63,10 @@ addAsyncTest(async function() {
"walker.previousSibling(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.previousSibling(nodeFront);
await newRoot;
ok(true, "The call to walker.previousSibling() didn't fail");
runNextTest();
@ -86,8 +78,10 @@ addAsyncTest(async function() {
"shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.addPseudoClassLock(nodeFront, ":hover");
await newRoot;
ok(true, "The call to walker.addPseudoClassLock() didn't fail");
runNextTest();
@ -99,8 +93,10 @@ addAsyncTest(async function() {
"shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.removePseudoClassLock(nodeFront, ":hover");
await newRoot;
ok(true, "The call to walker.removePseudoClassLock() didn't fail");
runNextTest();
@ -112,8 +108,10 @@ addAsyncTest(async function() {
"shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.clearPseudoClassLocks(nodeFront);
await newRoot;
ok(true, "The call to walker.clearPseudoClassLocks() didn't fail");
runNextTest();
@ -124,8 +122,10 @@ addAsyncTest(async function() {
"walker.innerHTML(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.innerHTML(nodeFront);
await newRoot;
ok(true, "The call to walker.innerHTML() didn't fail");
runNextTest();
@ -136,8 +136,10 @@ addAsyncTest(async function() {
"walker.setInnerHTML(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.setInnerHTML(nodeFront, "<span>innerHTML changed</span>");
await newRoot;
ok(true, "The call to walker.setInnerHTML() didn't fail");
runNextTest();
@ -148,8 +150,10 @@ addAsyncTest(async function() {
"walker.outerHTML(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.outerHTML(nodeFront);
await newRoot;
ok(true, "The call to walker.outerHTML() didn't fail");
runNextTest();
@ -160,8 +164,10 @@ addAsyncTest(async function() {
"walker.setOuterHTML(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.setOuterHTML(nodeFront, "<h1><span>innerHTML changed</span></h1>");
await newRoot;
ok(true, "The call to walker.setOuterHTML() didn't fail");
runNextTest();
@ -173,9 +179,11 @@ addAsyncTest(async function() {
"fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.insertAdjacentHTML(nodeFront, "afterEnd",
"<span>new adjacent HTML</span>");
await newRoot;
ok(true, "The call to walker.insertAdjacentHTML() didn't fail");
runNextTest();
@ -186,13 +194,15 @@ addAsyncTest(async function() {
"walker.removeNode(nodeFront) before the load completes should throw");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
let hasThrown = false;
try {
await gWalker.removeNode(nodeFront);
} catch (e) {
hasThrown = true;
}
await newRoot;
ok(hasThrown, "The call to walker.removeNode() threw");
runNextTest();
@ -205,13 +215,15 @@ addAsyncTest(async function() {
const nodeFront1 = await gWalker.querySelector(gWalker.rootNode, "h1");
const nodeFront2 = await gWalker.querySelector(gWalker.rootNode, "#longstring");
const nodeFront3 = await gWalker.querySelector(gWalker.rootNode, "#shortstring");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
let hasThrown = false;
try {
await gWalker.removeNodes([nodeFront1, nodeFront2, nodeFront3]);
} catch (e) {
hasThrown = true;
}
await newRoot;
ok(hasThrown, "The call to walker.removeNodes() threw");
runNextTest();
@ -224,8 +236,10 @@ addAsyncTest(async function() {
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
const newParentFront = await gWalker.querySelector(gWalker.rootNode, "#longlist");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.insertBefore(nodeFront, newParentFront);
await newRoot;
ok(true, "The call to walker.insertBefore() didn't fail");
runNextTest();
@ -239,8 +253,10 @@ addAsyncTest(async function() {
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
const newParentFront = await gWalker.querySelector(gWalker.rootNode, "#longlist");
const siblingFront = await gWalker.querySelector(gWalker.rootNode, "#b");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.insertBefore(nodeFront, newParentFront, siblingFront);
await newRoot;
ok(true, "The call to walker.insertBefore() didn't fail");
runNextTest();
@ -251,8 +267,10 @@ addAsyncTest(async function() {
"walker.editTagName(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.editTagName(nodeFront, "h2");
await newRoot;
ok(true, "The call to walker.editTagName() didn't fail");
runNextTest();
@ -263,8 +281,10 @@ addAsyncTest(async function() {
"walker.hideNode(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.hideNode(nodeFront);
await newRoot;
ok(true, "The call to walker.hideNode() didn't fail");
runNextTest();
@ -275,8 +295,10 @@ addAsyncTest(async function() {
"walker.unhideNode(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.unhideNode(nodeFront);
await newRoot;
ok(true, "The call to walker.unhideNode() didn't fail");
runNextTest();
@ -287,8 +309,10 @@ addAsyncTest(async function() {
"walker.releaseNode(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "h1");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.releaseNode(nodeFront);
await newRoot;
ok(true, "The call to walker.releaseNode() didn't fail");
runNextTest();
@ -299,8 +323,10 @@ addAsyncTest(async function() {
"walker.querySelector(nodeFront) before the load completes shouldn't fail");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "body");
await reloadTarget();
const newRoot = waitForMutation(gWalker, isNewRoot);
gDoc.defaultView.location.reload();
await gWalker.querySelector(nodeFront, "h1");
await newRoot;
ok(true, "The call to walker.querySelector() didn't fail");
runNextTest();

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

@ -28,41 +28,24 @@ addTest(async function setup() {
gInspectee = doc;
const walker = inspector.walker;
gWalker = await inspector.getWalker();
ok(walker === gWalker, "getWalker() twice should return the same walker.");
runNextTest();
});
addTest(async function testReload() {
addTest(function testReload() {
const oldRootID = gWalker.rootNode.actorID;
// watchRootNodeResolve will be assigned to different promises throughout the
// test to be able to await on individual calls of the watchRootNode callback.
let watchRootNodeResolve;
info("Start watching for root nodes and wait for the initial root node");
const onInitialRootNodePromise = new Promise(r => (watchRootNodeResolve = r));
const onInitialNewRootAvailable = gWalker.once("root-available");
gWalker.watchRootNode(() => watchRootNodeResolve());
await Promise.all([onInitialNewRootAvailable, onInitialRootNodePromise]);
info("Retrieve the node front for the selector `#a`");
const nodeFront = await gWalker.querySelector(gWalker.rootNode, "#a");
ok(nodeFront.actorID, "Node front has a valid actor ID");
info("Reload the page and wait for the newRoot mutation");
const onNewRootNodePromise = new Promise(r => (watchRootNodeResolve = r));
const onNewRootAvailable = gWalker.once("root-available");
gInspectee.defaultView.location.reload();
await Promise.all([onNewRootAvailable, onNewRootNodePromise]);
info("Retrieve the (new) node front for the selector `#a`");
const newNodeFront = await gWalker.querySelector(gWalker.rootNode, "#a");
ok(newNodeFront.actorID, "Got a new actor ID");
ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed.");
runNextTest();
// Load a node to populate the tree a bit.
promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => {
gInspectee.defaultView.location.reload();
return waitForMutation(gWalker, isNewRoot);
}).then(() => {
ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed.");
}).then(() => {
// Make sure we can still access the document
return gWalker.querySelector(gWalker.rootNode, "#a");
}).then(front => {
ok(front.actorID, "Got a new actor ID");
}).then(runNextTest));
});
addTest(function cleanup() {

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

@ -54,14 +54,6 @@ const walkerSpec = generateActorSpec({
"new-mutations": {
type: "newMutations",
},
"root-available": {
type: "root-available",
node: Arg(0, "nullable:domnode"),
},
"root-destroyed": {
type: "root-destroyed",
node: Arg(0, "nullable:domnode"),
},
"picker-node-picked": {
type: "pickerNodePicked",
node: Arg(0, "disconnectedNode"),
@ -371,14 +363,6 @@ const walkerSpec = generateActorSpec({
nodeFront: RetVal("disconnectedNode"),
},
},
watchRootNode: {
request: {},
response: {},
},
unwatchRootNode: {
request: {},
oneway: true,
},
},
});