Bug 1409040 - Make element.isXULElement more resiliant. r?maja_zf
Fixes isXULElement to recognise XBL elements, such as <xbl:framebox>.
Also tightens up the input checks so that arbitrary objects can be
tested, as checking node.namespaceURI directly could cause a JS error
if node is not an object. Before checking the namespace we also ensure
it's an element node so that text- and comment nodes are not picked up.
This patch also introduces tests, which were sorely missing.
MozReview-Commit-ID: 8LNF1z3X1gP
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -14,16 +14,17 @@ const {
NoSuchElementError,
StaleElementReferenceError,
} = Cu.import("chrome://marionette/content/error.js", {});
const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
const {PollPromise} = Cu.import("chrome://marionette/content/sync.js", {});
this.EXPORTED_SYMBOLS = ["element"];
+const XBLNS = "http://www.mozilla.org/xbl";
const XMLNS = "http://www.w3.org/1999/xhtml";
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/** XUL elements that support checked property. */
const XUL_CHECKED_ELS = new Set([
"button",
"checkbox",
"listitem",
@@ -1045,18 +1046,31 @@ element.isKeyboardInteractable = () => t
* Element to scroll into view.
*/
element.scrollIntoView = function(el) {
if (el.scrollIntoView) {
el.scrollIntoView({block: "end", inline: "nearest", behavior: "instant"});
}
};
-element.isXULElement = function(el) {
- return el.namespaceURI === XULNS;
+/**
+ * Ascertains whether <var>el</var> is a XUL- or XBL element.
+ *
+ * @param {*} node
+ * Element thought to be a XUL- or XBL element.
+ *
+ * @return {boolean}
+ * True if <var>node</var> is a XULElement or XBLElement,
+ * false otherwise.
+ */
+element.isXULElement = function(node) {
+ return typeof node == "object" &&
+ node !== null &&
+ node.nodeType === node.ELEMENT_NODE &&
+ [XBLNS, XULNS].includes(node.namespaceURI);
};
const boolEls = {
audio: ["autoplay", "controls", "loop", "muted"],
button: ["autofocus", "disabled", "formnovalidate"],
details: ["open"],
dialog: ["open"],
fieldset: ["disabled"],
--- a/testing/marionette/test_element.js
+++ b/testing/marionette/test_element.js
@@ -1,48 +1,73 @@
/* 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/. */
const {utils: Cu} = Components;
Cu.import("chrome://marionette/content/element.js");
-class DOMElement {
+const XBLNS = "http://www.mozilla.org/xbl";
+const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+class Element {
constructor(tagName, attrs = {}) {
this.tagName = tagName;
this.localName = tagName;
for (let attr in attrs) {
this[attr] = attrs[attr];
}
+ }
+
+ get nodeType() { return 1; }
+ get ELEMENT_NODE() { return 1; }
+}
+
+class DOMElement extends Element {
+ constructor(tagName, attrs = {}) {
+ super(tagName, attrs);
if (this.localName == "option") {
this.selected = false;
}
if (this.localName == "input" && ["checkbox", "radio"].includes(this.type)) {
this.checked = false;
}
}
- get nodeType() { return 1; }
- get ELEMENT_NODE() { return 1; }
-
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: 100,
height: 100,
};
}
-};
+}
+
+class XULElement extends Element {
+ constructor(tagName, attrs = {}) {
+ super(tagName, attrs);
+ this.namespaceURI = XULNS;
+ }
+}
+
+class XBLElement extends XULElement {
+ constructor(tagName, attrs = {}) {
+ super(tagName, attrs);
+ this.namespaceURI = XBLNS;
+ }
+}
const domEl = new DOMElement("p");
+const xulEl = new XULElement("browser");
+const xblEl = new XBLElement("framebox");
add_test(function test_isSelected() {
let checkbox = new DOMElement("input", {type: "checkbox"});
ok(!element.isSelected(checkbox));
checkbox.checked = true;
ok(element.isSelected(checkbox));
// selected is not a property of <input type=checkbox>
@@ -63,16 +88,26 @@ add_test(function test_isSelected() {
// anything else should not be selected
for (let typ of [domEl, undefined, null, "foo", true, [], {}]) {
ok(!element.isSelected(typ));
}
run_next_test();
});
+add_test(function test_isXULElement() {
+ ok(element.isXULElement(xulEl));
+ ok(element.isXULElement(xblEl));
+ for (let typ of [true, 42, {}, [], undefined, null]) {
+ ok(!element.isXULElement(typ));
+ }
+
+ run_next_test();
+});
+
add_test(function test_coordinates() {
let p = element.coordinates(domEl);
ok(p.hasOwnProperty("x"));
ok(p.hasOwnProperty("y"));
equal("number", typeof p.x);
equal("number", typeof p.y);
deepEqual({x: 50, y: 50}, element.coordinates(domEl));