123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- /* 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("");
- };
|