Bug 1592517 - Migrate devtools DOMHelpers.jsm from JSM to plain JS module r=ochameau

Depends on D51054

Summary of the changes here:
- move DOMHelpers.jsm to dom-helpers.js
- remove all unused methods
- converted to a static helper to avoid instanciating DOMHelpers objects for no reason
- updated call sites accordingly

Differential Revision: https://phabricator.services.mozilla.com/D51065

--HG--
rename : devtools/shared/DOMHelpers.jsm => devtools/shared/dom-helpers.js
extra : moz-landing-system : lando
This commit is contained in:
Julian Descottes 2019-10-30 11:25:01 +00:00
Родитель be1867fdfd
Коммит 3f0e2f4c4f
9 изменённых файлов: 73 добавлений и 200 удалений

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

@ -7,7 +7,7 @@
const EventEmitter = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise"); const promise = require("promise");
const Services = require("Services"); const Services = require("Services");
const { DOMHelpers } = require("resource://devtools/shared/DOMHelpers.jsm"); const { DOMHelpers } = require("devtools/shared/dom-helpers");
loader.lazyRequireGetter( loader.lazyRequireGetter(
this, this,
@ -78,12 +78,11 @@ BottomHost.prototype = {
this.frame.setAttribute("src", "about:blank"); this.frame.setAttribute("src", "about:blank");
const frame = await new Promise(resolve => { const frame = await new Promise(resolve => {
const domHelper = new DOMHelpers(this.frame.contentWindow);
const frameLoad = () => { const frameLoad = () => {
this.emit("ready", this.frame); this.emit("ready", this.frame);
resolve(this.frame); resolve(this.frame);
}; };
domHelper.onceDOMReady(frameLoad); DOMHelpers.onceDOMReady(this.frame.contentWindow, frameLoad);
focusTab(this.hostTab); focusTab(this.hostTab);
}); });
@ -174,12 +173,11 @@ class SidebarHost {
this.frame.setAttribute("src", "about:blank"); this.frame.setAttribute("src", "about:blank");
const frame = await new Promise(resolve => { const frame = await new Promise(resolve => {
const domHelper = new DOMHelpers(this.frame.contentWindow);
const frameLoad = () => { const frameLoad = () => {
this.emit("ready", this.frame); this.emit("ready", this.frame);
resolve(this.frame); resolve(this.frame);
}; };
domHelper.onceDOMReady(frameLoad); DOMHelpers.onceDOMReady(this.frame.contentWindow, frameLoad);
focusTab(this.hostTab); focusTab(this.hostTab);
}); });

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

@ -27,7 +27,7 @@ var EventEmitter = require("devtools/shared/event-emitter");
const Selection = require("devtools/client/framework/selection"); const Selection = require("devtools/client/framework/selection");
var Telemetry = require("devtools/client/shared/telemetry"); var Telemetry = require("devtools/client/shared/telemetry");
const { getUnicodeUrl } = require("devtools/client/shared/unicode-url"); const { getUnicodeUrl } = require("devtools/client/shared/unicode-url");
var { DOMHelpers } = require("resource://devtools/shared/DOMHelpers.jsm"); var { DOMHelpers } = require("devtools/shared/dom-helpers");
const { KeyCodes } = require("devtools/client/shared/keycodes"); const { KeyCodes } = require("devtools/client/shared/keycodes");
var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService( var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(
Ci.nsISupports Ci.nsISupports
@ -727,11 +727,14 @@ Toolbox.prototype = {
this._debugTargetData = this._getDebugTargetData(); this._debugTargetData = this._getDebugTargetData();
} }
const domHelper = new DOMHelpers(this.win);
const domReady = new Promise(resolve => { const domReady = new Promise(resolve => {
domHelper.onceDOMReady(() => { DOMHelpers.onceDOMReady(
this.win,
() => {
resolve(); resolve();
}, this._URL); },
this._URL
);
}); });
// Optimization: fire up a few other things before waiting on // Optimization: fire up a few other things before waiting on
@ -2479,8 +2482,7 @@ Toolbox.prototype = {
// on the DOM node every time because this won't work // on the DOM node every time because this won't work
// if the (xul chrome) iframe is loaded in a content docshell. // if the (xul chrome) iframe is loaded in a content docshell.
if (iframe.contentWindow) { if (iframe.contentWindow) {
const domHelper = new DOMHelpers(iframe.contentWindow); DOMHelpers.onceDOMReady(iframe.contentWindow, onLoad);
domHelper.onceDOMReady(onLoad);
} else { } else {
const callback = () => { const callback = () => {
iframe.removeEventListener("DOMContentLoaded", callback); iframe.removeEventListener("DOMContentLoaded", callback);

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

@ -200,11 +200,10 @@ async function testClickInInnerIframe(doc) {
iframe.srcdoc = "<div id=test style='height:50px'></div>"; iframe.srcdoc = "<div id=test style='height:50px'></div>";
await new Promise(r => { await new Promise(r => {
const domHelper = new DOMHelpers(iframe.contentWindow);
const frameLoad = () => { const frameLoad = () => {
r(); r();
}; };
domHelper.onceDOMReady(frameLoad); DOMHelpers.onceDOMReady(iframe.contentWindow, frameLoad);
}); });
await waitUntil(() => iframe.contentWindow.document.getElementById("test")); await waitUntil(() => iframe.contentWindow.document.getElementById("test"));

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

@ -13,9 +13,7 @@ Services.scriptloader.loadSubScript(
this this
); );
const { DOMHelpers } = ChromeUtils.import( const { DOMHelpers } = require("devtools/shared/dom-helpers");
"resource://devtools/shared/DOMHelpers.jsm"
);
const { Hosts } = require("devtools/client/framework/toolbox-hosts"); const { Hosts } = require("devtools/client/framework/toolbox-hosts");
const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/"; const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
@ -119,9 +117,8 @@ const createHost = async function(
const iframe = await host.create(); const iframe = await host.create();
await new Promise(resolve => { await new Promise(resolve => {
const domHelper = new DOMHelpers(iframe.contentWindow);
iframe.setAttribute("src", src); iframe.setAttribute("src", src);
domHelper.onceDOMReady(resolve); DOMHelpers.onceDOMReady(iframe.contentWindow, resolve);
}); });
return [host, iframe.contentWindow, iframe.contentDocument]; return [host, iframe.contentWindow, iframe.contentDocument];

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

@ -8,7 +8,7 @@ const {
} = require("devtools/client/shared/widgets/view-helpers"); } = require("devtools/client/shared/widgets/view-helpers");
const { getCurrentZoom } = require("devtools/shared/layout/utils"); const { getCurrentZoom } = require("devtools/shared/layout/utils");
const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DOMHelpers } = require("resource://devtools/shared/DOMHelpers.jsm"); const { DOMHelpers } = require("devtools/shared/dom-helpers");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter"); loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
@ -1293,8 +1293,7 @@ AbstractCanvasGraph.createIframe = function(url, parent, callback) {
// Use DOMHelpers to wait for the frame load. DOMHelpers relies on chromeEventHandler // Use DOMHelpers to wait for the frame load. DOMHelpers relies on chromeEventHandler
// so this will still work if DevTools are loaded in a content frame. // so this will still work if DevTools are loaded in a content frame.
const domHelper = new DOMHelpers(iframe.contentWindow); DOMHelpers.onceDOMReady(iframe.contentWindow, function() {
domHelper.onceDOMReady(function() {
callback(iframe); callback(iframe);
}); });

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

@ -146,7 +146,7 @@ loader.lazyRequireGetter(
loader.lazyRequireGetter( loader.lazyRequireGetter(
this, this,
"DOMHelpers", "DOMHelpers",
"resource://devtools/shared/DOMHelpers.jsm", "devtools/shared/dom-helpers",
true true
); );
@ -742,8 +742,7 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
const { contentDocument, contentWindow } = this.rawNode; const { contentDocument, contentWindow } = this.rawNode;
if (contentDocument && contentDocument.readyState !== "complete") { if (contentDocument && contentDocument.readyState !== "complete") {
await new Promise(resolve => { await new Promise(resolve => {
const domHelper = new DOMHelpers(contentWindow); DOMHelpers.onceDOMReady(contentWindow, resolve);
domHelper.onceDOMReady(resolve);
}); });
} }
}, },

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

@ -1,174 +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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
this.EXPORTED_SYMBOLS = ["DOMHelpers"];
/**
* DOMHelpers
* Makes DOM traversal easier. Goes through iframes.
*
* @constructor
* @param nsIDOMWindow win
* The content window, owning the document to traverse.
*/
this.DOMHelpers = function DOMHelpers(win) {
if (!win) {
throw new Error("window can't be null or undefined");
}
this.window = win;
};
DOMHelpers.prototype = {
getParentObject: function(node) {
const parentNode = node ? node.parentNode : null;
if (!parentNode) {
// Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
// and Notation. top level windows have no parentNode
if (node && node == this.window.Node.DOCUMENT_NODE) {
// document type
if (node.defaultView) {
const embeddingFrame = node.defaultView.frameElement;
if (embeddingFrame) {
return embeddingFrame.parentNode;
}
}
}
// a Document object without a parentNode or window
return null; // top level has no parent
}
if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
if (parentNode.defaultView) {
return parentNode.defaultView.frameElement;
}
// parent is document element, but no window at defaultView.
return null;
}
if (!parentNode.localName) {
return null;
}
return parentNode;
},
getChildObject: function(
node,
index,
previousSibling,
showTextNodesWithWhitespace
) {
if (!node) {
return null;
}
if (node.contentDocument) {
// then the node is a frame
if (index == 0) {
return node.contentDocument.documentElement; // the node's HTMLElement
}
return null;
}
if (node.getSVGDocument) {
const svgDocument = node.getSVGDocument();
if (svgDocument) {
// then the node is a frame
if (index == 0) {
return svgDocument.documentElement; // the node's SVGElement
}
return null;
}
}
let child = null;
if (previousSibling) {
// then we are walking
child = this.getNextSibling(previousSibling);
} else {
child = this.getFirstChild(node);
}
if (showTextNodesWithWhitespace) {
return child;
}
for (; child; child = this.getNextSibling(child)) {
if (!this.isWhitespaceText(child)) {
return child;
}
}
return null; // we have no children worth showing.
},
getFirstChild: function(node) {
const SHOW_ALL = nodeFilterConstants.SHOW_ALL;
this.treeWalker = node.ownerDocument.createTreeWalker(node, SHOW_ALL, null);
return this.treeWalker.firstChild();
},
getNextSibling: function(node) {
const next = this.treeWalker.nextSibling();
if (!next) {
delete this.treeWalker;
}
return next;
},
isWhitespaceText: function(node) {
return (
node.nodeType == this.window.Node.TEXT_NODE &&
!/[^\s]/.exec(node.nodeValue)
);
},
destroy: function() {
delete this.window;
delete this.treeWalker;
},
/**
* A simple way to be notified (once) when a window becomes
* interactive (DOMContentLoaded).
*
* It is based on the chromeEventHandler. This is useful when
* chrome iframes are loaded in content docshells (in Firefox
* tabs for example).
*/
onceDOMReady: function(callback, targetURL) {
const window = this.window;
const docShell = window.docShell;
const onReady = function(event) {
if (event.target == window.document) {
docShell.chromeEventHandler.removeEventListener(
"DOMContentLoaded",
onReady
);
// If in `callback` the URL of the window is changed and a listener to DOMContentLoaded
// is attached, the event we just received will be also be caught by the new listener.
// We want to avoid that so we execute the callback in the next queue.
Services.tm.dispatchToMainThread(callback);
}
};
if (
(window.document.readyState == "complete" ||
window.document.readyState == "interactive") &&
window.location.href == targetURL
) {
Services.tm.dispatchToMainThread(callback);
} else {
docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady);
}
},
};

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

@ -0,0 +1,53 @@
/* 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 Services = require("Services");
exports.DOMHelpers = {
/**
* A simple way to be notified (once) when a window becomes
* interactive (DOMContentLoaded).
*
* It is based on the chromeEventHandler. This is useful when
* chrome iframes are loaded in content docshells (in Firefox
* tabs for example).
*
* @param nsIDOMWindow win
* The content window, owning the document to traverse.
* @param Function callback
* The method to call when the frame is loaded.
* @param String targetURL
* (optional) Check that the frame URL corresponds to the provided URL
* before calling the callback.
*/
onceDOMReady: function(win, callback, targetURL) {
if (!win) {
throw new Error("window can't be null or undefined");
}
const docShell = win.docShell;
const onReady = function(event) {
if (event.target == win.document) {
docShell.chromeEventHandler.removeEventListener(
"DOMContentLoaded",
onReady
);
// If in `callback` the URL of the window is changed and a listener to DOMContentLoaded
// is attached, the event we just received will be also be caught by the new listener.
// We want to avoid that so we execute the callback in the next queue.
Services.tm.dispatchToMainThread(callback);
}
};
if (
(win.document.readyState == "complete" ||
win.document.readyState == "interactive") &&
win.location.href == targetURL
) {
Services.tm.dispatchToMainThread(callback);
} else {
docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady);
}
},
};

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

@ -53,9 +53,9 @@ DevToolsModules(
'debounce.js', 'debounce.js',
'defer.js', 'defer.js',
'DevToolsUtils.js', 'DevToolsUtils.js',
'dom-helpers.js',
'dom-node-constants.js', 'dom-node-constants.js',
'dom-node-filter-constants.js', 'dom-node-filter-constants.js',
'DOMHelpers.jsm',
'event-emitter.js', 'event-emitter.js',
'execution-point-utils.js', 'execution-point-utils.js',
'extend.js', 'extend.js',