Bug 1464461 - add back mochitests for screenshot command; r=ochameau, nchevobbe draft
authoryulia <ystartsev@mozilla.com>
Fri, 08 Jun 2018 20:14:36 +0200
changeset 813608 de0f6f715b4fcbd9acc4ddf20da8b1ffe69697e5
parent 813607 8330fb4ea04b3cdf166bcd2032a6d4e37db92f74
child 813609 7ded7b0fc58194a2e8de62c54f5661c099bc6158
push id114934
push userbmo:ystartsev@mozilla.com
push dateTue, 03 Jul 2018 13:34:11 +0000
reviewersochameau, nchevobbe
bugs1464461
milestone63.0a1
Bug 1464461 - add back mochitests for screenshot command; r=ochameau, nchevobbe MozReview-Commit-ID: 4yGM74AXwoP
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_clipboard.js
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_file.js
devtools/client/webconsole/test/mochitest/test_jsterm_screenshot_command.html
devtools/server/actors/webconsole/screenshot.js
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -98,16 +98,17 @@ support-files =
   test-ineffective-iframe-sandbox-warning4.html
   test-ineffective-iframe-sandbox-warning5.html
   test-insecure-frame.html
   test-insecure-passwords-about-blank-web-console-warning.html
   test-insecure-passwords-web-console-warning.html
   test-inspect-cross-domain-objects-frame.html
   test-inspect-cross-domain-objects-top.html
   test-jsterm-dollar.html
+  test_jsterm_screenshot_command.html
   test-local-session-storage.html
   test-location-debugger-link-console-log.js
   test-location-debugger-link-errors.js
   test-location-debugger-link.html
   test-location-styleeditor-link-1.css
   test-location-styleeditor-link-2.css
   test-location-styleeditor-link.html
   test-message-categories-canvas-css.html
@@ -222,16 +223,20 @@ skip-if = os != 'mac' # The tested ctrl+
 [browser_jsterm_instance_of.js]
 [browser_jsterm_multiline.js]
 [browser_jsterm_no_autocompletion_on_defined_variables.js]
 [browser_jsterm_no_input_and_tab_key_pressed.js]
 [browser_jsterm_no_input_change_and_tab_key_pressed.js]
 [browser_jsterm_null_undefined.js]
 [browser_jsterm_popup_close_on_tab_switch.js]
 [browser_jsterm_popup.js]
+[browser_jsterm_screenshot_command_clipboard.js]
+subsuite = clipboard
+[browser_jsterm_screenshot_command_file.js]
+skip-if = (os == 'win') # Bug 1464461, disabled on Win due to timeouts
 [browser_jsterm_selfxss.js]
 subsuite = clipboard
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
 tags = mcb
 [browser_webconsole_batching.js]
 [browser_webconsole_block_mixedcontent_securityerrors.js]
 tags = mcb
 [browser_webconsole_cached_messages.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_clipboard.js
@@ -0,0 +1,209 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that screenshot command works properly
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                 "test/mochitest/test_jsterm_screenshot_command.html";
+
+// on some machines, such as macOS, dpr is set to 2. This is expected behavior, however
+// to keep tests consistant across OSs we are setting the dpr to 1
+const dpr = "--dpr 1";
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+  ok(hud, "web console opened");
+
+  await testClipboard(hud);
+  await testFullpageClipboard(hud);
+  await testSelectorClipboard(hud);
+
+  // overflow
+  await createScrollbarOverflow();
+  await testFullpageClipboardScrollbar(hud);
+});
+
+async function testClipboard(hud) {
+  const command = `:screenshot --clipboard ${dpr}`;
+  const onMessage = waitForMessage(hud, "Screenshot copied to clipboard.");
+  hud.jsterm.execute(command);
+  await onMessage;
+  const contentSize = await getContentSize();
+  const imgSize = await getImageSizeFromClipboard();
+
+  is(imgSize.width, contentSize.innerWidth,
+     "Clipboard: Image width matches window size");
+  is(imgSize.height, contentSize.innerHeight,
+     "Clipboard: Image height matches window size");
+}
+
+async function testFullpageClipboard(hud) {
+  const command = `:screenshot --fullpage --clipboard ${dpr}`;
+  const onMessage = waitForMessage(hud, "Screenshot copied to clipboard.");
+  hud.jsterm.execute(command);
+  await onMessage;
+  const contentSize = await getContentSize();
+  const imgSize = await getImageSizeFromClipboard();
+
+  is(imgSize.width,
+    (contentSize.innerWidth + contentSize.scrollMaxX -
+     contentSize.scrollMinX),
+    "Fullpage Clipboard: Image width matches page size");
+  is(imgSize.height,
+    (contentSize.innerHeight + contentSize.scrollMaxY -
+     contentSize.scrollMinY),
+    "Fullpage Clipboard: Image height matches page size");
+}
+
+async function testSelectorClipboard(hud) {
+  const command = `:screenshot --selector "img#testImage" --clipboard ${dpr}`;
+  const messageReceived = waitForMessage(hud, "Screenshot copied to clipboard.");
+  hud.jsterm.execute(command);
+  await messageReceived;
+  const imgSize1 = await getImageSizeFromClipboard();
+  await ContentTask.spawn(
+    gBrowser.selectedBrowser,
+    imgSize1,
+    function(imgSize) {
+      const img = content.document.querySelector("#testImage");
+      is(imgSize.width, img.clientWidth,
+         "Selector Clipboard: Image width matches element size");
+      is(imgSize.height, img.clientHeight,
+         "Selector Clipboard: Image height matches element size");
+    }
+  );
+}
+
+async function testFullpageClipboardScrollbar(hud) {
+  const command = `:screenshot --fullpage --clipboard ${dpr}`;
+  const onMessage = waitForMessage(hud, "Screenshot copied to clipboard.");
+  hud.jsterm.execute(command);
+  await onMessage;
+  const contentSize = await getContentSize();
+  const scrollbarSize = await getScrollbarSize();
+  const imgSize = await getImageSizeFromClipboard();
+
+  is(imgSize.width,
+    (contentSize.innerWidth + contentSize.scrollMaxX -
+     contentSize.scrollMinX) - scrollbarSize.width,
+    "Scroll Fullpage Clipboard: Image width matches page size minus scrollbar size");
+  is(imgSize.height,
+    (contentSize.innerHeight + contentSize.scrollMaxY -
+     contentSize.scrollMinY) - scrollbarSize.height,
+    "Scroll Fullpage Clipboard: Image height matches page size minus scrollbar size");
+}
+
+async function createScrollbarOverflow() {
+  // Trigger scrollbars by forcing document to overflow
+  // This only affects results on OSes with scrollbars that reduce document size
+  // (non-floating scrollbars).  With default OS settings, this means Windows
+  // and Linux are affected, but Mac is not.  For Mac to exhibit this behavior,
+  // change System Preferences -> General -> Show scroll bars to Always.
+  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
+    content.document.body.classList.add("overflow");
+  });
+}
+
+async function getScrollbarSize() {
+  const scrollbarSize = await ContentTask.spawn(
+    gBrowser.selectedBrowser,
+    {},
+    function() {
+      const winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIDOMWindowUtils);
+      const scrollbarHeight = {};
+      const scrollbarWidth = {};
+      winUtils.getScrollbarSize(true, scrollbarWidth, scrollbarHeight);
+      return {
+        width: scrollbarWidth.value,
+        height: scrollbarHeight.value,
+      };
+    }
+  );
+  info(`Scrollbar size: ${scrollbarSize.width}x${scrollbarSize.height}`);
+  return scrollbarSize;
+}
+
+async function getContentSize() {
+  const contentSize = await ContentTask.spawn(
+    gBrowser.selectedBrowser,
+    {},
+    function() {
+      return {
+        scrollMaxY: content.scrollMaxY,
+        scrollMaxX: content.scrollMaxX,
+        scrollMinY: content.scrollMinY,
+        scrollMinX: content.scrollMinX,
+        innerWidth: content.innerWidth,
+        innerHeight: content.innerHeight
+      };
+    }
+  );
+
+  info(`content size: ${contentSize.innerWidth}x${contentSize.innerHeight}`);
+  return contentSize;
+}
+
+async function getImageSizeFromClipboard() {
+  const clipid = Ci.nsIClipboard;
+  const clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
+  const trans = Cc["@mozilla.org/widget/transferable;1"]
+                .createInstance(Ci.nsITransferable);
+  const flavor = "image/png";
+  trans.init(null);
+  trans.addDataFlavor(flavor);
+
+  clip.getData(trans, clipid.kGlobalClipboard);
+  const data = {};
+  const dataLength = {};
+  trans.getTransferData(flavor, data, dataLength);
+
+  ok(data.value, "screenshot exists");
+  ok(dataLength.value > 0, "screenshot has length");
+
+  let image = data.value;
+  let dataURI = `data:${flavor};base64,`;
+
+  // Due to the differences in how images could be stored in the clipboard the
+  // checks below are needed. The clipboard could already provide the image as
+  // byte streams, but also as pointer, or as image container. If it's not
+  // possible obtain a byte stream, the function returns `null`.
+  if (image instanceof Ci.nsISupportsInterfacePointer) {
+    image = image.data;
+  }
+
+  if (image instanceof Ci.imgIContainer) {
+    image = Cc["@mozilla.org/image/tools;1"]
+              .getService(Ci.imgITools)
+              .encodeImage(image, flavor);
+  }
+
+  if (image instanceof Ci.nsIInputStream) {
+    const binaryStream = Cc["@mozilla.org/binaryinputstream;1"]
+                         .createInstance(Ci.nsIBinaryInputStream);
+    binaryStream.setInputStream(image);
+    const rawData = binaryStream.readBytes(binaryStream.available());
+    const charCodes = Array.from(rawData, c => c.charCodeAt(0) & 0xff);
+    let encodedData = String.fromCharCode(...charCodes);
+    encodedData = btoa(encodedData);
+    dataURI = dataURI + encodedData;
+  } else {
+    throw new Error("Unable to read image data");
+  }
+
+  const img = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
+
+  const loaded =  once(img, "load");
+
+  img.src = dataURI;
+  document.documentElement.appendChild(img);
+  await loaded;
+  img.remove();
+
+  return {
+    width: img.width,
+    height: img.height,
+  };
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_file.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that screenshot command works properly
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                 "test/mochitest/test_jsterm_screenshot_command.html";
+
+const FileUtils = (ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})).FileUtils;
+// on some machines, such as macOS, dpr is set to 2. This is expected behavior, however
+// to keep tests consistant across OSs we are setting the dpr to 1
+const dpr = "--dpr 1";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+
+  const hud = await openConsole();
+  ok(hud, "web console opened");
+
+  await testFile(hud);
+});
+
+async function testFile(hud) {
+  // Test capture to file
+  const file = FileUtils.getFile("TmpD", [ "TestScreenshotFile.png" ]);
+  const command = `:screenshot ${file.path} ${dpr}`;
+  const onMessage = waitForMessage(hud, `Saved to ${file.path}`);
+  hud.jsterm.execute(command);
+  await onMessage;
+
+  ok(file.exists(), "Screenshot file exists");
+
+  if (file.exists()) {
+    file.remove(false);
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test_jsterm_screenshot_command.html
@@ -0,0 +1,18 @@
+<html>
+  <head>
+    <style>
+      img {
+        height: 100px;
+        width: 100px;
+      }
+      .overflow {
+        overflow: scroll;
+        height: 200%;
+        width: 200%;
+      }
+    </style>
+  </head>
+  <body>
+    <img id="testImage" ></img>
+  </body>
+</html>
--- a/devtools/server/actors/webconsole/screenshot.js
+++ b/devtools/server/actors/webconsole/screenshot.js
@@ -1,17 +1,17 @@
 /* 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 { Ci, Cu } = require("chrome");
 const { getRect } = require("devtools/shared/layout/utils");
-const { LocalizationHelper } = require("devtools/shared/L10N");
+const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const CONTAINER_FLASHING_DURATION = 500;
 const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 exports.screenshot = function takeAsyncScreenshot(owner, args = {}) {
   if (args.help) {
     // Early return as help will be handled on the client side.