summaryrefslogtreecommitdiffstats
path: root/testing/marionette/capture.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/marionette/capture.js')
-rw-r--r--testing/marionette/capture.js193
1 files changed, 193 insertions, 0 deletions
diff --git a/testing/marionette/capture.js b/testing/marionette/capture.js
new file mode 100644
index 000000000..274d20567
--- /dev/null
+++ b/testing/marionette/capture.js
@@ -0,0 +1,193 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {utils: Cu} = Components;
+Cu.importGlobalProperties(["crypto"]);
+
+this.EXPORTED_SYMBOLS = ["capture"];
+
+const CONTEXT_2D = "2d";
+const BG_COLOUR = "rgb(255,255,255)";
+const PNG_MIME = "image/png";
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/** Provides primitives to capture screenshots. */
+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.
+ *
+ * @return {HTMLCanvasElement}
+ * The canvas element where the element has been painted on.
+ */
+capture.element = function (node, highlights = []) {
+ let win = node.ownerDocument.defaultView;
+ let rect = node.getBoundingClientRect();
+
+ return capture.canvas(
+ win,
+ rect.left,
+ rect.top,
+ rect.width,
+ rect.height,
+ highlights);
+};
+
+/**
+ * Take a screenshot of the window's viewport by taking into account
+ * the current offsets.
+ *
+ * @param {DOMWindow} win
+ * The DOM window providing the document element to capture,
+ * and the offsets for the viewport.
+ * @param {Array.<Node>=} highlights
+ * Optional array of nodes, around which a border will be marked to
+ * highlight them in the screenshot.
+ *
+ * @return {HTMLCanvasElement}
+ * The canvas element where the viewport has been painted on.
+ */
+capture.viewport = function (win, highlights = []) {
+ let rootNode = win.document.documentElement;
+
+ return capture.canvas(
+ win,
+ win.pageXOffset,
+ win.pageYOffset,
+ rootNode.clientWidth,
+ rootNode.clientHeight,
+ highlights);
+};
+
+/**
+ * Low-level interface to draw a rectangle off the framebuffer.
+ *
+ * @param {DOMWindow} win
+ * The DOM window used for the framebuffer, and providing the interfaces
+ * for creating an HTMLCanvasElement.
+ * @param {number} left
+ * The left, X axis offset of the rectangle.
+ * @param {number} top
+ * The top, Y axis offset of the rectangle.
+ * @param {number} width
+ * The width dimension of the rectangle to paint.
+ * @param {number} height
+ * The height dimension of the rectangle to paint.
+ * @param {Array.<Node>=} highlights
+ * Optional array of nodes, around which a border will be marked to
+ * highlight them in the screenshot.
+ *
+ * @return {HTMLCanvasElement}
+ * The canvas on which the selection from the window's framebuffer
+ * has been painted on.
+ */
+capture.canvas = function (win, left, top, width, height, highlights = []) {
+ let scale = win.devicePixelRatio;
+
+ let canvas = win.document.createElementNS(XHTML_NS, "canvas");
+ canvas.width = width * scale;
+ canvas.height = height * scale;
+
+ let ctx = canvas.getContext(CONTEXT_2D);
+ let flags = ctx.DRAWWINDOW_DRAW_CARET;
+ // Disabled in bug 1243415 for webplatform-test failures due to out of view elements.
+ // Needs https://github.com/w3c/web-platform-tests/issues/4383 fixed.
+ // ctx.DRAWWINDOW_DRAW_VIEW;
+ // Bug 1009762 - Crash in [@ mozilla::gl::ReadPixelsIntoDataSurface]
+ // ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
+
+ ctx.scale(scale, scale);
+ ctx.drawWindow(win, left, top, width, height, BG_COLOUR, flags);
+ ctx = capture.highlight_(ctx, highlights, top, left);
+
+ return canvas;
+};
+
+capture.highlight_ = function (context, highlights, top = 0, left = 0) {
+ if (!highlights) {
+ return;
+ }
+
+ context.lineWidth = "2";
+ context.strokeStyle = "red";
+ context.save();
+
+ for (let el of highlights) {
+ let rect = el.getBoundingClientRect();
+ let oy = -top;
+ let ox = -left;
+
+ context.strokeRect(
+ rect.left + ox,
+ rect.top + oy,
+ rect.width,
+ rect.height);
+ }
+
+ return context;
+};
+
+/**
+ * Encode the contents of an HTMLCanvasElement to a Base64 encoded string.
+ *
+ * @param {HTMLCanvasElement} canvas
+ * The canvas to encode.
+ *
+ * @return {string}
+ * A Base64 encoded string.
+ */
+capture.toBase64 = function (canvas) {
+ let u = canvas.toDataURL(PNG_MIME);
+ return u.substring(u.indexOf(",") + 1);
+};
+
+/**
+* Hash the contents of an HTMLCanvasElement to a SHA-256 hex digest.
+*
+* @param {HTMLCanvasElement} canvas
+* The canvas to encode.
+*
+* @return {string}
+* A hex digest of the SHA-256 hash of the base64 encoded string.
+*/
+capture.toHash = function (canvas) {
+ let u = capture.toBase64(canvas);
+ let buffer = new TextEncoder("utf-8").encode(u);
+ return crypto.subtle.digest("SHA-256", buffer).then(hash => hex(hash));
+};
+
+/**
+* Convert buffer into to hex.
+*
+* @param {ArrayBuffer} buffer
+* The buffer containing the data to convert to hex.
+*
+* @return {string}
+* A hex digest of the input buffer.
+*/
+function hex(buffer) {
+ let hexCodes = [];
+ let view = new DataView(buffer);
+ for (let i = 0; i < view.byteLength; i += 4) {
+ let value = view.getUint32(i);
+ let stringValue = value.toString(16);
+ let padding = '00000000';
+ let paddedValue = (padding + stringValue).slice(-padding.length);
+ hexCodes.push(paddedValue);
+ }
+ return hexCodes.join("");
+}; \ No newline at end of file