Bug 1409040 - Add element.isDOMElement. r?maja_zf
Introduces a new element.isDOMElement function, similar to isXULElement,
for checking whether an element is a DOM element.
It follows the same formula as isXULElement by first testing if the
element is an object so we know we can accesss node.namespaceURI
and node.nodeType without causing a JS error.
MozReview-Commit-ID: 6Mlo33vu5LG
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -15,17 +15,17 @@ const {
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 XHTMLNS = "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",
"toolbarbutton",
@@ -736,20 +736,17 @@ element.isSelected = function(el) {
if (element.isXULElement(el)) {
if (XUL_CHECKED_ELS.has(el.tagName)) {
return el.checked;
} else if (XUL_SELECTED_ELS.has(el.tagName)) {
return el.selected;
}
- // TODO(ato): Use element.isDOMElement when bug 1400256 lands
- } else if (typeof el == "object" &&
- "nodeType" in el &&
- el.nodeType == el.ELEMENT_NODE) {
+ } else if (element.isDOMElement(el)) {
if (el.localName == "input" && ["checkbox", "radio"].includes(el.type)) {
return el.checked;
} else if (el.localName == "option") {
return el.selected;
}
}
return false;
@@ -1047,17 +1044,33 @@ element.isKeyboardInteractable = () => t
*/
element.scrollIntoView = function(el) {
if (el.scrollIntoView) {
el.scrollIntoView({block: "end", inline: "nearest", behavior: "instant"});
}
};
/**
- * Ascertains whether <var>el</var> is a XUL- or XBL element.
+ * Ascertains whether <var>node</var> is a DOM element.
+ *
+ * @param {*} node
+ * Element thought to be an <code>Element</code>.
+ *
+ * @return {boolean}
+ * True if <var>node</var> is a DOM element, false otherwise.
+ */
+element.isDOMElement = function(node) {
+ return typeof node == "object" &&
+ node !== null &&
+ node.nodeType === node.ELEMENT_NODE &&
+ node.namespaceURI === XHTMLNS;
+};
+
+/**
+ * Ascertains whether <var>node</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.
*/
@@ -1106,17 +1119,17 @@ const boolEls = {
* Element to test if <var>attr</var> is a boolean attribute on.
* @param {string} attr
* Attribute to test is a boolean attribute.
*
* @return {boolean}
* True if the attribute is boolean, false otherwise.
*/
element.isBooleanAttribute = function(el, attr) {
- if (el.namespaceURI !== XMLNS) {
+ if (!element.isDOMElement(el)) {
return false;
}
// global boolean attributes that apply to all HTML elements,
// except for custom elements
const customElement = !el.localName.includes("-");
if ((attr == "hidden" || attr == "itemscope") && customElement) {
return true;
--- a/testing/marionette/test_element.js
+++ b/testing/marionette/test_element.js
@@ -2,16 +2,17 @@
* 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");
const XBLNS = "http://www.mozilla.org/xbl";
+const XHTMLNS = "http://www.w3.org/1999/xhtml";
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) {
@@ -22,16 +23,18 @@ class Element {
get nodeType() { return 1; }
get ELEMENT_NODE() { return 1; }
}
class DOMElement extends Element {
constructor(tagName, attrs = {}) {
super(tagName, attrs);
+ this.namespaceURI = XHTMLNS;
+
if (this.localName == "option") {
this.selected = false;
}
if (this.localName == "input" && ["checkbox", "radio"].includes(this.type)) {
this.checked = false;
}
}
@@ -88,19 +91,30 @@ 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_isDOMElement() {
+ ok(element.isDOMElement(domEl));
+ ok(!element.isDOMElement(xulEl));
+ for (let typ of [true, 42, {}, [], undefined, null]) {
+ ok(!element.isDOMElement(typ));
+ }
+
+ run_next_test();
+});
+
add_test(function test_isXULElement() {
ok(element.isXULElement(xulEl));
ok(element.isXULElement(xblEl));
+ ok(!element.isXULElement(domEl));
for (let typ of [true, 42, {}, [], undefined, null]) {
ok(!element.isXULElement(typ));
}
run_next_test();
});
add_test(function test_coordinates() {