Bug 1410649 - Let WebDriver:TakeScreenshot accept web elements. r?whimboo draft
authorAndreas Tolfsen <ato@sny.no>
Sat, 21 Oct 2017 19:12:53 +0100
changeset 684396 b100dfcb181ad9a2665a9addec3cfc2ad6fb3c9c
parent 684366 d697979497a6196b62ed15879e799c2c0cb1f25c
child 736855 3368d7351a2fed1c87845a1a6fecd439b441811a
push id85606
push userbmo:ato@sny.no
push dateSat, 21 Oct 2017 18:20:31 +0000
reviewerswhimboo
bugs1410649
milestone58.0a1
Bug 1410649 - Let WebDriver:TakeScreenshot accept web elements. r?whimboo The WebDriver:TakeScreenshot command used to take an "id" field with a web element reference UUID, as well as an array of UUIDs for the "highlights" field. The first has been renamed to "element", falling back to "id" if missing, and both changed to also accept web element JSON Objects. This will allow us to simplify the input to the command and for the web elements to be automatically serialised and deserialised as they reach the content frame script. The fallbacks must be left in place until Firefox 60 ships. MozReview-Commit-ID: 8Gh1y23Vi4r
testing/marionette/capture.js
testing/marionette/driver.js
testing/marionette/listener.js
--- a/testing/marionette/capture.js
+++ b/testing/marionette/capture.js
@@ -16,21 +16,16 @@ const XHTML_NS = "http://www.w3.org/1999
 
 /**
  * Provides primitives to capture screenshots.
  *
  * @namespace
  */
 this.capture = {};
 
-capture.Format = {
-  Base64: 0,
-  Hash: 1,
-};
-
 /**
  * Take a screenshot of a single element.
  *
  * @param {Node} node
  *     The node to take a screenshot of.
  * @param {Array.<Node>=} highlights
  *     Optional array of nodes, around which a border will be marked to
  *     highlight them in the screenshot.
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2946,57 +2946,63 @@ GeckoDriver.prototype.deleteSession = fu
  *     scroll to the element.
  *
  * @return {string}
  *     If <var>hash</var> is false, PNG image encoded as Base64 encoded
  *     string.  If <var>hash</var> is true, hex digest of the SHA-256
  *     hash of the Base64 encoded string.
  */
 GeckoDriver.prototype.takeScreenshot = function(cmd) {
-  let win = assert.window(this.getCurrentWindow());
-
-  let {id, highlights, full, hash} = cmd.parameters;
-  highlights = highlights || [];
-  let format = hash ? capture.Format.Hash : capture.Format.Base64;
+  const win = assert.window(this.getCurrentWindow());
+
+  // TODO(ato): id is deprecated and can be removed with Firefox 60
+  let area = cmd.parameters.element || cmd.parameters.id;
+  if (typeof area == "string") {
+    area = WebElement.fromUUID(area, this.context);
+  } else if (area) {
+    area = WebElement.fromJSON(area, this.context);
+  }
+
+  // TODO(ato): with Firefox 60 highlights becomes an array of
+  // web elements instead of an array of string UUIDs
+  let highlights = cmd.parameters.highlights || [];
+  highlights.forEach((highlight, i, arr) => {
+    if (typeof highlight == "string") {
+      arr[i] = WebElement.fromUUID(highlight, this.context);
+    } else if (typeof highlight != "undefined") {
+      arr[i] = WebElement.fromJSON(highlight, this.context);
+    }
+  });
+
+  let {full, hash, scroll} = cmd.parameters;
 
   switch (this.context) {
     case Context.Chrome:
       let highlightEls = highlights
-          .map(ref => WebElement.fromUUID(ref, Context.Chrome))
           .map(webEl => this.curBrowser.seenEls.get(webEl));
 
-      // viewport
       let canvas;
-      if (!id && !full) {
+      if (!area && !full) {
         canvas = capture.viewport(win, highlightEls);
-
-      // element or full document element
+      } else if (area) {
+        let el = this.curBrowser.seenEls.get(area);
+        canvas = capture.element(el, highlightEls);
       } else {
-        let node;
-        if (id) {
-          let webEl = WebElement.fromUUID(id, Context.Chrome);
-          node = this.curBrowser.seenEls.get(webEl);
-        } else {
-          node = win.document.documentElement;
-        }
-
-        canvas = capture.element(node, highlightEls);
+        let {documentElement} = win.document;
+        canvas = capture.element(documentElement, highlightEls);
       }
 
-      switch (format) {
-        case capture.Format.Hash:
-          return capture.toHash(canvas);
-
-        case capture.Format.Base64:
-          return capture.toBase64(canvas);
+      if (hash) {
+        return capture.toHash(canvas);
       }
-      break;
+      return capture.toBase64(canvas);
 
     case Context.Content:
-      return this.listener.takeScreenshot(format, cmd.parameters);
+      return this.listener.takeScreenshot(
+          {area, highlights, full, scroll, hash});
   }
 
   throw new TypeError(`Unknown context: ${this.context}`);
 };
 
 /**
  * Get the current browser orientation.
  *
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -1638,82 +1638,58 @@ function switchToFrame(msg) {
 
     sendOk(commandID);
   }
 }
 
 /**
  * Perform a screen capture in content context.
  *
- * Accepted values for |opts|:
- *
- *     @param {UUID=} id
- *         Optional web element reference of an element to take a screenshot
- *         of.
- *     @param {boolean=} full
- *         True to take a screenshot of the entire document element.  Is not
- *         considered if {@code id} is not defined.  Defaults to true.
- *     @param {Array.<UUID>=} highlights
- *         Draw a border around the elements found by their web element
- *         references.
- *     @param {boolean=} scroll
- *         When |id| is given, scroll it into view before taking the
- *         screenshot.  Defaults to true.
- *
- * @param {capture.Format} format
- *     Format to return the screenshot in.
- * @param {Object.<string, ?>} opts
- *     Options.
+ * @param {WebElement=} area
+ *     Optional web element to take a screenshot of.  By default
+ * @param {Array.<WebElement>=} highlights
+ *     Draw a border around the elements.
+ * @param {boolean=} [full=true] full
+ *     True to take a screenshot of the entire document element.
+ *     Is not considered <var>captureElement</var> is not defined.
+ *     Defaults to true.
+ * @param {boolean=} [scroll=true] scroll
+ *     When <var>captureElement</var> is given, scroll it into view
+ *     before taking the screenshot.  Defaults to true.
  *
  * @return {string}
  *     Base64 encoded string or a SHA-256 hash of the screenshot.
  */
-function takeScreenshot(format, opts = {}) {
-  let id = opts.id;
-  let full = !!opts.full;
-  let highlights = opts.highlights || [];
-  let scroll = !!opts.scroll;
-
-  let win = curContainer.frame;
+function takeScreenshot(
+    {
+      area = null,
+      highlights = [],
+      full = true,
+      scroll = true,
+      hash = true,
+    } = {}) {
+  const win = curContainer.frame;
 
   let canvas;
-  let highlightEls = highlights
-      .map(ref => WebElement.fromUUID(ref, "content"))
-      .map(webEl => seenEls.get(webEl, win));
-
-  // viewport
-  if (!id && !full) {
-    canvas = capture.viewport(win, highlightEls);
-
-  // element or full document element
+  if (!area && !full) {
+    canvas = capture.viewport(win, highlights);
+  } else if (area) {
+    if (scroll) {
+      element.scrollIntoView(area);
+    }
+    canvas = capture.element(area, highlights);
   } else {
-    let el;
-    if (id) {
-      let webEl = WebElement.fromUUID(id, "content");
-      el = seenEls.get(webEl, win);
-      if (scroll) {
-        element.scrollIntoView(el);
-      }
-    } else {
-      el = win.document.documentElement;
-    }
-
-    canvas = capture.element(el, highlightEls);
+    let {documentElement} = win.document;
+    canvas = capture.element(documentElement, highlights);
   }
 
-  switch (format) {
-    case capture.Format.Base64:
-      return capture.toBase64(canvas);
-
-    case capture.Format.Hash:
-      return capture.toHash(canvas);
-
-    default:
-      throw new TypeError("Unknown screenshot format: " + format);
+  if (hash) {
+    return capture.toHash(canvas);
   }
+  return capture.toBase64(canvas);
 }
 
 function flushRendering() {
   let content = curContainer.frame;
   let anyPendingPaintsGeneratedInDescendants = false;
 
   let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils);