Bug 1366188 - Part 1: Fix Marionette's DateTimeValue when dom.forms.datetime is enabled. r=ato
authorJessica Jong <jjong>
Tue, 30 May 2017 23:37:00 +0200
changeset 361794 f4448d4bcf9828c099be8a6c40c28b48d1aa6698
parent 361793 001d49708a355c9b127fc338722959874cfc7552
child 361795 7abafd0fead900aec67b0de342f71ec856f561cf
push id31943
push userryanvm@gmail.com
push dateThu, 01 Jun 2017 15:54:45 +0000
treeherdermozilla-central@62005e6aecdf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1366188
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1366188 - Part 1: Fix Marionette's DateTimeValue when dom.forms.datetime is enabled. r=ato When we enable "dom.forms.datetime", <input type=date> and <input type=time> are displayed as multi-field text box, where the fields are ordered depending on locale. To avoid too much overhead, instead of synthesising each key event, we set the element's value property and fire the corresponding events to emulate user interaction. MozReview-Commit-ID: Ao6ip61CNT6
testing/marionette/interaction.js
testing/marionette/listener.js
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -71,16 +71,40 @@ const SELECTED_PROPERTY_SUPPORTED_XUL = 
   "MENU",
   "MENUITEM",
   "MENUSEPARATOR",
   "RADIO",
   "RICHLISTITEM",
   "TAB",
 ]);
 
+/**
+ * Common form controls that user can change the value property interactively.
+ */
+const COMMON_FORM_CONTROLS = new Set([
+  "input",
+  "textarea",
+  "select",
+]);
+
+/**
+ * Input elements that do not fire "input" and "change" events when value
+ * property changes.
+ */
+const INPUT_TYPES_NO_EVENT = new Set([
+  "checkbox",
+  "radio",
+  "file",
+  "hidden",
+  "image",
+  "reset",
+  "button",
+  "submit",
+]);
+
 this.interaction = {};
 
 /**
  * Interact with an element by clicking it.
  *
  * The element is scrolled into view before visibility- or interactability
  * checks are performed.
  *
@@ -340,16 +364,42 @@ interaction.uploadFile = function* (el, 
   event.click(el);
 
   el.mozSetFileArray(fs);
 
   event.change(el);
 };
 
 /**
+ * Sets a form element's value.
+ *
+ * @param {DOMElement} el
+ *     An form element, e.g. input, textarea, etc.
+ * @param {string} value
+ *     The value to be set.
+ *
+ * @throws TypeError
+ *     If |el| is not an supported form element.
+ */
+interaction.setFormControlValue = function* (el, value) {
+  if (!COMMON_FORM_CONTROLS.has(el.localName)) {
+    throw new TypeError("This function is for form elements only");
+  }
+
+  el.value = value;
+
+  if (INPUT_TYPES_NO_EVENT.has(el.type)) {
+    return;
+  }
+
+  event.input(el);
+  event.change(el);
+};
+
+/**
  * Send keys to element.
  *
  * @param {DOMElement|XULElement} el
  *     Element to send key events to.
  * @param {Array.<string>} value
  *     Sequence of keystrokes to send to the element.
  * @param {boolean} ignoreVisibility
  *     Flag to enable or disable element visibility tests.
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -25,21 +25,24 @@ Cu.import("chrome://marionette/content/i
 Cu.import("chrome://marionette/content/legacyaction.js");
 Cu.import("chrome://marionette/content/logging.js");
 Cu.import("chrome://marionette/content/navigate.js");
 Cu.import("chrome://marionette/content/proxy.js");
 Cu.import("chrome://marionette/content/session.js");
 Cu.import("chrome://marionette/content/simpletest.js");
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
+const INPUT_DATETIME_PREF = "dom.forms.datetime";
+
 var contentLog = new logging.ContentLogger();
 
 var marionetteTestName;
 var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils);
 var listenerId = null; // unique ID of this listener
 var curContainer = { frame: content, shadowRoot: null };
 var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
@@ -1432,16 +1435,19 @@ function isElementSelected(id) {
   return interaction.isElementSelected(
       el, capabilities.get("moz:accessibilityChecks"));
 }
 
 function* sendKeysToElement(id, val) {
   let el = seenEls.get(id, curContainer);
   if (el.type == "file") {
     yield interaction.uploadFile(el, val);
+  } else if ((el.type == "date" || el.type == "time") &&
+      Preferences.get(INPUT_DATETIME_PREF)) {
+    yield interaction.setFormControlValue(el, val);
   } else {
     yield interaction.sendKeysToElement(
         el, val, false, capabilities.get("moz:accessibilityChecks"));
   }
 }
 
 /**
  * Clear the text of an element.