Backed out changeset 2984e7af1c0e (bug 1244425) for mn bustage a=backout
authorWes Kocher <wkocher@mozilla.com>
Thu, 01 Sep 2016 14:13:14 -0700
changeset 347980 5779143fc12d6a751fedb0b8049d52f4bd10dad9
parent 347979 97d651e84739f5fadf1c39604ce3235122549176
child 347981 0a787a252077640330496045bdde5ca018ccc727
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1244425
milestone50.0a2
backs out2984e7af1c0e17309abcc6f9e20bdf0e3af1f4f8
Backed out changeset 2984e7af1c0e (bug 1244425) for mn bustage a=backout
testing/marionette/driver.js
testing/marionette/frame.js
testing/marionette/interaction.js
testing/marionette/listener.js
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2010,17 +2010,17 @@ GeckoDriver.prototype.sendKeysToElement 
     case Context.CHROME:
       let win = this.getCurrentWindow();
       let el = this.curBrowser.seenEls.get(id, {frame: win});
       yield interaction.sendKeysToElement(
           el, value, true, this.sessionCapabilities.raisesAccessibilityExceptions);
       break;
 
     case Context.CONTENT:
-      yield this.listener.sendKeysToElement(id, value);
+      yield this.listener.sendKeysToElement({id: id, value: value});
       break;
   }
 };
 
 /** Sets the test name.  The test name is used for logging purposes. */
 GeckoDriver.prototype.setTestName = function*(cmd, resp) {
   let val = cmd.parameters.value;
   this.testName = val;
@@ -2655,16 +2655,33 @@ GeckoDriver.prototype.receiveMessage = f
             });
             break;
           }
           hostname = hostname.replace(/^.*?\./, "");
         } while (hostname.indexOf(".") != -1);
       }
       return results;
 
+    case "Marionette:getFiles":
+      // Generates file objects to send back to the content script
+      // for handling file uploads.
+      let val = message.json.value;
+      let command_id = message.json.command_id;
+      Cu.importGlobalProperties(["File"]);
+      try {
+        let file = new File(val);
+        this.sendAsync("receiveFiles",
+                       {file: file, command_id: command_id});
+      } catch (e) {
+        let err = `File not found: ${val}`;
+        this.sendAsync("receiveFiles",
+                       {error: err, command_id: command_id});
+      }
+      break;
+
     case "Marionette:emitTouchEvent":
       globalMessageManager.broadcastAsyncMessage(
           "MarionetteMainListener:emitTouchEvent", message.json);
       break;
 
     case "Marionette:register":
       let wid = message.json.value;
       let be = message.target;
--- a/testing/marionette/frame.js
+++ b/testing/marionette/frame.js
@@ -218,16 +218,17 @@ frame.Manager = class {
     mm.addWeakMessageListener("Marionette:log", this.driver);
     mm.addWeakMessageListener("Marionette:shareData", this.driver);
     mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.driver);
     mm.addWeakMessageListener("Marionette:switchedToFrame", this.driver);
     mm.addWeakMessageListener("Marionette:getVisibleCookies", this.driver);
     mm.addWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts);
     mm.addWeakMessageListener("Marionette:register", this.driver);
     mm.addWeakMessageListener("Marionette:listenersAttached", this.driver);
+    mm.addWeakMessageListener("Marionette:getFiles", this.driver);
     mm.addWeakMessageListener("MarionetteFrame:handleModal", this);
     mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
     mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
   }
 
   /**
    * Removes listeners for messages from content frame scripts.
    * We do not remove the MarionetteFrame:getInterruptedState or
@@ -246,15 +247,16 @@ frame.Manager = class {
     mm.removeWeakMessageListener("Marionette:error", this.driver);
     mm.removeWeakMessageListener("Marionette:log", this.driver);
     mm.removeWeakMessageListener("Marionette:shareData", this.driver);
     mm.removeWeakMessageListener("Marionette:switchedToFrame", this.driver);
     mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.driver);
     mm.removeWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts);
     mm.removeWeakMessageListener("Marionette:listenersAttached", this.driver);
     mm.removeWeakMessageListener("Marionette:register", this.driver);
+    mm.removeWeakMessageListener("Marionette:getFiles", this.driver);
     mm.removeWeakMessageListener("MarionetteFrame:handleModal", this);
     mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
   }
 };
 
 frame.Manager.prototype.QueryInterface = XPCOMUtils.generateQI(
     [Ci.nsIMessageListener, Ci.nsISupportsWeakReference]);
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -7,18 +7,16 @@
 const {utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/accessibility.js");
 Cu.import("chrome://marionette/content/atom.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/event.js");
 
-Cu.importGlobalProperties(["File"]);
-
 this.EXPORTED_SYMBOLS = ["interaction"];
 
 /**
  * XUL elements that support disabled attribute.
  */
 const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([
   "ARROWSCROLLBOX",
   "BUTTON",
@@ -223,27 +221,20 @@ interaction.selectOption = function(el) 
   event.click(parent);
 };
 
 /**
  * Appends |path| to an <input type=file>'s file list.
  *
  * @param {HTMLInputElement} el
  *     An <input type=file> element.
- * @param {string} path
- *     Full path to file.
+ * @param {File} file
+ *     File object to assign to |el|.
  */
-interaction.uploadFile = function(el, path) {
-  let file;
-  try {
-    file = new File(path);
-  } catch (e) {
-    throw new InvalidArgumentError("File not found: " + path);
-  }
-
+interaction.uploadFile = function(el, file) {
   let fs = Array.prototype.slice.call(el.files);
   fs.push(file);
 
   // <input type=file> opens OS widget dialogue
   // which means the mousedown/focus/mouseup/click events
   // occur before the change event
   event.mouseover(el);
   event.mousemove(el);
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -56,16 +56,20 @@ var SUPPORTED_STRATEGIES = new Set([
   element.Strategy.TagName,
   element.Strategy.XPath,
 ]);
 
 var capabilities = {};
 
 var actions = new action.Chain(checkForInterrupted);
 
+// Contains the last file input element that was the target of
+// sendKeysToElement.
+var fileInputElement;
+
 // the unload handler
 var onunload;
 
 // Flag to indicate whether an async script is currently running or not.
 var asyncTestRunning = false;
 var asyncTestCommandId;
 var asyncTestTimeoutId;
 
@@ -243,22 +247,22 @@ var getScreenshotHashFn = dispatch(getSc
 var actionChainFn = dispatch(actionChain);
 var multiActionFn = dispatch(multiAction);
 var addCookieFn = dispatch(addCookie);
 var deleteCookieFn = dispatch(deleteCookie);
 var deleteAllCookiesFn = dispatch(deleteAllCookies);
 var executeFn = dispatch(execute);
 var executeInSandboxFn = dispatch(executeInSandbox);
 var executeSimpleTestFn = dispatch(executeSimpleTest);
-var sendKeysToElementFn = dispatch(sendKeysToElement);
 
 /**
  * Start all message listeners
  */
 function startListeners() {
+  addMessageListenerId("Marionette:receiveFiles", receiveFiles);
   addMessageListenerId("Marionette:newSession", newSession);
   addMessageListenerId("Marionette:execute", executeFn);
   addMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn);
   addMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn);
   addMessageListenerId("Marionette:singleTap", singleTapFn);
   addMessageListenerId("Marionette:actionChain", actionChainFn);
   addMessageListenerId("Marionette:multiAction", multiActionFn);
   addMessageListenerId("Marionette:get", get);
@@ -278,17 +282,17 @@ function startListeners() {
   addMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
   addMessageListenerId("Marionette:getElementText", getElementTextFn);
   addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
   addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
   addMessageListenerId("Marionette:getElementRect", getElementRectFn);
   addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   addMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
-  addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn);
+  addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
   addMessageListenerId("Marionette:clearElement", clearElementFn);
   addMessageListenerId("Marionette:switchToFrame", switchToFrame);
   addMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
   addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
   addMessageListenerId("Marionette:deleteSession", deleteSession);
   addMessageListenerId("Marionette:sleepSession", sleepSession);
   addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
   addMessageListenerId("Marionette:setTestName", setTestName);
@@ -353,16 +357,17 @@ function restart(msg) {
   }
   registerSelf();
 }
 
 /**
  * Removes all listeners
  */
 function deleteSession(msg) {
+  removeMessageListenerId("Marionette:receiveFiles", receiveFiles);
   removeMessageListenerId("Marionette:newSession", newSession);
   removeMessageListenerId("Marionette:execute", executeFn);
   removeMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn);
   removeMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn);
   removeMessageListenerId("Marionette:singleTap", singleTapFn);
   removeMessageListenerId("Marionette:actionChain", actionChainFn);
   removeMessageListenerId("Marionette:multiAction", multiActionFn);
   removeMessageListenerId("Marionette:get", get);
@@ -382,17 +387,17 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
   removeMessageListenerId("Marionette:getElementText", getElementTextFn);
   removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
   removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
   removeMessageListenerId("Marionette:getElementRect", getElementRectFn);
   removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   removeMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
-  removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn);
+  removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
   removeMessageListenerId("Marionette:clearElement", clearElementFn);
   removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
   removeMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
   removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
   removeMessageListenerId("Marionette:deleteSession", deleteSession);
   removeMessageListenerId("Marionette:sleepSession", sleepSession);
   removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
   removeMessageListenerId("Marionette:setTestName", setTestName);
@@ -581,16 +586,39 @@ function* executeSimpleTest(script, args
  * Sets the test name, used in logging messages.
  */
 function setTestName(msg) {
   marionetteTestName = msg.json.value;
   sendOk(msg.json.command_id);
 }
 
 /**
+ * Receive file objects from chrome in order to complete a
+ * sendKeysToElement action on a file input element.
+ */
+function receiveFiles(msg) {
+  if ("error" in msg.json) {
+    let err = new InvalidArgumentError(msg.json.error);
+    sendError(err, msg.json.command_id);
+    return;
+  }
+
+  if (!fileInputElement) {
+    let err = new InvalidElementStateError("receiveFiles called with no valid fileInputElement");
+    sendError(err, msg.json.command_id);
+    return;
+  }
+
+  interaction.uploadFile(fileInputElement, msg.json.file);
+  fileInputElement = null;
+
+  sendOk(msg.json.command_id);
+}
+
+/**
  * This function creates a touch event given a touch type and a touch
  */
 function emitTouchEvent(type, touch) {
   if (!wasInterrupted()) {
     let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport";
     dumpLog(loggingInfo);
     var docShell = curContainer.frame.document.defaultView.
                    QueryInterface(Components.interfaces.nsIInterfaceRequestor).
@@ -1275,24 +1303,38 @@ function isElementEnabled(id) {
  * and Radio Button states, or option elements.
  */
 function isElementSelected(id) {
   let el = seenEls.get(id, curContainer);
   return interaction.isElementSelected(
       el, capabilities.raisesAccessibilityExceptions);
 }
 
-function* sendKeysToElement(id, val) {
+/**
+ * Send keys to element
+ */
+function sendKeysToElement(msg) {
+  let command_id = msg.json.command_id;
+  let val = msg.json.value;
+  let id = msg.json.id;
   let el = seenEls.get(id, curContainer);
+
   if (el.type == "file") {
-    let path = val.join("");
-    yield interaction.uploadFile(el, path);
+    let p = val.join("");
+        fileInputElement = el;
+        // In e10s, we can only construct File objects in the parent process,
+        // so pass the filename to driver.js, which in turn passes them back
+        // to this frame script in receiveFiles.
+        sendSyncMessage("Marionette:getFiles",
+            {value: p, command_id: command_id});
   } else {
-    yield interaction.sendKeysToElement(
-        el, val, false, capabilities.raisesAccessibilityExceptions);
+    let promise = interaction.sendKeysToElement(
+        el, val, false, capabilities.raisesAccessibilityExceptions)
+      .then(() => sendOk(command_id))
+      .catch(e => sendError(e, command_id));
   }
 }
 
 /**
  * Clear the text of an element.
  */
 function clearElement(id) {
   try {