Bug 1409040 - Add element.isDOMElement. r?maja_zf draft
authorAndreas Tolfsen <ato@sny.no>
Fri, 13 Oct 2017 19:20:26 +0100
changeset 682759 dc5b9d0e522971ebe77cc3f52f117df03784df8a
parent 682758 370f1858ff14efbc49ff0b65c927332d9d679385
child 682760 41e0e116dc1bd1fc5c6bcef7ec3badc7dcceb0ac
child 682765 3a10d857dfde878336102e8f8a222271020a3f53
push id85133
push userbmo:ato@sny.no
push dateWed, 18 Oct 2017 18:09:06 +0000
reviewersmaja_zf
bugs1409040
milestone58.0a1
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
testing/marionette/element.js
testing/marionette/test_element.js
--- 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() {