Bug 1461463 - [marionette] Empty response value should be null and not {}. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 15 May 2018 15:17:41 +0200
changeset 800973 015c2adb9457f77318b7b1f7cb9a8f4da9460b95
parent 800972 da9ba72403b91d8a600cc0736ed4b5f7ec1d144d
child 800974 22501a95c09e371c173ae8d61562ab69d34d6957
push id111539
push userbmo:hskupin@gmail.com
push dateTue, 29 May 2018 15:41:27 +0000
bugs1461463
milestone62.0a1
Bug 1461463 - [marionette] Empty response value should be null and not {}. WebDriver commands which do not return a value have to send null. But currently Marionette returns an empty object. MozReview-Commit-ID: FILv9IkojIj
testing/marionette/driver.js
testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py
testing/marionette/message.js
testing/marionette/server.js
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -786,18 +786,18 @@ GeckoDriver.prototype.newSession = async
  * Capabilities informs the client of which WebDriver features are
  * supported by Firefox and Marionette.  They are immutable for the
  * length of the session.
  *
  * The return value is an immutable map of string keys
  * ("capabilities") to values, which may be of types boolean,
  * numerical or string.
  */
-GeckoDriver.prototype.getSessionCapabilities = function(cmd, resp) {
-  resp.body.capabilities = this.capabilities;
+GeckoDriver.prototype.getSessionCapabilities = function() {
+  return {capabilities: this.capabilities};
 };
 
 /**
  * Sets the context of the subsequent commands.
  *
  * All subsequent requests to commands that in some way involve
  * interaction with a browsing context will target the chosen browsing
  * context.
@@ -871,28 +871,29 @@ GeckoDriver.prototype.getContext = funct
  *     JavaScript notion of null or undefined.
  *
  * @throws {ScriptTimeoutError}
  *     If the script was interrupted due to reaching the
  *     <var>scriptTimeout</var> or default timeout.
  * @throws {JavaScriptError}
  *     If an {@link Error} was thrown whilst evaluating the script.
  */
-GeckoDriver.prototype.executeScript = async function(cmd, resp) {
+GeckoDriver.prototype.executeScript = async function(cmd) {
   let {script, args} = cmd.parameters;
   let opts = {
     script: cmd.parameters.script,
     args: cmd.parameters.args,
     timeout: cmd.parameters.scriptTimeout,
     sandboxName: cmd.parameters.sandbox,
     newSandbox: cmd.parameters.newSandbox,
     file: cmd.parameters.filename,
     line: cmd.parameters.line,
   };
-  resp.body.value = await this.execute_(script, args, opts);
+
+  return {value: await this.execute_(script, args, opts)};
 };
 
 /**
  * Executes a JavaScript function in the context of the current browsing
  * context, if in content space, or in chrome space otherwise, and returns
  * the object passed to the callback.
  *
  * The callback is always the last argument to the <var>arguments</var>
@@ -939,29 +940,30 @@ GeckoDriver.prototype.executeScript = as
  *     JavaScript notion of null or undefined.
  *
  * @throws {ScriptTimeoutError}
  *     If the script was interrupted due to reaching the
  *     <var>scriptTimeout</var> or default timeout.
  * @throws {JavaScriptError}
  *     If an Error was thrown whilst evaluating the script.
  */
-GeckoDriver.prototype.executeAsyncScript = async function(cmd, resp) {
+GeckoDriver.prototype.executeAsyncScript = async function(cmd) {
   let {script, args} = cmd.parameters;
   let opts = {
     script: cmd.parameters.script,
     args: cmd.parameters.args,
     timeout: cmd.parameters.scriptTimeout,
     sandboxName: cmd.parameters.sandbox,
     newSandbox: cmd.parameters.newSandbox,
     file: cmd.parameters.filename,
     line: cmd.parameters.line,
     async: true,
   };
-  resp.body.value = await this.execute_(script, args, opts);
+
+  return {value: await this.execute_(script, args, opts)};
 };
 
 GeckoDriver.prototype.execute_ = async function(
     script,
     args = [],
     {
       timeout = null,
       sandboxName = null,
@@ -1120,47 +1122,48 @@ GeckoDriver.prototype.getCurrentUrl = fu
 GeckoDriver.prototype.getTitle = function() {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   return this.title;
 };
 
 /** Gets the current type of the window. */
-GeckoDriver.prototype.getWindowType = function(cmd, resp) {
+GeckoDriver.prototype.getWindowType = function() {
   assert.open(this.getCurrentWindow());
 
-  resp.body.value = this.windowType;
+  return this.windowType;
 };
 
 /**
  * Gets the page source of the content document.
  *
  * @return {string}
  *     String serialisation of the DOM of the current browsing context's
  *     active document.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getPageSource = async function(cmd, resp) {
+GeckoDriver.prototype.getPageSource = async function() {
   const win = assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   switch (this.context) {
     case Context.Chrome:
       let s = new win.XMLSerializer();
-      resp.body.value = s.serializeToString(win.document);
-      break;
+      return s.serializeToString(win.document);
 
     case Context.Content:
-      resp.body.value = await this.listener.getPageSource();
-      break;
+      return this.listener.getPageSource();
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Cause the browser to traverse one step backward in the joint history
  * of the current browsing context.
  *
  * @throws {UnsupportedOperationError}
@@ -1352,26 +1355,29 @@ GeckoDriver.prototype.getWindowHandles =
  * uniquely identifies it within this Marionette instance.  This can
  * be used to switch to this window at a later point.
  *
  * @return {string}
  *     Unique window handle.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
+ * @throws {UnknownError}
+ *     Internal browsing context reference not found
  */
-GeckoDriver.prototype.getChromeWindowHandle = function(cmd, resp) {
+GeckoDriver.prototype.getChromeWindowHandle = function() {
   assert.open(this.getCurrentWindow(Context.Chrome));
 
   for (let i in this.browsers) {
     if (this.curBrowser == this.browsers[i]) {
-      resp.body.value = i;
-      return;
+      return i;
     }
   }
+
+  throw new UnknownError("Invalid browsing context");
 };
 
 /**
  * Returns identifiers for each open chrome window for tests interested in
  * managing a set of chrome windows and tabs separately.
  *
  * @return {Array.<string>}
  *     Unique window handles.
@@ -1394,16 +1400,17 @@ GeckoDriver.prototype.getChromeWindowHan
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getWindowRect = function() {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
+
   return this.curBrowser.rect;
 };
 
 /**
  * Set the window position and size of the browser on the operating
  * system window manager.
  *
  * The supplied |width| and |height| values refer to the window outerWidth
@@ -1627,36 +1634,34 @@ GeckoDriver.prototype.setWindowHandle = 
 
     if ("tabIndex" in winProperties) {
       this.curBrowser.switchToTab(
           winProperties.tabIndex, winProperties.win, focus);
     }
   }
 };
 
-GeckoDriver.prototype.getActiveFrame = function(cmd, resp) {
+GeckoDriver.prototype.getActiveFrame = function() {
   assert.open(this.getCurrentWindow());
 
+  let frame = null;
+
   switch (this.context) {
     case Context.Chrome:
       // no frame means top-level
-      resp.body.value = null;
       if (this.curFrame) {
-        resp.body.value = this.curBrowser.seenEls.add(
-            this.curFrame.frameElement);
+        frame = this.curBrowser.seenEls.add(this.curFrame.frameElement);
       }
       break;
 
     case Context.Content:
-      resp.body.value = null;
-      if (this.currentFrameElement !== null) {
-        resp.body.value = this.currentFrameElement;
-      }
-      break;
+      frame = this.currentFrameElement;
   }
+
+  return frame;
 };
 
 /**
  * Set the current browsing context for future commands to the parent
  * of the current browsing context.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
@@ -1937,35 +1942,36 @@ GeckoDriver.prototype.releaseActions = a
  *
  * @throws {UnsupportedOperationError}
  *     Not applicable to application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.actionChain = async function(cmd, resp) {
+GeckoDriver.prototype.actionChain = async function(cmd) {
   const win = assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let {chain, nextId} = cmd.parameters;
 
   switch (this.context) {
     case Context.Chrome:
       // be conservative until this has a use case and is established
       // to work as expected in Fennec
       assert.firefox();
 
-      resp.body.value = await this.legacyactions.dispatchActions(
+      return this.legacyactions.dispatchActions(
           chain, nextId, {frame: win}, this.curBrowser.seenEls);
-      break;
 
     case Context.Content:
-      resp.body.value = await this.listener.actionChain(chain, nextId);
-      break;
+      return this.listener.actionChain(chain, nextId);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * A multi-action chain.
  *
  * @param {Object} value
  *     A nested array where the inner array represents eache vent,
@@ -1996,17 +2002,17 @@ GeckoDriver.prototype.multiAction = asyn
  * @param {string} value
  *     Value the client is looking for.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.findElement = async function(cmd, resp) {
+GeckoDriver.prototype.findElement = async function(cmd) {
   const win = assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let {using, value} = cmd.parameters;
   let startNode;
   if (typeof cmd.parameters.element != "undefined") {
     startNode = WebElement.fromUUID(cmd.parameters.element, this.context);
   }
@@ -2023,36 +2029,35 @@ GeckoDriver.prototype.findElement = asyn
         throw new InvalidSelectorError(`Strategy not supported: ${using}`);
       }
 
       let container = {frame: win};
       if (opts.startNode) {
         opts.startNode = this.curBrowser.seenEls.get(opts.startNode);
       }
       let el = await element.find(container, using, value, opts);
-      let webEl = this.curBrowser.seenEls.add(el);
-      resp.body.value = webEl;
-      break;
+      return this.curBrowser.seenEls.add(el);
 
     case Context.Content:
-      resp.body.value = await this.listener.findElementContent(
-          using, value, opts);
-      break;
+      return this.listener.findElementContent(using, value, opts);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Find elements using the indicated search strategy.
  *
  * @param {string} using
  *     Indicates which search method to use.
  * @param {string} value
  *     Value the client is looking for.
  */
-GeckoDriver.prototype.findElements = async function(cmd, resp) {
+GeckoDriver.prototype.findElements = async function(cmd) {
   const win = assert.open(this.getCurrentWindow());
 
   let {using, value} = cmd.parameters;
   let startNode;
   if (typeof cmd.parameters.element != "undefined") {
     startNode = WebElement.fromUUID(cmd.parameters.element, this.context);
   }
 
@@ -2068,23 +2073,23 @@ GeckoDriver.prototype.findElements = asy
         throw new InvalidSelectorError(`Strategy not supported: ${using}`);
       }
 
       let container = {frame: win};
       if (startNode) {
         opts.startNode = this.curBrowser.seenEls.get(opts.startNode);
       }
       let els = await element.find(container, using, value, opts);
-      let webEls = this.curBrowser.seenEls.addAll(els);
-      resp.body = webEls;
-      break;
+      return this.curBrowser.seenEls.addAll(els);
 
     case Context.Content:
-      resp.body = await this.listener.findElementsContent(using, value, opts);
-      break;
+      return this.listener.findElementsContent(using, value, opts);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Return the active element in the document.
  *
  * @return {WebElement}
  *     Active element of the current browsing context's document
@@ -2151,16 +2156,19 @@ GeckoDriver.prototype.clickElement = asy
           startTime: new Date().getTime(),
         };
         this.curBrowser.messageManager.sendAsyncMessage(
             "Marionette:waitForPageLoaded", parameters);
       });
 
       await click;
       break;
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Get a given attribute of an element.
  *
  * @param {string} id
  *     Web element reference ID to the element that will be inspected.
@@ -2174,33 +2182,34 @@ GeckoDriver.prototype.clickElement = asy
  *     If <var>id</var> or <var>name</var> are not strings.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementAttribute = async function(cmd, resp) {
+GeckoDriver.prototype.getElementAttribute = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let name = assert.string(cmd.parameters.name);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = el.getAttribute(name);
-      break;
+      return el.getAttribute(name);
 
     case Context.Content:
-      resp.body.value = await this.listener.getElementAttribute(webEl, name);
-      break;
+      return this.listener.getElementAttribute(webEl, name);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Returns the value of a property associated with given element.
  *
  * @param {string} id
  *     Web element reference ID to the element that will be inspected.
@@ -2214,33 +2223,34 @@ GeckoDriver.prototype.getElementAttribut
  *     If <var>id</var> or <var>name</var> are not strings.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementProperty = async function(cmd, resp) {
+GeckoDriver.prototype.getElementProperty = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let name = assert.string(cmd.parameters.name);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = el[name];
-      break;
+      return el[name];
 
     case Context.Content:
-      resp.body.value = await this.listener.getElementProperty(webEl, name);
-      break;
+      return this.listener.getElementProperty(webEl, name);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Get the text of an element, if any.  Includes the text of all child
  * elements.
  *
  * @param {string} id
@@ -2253,35 +2263,36 @@ GeckoDriver.prototype.getElementProperty
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementText = async function(cmd, resp) {
+GeckoDriver.prototype.getElementText = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // for chrome, we look at text nodes, and any node with a "label" field
       let el = this.curBrowser.seenEls.get(webEl);
       let lines = [];
       this.getVisibleText(el, lines);
-      resp.body.value = lines.join("\n");
-      break;
+      return lines.join("\n");
 
     case Context.Content:
-      resp.body.value = await this.listener.getElementText(webEl);
-      break;
+      return this.listener.getElementText(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Get the tag name of the element.
  *
  * @param {string} id
  *     Reference ID to the element that will be inspected.
@@ -2293,32 +2304,33 @@ GeckoDriver.prototype.getElementText = a
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementTagName = async function(cmd, resp) {
+GeckoDriver.prototype.getElementTagName = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = el.tagName.toLowerCase();
-      break;
+      return el.tagName.toLowerCase();
 
     case Context.Content:
-      resp.body.value = await this.listener.getElementTagName(webEl);
-      break;
+      return this.listener.getElementTagName(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Check if element is displayed.
  *
  * @param {string} id
  *     Reference ID to the element that will be inspected.
@@ -2330,33 +2342,33 @@ GeckoDriver.prototype.getElementTagName 
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.isElementDisplayed = async function(cmd, resp) {
+GeckoDriver.prototype.isElementDisplayed = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = await interaction.isElementDisplayed(
-          el, this.a11yChecks);
-      break;
+      return interaction.isElementDisplayed(el, this.a11yChecks);
 
     case Context.Content:
-      resp.body.value = await this.listener.isElementDisplayed(webEl);
-      break;
+      return this.listener.isElementDisplayed(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Return the property of the computed style of an element.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
@@ -2370,36 +2382,35 @@ GeckoDriver.prototype.isElementDisplayed
  *     If <var>id</var> or <var>propertyName</var> are not strings.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementValueOfCssProperty = async function(
-    cmd, resp) {
+GeckoDriver.prototype.getElementValueOfCssProperty = async function(cmd) {
   const win = assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let prop = assert.string(cmd.parameters.propertyName);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       let sty = win.document.defaultView.getComputedStyle(el);
-      resp.body.value = sty.getPropertyValue(prop);
-      break;
+      return sty.getPropertyValue(prop);
 
     case Context.Content:
-      resp.body.value = await this.listener
-          .getElementValueOfCssProperty(webEl, prop);
-      break;
+      return this.listener.getElementValueOfCssProperty(webEl, prop);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Check if element is enabled.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
@@ -2411,34 +2422,34 @@ GeckoDriver.prototype.getElementValueOfC
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.isElementEnabled = async function(cmd, resp) {
+GeckoDriver.prototype.isElementEnabled = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // Selenium atom doesn't quite work here
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = await interaction.isElementEnabled(
-          el, this.a11yChecks);
-      break;
+      return interaction.isElementEnabled(el, this.a11yChecks);
 
     case Context.Content:
-      resp.body.value = await this.listener.isElementEnabled(webEl);
-      break;
+      return this.listener.isElementEnabled(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Check if element is selected.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
@@ -2450,69 +2461,70 @@ GeckoDriver.prototype.isElementEnabled =
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.isElementSelected = async function(cmd, resp) {
+GeckoDriver.prototype.isElementSelected = async function(cmd) {
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // Selenium atom doesn't quite work here
       let el = this.curBrowser.seenEls.get(webEl);
-      resp.body.value = await interaction.isElementSelected(
-          el, this.a11yChecks);
-      break;
+      return interaction.isElementSelected(el, this.a11yChecks);
 
     case Context.Content:
-      resp.body.value = await this.listener.isElementSelected(webEl);
-      break;
+      return this.listener.isElementSelected(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * @throws {InvalidArgumentError}
  *     If <var>id</var> is not a string.
  * @throws {NoSuchElementError}
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getElementRect = async function(cmd, resp) {
+GeckoDriver.prototype.getElementRect = async function(cmd) {
   const win = assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       let rect = el.getBoundingClientRect();
-      resp.body = {
+      return {
         x: rect.x + win.pageXOffset,
         y: rect.y + win.pageYOffset,
         width: rect.width,
         height: rect.height,
       };
-      break;
 
     case Context.Content:
-      resp.body = await this.listener.getElementRect(webEl);
-      break;
+      return this.listener.getElementRect(webEl);
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Send key presses to element after focusing on it.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
@@ -2540,16 +2552,19 @@ GeckoDriver.prototype.sendKeysToElement 
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       await interaction.sendKeysToElement(el, text, this.a11yChecks);
       break;
 
     case Context.Content:
       await this.listener.sendKeysToElement(webEl, text);
       break;
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Clear the text of an element.
  *
  * @param {string} id
  *     Reference ID to the element that will be cleared.
@@ -2579,16 +2594,19 @@ GeckoDriver.prototype.clearElement = asy
       } else if (el.nodeName == "checkbox") {
         el.checked = false;
       }
       break;
 
     case Context.Content:
       await this.listener.clearElement(webEl);
       break;
+
+    default:
+      throw new TypeError(`Unknown context: ${this.context}`);
   }
 };
 
 /**
  * Switch to shadow root of the given host element.
  *
  * @param {string} id
  *     Reference ID to the element.
@@ -2649,23 +2667,23 @@ GeckoDriver.prototype.addCookie = functi
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getCookies = function(cmd, resp) {
+GeckoDriver.prototype.getCookies = function() {
   assert.content(this.context);
   assert.open(this.getCurrentWindow());
   this._assertAndDismissModal();
 
   let {hostname, pathname} = this.currentURL;
-  resp.body = [...cookie.iter(hostname, pathname)];
+  return [...cookie.iter(hostname, pathname)];
 };
 
 /**
  * Delete all cookies that are visible to a document.
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
@@ -2914,21 +2932,21 @@ GeckoDriver.prototype.takeScreenshot = f
 
 /**
  * Get the current browser orientation.
  *
  * Will return one of the valid primary orientation values
  * portrait-primary, landscape-primary, portrait-secondary, or
  * landscape-secondary.
  */
-GeckoDriver.prototype.getScreenOrientation = function(cmd, resp) {
+GeckoDriver.prototype.getScreenOrientation = function() {
   assert.fennec();
   let win = assert.open(this.getCurrentWindow());
 
-  resp.body.value = win.screen.mozOrientation;
+  return win.screen.mozOrientation;
 };
 
 /**
  * Set the current browser orientation.
  *
  * The supplied orientation should be given as one of the valid
  * orientation values.  If the orientation is unknown, an error will
  * be raised.
@@ -3144,22 +3162,21 @@ GeckoDriver.prototype.acceptDialog = fun
   button0.click();
   this.dialog = null;
 };
 
 /**
  * Returns the message shown in a currently displayed modal, or returns
  * a no such alert error if no modal is currently displayed.
  */
-GeckoDriver.prototype.getTextFromDialog = function(cmd, resp) {
+GeckoDriver.prototype.getTextFromDialog = function() {
   assert.open(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
-  let {infoBody} = this.dialog.ui;
-  resp.body.value = infoBody.textContent;
+  return this.dialog.ui.infoBody.textContent;
 };
 
 /**
  * Set the user prompt's value field.
  *
  * Sends keys to the input field of a currently displayed modal, or
  * returns a no such alert error if no modal is currently displayed. If
  * a tab modal is currently displayed but has no means for text input,
@@ -3245,17 +3262,17 @@ GeckoDriver.prototype.acceptConnections 
  *     Explaining the reason why the application quit.  This can be
  *     in response to a normal shutdown or restart, yielding "shutdown"
  *     or "restart", respectively.
  *
  * @throws {InvalidArgumentError}
  *     If <var>flags</var> contains unknown or incompatible flags,
  *     for example multiple Quit flags.
  */
-GeckoDriver.prototype.quit = async function(cmd, resp) {
+GeckoDriver.prototype.quit = async function(cmd) {
   const quits = ["eConsiderQuit", "eAttemptQuit", "eForceQuit"];
 
   let flags = [];
   if (typeof cmd.parameters.flags != "undefined") {
     flags = assert.array(cmd.parameters.flags);
   }
 
   // bug 1298921
@@ -3288,18 +3305,17 @@ GeckoDriver.prototype.quit = async funct
   let quitApplication = new Promise(resolve => {
     Services.obs.addObserver(
         (subject, topic, data) => resolve(data),
         "quit-application");
   });
 
   Services.startup.quit(mode);
 
-  resp.body.cause = await quitApplication;
-  resp.send();
+  return {cause: await quitApplication};
 };
 
 GeckoDriver.prototype.installAddon = function(cmd) {
   assert.firefox();
 
   let path = cmd.parameters.path;
   let temp = cmd.parameters.temporary || false;
   if (typeof path == "undefined" || typeof path != "string" ||
@@ -3381,27 +3397,27 @@ GeckoDriver.prototype.responseCompleted 
  * @param {Array.<string>} urls
  *     Array of .dtd URLs.
  * @param {string} id
  *     The ID of the entity to retrieve the localized string for.
  *
  * @return {string}
  *     The localized string for the requested entity.
  */
-GeckoDriver.prototype.localizeEntity = function(cmd, resp) {
+GeckoDriver.prototype.localizeEntity = function(cmd) {
   let {urls, id} = cmd.parameters;
 
   if (!Array.isArray(urls)) {
     throw new InvalidArgumentError("Value of `urls` should be of type 'Array'");
   }
   if (typeof id != "string") {
     throw new InvalidArgumentError("Value of `id` should be of type 'string'");
   }
 
-  resp.body.value = l10n.localizeEntity(urls, id);
+  return l10n.localizeEntity(urls, id);
 };
 
 /**
  * Retrieve the localized string for the specified property id.
  *
  * Example:
  *
  *     localizeProperty(
@@ -3410,27 +3426,27 @@ GeckoDriver.prototype.localizeEntity = f
  * @param {Array.<string>} urls
  *     Array of .properties URLs.
  * @param {string} id
  *     The ID of the property to retrieve the localized string for.
  *
  * @return {string}
  *     The localized string for the requested property.
  */
-GeckoDriver.prototype.localizeProperty = function(cmd, resp) {
+GeckoDriver.prototype.localizeProperty = function(cmd) {
   let {urls, id} = cmd.parameters;
 
   if (!Array.isArray(urls)) {
     throw new InvalidArgumentError("Value of `urls` should be of type 'Array'");
   }
   if (typeof id != "string") {
     throw new InvalidArgumentError("Value of `id` should be of type 'string'");
   }
 
-  resp.body.value = l10n.localizeProperty(urls, id);
+  return l10n.localizeProperty(urls, id);
 };
 
 /**
  * Initialize the reftest mode
  */
 GeckoDriver.prototype.setupReftest = async function(cmd) {
   if (this._reftest) {
     throw new UnsupportedOperationError(
@@ -3449,30 +3465,30 @@ GeckoDriver.prototype.setupReftest = asy
   }
 
   this._reftest = new reftest.Runner(this);
   await this._reftest.setup(urlCount, screenshot);
 };
 
 
 /** Run a reftest. */
-GeckoDriver.prototype.runReftest = async function(cmd, resp) {
+GeckoDriver.prototype.runReftest = async function(cmd) {
   let {test, references, expected, timeout} = cmd.parameters;
 
   if (!this._reftest) {
     throw new UnsupportedOperationError(
         "Called reftest:run before reftest:start");
   }
 
   assert.string(test);
   assert.string(expected);
   assert.array(references);
 
-  resp.body.value = await this._reftest.run(
-      test, references, expected, timeout);
+  return {value: await this._reftest.run(
+      test, references, expected, timeout)};
 };
 
 /**
  * End a reftest run.
  *
  * Closes the reftest window (without changing the current window handle),
  * and removes cached canvases.
  */
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py
@@ -70,18 +70,18 @@ class TestContext(MarionetteTestCase):
 
     def get_context(self):
         return self.marionette._send_message("getContext", key="value")
 
     def set_context(self, value):
         return self.marionette._send_message("setContext", {"value": value})
 
     def test_set_context(self):
-        self.assertEqual(self.set_context("content"), {})
-        self.assertEqual(self.set_context("chrome"), {})
+        self.assertEqual(self.set_context("content"), {"value": None})
+        self.assertEqual(self.set_context("chrome"), {"value": None})
 
         for typ in [True, 42, [], {}, None]:
             with self.assertRaises(errors.InvalidArgumentException):
                 self.set_context(typ)
 
         with self.assertRaises(errors.MarionetteException):
             self.set_context("foo")
 
--- a/testing/marionette/message.js
+++ b/testing/marionette/message.js
@@ -195,54 +195,16 @@ class Command extends Message {
       params = undefined;
     }
 
     return new Command(msgID, name, params);
   }
 }
 Command.Type = 0;
 
-const validator = {
-  exclusionary: {
-    "capabilities": ["error", "value"],
-    "error": ["value", "sessionId", "capabilities"],
-    "sessionId": ["error", "value"],
-    "value": ["error", "sessionId", "capabilities"],
-  },
-
-  set(obj, prop, val) {
-    let tests = this.exclusionary[prop];
-    if (tests) {
-      for (let t of tests) {
-        if (obj.hasOwnProperty(t)) {
-          throw new TypeError(`${t} set, cannot set ${prop}`);
-        }
-      }
-    }
-
-    obj[prop] = val;
-    return true;
-  },
-};
-
-/**
- * The response body is exposed as an argument to commands.
- * Commands can set fields on the body through defining properties.
- *
- * Setting properties invokes a validator that performs tests for
- * mutually exclusionary fields on the input against the existing data
- * in the body.
- *
- * For example setting the <code>error</code> property on
- * the body when <code>value</code>, <code>sessionId</code>, or
- * <code>capabilities</code> have been set previously will cause
- * an error.
- */
-const ResponseBody = () => new Proxy({}, validator);
-
 /**
  * @callback ResponseCallback
  *
  * @param {Response} resp
  *     Response to handle.
  */
 
 /**
@@ -267,17 +229,17 @@ const ResponseBody = () => new Proxy({},
  */
 class Response extends Message {
   constructor(messageID, respHandler = () => {}) {
     super(messageID);
 
     this.respHandler_ = assert.callable(respHandler);
 
     this.error = null;
-    this.body = ResponseBody();
+    this.body = {value: null};
 
     this.origin = Message.Origin.Server;
     this.sent = false;
   }
 
   /**
    * Sends response conditionally, given a predicate.
    *
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -288,19 +288,19 @@ class TCPConnection {
     if (typeof fn == "undefined") {
       throw new UnknownCommandError(cmd.name);
     }
 
     if (!["newSession", "WebDriver:NewSession"].includes(cmd.name)) {
       assert.session(this.driver);
     }
 
-    let rv = await fn.bind(this.driver)(cmd, resp);
+    let rv = await fn.bind(this.driver)(cmd);
 
-    if (typeof rv != "undefined") {
+    if (rv != null) {
       if (rv instanceof WebElement || typeof rv != "object") {
         resp.body = {value: rv};
       } else {
         resp.body = rv;
       }
     }
   }