.logger.warn(
      "Limiting screen capture height to maximum allowed " +
        MAX_CANVAS_DIMENSION +
        " pixels"
    );
    height = Math.floor(MAX_CANVAS_DIMENSION / scale);
    canvasHeight = height * scale;
  }

  // If the area is larger, reduce the height to keep the full width.
  if (canvasWidth * canvasHeight > MAX_CANVAS_AREA) {
    lazy.logger.warn(
      "Limiting screen capture area to maximum allowed " +
        MAX_CANVAS_AREA +
        " pixels"
    );
    height = Math.floor(MAX_CANVAS_AREA / (canvasWidth * scale));
    canvasHeight = height * scale;
  }

  if (canvas === null) {
    canvas = win.document.createElementNS(XHTML_NS, "canvas");
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
  }

  const ctx = canvas.getContext(CONTEXT_2D);

  if (readback) {
    if (flags === null) {
      flags =
        ctx.DRAWWINDOW_DRAW_CARET |
        ctx.DRAWWINDOW_DRAW_VIEW |
        ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
    }

    // drawWindow doesn't take scaling into account.
    ctx.scale(scale, scale);
    ctx.drawWindow(win, left + dX, top + dY, width, height, BG_COLOUR, flags);
  } else {
    let rect = new DOMRect(left, top, width, height);
    let snapshot = await browsingContext.currentWindowGlobal.drawSnapshot(
      rect,
      scale,
      BG_COLOUR
    );

    ctx.drawImage(snapshot, 0, 0);

    // Bug 1574935 - Huge dimensions can trigger an OOM because multiple copies
    // of the bitmap will exist in memory. Force the removal of the snapshot
    // because it is no longer needed.
    snapshot.close();
  }

  return canvas;
};

/**
 * Encode the contents of an HTMLCanvasElement to a Base64 encoded string.
 *
 * @param {HTMLCanvasElement} canvas
 *     The canvas to encode.
 * @param {string} type
 *     The image format to output such as `image/png`.
 * @param {number} encoderOptions
 *     A number between 0 and 1 representing the image quality. Defaults to `1.0`.
 *
 * @returns {string}
 *     A Base64 encoded string.
 */
capture.toBase64 = function (canvas, type, encoderOptions = 1.0) {
  let u = canvas.toDataURL(type, encoderOptions);
  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.
 *
 * @returns {string}
 *     A hex digest of the SHA-256 hash of the base64 encoded string.
 */
capture.toHash = function (canvas) {
  let u = capture.toBase64(canvas, "image/png");
  let buffer = new TextEncoder().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.
 *
 * @returns {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("");
}
PK