diff --git a/testing/marionette/element.js b/testing/marionette/element.js
index 207c1cd1dad7..38546d42c5cf 100644
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -34,6 +34,15 @@ const {
const ELEMENT_NODE = 1;
const DOCUMENT_NODE = 9;
+const UNEDITABLE_INPUTS = new Set([
+ "checkbox",
+ "radio",
+ "hidden",
+ "submit",
+ "button",
+ "image",
+]);
+
const XBLNS = "http://www.mozilla.org/xbl";
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@@ -834,6 +843,60 @@ element.isDisabled = function(el) {
}
};
+/**
+ * An editing host is a node that is either an HTML element with a
+ * contenteditable
attribute, or the HTML element child
+ * of a document whose designMode
is enabled.
+ *
+ * @param {Element} el
+ * Element to determine if is an editing host.
+ *
+ * @return {boolean}
+ * True if editing host, false otherwise.
+ */
+element.isEditingHost = function(el) {
+ return element.isDOMElement(el) &&
+ (el.isContentEditable || el.ownerDocument.designMode == "on");
+};
+
+/**
+ * Determines if an element is editable according to WebDriver.
+ *
+ * An element is considered editable if it is not read-only or
+ * disabled, and one of the following conditions are met:
+ *
+ *
+ * - It is a
<textarea>
element.
+ *
+ * - It is an
<input>
element that is not of
+ * the checkbox
, radio
, hidden
,
+ * submit
, button
, or image
types.
+ *
+ * - It is content-editable.
+ *
+ *
- It belongs to a document in design mode.
+ *
+ *
+ * @param {Element}
+ * Element to test if editable.
+ *
+ * @return {boolean}
+ * True if editable, false otherwise.
+ */
+element.isEditable = function(el) {
+ if (!element.isDOMElement(el)) {
+ return false;
+ }
+
+ if (element.isReadOnly(el) || element.isDisabled(el)) {
+ return false;
+ }
+
+ return (el.localName == "input" && !UNEDITABLE_INPUTS.has(el.type)) ||
+ el.localName == "textarea" ||
+ element.isEditingHost(el);
+};
+
/**
* This function generates a pair of coordinates relative to the viewport
* given a target element and coordinates relative to that element's
diff --git a/testing/marionette/test_element.js b/testing/marionette/test_element.js
index fdd514944ddc..db22375173ff 100644
--- a/testing/marionette/test_element.js
+++ b/testing/marionette/test_element.js
@@ -45,6 +45,9 @@ class DOMElement extends Element {
super(tagName, attrs);
this.namespaceURI = XHTMLNS;
+ if (typeof this.ownerDocument == "undefined") {
+ this.ownerDocument = {designMode: "off"};
+ }
if (this.localName == "option") {
this.selected = false;
@@ -228,6 +231,33 @@ add_test(function test_isDisabled() {
run_next_test();
});
+add_test(function test_isEditingHost() {
+ ok(!element.isEditingHost(null));
+ ok(element.isEditingHost(new DOMElement("p", {isContentEditable: true})));
+ ok(element.isEditingHost(new DOMElement("p", {ownerDocument: {designMode: "on"}})));
+
+ run_next_test();
+});
+
+add_test(function test_isEditable() {
+ ok(!element.isEditable(null));
+ ok(!element.isEditable(domEl));
+ ok(!element.isEditable(new DOMElement("textarea", {readOnly: true})));
+ ok(!element.isEditable(new DOMElement("textarea", {disabled: true})));
+
+ for (let type of ["checkbox", "radio", "hidden", "submit", "button", "image"]) {
+ ok(!element.isEditable(new DOMElement("input", {type})));
+ }
+ ok(element.isEditable(new DOMElement("input", {type: "text"})));
+ ok(element.isEditable(new DOMElement("input")));
+
+ ok(element.isEditable(new DOMElement("textarea")));
+ ok(element.isEditable(new DOMElement("p", {ownerDocument: {designMode: "on"}})));
+ ok(element.isEditable(new DOMElement("p", {isContentEditable: true})));
+
+ run_next_test();
+});
+
add_test(function test_coordinates() {
let p = element.coordinates(domEl);
ok(p.hasOwnProperty("x"));