summaryrefslogtreecommitdiffstats
path: root/browser/components/screenshots/tests
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/screenshots/tests')
-rw-r--r--browser/components/screenshots/tests/browser/browser.toml55
-rw-r--r--browser/components/screenshots/tests/browser/browser_iframe_test.js123
-rw-r--r--browser/components/screenshots/tests/browser/browser_overlay_keyboard_test.js748
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js465
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js488
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js384
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js74
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_page_unload.js42
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_short_page_test.js123
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js466
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js186
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_escape.js35
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js175
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_page_crash.js54
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_screenshot_too_big.js90
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js289
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_toolbar_button.js26
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js356
-rw-r--r--browser/components/screenshots/tests/browser/browser_test_element_picker.js56
-rw-r--r--browser/components/screenshots/tests/browser/browser_test_resize.js100
-rw-r--r--browser/components/screenshots/tests/browser/first-iframe.html23
-rw-r--r--browser/components/screenshots/tests/browser/head.js951
-rw-r--r--browser/components/screenshots/tests/browser/iframe-test-page.html23
-rw-r--r--browser/components/screenshots/tests/browser/large-test-page.html9
-rw-r--r--browser/components/screenshots/tests/browser/second-iframe.html18
-rw-r--r--browser/components/screenshots/tests/browser/short-test-page.html8
-rw-r--r--browser/components/screenshots/tests/browser/test-page-resize.html25
-rw-r--r--browser/components/screenshots/tests/browser/test-page.html27
28 files changed, 5419 insertions, 0 deletions
diff --git a/browser/components/screenshots/tests/browser/browser.toml b/browser/components/screenshots/tests/browser/browser.toml
new file mode 100644
index 0000000000..b363c14732
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser.toml
@@ -0,0 +1,55 @@
+[DEFAULT]
+support-files = [
+ "head.js",
+ "iframe-test-page.html",
+ "first-iframe.html",
+ "second-iframe.html",
+ "test-page.html",
+ "short-test-page.html",
+ "large-test-page.html",
+ "test-page-resize.html",
+]
+
+prefs = [
+ "extensions.screenshots.disabled=false",
+ "screenshots.browser.component.enabled=true",
+]
+
+["browser_iframe_test.js"]
+
+["browser_overlay_keyboard_test.js"]
+
+["browser_screenshots_drag_scroll_test.js"]
+
+["browser_screenshots_drag_test.js"]
+
+["browser_screenshots_focus_test.js"]
+
+["browser_screenshots_overlay_panel_sync.js"]
+
+["browser_screenshots_page_unload.js"]
+
+["browser_screenshots_short_page_test.js"]
+
+["browser_screenshots_telemetry_tests.js"]
+
+["browser_screenshots_test_downloads.js"]
+
+["browser_screenshots_test_escape.js"]
+
+["browser_screenshots_test_full_page.js"]
+
+["browser_screenshots_test_page_crash.js"]
+skip-if = ["!crashreporter"]
+
+["browser_screenshots_test_screenshot_too_big.js"]
+
+["browser_screenshots_test_toggle_pref.js"]
+
+["browser_screenshots_test_toolbar_button.js"]
+
+["browser_screenshots_test_visible.js"]
+
+["browser_test_element_picker.js"]
+
+["browser_test_resize.js"]
diff --git a/browser/components/screenshots/tests/browser/browser_iframe_test.js b/browser/components/screenshots/tests/browser/browser_iframe_test.js
new file mode 100644
index 0000000000..bb853fbe28
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_iframe_test.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_selectingElementsInIframes() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: IFRAME_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ helper.triggerUIFromToolbar();
+
+ // There are two iframes in the test page. One iframe is nested in the
+ // other so we SpecialPowers.spawn into the iframes to get the
+ // dimension/position of the elements within each iframe.
+ let elementDimensions = await SpecialPowers.spawn(
+ browser,
+ [],
+ async () => {
+ let divDims = content.document
+ .querySelector("div")
+ .getBoundingClientRect();
+
+ let iframe = content.document.querySelector("iframe");
+ let iframesDivsDimArr = await SpecialPowers.spawn(
+ iframe,
+ [],
+ async () => {
+ let iframeDivDims = content.document
+ .querySelector("div")
+ .getBoundingClientRect();
+
+ // Element within the first iframe
+ iframeDivDims = {
+ left: iframeDivDims.left + content.window.mozInnerScreenX,
+ top: iframeDivDims.top + content.window.mozInnerScreenY,
+ width: iframeDivDims.width,
+ height: iframeDivDims.height,
+ };
+
+ let nestedIframe = content.document.querySelector("iframe");
+ let nestedIframeDivDims = await SpecialPowers.spawn(
+ nestedIframe,
+ [],
+ async () => {
+ let secondIframeDivDims = content.document
+ .querySelector("div")
+ .getBoundingClientRect();
+
+ // Element within the nested iframe
+ secondIframeDivDims = {
+ left:
+ secondIframeDivDims.left +
+ content.document.defaultView.mozInnerScreenX,
+ top:
+ secondIframeDivDims.top +
+ content.document.defaultView.mozInnerScreenY,
+ width: secondIframeDivDims.width,
+ height: secondIframeDivDims.height,
+ };
+
+ return secondIframeDivDims;
+ }
+ );
+
+ return [iframeDivDims, nestedIframeDivDims];
+ }
+ );
+
+ // Offset each element position for the browser window
+ for (let dims of iframesDivsDimArr) {
+ dims.left -= content.window.mozInnerScreenX;
+ dims.top -= content.window.mozInnerScreenY;
+ }
+
+ return [divDims].concat(iframesDivsDimArr);
+ }
+ );
+
+ info(JSON.stringify(elementDimensions, null, 2));
+
+ for (let el of elementDimensions) {
+ let x = el.left + el.width / 2;
+ let y = el.top + el.height / 2;
+
+ mouse.move(x, y);
+ await helper.waitForHoverElementRect(el.width, el.height);
+ mouse.click(x, y);
+
+ await helper.waitForStateChange("selected");
+
+ let dimensions = await helper.getSelectionRegionDimensions();
+
+ is(
+ dimensions.left,
+ el.left,
+ "The region left position matches the elements left position"
+ );
+ is(
+ dimensions.top,
+ el.top,
+ "The region top position matches the elements top position"
+ );
+ is(
+ dimensions.width,
+ el.width,
+ "The region width matches the elements width"
+ );
+ is(
+ dimensions.height,
+ el.height,
+ "The region height matches the elements height"
+ );
+
+ mouse.click(500, 500);
+ await helper.waitForStateChange("crosshairs");
+ }
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_overlay_keyboard_test.js b/browser/components/screenshots/tests/browser/browser_overlay_keyboard_test.js
new file mode 100644
index 0000000000..592587a67d
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_overlay_keyboard_test.js
@@ -0,0 +1,748 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+const KEY_TO_EXPECTED_POSITION_ARRAY = [
+ [
+ "ArrowRight",
+ {
+ top: 10,
+ left: 20,
+ bottom: 20,
+ right: 30,
+ },
+ ],
+ [
+ "ArrowDown",
+ {
+ top: 20,
+ left: 20,
+ bottom: 30,
+ right: 30,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: 20,
+ left: 10,
+ bottom: 30,
+ right: 20,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: 10,
+ left: 10,
+ bottom: 20,
+ right: 20,
+ },
+ ],
+ ["ArrowDown", { top: 20, left: 10, bottom: 30, right: 20 }],
+ [
+ "ArrowRight",
+ {
+ top: 20,
+ left: 20,
+ bottom: 30,
+ right: 30,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: 10,
+ left: 20,
+ bottom: 20,
+ right: 30,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: 10,
+ left: 10,
+ bottom: 20,
+ right: 20,
+ },
+ ],
+];
+
+const SHIFT_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY = [
+ [
+ "ArrowRight",
+ {
+ top: 100,
+ left: 200,
+ bottom: 200,
+ right: 300,
+ },
+ ],
+ [
+ "ArrowDown",
+ {
+ top: 200,
+ left: 200,
+ bottom: 300,
+ right: 300,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: 200,
+ left: 100,
+ bottom: 300,
+ right: 200,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: 100,
+ left: 100,
+ bottom: 200,
+ right: 200,
+ },
+ ],
+ ["ArrowDown", { top: 200, left: 100, bottom: 300, right: 200 }],
+ [
+ "ArrowRight",
+ {
+ top: 200,
+ left: 200,
+ bottom: 300,
+ right: 300,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: 100,
+ left: 200,
+ bottom: 200,
+ right: 300,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: 100,
+ left: 100,
+ bottom: 200,
+ right: 200,
+ },
+ ],
+];
+
+/**
+ *
+ */
+add_task(async function test_moveRegionWithKeyboard() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ // Because the screenshots state won't go from draggingReady to
+ // dragging until the diagonal distance is 40px, we have to resize
+ // it to get the region to 10px x 10px
+ await helper.dragOverlay(10, 10, 100, 100);
+ mouse.down(100, 100);
+ await helper.assertStateChange("resizing");
+ mouse.move(20, 20);
+ mouse.up(20, 20);
+ await helper.assertStateChange("selected");
+
+ await SpecialPowers.spawn(
+ browser,
+ [KEY_TO_EXPECTED_POSITION_ARRAY],
+ async keyToExpectedPositionArray => {
+ function assertSelectionRegionDimensions(
+ actualDimensions,
+ expectedDimensions
+ ) {
+ is(
+ actualDimensions.top,
+ expectedDimensions.top,
+ "Top dimension is correct"
+ );
+ is(
+ actualDimensions.left,
+ expectedDimensions.left,
+ "Left dimension is correct"
+ );
+ is(
+ actualDimensions.bottom,
+ expectedDimensions.bottom,
+ "Bottom dimension is correct"
+ );
+ is(
+ actualDimensions.right,
+ expectedDimensions.right,
+ "Right dimension is correct"
+ );
+ }
+
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ // Test moving each corner of the region
+ screenshotsChild.overlay.topLeftMover.focus();
+
+ // Check that initial position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 10,
+ left: 10,
+ bottom: 20,
+ right: 20,
+ }
+ );
+
+ for (let [key, expectedDimensions] of keyToExpectedPositionArray) {
+ EventUtils.synthesizeKey(key, { repeat: 20 }, content);
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+
+ // Test moving the highlight element
+ screenshotsChild.overlay.highlightEl.focus();
+
+ // Check that initial position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 10,
+ left: 10,
+ bottom: 20,
+ right: 20,
+ }
+ );
+
+ for (let [key, expectedDimensions] of keyToExpectedPositionArray) {
+ EventUtils.synthesizeKey(key, { repeat: 10 }, content);
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+ }
+ );
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlayClosed();
+ }
+ );
+});
+
+add_task(async function test_moveRegionWithKeyboardWithShift() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(100, 100, 200, 200);
+
+ await SpecialPowers.spawn(
+ browser,
+ [SHIFT_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY],
+ async shiftPlusKeyToExpectedPositionArray => {
+ function assertSelectionRegionDimensions(
+ actualDimensions,
+ expectedDimensions
+ ) {
+ is(
+ actualDimensions.top,
+ expectedDimensions.top,
+ "Top dimension is correct"
+ );
+ is(
+ actualDimensions.left,
+ expectedDimensions.left,
+ "Left dimension is correct"
+ );
+ is(
+ actualDimensions.bottom,
+ expectedDimensions.bottom,
+ "Bottom dimension is correct"
+ );
+ is(
+ actualDimensions.right,
+ expectedDimensions.right,
+ "Right dimension is correct"
+ );
+ }
+
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ // Test moving each corner of the region
+ screenshotsChild.overlay.topLeftMover.focus();
+
+ // Check that initial position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 100,
+ left: 100,
+ bottom: 200,
+ right: 200,
+ }
+ );
+
+ for (let [
+ key,
+ expectedDimensions,
+ ] of shiftPlusKeyToExpectedPositionArray) {
+ EventUtils.synthesizeKey(
+ key,
+ { repeat: 20, shiftKey: true },
+ content
+ );
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+
+ // Test moving the highlight element
+ screenshotsChild.overlay.highlightEl.focus();
+
+ // Check that initial position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 100,
+ left: 100,
+ bottom: 200,
+ right: 200,
+ }
+ );
+
+ for (let [
+ key,
+ expectedDimensions,
+ ] of shiftPlusKeyToExpectedPositionArray) {
+ EventUtils.synthesizeKey(
+ key,
+ { repeat: 10, shiftKey: true },
+ content
+ );
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+ }
+ );
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlayClosed();
+ }
+ );
+});
+
+add_task(async function test_moveRegionWithKeyboardWithAccelKey() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ await helper.scrollContentWindow(100, 100);
+
+ let contentWindowDimensions = await helper.getContentDimensions();
+ ok(contentWindowDimensions, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(100, 100, 200, 200);
+
+ info("Test moving the the highlight element");
+ await SpecialPowers.spawn(
+ browser,
+ [contentWindowDimensions],
+ async contentDimensions => {
+ function assertSelectionRegionDimensions(
+ actualDimensions,
+ expectedDimensions
+ ) {
+ is(
+ actualDimensions.top,
+ expectedDimensions.top,
+ "Top dimension is correct"
+ );
+ is(
+ actualDimensions.left,
+ expectedDimensions.left,
+ "Left dimension is correct"
+ );
+ is(
+ actualDimensions.bottom,
+ expectedDimensions.bottom,
+ "Bottom dimension is correct"
+ );
+ is(
+ actualDimensions.right,
+ expectedDimensions.right,
+ "Right dimension is correct"
+ );
+ }
+
+ let { scrollX, scrollY, clientHeight, clientWidth } =
+ contentDimensions;
+
+ const HIGHLIGHT_CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY = [
+ [
+ "ArrowRight",
+ {
+ top: scrollY,
+ left: scrollX + clientWidth - 100,
+ bottom: scrollY + 100,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowDown",
+ {
+ top: scrollY + clientHeight - 100,
+ left: scrollX + clientWidth - 100,
+ bottom: scrollY + clientHeight,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: scrollY + clientHeight - 100,
+ left: scrollX,
+ bottom: scrollY + clientHeight,
+ right: scrollX + 100,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: scrollY,
+ left: scrollX,
+ bottom: scrollY + 100,
+ right: scrollX + 100,
+ },
+ ],
+ [
+ "ArrowDown",
+ {
+ top: scrollY + clientHeight - 100,
+ left: scrollX,
+ bottom: scrollY + clientHeight,
+ right: scrollX + 100,
+ },
+ ],
+ [
+ "ArrowRight",
+ {
+ top: scrollY + clientHeight - 100,
+ left: scrollX + clientWidth - 100,
+ bottom: scrollY + clientHeight,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: scrollY,
+ left: scrollX + clientWidth - 100,
+ bottom: scrollY + 100,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: scrollY,
+ left: scrollX,
+ bottom: scrollY + 100,
+ right: scrollX + 100,
+ },
+ ],
+ ];
+
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ screenshotsChild.overlay.highlightEl.focus();
+
+ // Move the region around in a clockwise direction
+ // Check that original position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 100,
+ left: 100,
+ bottom: 200,
+ right: 200,
+ }
+ );
+
+ for (let [
+ key,
+ expectedDimensions,
+ ] of HIGHLIGHT_CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY) {
+ EventUtils.synthesizeKey(key, { accelKey: true }, content);
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+ }
+ );
+
+ mouse.click(300, 300);
+ await helper.assertStateChange("crosshairs");
+
+ await helper.dragOverlay(200, 200, 300, 300);
+
+ info("Test moving the corners clockwise");
+ await SpecialPowers.spawn(
+ browser,
+ [contentWindowDimensions],
+ async contentDimensions => {
+ function assertSelectionRegionDimensions(
+ actualDimensions,
+ expectedDimensions
+ ) {
+ is(
+ actualDimensions.top,
+ expectedDimensions.top,
+ "Top dimension is correct"
+ );
+ is(
+ actualDimensions.left,
+ expectedDimensions.left,
+ "Left dimension is correct"
+ );
+ is(
+ actualDimensions.bottom,
+ expectedDimensions.bottom,
+ "Bottom dimension is correct"
+ );
+ is(
+ actualDimensions.right,
+ expectedDimensions.right,
+ "Right dimension is correct"
+ );
+ }
+
+ let { scrollX, scrollY, clientHeight, clientWidth } =
+ contentDimensions;
+
+ const CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY = [
+ [
+ "ArrowRight",
+ {
+ top: scrollY + 100,
+ left: scrollX + 100 + 100,
+ bottom: scrollY + 100 + 100,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowDown",
+ {
+ top: scrollY + 100 + 100,
+ left: scrollX + 100 + 100,
+ bottom: scrollY + clientHeight,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: scrollY + 100 + 100,
+ left: scrollX,
+ bottom: scrollY + clientHeight,
+ right: scrollX + 100 + 100,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: scrollY,
+ left: scrollX,
+ bottom: scrollY + 100 + 100,
+ right: scrollX + 100 + 100,
+ },
+ ],
+ ];
+
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ screenshotsChild.overlay.topLeftMover.focus();
+
+ // Move the region around in a clockwise direction
+ // Check that original position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 200,
+ left: 200,
+ bottom: 300,
+ right: 300,
+ }
+ );
+
+ for (let [
+ key,
+ expectedDimensions,
+ ] of CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY) {
+ EventUtils.synthesizeKey(key, { accelKey: true }, content);
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+ }
+ );
+
+ mouse.click(400, 400);
+ await helper.assertStateChange("crosshairs");
+
+ await helper.dragOverlay(200, 200, 300, 300);
+
+ info("Test moving the corners counter clockwise");
+ await SpecialPowers.spawn(
+ browser,
+ [contentWindowDimensions],
+ async contentDimensions => {
+ function assertSelectionRegionDimensions(
+ actualDimensions,
+ expectedDimensions
+ ) {
+ is(
+ actualDimensions.top,
+ expectedDimensions.top,
+ "Top dimension is correct"
+ );
+ is(
+ actualDimensions.left,
+ expectedDimensions.left,
+ "Left dimension is correct"
+ );
+ is(
+ actualDimensions.bottom,
+ expectedDimensions.bottom,
+ "Bottom dimension is correct"
+ );
+ is(
+ actualDimensions.right,
+ expectedDimensions.right,
+ "Right dimension is correct"
+ );
+ }
+
+ let { scrollX, scrollY, clientHeight, clientWidth } =
+ contentDimensions;
+
+ const CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY = [
+ [
+ "ArrowDown",
+ {
+ top: scrollY + 100 + 100,
+ left: scrollX + 100,
+ bottom: scrollY + clientHeight,
+ right: scrollX + 100 + 100,
+ },
+ ],
+ [
+ "ArrowRight",
+ {
+ top: scrollY + 100 + 100,
+ left: scrollX + 100 + 100,
+ bottom: scrollY + clientHeight,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowUp",
+ {
+ top: scrollY,
+ left: scrollX + 100 + 100,
+ bottom: scrollY + 100 + 100,
+ right: scrollX + clientWidth,
+ },
+ ],
+ [
+ "ArrowLeft",
+ {
+ top: scrollY,
+ left: scrollX,
+ bottom: scrollY + 100 + 100,
+ right: scrollX + 100 + 100,
+ },
+ ],
+ ];
+
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ screenshotsChild.overlay.topLeftMover.focus();
+
+ // Move the region around in a clockwise direction
+ // Check that original position is correct
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ {
+ top: 200,
+ left: 200,
+ bottom: 300,
+ right: 300,
+ }
+ );
+
+ for (let [
+ key,
+ expectedDimensions,
+ ] of CONTROL_PLUS_KEY_TO_EXPECTED_POSITION_ARRAY) {
+ EventUtils.synthesizeKey(key, { accelKey: true }, content);
+ assertSelectionRegionDimensions(
+ screenshotsChild.overlay.selectionRegion.dimensions,
+ expectedDimensions
+ );
+ }
+ }
+ );
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js b/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js
new file mode 100644
index 0000000000..7fdb084ca6
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js
@@ -0,0 +1,465 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test that screenshots overlay covers the entire page
+ */
+add_task(async function test_overlayCoversEntirePage() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ info(JSON.stringify(contentInfo, null, 2));
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let { scrollWidth, scrollHeight } =
+ await helper.getScreenshotsOverlayDimensions();
+
+ is(
+ scrollWidth,
+ contentInfo.scrollWidth,
+ "The overlay spans the entire width of the page"
+ );
+
+ is(
+ scrollHeight,
+ contentInfo.scrollHeight,
+ "The overlay spans the entire height of the page"
+ );
+ }
+ );
+});
+
+/**
+ * Test dragging screenshots box off top left of screen
+ */
+add_task(async function test_draggingBoxOffTopLeft() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ let startX = 10;
+ let startY = 10;
+ let endX = 500;
+ let endY = 500;
+ await helper.dragOverlay(startX, startY, endX, endY);
+
+ mouse.down(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(10, 10);
+
+ // We moved the box to the edge of the screen so we need to wait until the box size is updated
+ await helper.waitForSelectionRegionSizeChange(490);
+
+ let dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, 0, "The box x1 position is now 0");
+ is(dimensions.top, 0, "The box y1 position is now 0");
+ is(dimensions.width, 255, "The box width is now 255");
+ is(dimensions.height, 255, "The box height is now 255");
+
+ mouse.move(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+
+ mouse.up(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+
+ // We moved the box off the edge of the screen so we need to wait until the box size is updated
+ await helper.waitForSelectionRegionSizeChange(255);
+
+ dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, 10, "The box x1 position is now 10 again");
+ is(dimensions.top, 10, "The box y1 position is now 10 again");
+ is(dimensions.width, 490, "The box width is now 490 again");
+ is(dimensions.height, 490, "The box height is now 490 again");
+ }
+ );
+});
+
+/**
+ * Test dragging screenshots box off bottom right of screen
+ */
+add_task(async function test_draggingBoxOffBottomRight() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ info(JSON.stringify(contentInfo));
+ ok(contentInfo, "Got dimensions back from the content");
+
+ await helper.scrollContentWindow(
+ contentInfo.scrollWidth - contentInfo.clientWidth,
+ contentInfo.scrollHeight - contentInfo.clientHeight
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ let startX = contentInfo.scrollWidth - 500;
+ let startY = contentInfo.scrollHeight - 500;
+ let endX = contentInfo.scrollWidth - 20;
+ let endY = contentInfo.scrollHeight - 20;
+
+ await helper.dragOverlay(startX, startY, endX, endY);
+
+ // move box off the bottom right of the screen
+ mouse.down(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+ mouse.move(
+ startX + 50 + Math.floor((endX - startX) / 2),
+ startY + 50 + Math.floor((endY - startY) / 2)
+ );
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(endX, endY);
+
+ // We moved the box to the edge of the screen so we need to wait until the box size is updated
+ await helper.waitForSelectionRegionSizeChange(480);
+
+ let dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, startX + 240, "The box x1 position is now 3748");
+ is(dimensions.top, startY + 240, "The box y1 position is now 3756");
+ is(dimensions.width, 260, "The box width is now 260");
+ is(dimensions.height, 260, "The box height is now 260");
+
+ mouse.move(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+
+ mouse.up(
+ startX + Math.floor((endX - startX) / 2),
+ startY + Math.floor((endY - startY) / 2)
+ );
+
+ // We moved the box off the edge of the screen so we need to wait until the box size is updated
+ await helper.waitForSelectionRegionSizeChange(252);
+
+ dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, startX, "The box x1 position is now 3508 again");
+ is(dimensions.top, startY, "The box y1 position is now 3516 again");
+ is(dimensions.width, 480, "The box width is now 480 again");
+ is(dimensions.height, 480, "The box height is now 480 again");
+ }
+ );
+});
+
+/**
+ * test scrolling while screenshots is open
+ */
+add_task(async function test_scrollingScreenshotsOpen() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ info(JSON.stringify(contentInfo));
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ let startX = 10;
+ let startY = 10;
+ let endX = 100;
+ let endY = 100;
+
+ await helper.dragOverlay(startX, startY, endX, endY);
+
+ let scrollX = 1000;
+ let scrollY = 1000;
+
+ await helper.scrollContentWindow(scrollX, scrollY);
+
+ let dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, startX, "The box x1 position is 10");
+ is(dimensions.top, startY, "The box y1 position is 10");
+ is(dimensions.width, endX - startX, "The box width is now 90");
+ is(dimensions.height, endY - startY, "The box height is now 90");
+
+ // reset screenshots box
+ await helper.escapeKeyInContent();
+ await helper.assertStateChange("crosshairs");
+
+ await helper.dragOverlay(
+ scrollX + startX,
+ scrollY + startY,
+ scrollX + endX,
+ scrollY + endY
+ );
+
+ await helper.scrollContentWindow(0, 0);
+
+ dimensions = await helper.getSelectionRegionDimensions();
+
+ is(dimensions.left, scrollX + startX, "The box x1 position is 1010");
+ is(dimensions.top, scrollY + startY, "The box y1 position is 1010");
+ is(dimensions.width, endX - startX, "The box width is now 90");
+ is(dimensions.height, endY - startY, "The box height is now 90");
+
+ // reset screenshots box
+ await helper.escapeKeyInContent();
+ await helper.assertStateChange("crosshairs");
+
+ await helper.dragOverlay(
+ startX,
+ startY,
+ contentInfo.clientWidth - 10,
+ contentInfo.clientHeight - 10
+ );
+
+ await helper.scrollContentWindow(
+ contentInfo.clientWidth - 20,
+ contentInfo.clientHeight - 20
+ );
+
+ mouse.down(contentInfo.clientWidth - 10, contentInfo.clientHeight - 10);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(
+ contentInfo.clientWidth * 2 - 30,
+ contentInfo.clientHeight * 2 - 30
+ );
+
+ mouse.up(
+ contentInfo.clientWidth * 2 - 30,
+ contentInfo.clientHeight * 2 - 30
+ );
+
+ await helper.assertStateChange("selected");
+
+ let { left, top, right, bottom, width, height } =
+ await helper.getSelectionRegionDimensions();
+ let { scrollWidth, scrollHeight } =
+ await helper.getScreenshotsOverlayDimensions();
+
+ is(left, startX, "The box left is 10");
+ is(top, startY, "The box top is 10");
+ is(
+ right,
+ contentInfo.clientWidth * 2 - 30,
+ "The box right is 2 x clientWidth - 30"
+ );
+ is(
+ bottom,
+ contentInfo.clientHeight * 2 - 30,
+ "The box right is 2 x clientHeight - 30"
+ );
+ is(
+ width,
+ contentInfo.clientWidth * 2 - 40,
+ "The box right is 2 x clientWidth - 40"
+ );
+ is(
+ height,
+ contentInfo.clientHeight * 2 - 40,
+ "The box right is 2 x clientHeight - 40"
+ );
+ is(
+ scrollWidth,
+ contentInfo.scrollWidth,
+ "The overlay spans the entire width of the page"
+ );
+ is(
+ scrollHeight,
+ contentInfo.scrollHeight,
+ "The overlay spans the entire height of the page"
+ );
+ }
+ );
+});
+
+/**
+ * test scroll if by edge
+ */
+add_task(async function test_scrollIfByEdge() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let windowX = 1000;
+ let windowY = 1000;
+
+ await helper.scrollContentWindow(windowX, windowY);
+
+ await TestUtils.waitForTick();
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let { scrollX, scrollY } = await helper.getContentDimensions();
+
+ is(scrollX, windowX, "Window x position is 1000");
+ is(scrollY, windowY, "Window y position is 1000");
+
+ let startX = 1100;
+ let startY = 1100;
+ let endX = 1010;
+ let endY = 1010;
+
+ // The window won't scroll if the state is draggingReady so we move to
+ // get into the dragging state and then move again to scroll the window
+ mouse.down(startX, startY);
+ await helper.assertStateChange("draggingReady");
+ mouse.move(1050, 1050);
+ await helper.assertStateChange("dragging");
+ mouse.move(endX, endY);
+ mouse.up(endX, endY);
+ await helper.assertStateChange("selected");
+
+ windowX = 990;
+ windowY = 990;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY } = await helper.getContentDimensions());
+
+ is(scrollX, windowX, "Window x position is 990");
+ is(scrollY, windowY, "Window y position is 990");
+
+ let contentInfo = await helper.getContentDimensions();
+
+ endX = windowX + contentInfo.clientWidth - 10;
+ endY = windowY + contentInfo.clientHeight - 10;
+
+ info(
+ `starting to drag overlay to ${endX}, ${endY} in test\nclientInfo: ${JSON.stringify(
+ contentInfo
+ )}\n`
+ );
+ await helper.dragOverlay(startX, startY, endX, endY, "selected");
+
+ windowX = 1000;
+ windowY = 1000;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY } = await helper.getContentDimensions());
+
+ is(scrollX, windowX, "Window x position is 1000");
+ is(scrollY, windowY, "Window y position is 1000");
+ }
+ );
+});
+
+add_task(async function test_scrollIfByEdgeWithKeyboard() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let windowX = 1000;
+ let windowY = 1000;
+
+ await helper.scrollContentWindow(windowX, windowY);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let { scrollX, scrollY, clientWidth, clientHeight } =
+ await helper.getContentDimensions();
+
+ is(scrollX, windowX, "Window x position is 1000");
+ is(scrollY, windowY, "Window y position is 1000");
+
+ await helper.dragOverlay(1020, 1020, 1120, 1120);
+
+ await helper.moveOverlayViaKeyboard("highlight", [
+ { key: "ArrowLeft", options: { shiftKey: true } },
+ { key: "ArrowLeft", options: {} },
+ { key: "ArrowUp", options: { shiftKey: true } },
+ { key: "ArrowUp", options: {} },
+ ]);
+
+ windowX = 989;
+ windowY = 989;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY, clientWidth, clientHeight } =
+ await helper.getContentDimensions());
+
+ is(scrollX, windowX, "Window x position is 989");
+ is(scrollY, windowY, "Window y position is 989");
+
+ mouse.click(1200, 1200);
+ await helper.assertStateChange("crosshairs");
+ await helper.dragOverlay(
+ scrollX + clientWidth - 100 - 20,
+ scrollY + clientHeight - 100 - 20,
+ scrollX + clientWidth - 20,
+ scrollY + clientHeight - 20
+ );
+
+ await helper.moveOverlayViaKeyboard("highlight", [
+ { key: "ArrowRight", options: { shiftKey: true } },
+ { key: "ArrowRight", options: {} },
+ { key: "ArrowDown", options: { shiftKey: true } },
+ { key: "ArrowDown", options: {} },
+ ]);
+
+ windowX = 1000;
+ windowY = 1000;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY } = await helper.getContentDimensions());
+
+ is(scrollX, windowX, "Window x position is 1000");
+ is(scrollY, windowY, "Window y position is 1000");
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js b/browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js
new file mode 100644
index 0000000000..605e0ae75c
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js
@@ -0,0 +1,488 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * This function drags to a 490x490 area and copies to the clipboard
+ */
+add_task(async function dragTest() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let expected = Math.floor(
+ 490 * (await getContentDevicePixelRatio(browser))
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expected,
+ expected
+ );
+
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ Assert.equal(
+ result.width,
+ expected,
+ `The copied image from the overlay is ${expected}px in width`
+ );
+ Assert.equal(
+ result.height,
+ expected,
+ `The copied image from the overlay is ${expected}px in height`
+ );
+ }
+ );
+});
+
+/**
+ * This function drags a 1.5 zoomed browser to a 490x490 area and copies to the clipboard
+ */
+add_task(async function dragTest1Point5Zoom() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ const zoom = 1.5;
+ let helper = new ScreenshotsHelper(browser);
+ helper.zoomBrowser(zoom);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let expected = Math.floor(
+ 50 * (await getContentDevicePixelRatio(browser))
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(300, 100, 350, 150);
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expected,
+ expected
+ );
+
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ result.zoom = zoom;
+ result.devicePixelRatio = window.devicePixelRatio;
+ result.contentDevicePixelRatio = await getContentDevicePixelRatio(
+ browser
+ );
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ Assert.equal(
+ result.width,
+ expected,
+ `The copied image from the overlay is ${expected}px in width`
+ );
+ Assert.equal(
+ result.height,
+ expected,
+ `The copied image from the overlay is ${expected}px in height`
+ );
+ }
+ );
+});
+
+/**
+ * This function drags an area and clicks elsewhere
+ * on the overlay to go back to the crosshairs state
+ */
+add_task(async function clickOverlayResetState() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 100, 100);
+
+ // click outside overlay
+ mouse.click(200, 200);
+
+ await helper.assertStateChange("crosshairs");
+ }
+ );
+});
+
+/**
+ * This function drags an area and clicks the
+ * cancel button to restart the overlay
+ */
+add_task(async function overlayCancelButton() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 300, 300);
+
+ await helper.clickCancelButton();
+
+ await helper.assertStateChange("crosshairs");
+ }
+ );
+});
+
+/**
+ * This function drags a 490x490 area and moves it along the edges
+ * and back to the center to confirm that the original size is preserved
+ */
+add_task(async function preserveBoxSizeWhenMovingOutOfWindowBounds() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let expected = Math.floor(
+ 490 * (await getContentDevicePixelRatio(browser))
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let startX = 10;
+ let startY = 10;
+ let endX = 500;
+ let endY = 500;
+
+ mouse.down(
+ Math.floor((endX - startX) / 2),
+ Math.floor((endY - startY) / 2)
+ );
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(10, 10);
+
+ mouse.move(contentInfo.clientWidth - 10, contentInfo.clientHeight - 10);
+
+ mouse.up(
+ Math.floor((endX - startX) / 2),
+ Math.floor((endY - startY) / 2)
+ );
+
+ await helper.assertStateChange("selected");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expected,
+ expected
+ );
+
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ Assert.equal(
+ result.width,
+ expected,
+ `The copied image from the overlay is ${expected}px in width`
+ );
+ Assert.equal(
+ result.height,
+ expected,
+ `The copied image from the overlay is ${expected}px in height`
+ );
+ }
+ );
+});
+
+/**
+ * This function drags a 490x490 area and resizes it to a 300x300 area
+ * with the 4 sides of the box
+ */
+add_task(async function resizeAllEdges() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let expected = Math.floor(
+ 300 * (await getContentDevicePixelRatio(browser))
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let startX = 10;
+ let startY = 10;
+ let endX = 500;
+ let endY = 500;
+
+ let x = Math.floor((endX - startX) / 2);
+
+ // drag top
+ mouse.down(x, 10);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(x, 100);
+ mouse.up(x, 100);
+
+ await helper.assertStateChange("selected");
+
+ // drag bottom
+ mouse.down(x, 500);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(x, 400);
+ mouse.up(x, 400);
+
+ await helper.assertStateChange("selected");
+
+ // drag right
+ let y = Math.floor((endY - startY) / 2);
+ mouse.down(500, y);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(400, y);
+ mouse.up(400, y);
+
+ await helper.assertStateChange("selected");
+
+ // drag left
+ mouse.down(10, y);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(100, y);
+ mouse.up(100, y);
+
+ await helper.assertStateChange("selected");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expected,
+ expected
+ );
+
+ helper.endX = 400;
+ helper.endY = 400;
+
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ Assert.equal(
+ result.width,
+ expected,
+ `The copied image from the overlay is ${expected}px in width`
+ );
+ Assert.equal(
+ result.height,
+ expected,
+ `The copied image from the overlay is ${expected}px in height`
+ );
+ }
+ );
+});
+
+/**
+ * This function drags a 490x490 area and resizes it to a 300x300 area
+ * with the 4 corners of the box
+ */
+add_task(async function resizeAllCorners() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let expected = Math.floor(
+ 300 * (await getContentDevicePixelRatio(browser))
+ );
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ // drag topright
+ mouse.down(500, 10);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(450, 50);
+ mouse.up(450, 50);
+
+ await helper.assertStateChange("selected");
+
+ // drag bottomright
+ mouse.down(450, 500);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(400, 450);
+ mouse.up(400, 450);
+
+ await helper.assertStateChange("selected");
+
+ // drag bottomleft
+ mouse.down(10, 450);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(50, 400);
+ mouse.up(50, 400);
+
+ await helper.assertStateChange("selected");
+
+ // drag topleft
+ mouse.down(50, 50);
+
+ await helper.assertStateChange("resizing");
+
+ mouse.move(100, 100);
+ mouse.up(100, 100);
+
+ await helper.assertStateChange("selected");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expected,
+ expected
+ );
+
+ helper.endX = 400;
+ helper.endY = 400;
+
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ Assert.equal(
+ result.width,
+ expected,
+ `The copied image from the overlay is ${expected}px in width`
+ );
+ Assert.equal(
+ result.height,
+ expected,
+ `The copied image from the overlay is ${expected}px in height`
+ );
+ }
+ );
+});
+
+/**
+ * This function tests clicking the overlay with the different mouse buttons
+ */
+add_task(async function test_otherMouseButtons() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 100, 100);
+
+ // click outside overlay
+ mouse.click(200, 200, { button: 1 });
+ mouse.click(200, 200, { button: 2 });
+
+ await TestUtils.waitForTick();
+
+ await helper.assertStateChange("selected");
+
+ mouse.click(200, 200);
+
+ await helper.assertStateChange("crosshairs");
+
+ mouse.down(10, 10, { button: 1 });
+ mouse.move(100, 100, { button: 1 });
+ mouse.up(100, 100, { button: 1 });
+
+ await TestUtils.waitForTick();
+
+ await helper.assertStateChange("crosshairs");
+
+ mouse.down(10, 10, { button: 2 });
+ mouse.move(100, 100, { button: 2 });
+ mouse.up(100, 100, { button: 2 });
+
+ await TestUtils.waitForTick();
+
+ await helper.assertStateChange("crosshairs");
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js b/browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js
new file mode 100644
index 0000000000..367f62205e
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js
@@ -0,0 +1,384 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SCREENSHOTS_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "canceled", object: "escape" },
+];
+
+const SCREENSHOTS_LAST_SCREENSHOT_METHOD_PREF =
+ "screenshots.browser.component.last-screenshot-method";
+const SCREENSHOTS_LAST_SAVED_METHOD_PREF =
+ "screenshots.browser.component.last-saved-method";
+
+async function restoreFocusOnEscape(initialFocusElem, helper) {
+ info(
+ `restoreFocusOnEscape, focusedElement: ${Services.focus.focusedElement.localName}#${Services.focus.focusedElement.id}`
+ );
+ is(
+ window,
+ BrowserWindowTracker.getTopWindow(),
+ "Our window is the top window"
+ );
+
+ let gotFocus;
+ if (Services.focus.focusedElement !== initialFocusElem) {
+ gotFocus = BrowserTestUtils.waitForEvent(initialFocusElem, "focus");
+ await SimpleTest.promiseFocus(initialFocusElem.ownerGlobal);
+ Services.focus.setFocus(initialFocusElem, Services.focus.FLAG_BYKEY);
+ info(
+ `Waiting to place focus on initialFocusElem: ${initialFocusElem.localName}#${initialFocusElem.id}`
+ );
+ await gotFocus;
+ }
+ is(
+ Services.focus.focusedElement,
+ initialFocusElem,
+ `The initial element #${initialFocusElem.id} has focus`
+ );
+
+ helper.assertPanelNotVisible();
+ // open Screenshots with the keyboard shortcut
+ info(
+ "Triggering screenshots UI with the ctrl+shift+s and waiting for the panel"
+ );
+ EventUtils.synthesizeKey("s", { shiftKey: true, accelKey: true });
+
+ let button = await helper.getPanelButton(".visible-page");
+ info("Panel is now visible, got button: " + button.className);
+ info(
+ `focusedElement: ${Services.focus.focusedElement.localName}.${Services.focus.focusedElement.className}`
+ );
+
+ await BrowserTestUtils.waitForCondition(async () => {
+ return button.getRootNode().activeElement === button;
+ }, "The first button in the panel should have focus");
+
+ info(
+ "Sending Escape to dismiss the screenshots UI and for the panel to be closed"
+ );
+
+ let exitObserved = TestUtils.topicObserved("screenshots-exit");
+ EventUtils.synthesizeKey("KEY_Escape");
+ await helper.waitForPanelClosed();
+ await exitObserved;
+ info("Waiting for the initialFocusElem to be the focusedElement");
+ await BrowserTestUtils.waitForCondition(async () => {
+ return Services.focus.focusedElement === initialFocusElem;
+ }, "The initially focused element should have focus");
+
+ info(
+ `Screenshots did exit, focusedElement: ${Services.focus.focusedElement.localName}#${Services.focus.focusedElement.id}`
+ );
+ helper.assertPanelNotVisible();
+}
+
+add_task(async function testPanelFocused() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ info("Opening Screenshots and waiting for the panel");
+ helper.triggerUIFromToolbar();
+
+ let button = await helper.getPanelButton(".visible-page");
+ info("Panel is now visible, got button: " + button.className);
+ info(
+ `focusedElement: ${Services.focus.focusedElement.localName}.${Services.focus.focusedElement.className}`
+ );
+
+ info("Waiting for the button to be the activeElement");
+ await BrowserTestUtils.waitForCondition(() => {
+ return button.getRootNode().activeElement === button;
+ }, "The first button in the panel should have focus");
+
+ info("Sending Escape to close Screenshots");
+ let exitObserved = TestUtils.topicObserved("screenshots-exit");
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ info("waiting for the panel to be closed");
+ await helper.waitForPanelClosed();
+ info("waiting for the overlay to be closed");
+ await helper.waitForOverlayClosed();
+ await exitObserved;
+
+ info("Checking telemetry");
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+ helper.assertPanelNotVisible();
+ }
+ );
+});
+
+add_task(async function testRestoreFocusToChromeOnEscape() {
+ for (let focusSelector of [
+ "#urlbar-input", // A focusable HTML chrome element
+ "tab[selected='true']", // The selected tab element
+ ]) {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ helper.assertPanelNotVisible();
+ let initialFocusElem = document.querySelector(focusSelector);
+ await SimpleTest.promiseFocus(window);
+ await restoreFocusOnEscape(initialFocusElem, helper);
+ }
+ );
+ }
+});
+
+add_task(async function testRestoreFocusToToolbarbuttonOnEscape() {
+ const focusId = "PanelUI-menu-button"; // a toolbarbutton
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ helper.assertPanelNotVisible();
+ let initialFocusElem = document.getElementById(focusId);
+ await SimpleTest.promiseFocus(window);
+ await restoreFocusOnEscape(initialFocusElem, helper);
+ }
+ );
+}).skip(); // Bug 1867687
+
+add_task(async function testRestoreFocusToContentOnEscape() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: 'data:text/html;charset=utf-8,%3Cinput type%3D"text" id%3D"field"%3E',
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ await SimpleTest.promiseFocus(browser);
+ await BrowserTestUtils.synthesizeMouse("#field", 2, 2, {}, browser);
+
+ let initialFocusElem = Services.focus.focusedElement;
+ await restoreFocusOnEscape(initialFocusElem, helper);
+
+ is(
+ initialFocusElem,
+ document.activeElement,
+ "The browser element has focus"
+ );
+ let focusId = await SpecialPowers.spawn(browser, [], () => {
+ return content.document.activeElement.id;
+ });
+ is(focusId, "field", "The button in the content document has focus");
+ }
+ );
+});
+
+add_task(async function test_focusLastUsedMethod() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [SCREENSHOTS_LAST_SCREENSHOT_METHOD_PREF, ""],
+ [SCREENSHOTS_LAST_SAVED_METHOD_PREF, ""],
+ ["browser.download.useDownloadDir", true],
+ ],
+ });
+
+ let publicDownloads = await Downloads.getList(Downloads.PUBLIC);
+ // First ensure we catch the download finishing.
+ let downloadFinishedPromise = new Promise(resolve => {
+ publicDownloads.addView({
+ onDownloadChanged(download) {
+ info("Download changed!");
+ if (download.succeeded || download.error) {
+ info("Download succeeded or errored");
+ publicDownloads.removeView(this);
+ resolve(download);
+ }
+ },
+ });
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let expectedFocusedButton = await helper.getPanelButton(".visible-page");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The visible button in the panel should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The visible button in the panel should have focus"
+ );
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+ let fullpageButton = await helper.getPanelButton(".full-page");
+ fullpageButton.click();
+ await screenshotReady;
+
+ let dialog = helper.getDialog();
+ let retryButton = dialog._frame.contentDocument.getElementById("retry");
+ retryButton.click();
+
+ await helper.waitForOverlay();
+
+ expectedFocusedButton = await helper.getPanelButton(".full-page");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The full page button in the panel should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The full button in the panel should have focus"
+ );
+
+ screenshotReady = TestUtils.topicObserved("screenshots-preview-ready");
+ let visiblepageButton = await helper.getPanelButton(".visible-page");
+ visiblepageButton.click();
+ await screenshotReady;
+
+ dialog = helper.getDialog();
+ retryButton = dialog._frame.contentDocument.getElementById("retry");
+ retryButton.click();
+
+ await helper.waitForOverlay();
+
+ expectedFocusedButton = await helper.getPanelButton(".visible-page");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The visible button in the panel should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The visible button in the panel should have focus"
+ );
+
+ screenshotReady = TestUtils.topicObserved("screenshots-preview-ready");
+ expectedFocusedButton.click();
+ await screenshotReady;
+
+ dialog = helper.getDialog();
+
+ expectedFocusedButton =
+ dialog._frame.contentDocument.getElementById("download");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The download button in the preview dialog should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The download button in the preview dialog should have focus"
+ );
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ copyButton.click();
+ await screenshotExit;
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let visibleButton = await helper.getPanelButton(".visible-page");
+
+ screenshotReady = TestUtils.topicObserved("screenshots-preview-ready");
+ visibleButton.click();
+ await screenshotReady;
+
+ dialog = helper.getDialog();
+
+ expectedFocusedButton =
+ dialog._frame.contentDocument.getElementById("copy");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The copy button in the preview dialog should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The copy button in the preview dialog should have focus"
+ );
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ let downloadButton =
+ dialog._frame.contentDocument.getElementById("download");
+ downloadButton.click();
+
+ await Promise.all([screenshotExit, downloadFinishedPromise]);
+
+ await publicDownloads.removeFinished();
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ visibleButton = await helper.getPanelButton(".visible-page");
+
+ screenshotReady = TestUtils.topicObserved("screenshots-preview-ready");
+ visibleButton.click();
+ await screenshotReady;
+
+ dialog = helper.getDialog();
+
+ expectedFocusedButton =
+ dialog._frame.contentDocument.getElementById("download");
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ expectedFocusedButton.getRootNode().activeElement ===
+ expectedFocusedButton
+ );
+ }, "The download button in the preview dialog should have focus");
+
+ is(
+ Services.focus.focusedElement,
+ expectedFocusedButton,
+ "The download button in the preview dialog should have focus"
+ );
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ helper.triggerUIFromToolbar();
+ await screenshotExit;
+ }
+ );
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js b/browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js
new file mode 100644
index 0000000000..0f54255dc0
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function waitOnTabSwitch() {
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 300));
+}
+
+add_task(async function test_overlay_and_panel_state() {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+
+ let screenshotsTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE
+ );
+ let browser = screenshotsTab.linkedBrowser;
+
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ helper.assertPanelVisible();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ await helper.assertStateChange("selected");
+
+ helper.assertPanelNotVisible();
+
+ mouse.click(600, 600);
+
+ await helper.assertStateChange("crosshairs");
+
+ await helper.waitForOverlay();
+
+ helper.assertPanelVisible();
+
+ await BrowserTestUtils.switchTab(gBrowser, tab1);
+ await BrowserTestUtils.switchTab(gBrowser, screenshotsTab);
+
+ await helper.waitForOverlayClosed();
+
+ Assert.ok(!(await helper.isOverlayInitialized()), "Overlay is closed");
+ helper.assertPanelNotVisible();
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ helper.assertPanelVisible();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ await helper.assertStateChange("selected");
+
+ await BrowserTestUtils.switchTab(gBrowser, tab1);
+ await BrowserTestUtils.switchTab(gBrowser, screenshotsTab);
+
+ Assert.ok(await helper.isOverlayInitialized(), "Overlay is open");
+ helper.assertPanelNotVisible();
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlayClosed();
+
+ Assert.ok(!(await helper.isOverlayInitialized()), "Overlay is closed");
+ helper.assertPanelNotVisible();
+
+ gBrowser.removeTab(tab1);
+ gBrowser.removeTab(screenshotsTab);
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_page_unload.js b/browser/components/screenshots/tests/browser/browser_screenshots_page_unload.js
new file mode 100644
index 0000000000..fbb5788a5a
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_page_unload.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SCREENSHOTS_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "canceled", object: "navigation" },
+];
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ await SpecialPowers.spawn(browser, [SHORT_TEST_PAGE], url => {
+ let a = content.document.createElement("a");
+ a.id = "clickMe";
+ a.href = url;
+ a.textContent = "Click me to unload page";
+ content.document.querySelector("body").appendChild(a);
+ });
+
+ let helper = new ScreenshotsHelper(browser);
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.querySelector("#clickMe").click();
+ });
+
+ await helper.waitForOverlayClosed();
+
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_short_page_test.js b/browser/components/screenshots/tests/browser/browser_screenshots_short_page_test.js
new file mode 100644
index 0000000000..bebc70e915
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_short_page_test.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * This test ensures the overlay is covering the entire window event thought
+ * the body is smaller than the viewport
+ */
+add_task(async function test_overlay() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let { scrollWidth, scrollHeight } =
+ await helper.getScreenshotsOverlayDimensions();
+ Assert.equal(
+ scrollWidth,
+ contentInfo.clientWidth,
+ "The overlay spans the width of the window"
+ );
+
+ Assert.equal(
+ scrollHeight,
+ contentInfo.clientHeight,
+ "The overlay spans the height of the window"
+ );
+ }
+ );
+});
+
+add_task(async function test_window_resize() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ const originalWindowWidth = window.outerWidth;
+ const originalWindowHeight = window.outerHeight;
+
+ const BIG_WINDOW_SIZE = 920;
+ const SMALL_WINDOW_SIZE = 620;
+
+ await helper.resizeContentWindow(SMALL_WINDOW_SIZE, SMALL_WINDOW_SIZE);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+ await helper.dragOverlay(10, 10, 100, 100);
+
+ let dimensions = await helper.getScreenshotsOverlayDimensions();
+ let oldWidth = dimensions.scrollWidth;
+ let oldHeight = dimensions.scrollHeight;
+
+ await helper.resizeContentWindow(BIG_WINDOW_SIZE, BIG_WINDOW_SIZE);
+ await helper.waitForSelectionLayerDimensionChange(oldWidth, oldHeight);
+
+ contentInfo = await helper.getContentDimensions();
+ dimensions = await helper.getScreenshotsOverlayDimensions();
+ Assert.equal(
+ dimensions.scrollWidth,
+ contentInfo.clientWidth,
+ "The overlay spans the width of the window"
+ );
+ Assert.equal(
+ dimensions.scrollHeight,
+ contentInfo.clientHeight,
+ "The overlay spans the height of the window"
+ );
+
+ oldWidth = dimensions.scrollWidth;
+ oldHeight = dimensions.scrollHeight;
+
+ await helper.resizeContentWindow(SMALL_WINDOW_SIZE, SMALL_WINDOW_SIZE);
+ await helper.waitForSelectionLayerDimensionChange(oldWidth, oldHeight);
+
+ contentInfo = await helper.getContentDimensions();
+ dimensions = await helper.getScreenshotsOverlayDimensions();
+ Assert.equal(
+ dimensions.scrollWidth,
+ contentInfo.clientWidth,
+ "The overlay spans the width of the window"
+ );
+ Assert.equal(
+ dimensions.scrollHeight,
+ contentInfo.clientHeight,
+ "The overlay spans the height of the window"
+ );
+
+ Assert.less(
+ dimensions.scrollWidth,
+ BIG_WINDOW_SIZE,
+ "Screenshots overlay is smaller than the big window width"
+ );
+ Assert.less(
+ dimensions.scrollHeight,
+ BIG_WINDOW_SIZE,
+ "Screenshots overlay is smaller than the big window height"
+ );
+
+ await helper.resizeContentWindow(
+ originalWindowWidth,
+ originalWindowHeight
+ );
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js b/browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js
new file mode 100644
index 0000000000..782ffa3fd3
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js
@@ -0,0 +1,466 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const STARTED_AND_CANCELED_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "canceled", object: "toolbar_button" },
+ { category: "screenshots", method: "started", object: "shortcut" },
+ { category: "screenshots", method: "canceled", object: "shortcut" },
+ { category: "screenshots", method: "started", object: "context_menu" },
+ { category: "screenshots", method: "canceled", object: "context_menu" },
+ { category: "screenshots", method: "started", object: "quick_actions" },
+ { category: "screenshots", method: "canceled", object: "quick_actions" },
+];
+
+const STARTED_RETRY_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "selected", object: "visible" },
+ { category: "screenshots", method: "started", object: "preview_retry" },
+];
+
+const CANCEL_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "selected", object: "full_page" },
+ { category: "screenshots", method: "canceled", object: "preview_cancel" },
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "canceled", object: "overlay_cancel" },
+];
+
+const COPY_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "selected", object: "visible" },
+ { category: "screenshots", method: "copy", object: "preview_copy" },
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "copy", object: "overlay_copy" },
+];
+
+const CONTENT_EVENTS = [
+ { category: "screenshots", method: "selected", object: "region_selection" },
+ { category: "screenshots", method: "selected", object: "region_selection" },
+ { category: "screenshots", method: "started", object: "overlay_retry" },
+ { category: "screenshots", method: "selected", object: "element" },
+];
+
+const EXTRA_OVERLAY_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ {
+ category: "screenshots",
+ method: "copy",
+ object: "overlay_copy",
+ extra: {
+ element: "1",
+ region: "1",
+ move: "1",
+ resize: "1",
+ fullpage: "0",
+ visible: "0",
+ },
+ },
+];
+
+const EXTRA_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "selected", object: "visible" },
+ { category: "screenshots", method: "started", object: "preview_retry" },
+ { category: "screenshots", method: "selected", object: "full_page" },
+ {
+ category: "screenshots",
+ method: "copy",
+ object: "preview_copy",
+ extra: {
+ element: "0",
+ region: "0",
+ move: "0",
+ resize: "0",
+ fullpage: "1",
+ visible: "1",
+ },
+ },
+];
+
+add_task(async function test_started_and_canceled_events() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.urlbar.quickactions.enabled", true],
+ ["browser.urlbar.suggest.quickactions", true],
+ ["browser.urlbar.shortcuts.quickactions", true],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+ let screenshotExit;
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ EventUtils.synthesizeKey("s", { shiftKey: true, accelKey: true });
+ await helper.waitForOverlay();
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ EventUtils.synthesizeKey("s", { shiftKey: true, accelKey: true });
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtPoint(
+ 50,
+ 50,
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ browser
+ );
+ await popupShownPromise;
+
+ contextMenu.activateItem(
+ contextMenu.querySelector("#context-take-screenshot")
+ );
+ await helper.waitForOverlay();
+
+ popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtPoint(
+ 50,
+ 50,
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ browser
+ );
+ await popupShownPromise;
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ contextMenu.activateItem(
+ contextMenu.querySelector("#context-take-screenshot")
+ );
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "screenshot",
+ waitForFocus: SimpleTest.waitForFocus,
+ });
+ let { result } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+ Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.DYNAMIC);
+ Assert.equal(result.providerName, "quickactions");
+
+ info("Trigger the screenshot mode");
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Enter", {}, window);
+ await helper.waitForOverlay();
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "screenshot",
+ waitForFocus: SimpleTest.waitForFocus,
+ });
+ ({ result } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1));
+ Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.DYNAMIC);
+ Assert.equal(result.providerName, "quickactions");
+
+ info("Trigger the screenshot mode");
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Enter", {}, window);
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ await assertScreenshotsEvents(STARTED_AND_CANCELED_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_started_retry() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+ await screenshotReady;
+
+ let dialog = helper.getDialog();
+ let retryButton = dialog._frame.contentDocument.getElementById("retry");
+ ok(retryButton, "Got the retry button");
+ retryButton.click();
+
+ await helper.waitForOverlay();
+
+ await assertScreenshotsEvents(STARTED_RETRY_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_canceled() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the full page button in panel
+ let fullPageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".full-page");
+ fullPageButton.click();
+ await screenshotReady;
+
+ let dialog = helper.getDialog();
+ let cancelButton = dialog._frame.contentDocument.getElementById("cancel");
+ ok(cancelButton, "Got the cancel button");
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ cancelButton.click();
+
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ await helper.clickPreviewCancelButton();
+
+ await helper.waitForOverlayClosed();
+ await screenshotExit;
+
+ await assertScreenshotsEvents(CANCEL_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_copy() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+
+ helper.triggerUIFromToolbar();
+ info("waiting for overlay");
+ await helper.waitForOverlay();
+
+ info("waiting for panel");
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+ info("clicked visible page, waiting for screenshots-preview-ready");
+ await screenshotReady;
+
+ let dialog = helper.getDialog();
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ info("clicking the copy button");
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+ info("waiting for screenshot exit");
+ await screenshotExit;
+
+ helper.triggerUIFromToolbar();
+ info("waiting for overlay again");
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(50, 50, 300, 300);
+
+ clipboardChanged = helper.waitForRawClipboardChange(
+ devicePixelRatio * 250,
+ devicePixelRatio * 250
+ );
+
+ screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ await helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+ info("Waiting for exit again");
+ await screenshotExit;
+
+ info("Waiting for assertScreenshotsEvents");
+ await assertScreenshotsEvents(COPY_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_content_events() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(50, 50, 300, 300);
+
+ await helper.dragOverlay(300, 300, 333, 333, "selected");
+
+ await helper.dragOverlay(150, 150, 200, 200, "selected");
+
+ mouse.click(11, 11);
+ await helper.assertStateChange("crosshairs");
+
+ await helper.clickTestPageElement();
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+
+ await helper.clickCopyButton();
+
+ info("Waiting for exit");
+ await screenshotExit;
+
+ await assertScreenshotsEvents(CONTENT_EVENTS, "content", false);
+ await assertScreenshotsEvents(EXTRA_OVERLAY_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_extra_telemetry() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+
+ helper.triggerUIFromToolbar();
+ info("waiting for overlay");
+ await helper.waitForOverlay();
+
+ info("waiting for panel");
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+ info("clicked visible page, waiting for screenshots-preview-ready");
+ await screenshotReady;
+
+ let dialog = helper.getDialog();
+ let retryButton = dialog._frame.contentDocument.getElementById("retry");
+ retryButton.click();
+
+ info("waiting for panel");
+ panel = await helper.waitForPanel();
+
+ screenshotReady = TestUtils.topicObserved("screenshots-preview-ready");
+
+ // click the full page button in panel
+ let fullPageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".full-page");
+ fullPageButton.click();
+ await screenshotReady;
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+
+ dialog = helper.getDialog();
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ retryButton.click();
+ // click copy button on dialog box
+ info("clicking the copy button");
+ copyButton.click();
+
+ info("waiting for screenshot exit");
+ await screenshotExit;
+
+ info("Waiting for assertScreenshotsEvents");
+ await assertScreenshotsEvents(EXTRA_EVENTS);
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
new file mode 100644
index 0000000000..770a7ae06b
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
@@ -0,0 +1,186 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SCREENSHOTS_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "download", object: "overlay_download" },
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "selected", object: "visible" },
+ { category: "screenshots", method: "download", object: "preview_download" },
+];
+
+const MockFilePicker = SpecialPowers.MockFilePicker;
+
+add_setup(async function () {
+ let tmpDir = PathUtils.join(
+ PathUtils.tempDir,
+ "testsavedir" + Math.floor(Math.random() * 2 ** 32)
+ );
+ // Create this dir if it doesn't exist (ignores existing dirs)
+ await IOUtils.makeDirectory(tmpDir);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.download.start_downloads_in_tmp_dir", true],
+ ["browser.helperApps.deleteTempFileOnExit", true],
+ ["browser.download.folderList", 2],
+ ["browser.download.dir", tmpDir],
+ ],
+ });
+
+ MockFilePicker.init(window);
+ MockFilePicker.useAnyFile();
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+ registerCleanupFunction(async () => {
+ Services.prefs.clearUserPref("browser.download.dir");
+ Services.prefs.clearUserPref("browser.download.folderList");
+
+ MockFilePicker.cleanup();
+ });
+});
+
+function waitForFilePicker() {
+ return new Promise(resolve => {
+ MockFilePicker.showCallback = () => {
+ MockFilePicker.showCallback = null;
+ ok(true, "Saw the file picker");
+ resolve();
+ };
+ });
+}
+
+add_task(async function test_download_without_filepicker() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.useDownloadDir", true]],
+ });
+
+ let publicDownloads = await Downloads.getList(Downloads.PUBLIC);
+ // First ensure we catch the download finishing.
+ let downloadFinishedPromise = new Promise(resolve => {
+ publicDownloads.addView({
+ onDownloadChanged(download) {
+ info("Download changed!");
+ if (download.succeeded || download.error) {
+ info("Download succeeded or errored");
+ publicDownloads.removeView(this);
+ resolve(download);
+ }
+ },
+ });
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ await helper.clickDownloadButton();
+
+ info("wait for download to finish");
+ let download = await downloadFinishedPromise;
+
+ ok(download.succeeded, "Download should succeed");
+
+ await publicDownloads.removeFinished();
+
+ await waitForScreenshotsEventCount(2);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let downloadButton =
+ dialog._frame.contentDocument.getElementById("download");
+ ok(downloadButton, "Got the download button");
+
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ // click download button on dialog box
+ downloadButton.click();
+
+ info("wait for download to finish");
+ download = await downloadFinishedPromise;
+
+ ok(download.succeeded, "Download should succeed");
+
+ await publicDownloads.removeFinished();
+ await screenshotExit;
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+ }
+ );
+});
+
+add_task(async function test_download_with_filepicker() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.useDownloadDir", false]],
+ });
+
+ let publicDownloads = await Downloads.getList(Downloads.PUBLIC);
+ // First ensure we catch the download finishing.
+ let downloadFinishedPromise = new Promise(resolve => {
+ publicDownloads.addView({
+ onDownloadChanged(download) {
+ info("Download changed!");
+ if (download.succeeded || download.error) {
+ info("Download succeeded or errored");
+ publicDownloads.removeView(this);
+ resolve(download);
+ }
+ },
+ });
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let filePicker = waitForFilePicker();
+ let screenshotExit = TestUtils.topicObserved("screenshots-exit");
+ await helper.clickDownloadButton();
+
+ await filePicker;
+ ok(true, "Export file picker opened");
+
+ info("wait for download to finish");
+ let download = await downloadFinishedPromise;
+
+ ok(download.succeeded, "Download should succeed");
+ await publicDownloads.removeFinished();
+ await screenshotExit;
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_escape.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_escape.js
new file mode 100644
index 0000000000..73db436ff1
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_escape.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SCREENSHOTS_EVENTS = [
+ { category: "screenshots", method: "started", object: "toolbar_button" },
+ { category: "screenshots", method: "canceled", object: "escape" },
+];
+
+add_task(async function test_fullpageScreenshot() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ EventUtils.synthesizeKey("KEY_F6", { shiftKey: true });
+
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ await helper.waitForOverlayClosed();
+
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js
new file mode 100644
index 0000000000..51cda963d9
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js
@@ -0,0 +1,175 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function assertRange(lhs, rhsMin, rhsMax, msg) {
+ Assert.ok(lhs >= rhsMin && lhs <= rhsMax, msg);
+}
+
+add_task(async function test_fullpageScreenshot() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.scrollWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.scrollHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the full page button in panel
+ let visiblePage = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".full-page");
+ visiblePage.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ assertRange(result.color.topLeft[0], 110, 111, "R color value");
+ assertRange(result.color.topLeft[1], 110, 111, "G color value");
+ assertRange(result.color.topLeft[2], 110, 111, "B color value");
+
+ // top right
+ assertRange(result.color.topRight[0], 55, 56, "R color value");
+ assertRange(result.color.topRight[1], 155, 156, "G color value");
+ assertRange(result.color.topRight[2], 155, 156, "B color value");
+
+ // bottom left
+ assertRange(result.color.bottomLeft[0], 105, 106, "R color value");
+ assertRange(result.color.bottomLeft[1], 55, 56, "G color value");
+ assertRange(result.color.bottomLeft[2], 105, 106, "B color value");
+
+ // bottom right
+ assertRange(result.color.bottomRight[0], 52, 53, "R color value");
+ assertRange(result.color.bottomRight[1], 127, 128, "G color value");
+ assertRange(result.color.bottomRight[2], 152, 153, "B color value");
+ }
+ );
+});
+
+add_task(async function test_fullpageScreenshotScrolled() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ await SpecialPowers.spawn(browser, [], () => {
+ content.scrollTo(0, 2008);
+ });
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.scrollWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.scrollHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = await helper.waitForPanel();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the full page button in panel
+ let visiblePage = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".full-page");
+ visiblePage.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ assertRange(result.color.topLeft[0], 110, 111, "R color value");
+ assertRange(result.color.topLeft[1], 110, 111, "G color value");
+ assertRange(result.color.topLeft[2], 110, 111, "B color value");
+
+ // top right
+ assertRange(result.color.topRight[0], 55, 56, "R color value");
+ assertRange(result.color.topRight[1], 155, 156, "G color value");
+ assertRange(result.color.topRight[2], 155, 156, "B color value");
+
+ // bottom left
+ assertRange(result.color.bottomLeft[0], 105, 106, "R color value");
+ assertRange(result.color.bottomLeft[1], 55, 56, "G color value");
+ assertRange(result.color.bottomLeft[2], 105, 106, "B color value");
+
+ // bottom right
+ assertRange(result.color.bottomRight[0], 52, 53, "R color value");
+ assertRange(result.color.bottomRight[1], 127, 128, "G color value");
+ assertRange(result.color.bottomRight[2], 152, 153, "B color value");
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_page_crash.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_page_crash.js
new file mode 100644
index 0000000000..bed9d006db
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_page_crash.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_fullpageScreenshot() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+
+ // click toolbar button so UI shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = await helper.waitForPanel(gBrowser.selectedBrowser);
+
+ let waitForPanelHide = BrowserTestUtils.waitForMutationCondition(
+ panel,
+ { attributes: true },
+ () => {
+ return BrowserTestUtils.isHidden(panel);
+ }
+ );
+
+ await BrowserTestUtils.crashFrame(browser);
+
+ await waitForPanelHide;
+ ok(
+ BrowserTestUtils.isHidden(panel),
+ "Panel buttons are hidden after page crash"
+ );
+
+ await ContentTask.spawn(browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ Assert.ok(!screenshotsChild._overlay, "The overlay doesn't exist");
+ });
+
+ let tab = gBrowser.getTabForBrowser(browser);
+
+ SessionStore.reviveCrashedTab(tab);
+
+ ok(
+ BrowserTestUtils.isHidden(panel),
+ "Panel buttons are hidden after page crash"
+ );
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_screenshot_too_big.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_screenshot_too_big.js
new file mode 100644
index 0000000000..4e2da3d494
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_screenshot_too_big.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SCREENSHOTS_EVENTS = [
+ { category: "screenshots", method: "failed", object: "screenshot_too_large" },
+ { category: "screenshots", method: "failed", object: "screenshot_too_large" },
+ { category: "screenshots", method: "failed", object: "screenshot_too_large" },
+ { category: "screenshots", method: "failed", object: "screenshot_too_large" },
+];
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+ChromeUtils.defineESModuleGetters(this, {
+ ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
+});
+
+add_task(async function test_screenshot_too_large_cropped() {
+ await clearAllTelemetryEvents();
+ const screenshotsLocalization = new Localization(
+ ["browser/screenshots.ftl"],
+ true
+ );
+
+ let [errorTitle, errorMessage] = screenshotsLocalization.formatMessagesSync([
+ { id: "screenshots-too-large-error-title" },
+ { id: "screenshots-too-large-error-details" },
+ ]);
+ let showAlertMessageStub = sinon
+ .stub(ScreenshotsUtils, "showAlertMessage")
+ .callsFake(function (title, message) {
+ is(title, errorTitle.value, "Got error title");
+ is(message, errorMessage.value, "Got error message");
+ });
+
+ let rect = { x: 0, y: 0, width: 40000, height: 40000, devicePixelRatio: 1 };
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(
+ rect.width,
+ MAX_CAPTURE_DIMENSION,
+ `The width is ${MAX_CAPTURE_DIMENSION}`
+ );
+ is(
+ rect.height,
+ Math.floor(MAX_CAPTURE_AREA / MAX_CAPTURE_DIMENSION),
+ `The height is ${MAX_CAPTURE_AREA} / ${MAX_CAPTURE_DIMENSION}`
+ );
+
+ rect.width = 40000;
+ rect.hegith = 1;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(
+ rect.width,
+ MAX_CAPTURE_DIMENSION,
+ `The width was cropped to the max capture dimension (${MAX_CAPTURE_DIMENSION}).`
+ );
+
+ rect.width = 1;
+ rect.height = 40000;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(
+ rect.height,
+ MAX_CAPTURE_DIMENSION,
+ `The height was cropped to the max capture dimension (${MAX_CAPTURE_DIMENSION}).`
+ );
+
+ rect.width = 25000;
+ rect.height = 25000;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(rect.width, 25000, "The width was not cropped");
+ is(
+ rect.height,
+ Math.floor(MAX_CAPTURE_AREA / 25000),
+ `The height was cropped to ${MAX_CAPTURE_AREA / 25000}`
+ );
+
+ showAlertMessageStub.restore();
+
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js
new file mode 100644
index 0000000000..0aafba8fb3
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js
@@ -0,0 +1,289 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+ChromeUtils.defineESModuleGetters(this, {
+ ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
+});
+ChromeUtils.defineLazyGetter(this, "ExtensionManagement", () => {
+ const { Management } = ChromeUtils.importESModule(
+ "resource://gre/modules/Extension.sys.mjs"
+ );
+ return Management;
+});
+
+add_task(async function test() {
+ let observerSpy = sinon.spy();
+ let notifierSpy = sinon.spy();
+
+ let observerStub = sinon
+ .stub(ScreenshotsUtils, "observe")
+ .callsFake(observerSpy);
+ let notifierStub = sinon
+ .stub(ScreenshotsUtils, "notify")
+ .callsFake(function (window, type) {
+ notifierSpy();
+ ScreenshotsUtils.notify.wrappedMethod.apply(this, arguments);
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ function awaitExtensionEvent(eventName, id) {
+ return new Promise(resolve => {
+ let listener = (_eventName, ...args) => {
+ let extension = args[0];
+ if (_eventName === eventName && extension.id == id) {
+ ExtensionManagement.off(eventName, listener);
+ resolve();
+ }
+ };
+ ExtensionManagement.on(eventName, listener);
+ });
+ }
+ const SCREENSHOT_EXTENSION = "screenshots@mozilla.org";
+
+ let helper = new ScreenshotsHelper(browser);
+
+ ok(observerSpy.notCalled, "Observer not called");
+ helper.triggerUIFromToolbar();
+ Assert.equal(observerSpy.callCount, 1, "Observer function called once");
+
+ ok(notifierSpy.notCalled, "Notifier not called");
+ EventUtils.synthesizeKey("s", { accelKey: true, shiftKey: true });
+
+ await TestUtils.waitForCondition(() => notifierSpy.callCount == 1);
+ Assert.equal(notifierSpy.callCount, 1, "Notify function called once");
+
+ await TestUtils.waitForCondition(() => observerSpy.callCount == 2);
+ Assert.equal(observerSpy.callCount, 2, "Observer function called twice");
+
+ let menu = document.getElementById("contentAreaContextMenu");
+ let popupshown = BrowserTestUtils.waitForPopupEvent(menu, "shown");
+ EventUtils.synthesizeMouseAtCenter(document.body, {
+ type: "contextmenu",
+ });
+ await popupshown;
+ Assert.equal(menu.state, "open", "Context menu is open");
+
+ let popuphidden = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
+ menu.activateItem(menu.querySelector("#context-take-screenshot"));
+ await popuphidden;
+
+ Assert.equal(observerSpy.callCount, 3, "Observer function called thrice");
+
+ const COMPONENT_PREF = "screenshots.browser.component.enabled";
+ await SpecialPowers.pushPrefEnv({
+ set: [[COMPONENT_PREF, false]],
+ });
+ ok(!Services.prefs.getBoolPref(COMPONENT_PREF), "Extension enabled");
+ await awaitExtensionEvent("ready", SCREENSHOT_EXTENSION);
+
+ helper.triggerUIFromToolbar();
+ Assert.equal(
+ observerSpy.callCount,
+ 3,
+ "Observer function still called thrice"
+ );
+
+ await SpecialPowers.spawn(
+ browser,
+ ["#firefox-screenshots-preselection-iframe"],
+ async function (iframeSelector) {
+ info(
+ `in waitForUIContent content function, iframeSelector: ${iframeSelector}`
+ );
+ let iframe;
+ await ContentTaskUtils.waitForCondition(() => {
+ iframe = content.document.querySelector(iframeSelector);
+ if (!iframe || !ContentTaskUtils.isVisible(iframe)) {
+ info("in waitForUIContent, no visible iframe yet");
+ return false;
+ }
+ return true;
+ });
+ // wait a frame for the screenshots UI to finish any init
+ await new content.Promise(res => content.requestAnimationFrame(res));
+ }
+ );
+
+ helper.triggerUIFromToolbar();
+ await SpecialPowers.spawn(
+ browser,
+ ["#firefox-screenshots-preselection-iframe"],
+ async function (iframeSelector) {
+ info(
+ `in waitForUIContent content function, iframeSelector: ${iframeSelector}`
+ );
+ let iframe;
+ await ContentTaskUtils.waitForCondition(() => {
+ iframe = content.document.querySelector(iframeSelector);
+ if (!iframe || !ContentTaskUtils.isVisible(iframe)) {
+ info("in waitForUIContent, no visible iframe yet");
+ return true;
+ }
+ return false;
+ });
+ // wait a frame for the screenshots UI to finish any init
+ await new content.Promise(res => content.requestAnimationFrame(res));
+ }
+ );
+
+ popupshown = BrowserTestUtils.waitForPopupEvent(menu, "shown");
+ EventUtils.synthesizeMouseAtCenter(document.body, {
+ type: "contextmenu",
+ });
+ await popupshown;
+ Assert.equal(menu.state, "open", "Context menu is open");
+
+ popuphidden = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
+ menu.activateItem(menu.querySelector("#context-take-screenshot"));
+ await popuphidden;
+
+ Assert.equal(
+ observerSpy.callCount,
+ 3,
+ "Observer function still called thrice"
+ );
+
+ await SpecialPowers.spawn(
+ browser,
+ ["#firefox-screenshots-preselection-iframe"],
+ async function (iframeSelector) {
+ info(
+ `in waitForUIContent content function, iframeSelector: ${iframeSelector}`
+ );
+ let iframe;
+ await ContentTaskUtils.waitForCondition(() => {
+ iframe = content.document.querySelector(iframeSelector);
+ if (!iframe || !ContentTaskUtils.isVisible(iframe)) {
+ info("in waitForUIContent, no visible iframe yet");
+ return false;
+ }
+ return true;
+ });
+ // wait a frame for the screenshots UI to finish any init
+ await new content.Promise(res => content.requestAnimationFrame(res));
+ }
+ );
+
+ helper.triggerUIFromToolbar();
+ await SpecialPowers.spawn(
+ browser,
+ ["#firefox-screenshots-preselection-iframe"],
+ async function (iframeSelector) {
+ info(
+ `in waitForUIContent content function, iframeSelector: ${iframeSelector}`
+ );
+ let iframe;
+ await ContentTaskUtils.waitForCondition(() => {
+ iframe = content.document.querySelector(iframeSelector);
+ if (!iframe || !ContentTaskUtils.isVisible(iframe)) {
+ return true;
+ }
+ info("in waitForUIContent, iframe still visible");
+ info(iframe);
+ return false;
+ });
+ // wait a frame for the screenshots UI to finish any init
+ await new content.Promise(res => content.requestAnimationFrame(res));
+ }
+ );
+
+ let componentReady = TestUtils.topicObserved(
+ "screenshots-component-initialized"
+ );
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[COMPONENT_PREF, true]],
+ });
+ ok(Services.prefs.getBoolPref(COMPONENT_PREF), "Component enabled");
+ // Needed for component to initialize
+ await componentReady;
+
+ helper.triggerUIFromToolbar();
+ Assert.equal(
+ observerSpy.callCount,
+ 4,
+ "Observer function called four times"
+ );
+
+ const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
+ await SpecialPowers.pushPrefEnv({
+ set: [[SCREENSHOTS_PREF, true]],
+ });
+ ok(Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots disabled");
+ }
+ );
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
+ ok(Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots disabled");
+
+ ok(
+ document.getElementById("screenshot-button").disabled,
+ "Toolbar button disabled"
+ );
+
+ let menu = document.getElementById("contentAreaContextMenu");
+ let popupshown = BrowserTestUtils.waitForPopupEvent(menu, "shown");
+ EventUtils.synthesizeMouseAtCenter(document.body, {
+ type: "contextmenu",
+ });
+ await popupshown;
+ Assert.equal(menu.state, "open", "Context menu is open");
+
+ ok(
+ menu.querySelector("#context-take-screenshot").hidden,
+ "Take screenshot is not in context menu"
+ );
+
+ let popuphidden = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
+ menu.hidePopup();
+ await popuphidden;
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[SCREENSHOTS_PREF, false]],
+ });
+ ok(!Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots enabled");
+ }
+ );
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: SHORT_TEST_PAGE,
+ },
+ async browser => {
+ const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
+ ok(!Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots enabled");
+
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ Assert.equal(
+ observerSpy.callCount,
+ 5,
+ "Observer function called for the fifth time"
+ );
+ }
+ );
+
+ observerStub.restore();
+ notifierStub.restore();
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_toolbar_button.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_toolbar_button.js
new file mode 100644
index 0000000000..5ad7d32192
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_toolbar_button.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testScreenshotButtonDisabled() {
+ info("Test the Screenshots button in the panel");
+
+ let screenshotBtn = document.getElementById("screenshot-button");
+ Assert.ok(screenshotBtn, "The screenshots button was added to the nav bar");
+
+ await BrowserTestUtils.withNewTab("https://example.com/", () => {
+ Assert.equal(
+ screenshotBtn.disabled,
+ false,
+ "Screenshots button is enabled"
+ );
+ });
+ await BrowserTestUtils.withNewTab("about:home", () => {
+ Assert.equal(
+ screenshotBtn.disabled,
+ false,
+ "Screenshots button is still enabled on about pages"
+ );
+ });
+});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
new file mode 100644
index 0000000000..7b7a46f73d
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
@@ -0,0 +1,356 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_visibleScreenshot() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ Assert.equal(111, result.color.topLeft[0], "R color value");
+ Assert.equal(111, result.color.topLeft[1], "G color value");
+ Assert.equal(111, result.color.topLeft[2], "B color value");
+
+ // top right
+ Assert.equal(111, result.color.topRight[0], "R color value");
+ Assert.equal(111, result.color.topRight[1], "G color value");
+ Assert.equal(111, result.color.topRight[2], "B color value");
+
+ // bottom left
+ Assert.equal(111, result.color.bottomLeft[0], "R color value");
+ Assert.equal(111, result.color.bottomLeft[1], "G color value");
+ Assert.equal(111, result.color.bottomLeft[2], "B color value");
+
+ // bottom right
+ Assert.equal(111, result.color.bottomRight[0], "R color value");
+ Assert.equal(111, result.color.bottomRight[1], "G color value");
+ Assert.equal(111, result.color.bottomRight[2], "B color value");
+ }
+ );
+});
+
+add_task(async function test_visibleScreenshotScrolledY() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ content.scrollTo(0, 2008);
+ });
+
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ // let result = await helper.getImageSizeAndColorFromClipboard();
+ // debugger;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ Assert.equal(105, result.color.topLeft[0], "R color value");
+ Assert.equal(55, result.color.topLeft[1], "G color value");
+ Assert.equal(105, result.color.topLeft[2], "B color value");
+
+ // top right
+ Assert.equal(105, result.color.topRight[0], "R color value");
+ Assert.equal(55, result.color.topRight[1], "G color value");
+ Assert.equal(105, result.color.topRight[2], "B color value");
+
+ // bottom left
+ Assert.equal(105, result.color.bottomLeft[0], "R color value");
+ Assert.equal(55, result.color.bottomLeft[1], "G color value");
+ Assert.equal(105, result.color.bottomLeft[2], "B color value");
+
+ // bottom right
+ Assert.equal(105, result.color.bottomRight[0], "R color value");
+ Assert.equal(55, result.color.bottomRight[1], "G color value");
+ Assert.equal(105, result.color.bottomRight[2], "B color value");
+ }
+ );
+});
+
+add_task(async function test_visibleScreenshotScrolledX() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ content.scrollTo(2004, 0);
+ });
+
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ Assert.equal(55, result.color.topLeft[0], "R color value");
+ Assert.equal(155, result.color.topLeft[1], "G color value");
+ Assert.equal(155, result.color.topLeft[2], "B color value");
+
+ // top right
+ Assert.equal(55, result.color.topRight[0], "R color value");
+ Assert.equal(155, result.color.topRight[1], "G color value");
+ Assert.equal(155, result.color.topRight[2], "B color value");
+
+ // bottom left
+ Assert.equal(55, result.color.bottomLeft[0], "R color value");
+ Assert.equal(155, result.color.bottomLeft[1], "G color value");
+ Assert.equal(155, result.color.bottomLeft[2], "B color value");
+
+ // bottom right
+ Assert.equal(55, result.color.bottomRight[0], "R color value");
+ Assert.equal(155, result.color.bottomRight[1], "G color value");
+ Assert.equal(155, result.color.bottomRight[2], "B color value");
+ }
+ );
+});
+
+add_task(async function test_visibleScreenshotScrolledXAndY() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ content.scrollTo(2004, 2008);
+ });
+
+ let helper = new ScreenshotsHelper(browser);
+ let contentInfo = await helper.getContentDimensions();
+ ok(contentInfo, "Got dimensions back from the content");
+ let devicePixelRatio = await getContentDevicePixelRatio(browser);
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+
+ // click toolbar button so panel shows
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+
+ let screenshotReady = TestUtils.topicObserved(
+ "screenshots-preview-ready"
+ );
+
+ // click the visible page button in panel
+ let visiblePageButton = panel
+ .querySelector("screenshots-buttons")
+ .shadowRoot.querySelector(".visible-page");
+ visiblePageButton.click();
+
+ let dialog = helper.getDialog();
+
+ await screenshotReady;
+
+ let copyButton = dialog._frame.contentDocument.getElementById("copy");
+ ok(copyButton, "Got the copy button");
+
+ let clipboardChanged = helper.waitForRawClipboardChange(
+ expectedWidth,
+ expectedHeight
+ );
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ let result = await clipboardChanged;
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+ Assert.equal(result.height, expectedHeight, "Heights should be equal");
+
+ // top left
+ Assert.equal(52, result.color.topLeft[0], "R color value");
+ Assert.equal(127, result.color.topLeft[1], "G color value");
+ Assert.equal(152, result.color.topLeft[2], "B color value");
+
+ // top right
+ Assert.equal(52, result.color.topRight[0], "R color value");
+ Assert.equal(127, result.color.topRight[1], "G color value");
+ Assert.equal(152, result.color.topRight[2], "B color value");
+
+ // bottom left
+ Assert.equal(52, result.color.bottomLeft[0], "R color value");
+ Assert.equal(127, result.color.bottomLeft[1], "G color value");
+ Assert.equal(152, result.color.bottomLeft[2], "B color value");
+
+ // bottom right
+ Assert.equal(52, result.color.bottomRight[0], "R color value");
+ Assert.equal(127, result.color.bottomRight[1], "G color value");
+ Assert.equal(152, result.color.bottomRight[2], "B color value");
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_test_element_picker.js b/browser/components/screenshots/tests/browser/browser_test_element_picker.js
new file mode 100644
index 0000000000..17ed2a0190
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_test_element_picker.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_element_picker() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.clickTestPageElement();
+
+ let rect = await helper.getTestPageElementRect();
+ let region = await helper.getSelectionRegionDimensions();
+
+ is(
+ region.left,
+ rect.left,
+ "The selected region left is the same as the element left"
+ );
+ is(
+ region.right,
+ rect.right,
+ "The selected region right is the same as the element right"
+ );
+ is(
+ region.top,
+ rect.top,
+ "The selected region top is the same as the element top"
+ );
+ is(
+ region.bottom,
+ rect.bottom,
+ "The selected region bottom is the same as the element bottom"
+ );
+
+ mouse.click(10, 10);
+ await helper.waitForStateChange("crosshairs");
+
+ let hoverElementRegionValid = await helper.isHoverElementRegionValid();
+
+ ok(!hoverElementRegionValid, "Hover element rect is null");
+
+ mouse.click(10, 10);
+ await helper.waitForStateChange("crosshairs");
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/browser_test_resize.js b/browser/components/screenshots/tests/browser/browser_test_resize.js
new file mode 100644
index 0000000000..b249a346d6
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_test_resize.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const windowWidth = 768;
+
+add_task(async function test_window_resize() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: RESIZE_TEST_PAGE,
+ },
+ async browser => {
+ let helper = new ScreenshotsHelper(browser);
+ await helper.resizeContentWindow(windowWidth, window.outerHeight);
+ const originalContentDimensions = await helper.getContentDimensions();
+ info(JSON.stringify(originalContentDimensions, null, 2));
+
+ await helper.zoomBrowser(1.5);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.scrollContentWindow(windowWidth, window.outerHeight);
+
+ await helper.clickTestPageElement("hello");
+
+ await helper.zoomBrowser(1);
+
+ await helper.waitForOverlaySizeChangeTo(
+ originalContentDimensions.scrollWidth,
+ originalContentDimensions.scrollHeight
+ );
+
+ let contentDims = await helper.getContentDimensions();
+ info(JSON.stringify(contentDims, null, 2));
+
+ is(
+ contentDims.scrollWidth,
+ originalContentDimensions.scrollWidth,
+ "Width of page is back to original"
+ );
+ is(
+ contentDims.scrollHeight,
+ originalContentDimensions.scrollHeight,
+ "Height of page is back to original"
+ );
+ }
+ );
+});
+
+add_task(async function test_window_resize_vertical_writing_mode() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: RESIZE_TEST_PAGE,
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.documentElement.style = "writing-mode: vertical-lr;";
+ });
+
+ let helper = new ScreenshotsHelper(browser);
+ await helper.resizeContentWindow(windowWidth, window.outerHeight);
+ const originalContentDimensions = await helper.getContentDimensions();
+ info(JSON.stringify(originalContentDimensions, null, 2));
+
+ await helper.zoomBrowser(1.5);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.scrollContentWindow(windowWidth, window.outerHeight);
+
+ await helper.clickTestPageElement("hello");
+
+ await helper.zoomBrowser(1);
+
+ await helper.waitForOverlaySizeChangeTo(
+ originalContentDimensions.scrollWidth,
+ originalContentDimensions.scrollHeight
+ );
+
+ let contentDims = await helper.getContentDimensions();
+ info(JSON.stringify(contentDims, null, 2));
+
+ is(
+ contentDims.scrollWidth,
+ originalContentDimensions.scrollWidth,
+ "Width of page is back to original"
+ );
+ is(
+ contentDims.scrollHeight,
+ originalContentDimensions.scrollHeight,
+ "Height of page is back to original"
+ );
+ }
+ );
+});
diff --git a/browser/components/screenshots/tests/browser/first-iframe.html b/browser/components/screenshots/tests/browser/first-iframe.html
new file mode 100644
index 0000000000..9b0c123486
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/first-iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ div {
+ font-size: 40px;
+ margin: 30px;
+ width: 234px;
+ height: 51px;
+ color: blue;
+ }
+ </style>
+ </head>
+<body>
+ <div>Hello world!</div>
+ <iframe
+ width="300"
+ height="300"
+ src="https://example.org/browser/browser/components/screenshots/tests/browser/second-iframe.html"
+ ></iframe>
+</body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/head.js b/browser/components/screenshots/tests/browser/head.js
new file mode 100644
index 0000000000..171e3b8c41
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/head.js
@@ -0,0 +1,951 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+const { UrlbarTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlbarTestUtils.sys.mjs"
+);
+
+const TEST_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+const TEST_PAGE = TEST_ROOT + "test-page.html";
+const SHORT_TEST_PAGE = TEST_ROOT + "short-test-page.html";
+const LARGE_TEST_PAGE = TEST_ROOT + "large-test-page.html";
+const IFRAME_TEST_PAGE = TEST_ROOT + "iframe-test-page.html";
+const RESIZE_TEST_PAGE = TEST_ROOT + "test-page-resize.html";
+
+const { MAX_CAPTURE_DIMENSION, MAX_CAPTURE_AREA } = ChromeUtils.importESModule(
+ "resource:///modules/ScreenshotsUtils.sys.mjs"
+);
+
+const gScreenshotUISelectors = {
+ panel: "#screenshotsPagePanel",
+ fullPageButton: "button.full-page",
+ visiblePageButton: "button.visible-page",
+ copyButton: "button.#copy",
+};
+
+// MouseEvents is for the mouse events on the Anonymous content
+const MouseEvents = {
+ mouse: new Proxy(
+ {},
+ {
+ get: (target, name) =>
+ async function (x, y, options = {}) {
+ if (name === "click") {
+ this.down(x, y, options);
+ this.up(x, y, options);
+ } else {
+ await safeSynthesizeMouseEventInContentPage(":root", x, y, {
+ type: "mouse" + name,
+ ...options,
+ });
+ }
+ },
+ }
+ ),
+};
+
+const { mouse } = MouseEvents;
+
+class ScreenshotsHelper {
+ constructor(browser) {
+ this.browser = browser;
+ this.selector = gScreenshotUISelectors;
+ }
+
+ get toolbarButton() {
+ return this.browser.ownerDocument.getElementById("screenshot-button");
+ }
+
+ get panel() {
+ return this.browser.ownerDocument.querySelector(this.selector.panel);
+ }
+
+ /**
+ * Click the screenshots button in the toolbar
+ */
+ triggerUIFromToolbar() {
+ let button = this.toolbarButton;
+ ok(
+ BrowserTestUtils.isVisible(button),
+ "The screenshot toolbar button is visible"
+ );
+ button.click();
+ }
+
+ async getPanelButton(selector) {
+ let panel = await this.waitForPanel();
+ let screenshotsButtons = panel.querySelector("screenshots-buttons");
+ ok(screenshotsButtons, "Found the screenshots-buttons");
+ let button = screenshotsButtons.shadowRoot.querySelector(selector);
+ ok(button, `Found ${selector} button`);
+ return button;
+ }
+
+ async waitForPanel() {
+ let panel = this.panel;
+ await BrowserTestUtils.waitForCondition(async () => {
+ if (!panel) {
+ panel = this.panel;
+ }
+ return panel && BrowserTestUtils.isVisible(panel);
+ });
+ return panel;
+ }
+
+ async waitForOverlay() {
+ const panel = await this.waitForPanel();
+ ok(BrowserTestUtils.isVisible(panel), "Panel buttons are visible");
+
+ await BrowserTestUtils.waitForCondition(async () => {
+ let init = await this.isOverlayInitialized();
+ return init;
+ });
+ info("Overlay is visible");
+ }
+
+ async waitForPanelClosed() {
+ let panel = this.panel;
+ if (!panel) {
+ info("waitForPanelClosed: Panel doesnt exist");
+ return;
+ }
+ if (panel.hidden) {
+ info("waitForPanelClosed: panel is already hidden");
+ return;
+ }
+ info("waitForPanelClosed: waiting for the panel to become hidden");
+ await BrowserTestUtils.waitForMutationCondition(
+ panel,
+ { attributes: true },
+ () => {
+ return BrowserTestUtils.isHidden(panel);
+ }
+ );
+ ok(BrowserTestUtils.isHidden(panel), "Panel buttons are hidden");
+ info("waitForPanelClosed, panel is hidden: " + panel.hidden);
+ }
+
+ async waitForOverlayClosed() {
+ await this.waitForPanelClosed();
+ await BrowserTestUtils.waitForCondition(async () => {
+ let init = !(await this.isOverlayInitialized());
+ info("Is overlay initialized: " + !init);
+ return init;
+ });
+ info("Overlay is not visible");
+ }
+
+ async isOverlayInitialized() {
+ return SpecialPowers.spawn(this.browser, [], () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ return screenshotsChild?.overlay?.initialized;
+ });
+ }
+
+ waitForStateChange(newState) {
+ return SpecialPowers.spawn(this.browser, [newState], async state => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ await ContentTaskUtils.waitForCondition(() => {
+ info(`got ${screenshotsChild.overlay.state}. expected ${state}`);
+ return screenshotsChild.overlay.state === state;
+ }, `Wait for overlay state to be ${state}`);
+
+ return screenshotsChild.overlay.state;
+ });
+ }
+
+ async assertStateChange(newState) {
+ let currentState = await this.waitForStateChange(newState);
+
+ is(
+ currentState,
+ newState,
+ `The current state is ${currentState}, expected ${newState}`
+ );
+ }
+
+ getHoverElementRect() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ return screenshotsChild.overlay.hoverElementRegion.dimensions;
+ });
+ }
+
+ isHoverElementRegionValid() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ return screenshotsChild.overlay.hoverElementRegion.isRegionValid;
+ });
+ }
+
+ async waitForHoverElementRect(expectedWidth, expectedHeight) {
+ return SpecialPowers.spawn(
+ this.browser,
+ [expectedWidth, expectedHeight],
+ async (width, height) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ let dimensions;
+ await ContentTaskUtils.waitForCondition(() => {
+ dimensions = screenshotsChild.overlay.hoverElementRegion.dimensions;
+ return dimensions.width === width && dimensions.height === height;
+ }, "The hover element region is the expected width and height");
+ return dimensions;
+ }
+ );
+ }
+
+ async waitForSelectionRegionSizeChange(currentWidth) {
+ await ContentTask.spawn(
+ this.browser,
+ [currentWidth],
+ async ([currWidth]) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ let dimensions = screenshotsChild.overlay.selectionRegion.dimensions;
+ await ContentTaskUtils.waitForCondition(() => {
+ dimensions = screenshotsChild.overlay.selectionRegion.dimensions;
+ return dimensions.width !== currWidth;
+ }, "Wait for selection box width change");
+ }
+ );
+ }
+
+ /**
+ * This will drag an overlay starting at the given startX and startY coordinates and ending
+ * at the given endX and endY coordinates.
+ *
+ * endY should be at least 70px from the bottom of window and endX should be at least
+ * 265px from the left of the window. If these requirements are not met then the
+ * overlay buttons (cancel, copy, download) will be positioned different from the default
+ * and the methods to click the overlay buttons will not work unless the updated
+ * position coordinates are supplied.
+ * See https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs#1789,1798
+ * for how the overlay buttons are positioned when the overlay rect is near the bottom or
+ * left edge of the window.
+ *
+ * Note: The distance of the rect should be greater than 40 to enter in the "dragging" state.
+ * See https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs#809
+ * @param {Number} startX The starting X coordinate. The left edge of the overlay rect.
+ * @param {Number} startY The starting Y coordinate. The top edge of the overlay rect.
+ * @param {Number} endX The end X coordinate. The right edge of the overlay rect.
+ * @param {Number} endY The end Y coordinate. The bottom edge of the overlay rect.
+ */
+ async dragOverlay(
+ startX,
+ startY,
+ endX,
+ endY,
+ expectedStartingState = "crosshairs"
+ ) {
+ await this.assertStateChange(expectedStartingState);
+
+ mouse.down(startX, startY);
+
+ await Promise.any([
+ this.waitForStateChange("draggingReady"),
+ this.waitForStateChange("resizing"),
+ ]);
+ Assert.ok(true, "The overlay is in the draggingReady or resizing state");
+
+ mouse.move(endX, endY);
+
+ await Promise.any([
+ this.waitForStateChange("dragging"),
+ this.waitForStateChange("resizing"),
+ ]);
+ Assert.ok(true, "The overlay is in the dragging or resizing state");
+
+ mouse.up(endX, endY);
+
+ await this.assertStateChange("selected");
+
+ this.endX = endX;
+ this.endY = endY;
+ }
+
+ async moveOverlayViaKeyboard(mover, events) {
+ await SpecialPowers.spawn(
+ this.browser,
+ [mover, events],
+ async (moverToFocus, eventsArr) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ let overlay = screenshotsChild.overlay;
+
+ switch (moverToFocus) {
+ case "highlight":
+ overlay.highlightEl.focus({ focusVisible: true });
+ break;
+ case "mover-bottomLeft":
+ overlay.bottomLeftMover.focus({ focusVisible: true });
+ break;
+ case "mover-bottomRight":
+ overlay.bottomRightMover.focus({ focusVisible: true });
+ break;
+ case "mover-topLeft":
+ overlay.topLeftMover.focus({ focusVisible: true });
+ break;
+ case "mover-topRight":
+ overlay.topRightMover.focus({ focusVisible: true });
+ break;
+ }
+ screenshotsChild.overlay.highlightEl.focus();
+
+ for (let event of eventsArr) {
+ EventUtils.synthesizeKey(
+ event.key,
+ { type: "keydown", ...event.options },
+ content
+ );
+
+ await ContentTaskUtils.waitForCondition(
+ () => overlay.state === "resizing",
+ "Wait for overlay state to be resizing"
+ );
+
+ EventUtils.synthesizeKey(
+ event.key,
+ { type: "keyup", ...event.options },
+ content
+ );
+
+ await ContentTaskUtils.waitForCondition(
+ () => overlay.state === "selected",
+ "Wait for overlay state to be selected"
+ );
+ }
+ }
+ );
+ }
+
+ async scrollContentWindow(x, y) {
+ let promise = BrowserTestUtils.waitForContentEvent(this.browser, "scroll");
+ let contentDims = await this.getContentDimensions();
+ await ContentTask.spawn(
+ this.browser,
+ [x, y, contentDims],
+ async ([xPos, yPos, cDims]) => {
+ content.window.scroll(xPos, yPos);
+
+ info(JSON.stringify(cDims, null, 2));
+ const scrollbarHeight = {};
+ const scrollbarWidth = {};
+ content.window.windowUtils.getScrollbarSize(
+ false,
+ scrollbarWidth,
+ scrollbarHeight
+ );
+
+ await ContentTaskUtils.waitForCondition(() => {
+ function isCloseEnough(a, b, diff) {
+ return Math.abs(a - b) <= diff;
+ }
+
+ info(
+ `scrollbarWidth: ${scrollbarWidth.value}, scrollbarHeight: ${scrollbarHeight.value}`
+ );
+ info(
+ `scrollX: ${content.window.scrollX}, scrollY: ${content.window.scrollY}, scrollMaxX: ${content.window.scrollMaxX}, scrollMaxY: ${content.window.scrollMaxY}`
+ );
+
+ // Sometimes (read intermittently) the scroll width/height will be
+ // off by the width/height of the scrollbar when we are expecting the
+ // page to be scrolled to the very end. To mitigate this, we check
+ // that the below differences are within the scrollbar width/height.
+ return (
+ (content.window.scrollX === xPos ||
+ isCloseEnough(
+ cDims.clientWidth + content.window.scrollX,
+ cDims.scrollWidth,
+ scrollbarWidth.value + 1
+ )) &&
+ (content.window.scrollY === yPos ||
+ isCloseEnough(
+ cDims.clientHeight + content.window.scrollY,
+ cDims.scrollHeight,
+ scrollbarHeight.value + 1
+ ))
+ );
+ }, `Waiting for window to scroll to ${xPos}, ${yPos}`);
+ }
+ );
+ await promise;
+ }
+
+ async waitForScrollTo(x, y) {
+ await ContentTask.spawn(this.browser, [x, y], async ([xPos, yPos]) => {
+ await ContentTaskUtils.waitForCondition(() => {
+ info(
+ `Got scrollX: ${content.window.scrollX}. scrollY: ${content.window.scrollY}`
+ );
+ return (
+ content.window.scrollX === xPos && content.window.scrollY === yPos
+ );
+ }, `Waiting for window to scroll to ${xPos}, ${yPos}`);
+ });
+ }
+
+ async resizeContentWindow(width, height) {
+ this.browser.ownerGlobal.resizeTo(width, height);
+ await TestUtils.waitForCondition(
+ () => window.outerHeight === height && window.outerWidth === width,
+ "Waiting for window to resize"
+ );
+ }
+
+ async clickDownloadButton() {
+ let { centerX: x, centerY: y } = await ContentTask.spawn(
+ this.browser,
+ null,
+ async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ let { left, top, width, height } =
+ screenshotsChild.overlay.downloadButton.getBoundingClientRect();
+ let centerX = left + width / 2;
+ let centerY = top + height / 2;
+ return { centerX, centerY };
+ }
+ );
+
+ info(`clicking download button at ${x}, ${y}`);
+ mouse.click(x, y);
+ }
+
+ async clickCopyButton() {
+ let { centerX: x, centerY: y } = await ContentTask.spawn(
+ this.browser,
+ null,
+ async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ let { left, top, width, height } =
+ screenshotsChild.overlay.copyButton.getBoundingClientRect();
+ let centerX = left + width / 2;
+ let centerY = top + height / 2;
+ return { centerX, centerY };
+ }
+ );
+
+ info(`clicking copy button at ${x}, ${y}`);
+ mouse.click(x, y);
+ }
+
+ async clickCancelButton() {
+ let { centerX: x, centerY: y } = await ContentTask.spawn(
+ this.browser,
+ null,
+ async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ let { left, top, width, height } =
+ screenshotsChild.overlay.cancelButton.getBoundingClientRect();
+ let centerX = left + width / 2;
+ let centerY = top + height / 2;
+ return { centerX, centerY };
+ }
+ );
+
+ info(`clicking cancel button at ${x}, ${y}`);
+ mouse.click(x, y);
+ }
+
+ async clickPreviewCancelButton() {
+ let { centerX: x, centerY: y } = await ContentTask.spawn(
+ this.browser,
+ null,
+ async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ let { left, top, width, height } =
+ screenshotsChild.overlay.previewCancelButton.getBoundingClientRect();
+ let centerX = left + width / 2;
+ let centerY = top + height / 2;
+ return { centerX, centerY };
+ }
+ );
+
+ info(`clicking cancel button at ${x}, ${y}`);
+ mouse.click(x, y);
+ }
+
+ escapeKeyInContent() {
+ return SpecialPowers.spawn(this.browser, [], () => {
+ EventUtils.synthesizeKey("KEY_Escape", {}, content);
+ });
+ }
+
+ getTestPageElementRect(elementId = "testPageElement") {
+ return ContentTask.spawn(this.browser, [elementId], async id => {
+ let ele = content.document.getElementById(id);
+ return ele.getBoundingClientRect();
+ });
+ }
+
+ async clickTestPageElement(elementId = "testPageElement") {
+ let rect = await this.getTestPageElementRect(elementId);
+ let dims = await this.getContentDimensions();
+
+ let x = Math.floor(rect.x + dims.scrollX + rect.width / 2);
+ let y = Math.floor(rect.y + dims.scrollY + rect.height / 2);
+
+ mouse.move(x, y);
+ await this.waitForHoverElementRect(rect.width, rect.height);
+ mouse.down(x, y);
+ await this.assertStateChange("draggingReady");
+ mouse.up(x, y);
+ await this.assertStateChange("selected");
+ }
+
+ async zoomBrowser(zoom) {
+ let promise = BrowserTestUtils.waitForContentEvent(this.browser, "resize");
+ await SpecialPowers.spawn(this.browser, [zoom], zoomLevel => {
+ const { Layout } = ChromeUtils.importESModule(
+ "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs"
+ );
+ Layout.zoomDocument(content.document, zoomLevel);
+ });
+ await promise;
+ }
+
+ /**
+ * Gets the dialog box
+ * @returns The dialog box
+ */
+ getDialog() {
+ let currDialogBox = this.browser.tabDialogBox;
+ let manager = currDialogBox.getTabDialogManager();
+ let dialogs = manager.hasDialogs && manager.dialogs;
+ return dialogs[0];
+ }
+
+ assertPanelVisible() {
+ info("assertPanelVisible, panel.hidden:" + this.panel?.hidden);
+ Assert.ok(
+ BrowserTestUtils.isVisible(this.panel),
+ "Screenshots panel is visible"
+ );
+ }
+
+ assertPanelNotVisible() {
+ info("assertPanelNotVisible, panel.hidden:" + this.panel?.hidden);
+ Assert.ok(
+ !this.panel || BrowserTestUtils.isHidden(this.panel),
+ "Screenshots panel is not visible"
+ );
+ }
+
+ /**
+ * Copied from screenshots extension
+ * Returns a promise that resolves when the clipboard data has changed
+ * Otherwise rejects
+ */
+ waitForRawClipboardChange(epectedWidth, expectedHeight) {
+ const initialClipboardData = Date.now().toString();
+ SpecialPowers.clipboardCopyString(initialClipboardData);
+
+ return TestUtils.waitForCondition(
+ async () => {
+ let data;
+ try {
+ data = await this.getImageSizeAndColorFromClipboard();
+ } catch (e) {
+ console.log("Failed to get image/png clipboard data:", e);
+ return false;
+ }
+ if (
+ data &&
+ initialClipboardData !== data &&
+ data.height === expectedHeight &&
+ data.width === epectedWidth
+ ) {
+ return data;
+ }
+ return false;
+ },
+ "Waiting for screenshot to copy to clipboard",
+ 200
+ );
+ }
+
+ /**
+ * Gets the client and scroll demensions on the window
+ * @returns { Object }
+ * clientHeight The visible height
+ * clientWidth The visible width
+ * scrollHeight The scrollable height
+ * scrollWidth The scrollable width
+ * scrollX The scroll x position
+ * scrollY The scroll y position
+ */
+ getContentDimensions() {
+ return SpecialPowers.spawn(this.browser, [], async function () {
+ let {
+ innerWidth,
+ innerHeight,
+ scrollMaxX,
+ scrollMaxY,
+ scrollX,
+ scrollY,
+ } = content.window;
+ let width = innerWidth + scrollMaxX;
+ let height = innerHeight + scrollMaxY;
+
+ const scrollbarHeight = {};
+ const scrollbarWidth = {};
+ content.window.windowUtils.getScrollbarSize(
+ false,
+ scrollbarWidth,
+ scrollbarHeight
+ );
+ width -= scrollbarWidth.value;
+ height -= scrollbarHeight.value;
+ innerWidth -= scrollbarWidth.value;
+ innerHeight -= scrollbarHeight.value;
+
+ return {
+ clientHeight: innerHeight,
+ clientWidth: innerWidth,
+ scrollHeight: height,
+ scrollWidth: width,
+ scrollX,
+ scrollY,
+ };
+ });
+ }
+
+ async getScreenshotsOverlayDimensions() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ Assert.ok(screenshotsChild.overlay.initialized, "The overlay exists");
+
+ let screenshotsContainer = screenshotsChild.overlay.screenshotsContainer;
+
+ await ContentTaskUtils.waitForCondition(() => {
+ return !screenshotsContainer.hasAttribute("resizing");
+ }, "Waiting for overlay to be done resizing");
+
+ info(
+ `${screenshotsContainer.style.width} ${
+ screenshotsContainer.style.height
+ } ${screenshotsContainer.hasAttribute("resizing")}`
+ );
+
+ return {
+ scrollWidth: screenshotsContainer.scrollWidth,
+ scrollHeight: screenshotsContainer.scrollHeight,
+ };
+ });
+ }
+
+ async waitForSelectionLayerDimensionChange(oldWidth, oldHeight) {
+ await ContentTask.spawn(
+ this.browser,
+ [oldWidth, oldHeight],
+ async ([prevWidth, prevHeight]) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ await ContentTaskUtils.waitForCondition(() => {
+ let screenshotsContainer =
+ screenshotsChild.overlay.screenshotsContainer;
+ info(
+ `old height: ${prevHeight}. new height: ${screenshotsContainer.scrollHeight}.\nold width: ${prevWidth}. new width: ${screenshotsContainer.scrollWidth}`
+ );
+ return (
+ screenshotsContainer.scrollHeight !== prevHeight &&
+ screenshotsContainer.scrollWidth !== prevWidth
+ );
+ }, "Wait for selection box width change");
+ }
+ );
+ }
+
+ waitForOverlaySizeChangeTo(width, height) {
+ return ContentTask.spawn(
+ this.browser,
+ [width, height],
+ async ([newWidth, newHeight]) => {
+ await ContentTaskUtils.waitForCondition(() => {
+ let {
+ innerHeight,
+ innerWidth,
+ scrollMaxY,
+ scrollMaxX,
+ scrollMinY,
+ scrollMinX,
+ } = content.window;
+ let scrollWidth = innerWidth + scrollMaxX - scrollMinX;
+ let scrollHeight = innerHeight + scrollMaxY - scrollMinY;
+
+ const scrollbarHeight = {};
+ const scrollbarWidth = {};
+ content.window.windowUtils.getScrollbarSize(
+ false,
+ scrollbarWidth,
+ scrollbarHeight
+ );
+ scrollWidth -= scrollbarWidth.value;
+ scrollHeight -= scrollbarHeight.value;
+ info(
+ `${scrollHeight}, ${newHeight}, ${scrollWidth}, ${newWidth}, ${content.window.scrollMaxX}`
+ );
+ return scrollHeight === newHeight && scrollWidth === newWidth;
+ }, "Wait for document size change");
+ }
+ );
+ }
+
+ getSelectionRegionDimensions() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ Assert.ok(screenshotsChild.overlay.initialized, "The overlay exists");
+
+ return screenshotsChild.overlay.selectionRegion.dimensions;
+ });
+ }
+
+ /**
+ * Copied from screenshots extension
+ * A helper that returns the size of the image that was just put into the clipboard by the
+ * :screenshot command.
+ * @return The {width, height, color} dimension and color object.
+ */
+ async getImageSizeAndColorFromClipboard() {
+ let flavor = "image/png";
+ let image = getRawClipboardData(flavor);
+ if (!image) {
+ return false;
+ }
+
+ // 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 or as image container. If it's not possible obtain a
+ // byte stream, the function throws.
+
+ if (image instanceof Ci.imgIContainer) {
+ image = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools)
+ .encodeImage(image, flavor);
+ }
+
+ if (!(image instanceof Ci.nsIInputStream)) {
+ throw new Error("Unable to read image data");
+ }
+
+ const binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+ binaryStream.setInputStream(image);
+ const available = binaryStream.available();
+ const buffer = new ArrayBuffer(available);
+ info(
+ `${binaryStream.readArrayBuffer(
+ available,
+ buffer
+ )} read, ${available} available`
+ );
+
+ // We are going to load the image in the content page to measure its size.
+ // We don't want to insert the image directly in the browser's document
+ // which could mess all sorts of things up
+ return SpecialPowers.spawn(
+ this.browser,
+ [buffer],
+ async function (_buffer) {
+ const img = content.document.createElement("img");
+ const loaded = new Promise(r => {
+ img.addEventListener("load", r, { once: true });
+ });
+ const url = content.URL.createObjectURL(
+ new Blob([_buffer], { type: "image/png" })
+ );
+
+ img.src = url;
+ content.document.documentElement.appendChild(img);
+
+ info("Waiting for the clipboard image to load in the content page");
+ await loaded;
+
+ let canvas = content.document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "html:canvas"
+ );
+ let context = canvas.getContext("2d");
+ canvas.width = img.width;
+ canvas.height = img.height;
+ context.drawImage(img, 0, 0);
+ let topLeft = context.getImageData(0, 0, 1, 1);
+ let topRight = context.getImageData(img.width - 1, 0, 1, 1);
+ let bottomLeft = context.getImageData(0, img.height - 1, 1, 1);
+ let bottomRight = context.getImageData(
+ img.width - 1,
+ img.height - 1,
+ 1,
+ 1
+ );
+
+ img.remove();
+ content.URL.revokeObjectURL(url);
+
+ return {
+ width: img.width,
+ height: img.height,
+ color: {
+ topLeft: topLeft.data,
+ topRight: topRight.data,
+ bottomLeft: bottomLeft.data,
+ bottomRight: bottomRight.data,
+ },
+ };
+ }
+ );
+ }
+}
+
+/**
+ * Get the raw clipboard data
+ * @param flavor Type of data to get from clipboard
+ * @returns The data from the clipboard
+ */
+function getRawClipboardData(flavor) {
+ const whichClipboard = Services.clipboard.kGlobalClipboard;
+ const xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ xferable.init(null);
+ xferable.addDataFlavor(flavor);
+ Services.clipboard.getData(
+ xferable,
+ whichClipboard,
+ SpecialPowers.wrap(window).browsingContext.currentWindowContext
+ );
+ let data = {};
+ try {
+ // xferable.getTransferData(flavor, data);
+ xferable.getAnyTransferData({}, data);
+ info(JSON.stringify(data, null, 2));
+ } catch (e) {
+ info(e);
+ }
+ data = data.value || null;
+ return data;
+}
+
+/**
+ * Synthesize a mouse event on an element, after ensuring that it is visible
+ * in the viewport.
+ *
+ * @param {String} selector: The node selector to get the node target for the event.
+ * @param {number} x
+ * @param {number} y
+ * @param {object} options: Options that will be passed to BrowserTestUtils.synthesizeMouse
+ */
+async function safeSynthesizeMouseEventInContentPage(
+ selector,
+ x,
+ y,
+ options = {}
+) {
+ let context = gBrowser.selectedBrowser.browsingContext;
+ BrowserTestUtils.synthesizeMouse(selector, x, y, options, context);
+}
+
+add_setup(async () => {
+ CustomizableUI.addWidgetToArea(
+ "screenshot-button",
+ CustomizableUI.AREA_NAVBAR,
+ 0
+ );
+ let screenshotBtn = document.getElementById("screenshot-button");
+ Assert.ok(screenshotBtn, "The screenshots button was added to the nav bar");
+});
+
+function getContentDevicePixelRatio(browser) {
+ return SpecialPowers.spawn(browser, [], async function () {
+ return content.window.devicePixelRatio;
+ });
+}
+
+async function clearAllTelemetryEvents() {
+ // Clear everything.
+ info("Clearing all telemetry events");
+ await TestUtils.waitForCondition(() => {
+ Services.telemetry.clearEvents();
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ true
+ );
+ let content = events.content;
+ let parent = events.parent;
+
+ return (!content && !parent) || (!content.length && !parent.length);
+ });
+}
+
+async function waitForScreenshotsEventCount(count, process = "parent") {
+ await TestUtils.waitForCondition(
+ () => {
+ let events = TelemetryTestUtils.getEvents(
+ { category: "screenshots" },
+ { process }
+ );
+
+ info(`Got ${events?.length} event(s)`);
+ info(`Actual events: ${JSON.stringify(events, null, 2)}`);
+ return events.length === count ? events : null;
+ },
+ `Waiting for ${count} ${process} event(s).`,
+ 200,
+ 100
+ );
+}
+
+async function assertScreenshotsEvents(
+ expectedEvents,
+ process = "parent",
+ clearEvents = true
+) {
+ info(`Expected events: ${JSON.stringify(expectedEvents, null, 2)}`);
+ // Make sure we have recorded the correct number of events
+ await waitForScreenshotsEventCount(expectedEvents.length, process);
+
+ TelemetryTestUtils.assertEvents(
+ expectedEvents,
+ { category: "screenshots" },
+ { clear: clearEvents, process }
+ );
+}
diff --git a/browser/components/screenshots/tests/browser/iframe-test-page.html b/browser/components/screenshots/tests/browser/iframe-test-page.html
new file mode 100644
index 0000000000..5439552734
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/iframe-test-page.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <title>Screenshots</title>
+ <style>
+ div {
+ font-size: 40px;
+ margin: 30px;
+ width: 233px;
+ height: 50px;
+ color: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div>Hello world!</div>
+ <iframe
+ width="500"
+ height="500"
+ src="https://example.com/browser/browser/components/screenshots/tests/browser/first-iframe.html"
+ ></iframe>
+ </body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/large-test-page.html b/browser/components/screenshots/tests/browser/large-test-page.html
new file mode 100644
index 0000000000..ab2eb8d601
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/large-test-page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Screenshots</title>
+</head>
+<body style="height:40000px; width:40000px; background-repeat: no-repeat; background-size: 40008px 40016px; background-color: rgb(111, 111, 111); background-image:linear-gradient(to right, transparent 50%, rgba(0, 200, 200, 0.5) 50%),linear-gradient(to bottom, transparent 50%, rgba(100, 0, 100, 0.5) 50%);">
+</body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/second-iframe.html b/browser/components/screenshots/tests/browser/second-iframe.html
new file mode 100644
index 0000000000..ca5de26bb9
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/second-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ div {
+ font-size: 40px;
+ margin: 30px;
+ width: 235px;
+ height: 52px;
+ color: red;
+ }
+ </style>
+ </head>
+<body>
+ <div>Hello world!</div>
+</body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/short-test-page.html b/browser/components/screenshots/tests/browser/short-test-page.html
new file mode 100644
index 0000000000..7718892f8c
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/short-test-page.html
@@ -0,0 +1,8 @@
+<html lang="en">
+<head>
+ <title>Screenshots</title>
+</head>
+<body>
+ <div style="height:500px; width:500px; background-color: blue;"></div>
+</body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/test-page-resize.html b/browser/components/screenshots/tests/browser/test-page-resize.html
new file mode 100644
index 0000000000..dea57909b4
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/test-page-resize.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+ <title>Screenshots</title>
+</head>
+<body>
+ <div style="display: flex;flex-flow: row wrap;gap: 8px;">
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ <div style="height:100px; width:100px; background-color: blue;"></div>
+ </div>
+ <div style="display: flex;flex-direction: column;gap: 8px;">
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div style="height:100px; width:100px; background-color: red;"></div>
+ <div id="hello" style="height:100px; width:100px; background-color: red;"></div>
+ </div>
+</body>
+</html>
diff --git a/browser/components/screenshots/tests/browser/test-page.html b/browser/components/screenshots/tests/browser/test-page.html
new file mode 100644
index 0000000000..5ddc1d6eb6
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/test-page.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Screenshots</title>
+</head>
+<body style="height:4000px; width:4000px; background-repeat: no-repeat; background-size: 4008px 4016px; background-color: rgb(111, 111, 111); background-image:linear-gradient(to right, transparent 50%, rgba(0, 200, 200, 0.5) 50%),linear-gradient(to bottom, transparent 50%, rgba(100, 0, 100, 0.5) 50%);">
+ <div id="testPageElement" style="position:absolute; top:91px; left:93px; width:92px; height:94px; border:solid red;"></div>
+ <script>
+ // Make sure the screenshots overlay anonymous document always receives events
+ // that web content would normally be able to intercept, as that could break the
+ // overlay
+ function disabledEvent(event) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+
+ window.addEventListener("click", disabledEvent, true);
+ window.addEventListener("pointerdown", disabledEvent, true);
+ window.addEventListener("pointermove", disabledEvent, true);
+ window.addEventListener("mousemove", disabledEvent, true);
+ window.addEventListener("pointerup", disabledEvent, true);
+ window.addEventListener("keydown", disabledEvent, true);
+ window.addEventListener("keyup", disabledEvent, true);
+ </script>
+</body>
+</html>