summaryrefslogtreecommitdiffstats
path: root/browser/components/screenshots/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/screenshots/tests
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/screenshots/tests')
-rw-r--r--browser/components/screenshots/tests/browser/browser.ini29
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js400
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js475
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js44
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js82
-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.js148
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js309
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js185
-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.js173
-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.js86
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js287
-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.js345
-rw-r--r--browser/components/screenshots/tests/browser/head.js681
-rw-r--r--browser/components/screenshots/tests/browser/large-test-page.html9
-rw-r--r--browser/components/screenshots/tests/browser/short-test-page.html8
-rw-r--r--browser/components/screenshots/tests/browser/test-page.html10
20 files changed, 3428 insertions, 0 deletions
diff --git a/browser/components/screenshots/tests/browser/browser.ini b/browser/components/screenshots/tests/browser/browser.ini
new file mode 100644
index 0000000000..c850f03a97
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser.ini
@@ -0,0 +1,29 @@
+[DEFAULT]
+support-files =
+ head.js
+ test-page.html
+ short-test-page.html
+ large-test-page.html
+
+prefs =
+ extensions.screenshots.disabled=false
+ screenshots.browser.component.enabled=true
+
+[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]
+skip-if =
+ (!debug && os == 'win' && os_version == '6.1') # Bug 1746281
+[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]
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..19669b7427
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js
@@ -0,0 +1,400 @@
+/* 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 dimensions = await helper.getSelectionLayerDimensions();
+ info(JSON.stringify(dimensions));
+ is(
+ dimensions.scrollWidth,
+ contentInfo.scrollWidth,
+ "The overlay spans the entire width of the page"
+ );
+
+ is(
+ dimensions.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.waitForStateChange("resizing");
+ let state = await helper.getOverlayState();
+ is(state, "resizing", "The overlay is in the resizing state");
+
+ 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.waitForSelectionBoxSizeChange(490);
+
+ let dimensions = await helper.getSelectionBoxDimensions();
+
+ is(dimensions.x1, 0, "The box x1 position is now 0");
+ is(dimensions.y1, 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.waitForSelectionBoxSizeChange(255);
+
+ dimensions = await helper.getSelectionBoxDimensions();
+
+ is(dimensions.x1, 10, "The box x1 position is now 10 again");
+ is(dimensions.y1, 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.waitForStateChange("resizing");
+ let state = await helper.getOverlayState();
+ is(state, "resizing", "The overlay is in the resizing state");
+
+ 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.waitForSelectionBoxSizeChange(480);
+
+ let dimensions = await helper.getSelectionBoxDimensions();
+
+ is(dimensions.x1, startX + 240, "The box x1 position is now 3748");
+ is(dimensions.y1, 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.waitForSelectionBoxSizeChange(252);
+
+ dimensions = await helper.getSelectionBoxDimensions();
+
+ is(dimensions.x1, startX, "The box x1 position is now 3508 again");
+ is(dimensions.y1, 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.getSelectionBoxDimensions();
+
+ is(dimensions.x1, startX, "The box x1 position is 10");
+ is(dimensions.y1, 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
+ mouse.click(scrollX + startX, scrollY + endY);
+ await helper.waitForStateChange("crosshairs");
+
+ await helper.dragOverlay(
+ scrollX + startX,
+ scrollY + startY,
+ scrollX + endX,
+ scrollY + endY
+ );
+
+ await helper.scrollContentWindow(0, 0);
+
+ dimensions = await helper.getSelectionBoxDimensions();
+
+ is(dimensions.x1, scrollX + startX, "The box x1 position is 1010");
+ is(dimensions.y1, 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
+ mouse.click(10, 10);
+ await helper.waitForStateChange("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.waitForStateChange("resizing");
+
+ mouse.move(
+ contentInfo.clientWidth * 2 - 30,
+ contentInfo.clientHeight * 2 - 30
+ );
+
+ mouse.up(
+ contentInfo.clientWidth * 2 - 30,
+ contentInfo.clientHeight * 2 - 30
+ );
+
+ await helper.waitForStateChange("selected");
+
+ dimensions = await helper.getSelectionLayerDimensions();
+ info(JSON.stringify(dimensions));
+ is(dimensions.boxLeft, startX, "The box left is 10");
+ is(dimensions.boxTop, startY, "The box top is 10");
+ is(
+ dimensions.boxRight,
+ contentInfo.clientWidth * 2 - 30,
+ "The box right is 2 x clientWidth - 30"
+ );
+ is(
+ dimensions.boxBottom,
+ contentInfo.clientHeight * 2 - 30,
+ "The box right is 2 x clientHeight - 30"
+ );
+ is(
+ dimensions.boxWidth,
+ contentInfo.clientWidth * 2 - 40,
+ "The box right is 2 x clientWidth - 40"
+ );
+ is(
+ dimensions.boxHeight,
+ contentInfo.clientHeight * 2 - 40,
+ "The box right is 2 x clientHeight - 40"
+ );
+ is(
+ dimensions.scrollWidth,
+ contentInfo.scrollWidth,
+ "The overlay spans the entire width of the page"
+ );
+ is(
+ dimensions.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.getWindowPosition();
+
+ 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.waitForStateChange("draggingReady");
+ mouse.move(1050, 1050);
+ await helper.waitForStateChange("dragging");
+ mouse.move(endX, endY);
+ mouse.up(endX, endY);
+ await helper.waitForStateChange("selected");
+
+ windowX = 980;
+ windowY = 980;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY } = await helper.getWindowPosition());
+
+ is(scrollX, windowX, "Window x position is 980");
+ is(scrollY, windowY, "Window y position is 980");
+
+ 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`
+ );
+ mouse.down(startX, startY);
+ await helper.waitForStateChange("resizing");
+ mouse.move(endX, endY);
+ mouse.up(endX, endY);
+ await helper.waitForStateChange("selected");
+
+ windowX = 1000;
+ windowY = 1000;
+ await helper.waitForScrollTo(windowX, windowY);
+
+ ({ scrollX, scrollY } = await helper.getWindowPosition());
+
+ 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..a8c05dcb49
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_drag_test.js
@@ -0,0 +1,475 @@
+/* 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");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ let expected = Math.floor(
+ 490 * (await getContentDevicePixelRatio(browser))
+ );
+
+ 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");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(300, 100, 350, 150);
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+ result.zoom = zoom;
+ result.devicePixelRatio = window.devicePixelRatio;
+ result.contentDevicePixelRatio = await getContentDevicePixelRatio(
+ browser
+ );
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ let expected = Math.floor(
+ 50 * (await getContentDevicePixelRatio(browser))
+ );
+
+ 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.waitForStateChange("crosshairs");
+ let state = await helper.getOverlayState();
+ Assert.equal(state, "crosshairs", "The state is back to crosshairs");
+ }
+ );
+});
+
+/**
+ * This function drags an area and clicks the
+ * cancel button to cancel 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);
+
+ helper.clickCancelButton();
+
+ await helper.waitForOverlayClosed();
+
+ ok(!(await helper.isOverlayInitialized()), "Overlay is not initialized");
+ }
+ );
+});
+
+/**
+ * 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: 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 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.waitForStateChange("resizing");
+ let state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(10, 10);
+
+ mouse.move(contentInfo.clientWidth - 10, contentInfo.clientHeight - 10);
+
+ mouse.up(
+ Math.floor((endX - startX) / 2),
+ Math.floor((endY - startY) / 2)
+ );
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ let expected = Math.floor(
+ 490 * (await getContentDevicePixelRatio(browser))
+ );
+
+ 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");
+
+ 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.waitForStateChange("resizing");
+ let state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(x, 100);
+ mouse.up(x, 100);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag bottom
+ mouse.down(x, 500);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(x, 400);
+ mouse.up(x, 400);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag right
+ let y = Math.floor((endY - startY) / 2);
+ mouse.down(500, y);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(400, y);
+ mouse.up(400, y);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag left
+ mouse.down(10, y);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(100, y);
+ mouse.up(100, y);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.endX = 400;
+ helper.endY = 400;
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ let expected = Math.floor(
+ 300 * (await getContentDevicePixelRatio(browser))
+ );
+
+ 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");
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(10, 10, 500, 500);
+
+ // drag topright
+ mouse.down(500, 10);
+
+ await helper.waitForStateChange("resizing");
+ let state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(450, 50);
+ mouse.up(450, 50);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag bottomright
+ mouse.down(450, 500);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(400, 450);
+ mouse.up(400, 450);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag bottomleft
+ mouse.down(10, 450);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(50, 400);
+ mouse.up(50, 400);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ // drag topleft
+ mouse.down(50, 50);
+
+ await helper.waitForStateChange("resizing");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "resizing", "The overlay is in the resizing state");
+
+ mouse.move(100, 100);
+ mouse.up(100, 100);
+
+ await helper.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.endX = 400;
+ helper.endY = 400;
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+
+ let expected = Math.floor(
+ 300 * (await getContentDevicePixelRatio(browser))
+ );
+
+ 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`
+ );
+ }
+ );
+});
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..5e9937f8e1
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_focus_test.js
@@ -0,0 +1,44 @@
+/* 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 testPanelFocused() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+
+ await helper.waitForOverlay();
+
+ let screenshotsButtons = gBrowser.selectedBrowser.ownerDocument
+ .querySelector("#screenshotsPagePanel")
+ .querySelector("screenshots-buttons").shadowRoot;
+
+ let focusedElement = screenshotsButtons.querySelector(".visible-page");
+
+ is(
+ focusedElement,
+ screenshotsButtons.activeElement,
+ "Visible button is focused"
+ );
+
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ await helper.waitForOverlayClosed();
+
+ await assertScreenshotsEvents(SCREENSHOTS_EVENTS);
+ }
+ );
+});
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..b66a4d2b57
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_overlay_panel_sync.js
@@ -0,0 +1,82 @@
+/* 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.waitForStateChange("selected");
+ let state = await helper.getOverlayState();
+ is(state, "selected", "The overlay is in the selected state");
+
+ helper.assertPanelNotVisible();
+
+ mouse.click(600, 600);
+
+ await helper.waitForStateChange("crosshairs");
+ state = await helper.getOverlayState();
+ is(state, "crosshairs", "The overlay is in the crosshairs state");
+
+ 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.waitForStateChange("selected");
+ state = await helper.getOverlayState();
+ is(state, "selected", "The overlay is in the selected state");
+
+ await BrowserTestUtils.switchTab(gBrowser, tab1);
+ await BrowserTestUtils.switchTab(gBrowser, screenshotsTab);
+
+ Assert.ok(await helper.isOverlayInitialized(), "Overlay is open");
+ helper.assertPanelNotVisible();
+ state = await helper.getOverlayState();
+ is(state, "selected", "The overlay is in the selected state");
+
+ 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..e228e304b8
--- /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: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ await SpecialPowers.spawn(browser, [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..f31b9cc71d
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_short_page_test.js
@@ -0,0 +1,148 @@
+/* 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 dimensions = await helper.getSelectionLayerDimensions();
+ 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"
+ );
+ }
+ );
+});
+
+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;
+
+ window.resizeTo(SMALL_WINDOW_SIZE, SMALL_WINDOW_SIZE);
+ await TestUtils.waitForCondition(() => {
+ info(
+ `Got ${window.outerWidth}x${
+ window.outerHeight
+ }. Expecting ${SMALL_WINDOW_SIZE}. ${
+ window.outerHeight === SMALL_WINDOW_SIZE
+ } ${window.outerWidth === SMALL_WINDOW_SIZE}`
+ );
+ return (
+ window.outerHeight === SMALL_WINDOW_SIZE &&
+ window.outerWidth === SMALL_WINDOW_SIZE
+ );
+ }, "Waiting for window to resize");
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+ await helper.dragOverlay(10, 10, 100, 100);
+
+ let dimensions = await helper.getSelectionLayerDimensions();
+ let oldWidth = dimensions.scrollWidth;
+ let oldHeight = dimensions.scrollHeight;
+
+ window.resizeTo(BIG_WINDOW_SIZE, BIG_WINDOW_SIZE);
+ await TestUtils.waitForCondition(
+ () =>
+ window.outerHeight === BIG_WINDOW_SIZE &&
+ window.outerWidth === BIG_WINDOW_SIZE,
+ "Waiting for window to resize"
+ );
+ await helper.waitForSelectionLayerDimensionChange(oldWidth, oldHeight);
+
+ contentInfo = await helper.getContentDimensions();
+ dimensions = await helper.getSelectionLayerDimensions();
+ 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;
+
+ window.resizeTo(SMALL_WINDOW_SIZE, SMALL_WINDOW_SIZE);
+ await TestUtils.waitForCondition(
+ () =>
+ window.outerHeight === SMALL_WINDOW_SIZE &&
+ window.outerWidth === SMALL_WINDOW_SIZE,
+ "Waiting for window to resize"
+ );
+ await helper.waitForSelectionLayerDimensionChange(oldWidth, oldHeight);
+
+ contentInfo = await helper.getContentDimensions();
+ dimensions = await helper.getSelectionLayerDimensions();
+ 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"
+ );
+
+ ok(
+ dimensions.scrollWidth < BIG_WINDOW_SIZE,
+ "Screenshots overlay is smaller than the big window width"
+ );
+ ok(
+ dimensions.scrollHeight < BIG_WINDOW_SIZE,
+ "Screenshots overlay is smaller than the big window height"
+ );
+
+ window.resizeTo(originalWindowWidth, originalWindowHeight);
+ await TestUtils.waitForCondition(
+ () =>
+ window.outerHeight === originalWindowHeight &&
+ window.outerWidth === originalWindowWidth,
+ "Waiting for window to resize"
+ );
+ }
+ );
+});
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..74c2335beb
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_telemetry_tests.js
@@ -0,0 +1,309 @@
+/* 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: "started", object: "overlay_retry" },
+ { category: "screenshots", method: "selected", object: "element" },
+];
+
+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: TEST_PAGE,
+ },
+ async browser => {
+ await clearAllTelemetryEvents();
+ let helper = new ScreenshotsHelper(browser);
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlayClosed();
+
+ EventUtils.synthesizeKey("s", { shiftKey: true, accelKey: true });
+ await helper.waitForOverlay();
+
+ EventUtils.synthesizeKey("s", { shiftKey: true, accelKey: true });
+ await helper.waitForOverlayClosed();
+
+ 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;
+
+ contextMenu.activateItem(
+ contextMenu.querySelector("#context-take-screenshot")
+ );
+ await helper.waitForOverlayClosed();
+
+ 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");
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Enter", {}, window);
+ await helper.waitForOverlayClosed();
+
+ 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");
+ cancelButton.click();
+
+ await helper.waitForOverlayClosed();
+
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(50, 50, 300, 300);
+ helper.clickCancelButton();
+
+ await helper.waitForOverlayClosed();
+
+ 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);
+
+ 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 copyButton = dialog._frame.contentDocument.getElementById("copy");
+
+ let clipboardChanged = helper.waitForRawClipboardChange();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+ helper.triggerUIFromToolbar();
+ await helper.waitForOverlay();
+
+ await helper.dragOverlay(50, 50, 300, 300);
+
+ clipboardChanged = helper.waitForRawClipboardChange();
+
+ helper.clickCopyButton();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ 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);
+
+ mouse.click(11, 11);
+ await helper.waitForStateChange("crosshairs");
+
+ await helper.clickTestPageElement();
+
+ await assertScreenshotsEvents(CONTENT_EVENTS, "content");
+ }
+ );
+});
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..3f7af492fa
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
@@ -0,0 +1,185 @@
+/* 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);
+
+ 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");
+
+ // 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 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();
+
+ 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();
+ }
+ );
+});
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..c66b4c77d9
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js
@@ -0,0 +1,173 @@
+/* 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");
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(
+ contentInfo.scrollWidth,
+ result.width,
+ "Widths should be equal"
+ );
+
+ Assert.equal(
+ contentInfo.scrollHeight,
+ result.height,
+ "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");
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ Assert.equal(
+ contentInfo.scrollWidth,
+ result.width,
+ "Widths should be equal"
+ );
+
+ Assert.equal(
+ contentInfo.scrollHeight,
+ result.height,
+ "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..4b65110c50
--- /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.is_hidden(panel);
+ }
+ );
+
+ await BrowserTestUtils.crashFrame(browser);
+
+ await waitForPanelHide;
+ ok(
+ BrowserTestUtils.is_hidden(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.is_hidden(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..1c1bac32db
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_screenshot_too_big.js
@@ -0,0 +1,86 @@
+/* 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 32767");
+ is(
+ rect.height,
+ Math.floor(MAX_CAPTURE_AREA / MAX_CAPTURE_DIMENSION),
+ "The height is 124925329 / 32767"
+ );
+
+ rect.width = 40000;
+ rect.hegith = 1;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(
+ rect.width,
+ MAX_CAPTURE_DIMENSION,
+ "The width was cropped to the max capture dimension (32767)."
+ );
+
+ rect.width = 1;
+ rect.height = 40000;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(
+ rect.height,
+ MAX_CAPTURE_DIMENSION,
+ "The height was cropped to the max capture dimension (32767)."
+ );
+
+ rect.width = 12345;
+ rect.height = 12345;
+
+ ScreenshotsUtils.cropScreenshotRectIfNeeded(rect);
+
+ is(rect.width, 12345, "The width was not cropped");
+ is(
+ rect.height,
+ Math.floor(MAX_CAPTURE_AREA / 12345),
+ "The height was cropped to 10119"
+ );
+
+ 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..1bd1933799
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js
@@ -0,0 +1,287 @@
+/* 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",
+});
+XPCOMUtils.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: 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.is_visible(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.is_visible(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.is_visible(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.is_visible(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: 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: 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();
+});
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..26d28566c8
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
@@ -0,0 +1,345 @@
+/* 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);
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+ 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_visibleScreenshotScrolled() {
+ 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");
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+ 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_visibleScreenshotScrolled() {
+ 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");
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+ 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_visibleScreenshotScrolled() {
+ 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");
+
+ // 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();
+
+ // click copy button on dialog box
+ copyButton.click();
+
+ info("Waiting for clipboard change");
+ await clipboardChanged;
+
+ let result = await helper.getImageSizeAndColorFromClipboard();
+
+ info("result: " + JSON.stringify(result, null, 2));
+ info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
+
+ let expectedWidth = Math.floor(
+ devicePixelRatio * contentInfo.clientWidth
+ );
+ Assert.equal(result.width, expectedWidth, "Widths should be equal");
+
+ let expectedHeight = Math.floor(
+ devicePixelRatio * contentInfo.clientHeight
+ );
+ 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/head.js b/browser/components/screenshots/tests/browser/head.js
new file mode 100644
index 0000000000..5ab054a70c
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/head.js
@@ -0,0 +1,681 @@
+/* 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",
+ "http://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 MAX_CAPTURE_DIMENSION = 32767;
+const MAX_CAPTURE_AREA = 124925329;
+
+const gScreenshotUISelectors = {
+ panelButtons: "#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, selector = ":root") {
+ if (name === "click") {
+ this.down(x, y);
+ this.up(x, y);
+ } else {
+ await safeSynthesizeMouseEventInContentPage(selector, x, y, {
+ type: "mouse" + name,
+ });
+ }
+ },
+ }
+ ),
+};
+
+const { mouse } = MouseEvents;
+
+class ScreenshotsHelper {
+ constructor(browser) {
+ this.browser = browser;
+ this.selector = gScreenshotUISelectors;
+ }
+
+ get toolbarButton() {
+ return document.getElementById("screenshot-button");
+ }
+
+ /**
+ * Click the screenshots button in the toolbar
+ */
+ triggerUIFromToolbar() {
+ let button = this.toolbarButton;
+ ok(
+ BrowserTestUtils.is_visible(button),
+ "The screenshot toolbar button is visible"
+ );
+ button.click();
+ }
+
+ async waitForPanel() {
+ let panel = this.browser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+ await BrowserTestUtils.waitForCondition(async () => {
+ if (!panel) {
+ panel = this.browser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+ }
+ return panel?.state === "open" && BrowserTestUtils.is_visible(panel);
+ });
+ return panel;
+ }
+
+ async waitForOverlay() {
+ const panel = await this.waitForPanel();
+ ok(BrowserTestUtils.is_visible(panel), "Panel buttons are visible");
+
+ await BrowserTestUtils.waitForCondition(async () => {
+ let init = await this.isOverlayInitialized();
+ return init;
+ });
+ info("Overlay is visible");
+ }
+
+ async waitForOverlayClosed() {
+ let panel = this.browser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+ if (!panel) {
+ panel = await this.waitForPanel();
+ }
+ await BrowserTestUtils.waitForMutationCondition(
+ panel,
+ { attributes: true },
+ () => {
+ return BrowserTestUtils.is_hidden(panel);
+ }
+ );
+ ok(BrowserTestUtils.is_hidden(panel), "Panel buttons are hidden");
+
+ 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;
+ });
+ }
+
+ async getOverlayState() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ return screenshotsChild._overlay.stateHandler.getState();
+ });
+ }
+
+ async waitForStateChange(newState) {
+ await BrowserTestUtils.waitForCondition(async () => {
+ let state = await this.getOverlayState();
+ return state === newState;
+ }, `Waiting for state change to ${newState}`);
+ }
+
+ async getHoverElementRect() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ return screenshotsChild._overlay.stateHandler.getHoverElementBoxRect();
+ });
+ }
+
+ async waitForHoverElementRect() {
+ return TestUtils.waitForCondition(async () => {
+ let rect = await this.getHoverElementRect();
+ return rect;
+ });
+ }
+
+ async waitForSelectionBoxSizeChange(currentWidth) {
+ await ContentTask.spawn(
+ this.browser,
+ [currentWidth],
+ async ([currWidth]) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ let dimensions =
+ screenshotsChild._overlay.screenshotsContainer.getSelectionLayerDimensions();
+ // return dimensions.boxWidth;
+ await ContentTaskUtils.waitForCondition(() => {
+ dimensions =
+ screenshotsChild._overlay.screenshotsContainer.getSelectionLayerDimensions();
+ return dimensions.boxWidth !== 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) {
+ await this.waitForStateChange("crosshairs");
+ let state = await this.getOverlayState();
+ Assert.equal(state, "crosshairs", "The overlay is in the crosshairs state");
+
+ mouse.down(startX, startY);
+
+ await this.waitForStateChange("draggingReady");
+ state = await this.getOverlayState();
+ Assert.equal(
+ state,
+ "draggingReady",
+ "The overlay is in the draggingReady state"
+ );
+
+ mouse.move(endX, endY);
+
+ await this.waitForStateChange("dragging");
+ state = await this.getOverlayState();
+ Assert.equal(state, "dragging", "The overlay is in the dragging state");
+
+ mouse.up(endX, endY);
+
+ await this.waitForStateChange("selected");
+ state = await this.getOverlayState();
+ Assert.equal(state, "selected", "The overlay is in the selected state");
+
+ this.endX = endX;
+ this.endY = endY;
+ }
+
+ async scrollContentWindow(x, y) {
+ let promise = BrowserTestUtils.waitForContentEvent(this.browser, "scroll");
+ await ContentTask.spawn(this.browser, [x, y], async ([xPos, yPos]) => {
+ content.window.scroll(xPos, yPos);
+
+ await ContentTaskUtils.waitForCondition(() => {
+ return (
+ content.window.scrollX === xPos && content.window.scrollY === yPos
+ );
+ }, `Waiting for window to scroll to ${xPos}, ${yPos}`);
+ });
+ await promise;
+ }
+
+ getWindowPosition() {
+ return ContentTask.spawn(this.browser, [], () => {
+ return {
+ scrollX: content.window.scrollX,
+ scrollY: content.window.scrollY,
+ };
+ });
+ }
+
+ 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}`);
+ });
+ }
+
+ clickDownloadButton() {
+ // Click the download button with last x and y position from dragOverlay.
+ // The middle of the copy button is last X - 70 and last Y + 36.
+ // Ex. 500, 500 would be 530, 536
+ mouse.click(this.endX - 70, this.endY + 36);
+ }
+
+ clickCopyButton(overrideX = null, overrideY = null) {
+ // Click the copy button with last x and y position from dragOverlay.
+ // The middle of the copy button is last X - 183 and last Y + 36.
+ // Ex. 500, 500 would be 317, 536
+ if (overrideX && overrideY) {
+ mouse.click(overrideX - 183, overrideY + 36);
+ } else {
+ mouse.click(this.endX - 183, this.endY + 36);
+ }
+ }
+
+ clickCancelButton() {
+ // Click the cancel button with last x and y position from dragOverlay.
+ // The middle of the copy button is last X - 259 and last Y + 36.
+ // Ex. 500, 500 would be 241, 536
+ mouse.click(this.endX - 259, this.endY + 36);
+ }
+
+ async clickTestPageElement() {
+ let rect = await ContentTask.spawn(this.browser, [], async () => {
+ let ele = content.document.getElementById("testPageElement");
+ return ele.getBoundingClientRect();
+ });
+
+ let x = Math.floor(rect.x + rect.width / 2);
+ let y = Math.floor(rect.y + rect.height / 2);
+
+ mouse.move(x, y);
+ await this.waitForHoverElementRect();
+ mouse.down(x, y);
+ await this.waitForStateChange("draggingReady");
+ mouse.up(x, y);
+ await this.waitForStateChange("selected");
+ }
+
+ async zoomBrowser(zoom) {
+ 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);
+ });
+ }
+
+ /**
+ * 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() {
+ let panel = this.browser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+ Assert.ok(
+ BrowserTestUtils.is_visible(panel),
+ "Screenshots panel is visible"
+ );
+ }
+
+ assertPanelNotVisible() {
+ let panel = this.browser.ownerDocument.querySelector(
+ "#screenshotsPagePanel"
+ );
+ Assert.ok(
+ BrowserTestUtils.is_hidden(panel),
+ "Screenshots panel is not visible"
+ );
+ }
+
+ /**
+ * Copied from screenshots extension
+ * Returns a promise that resolves when the clipboard data has changed
+ * Otherwise rejects
+ */
+ waitForRawClipboardChange() {
+ const initialClipboardData = Date.now().toString();
+ SpecialPowers.clipboardCopyString(initialClipboardData);
+
+ let promiseChanged = TestUtils.waitForCondition(() => {
+ let data;
+ try {
+ data = getRawClipboardData("image/png");
+ } catch (e) {
+ console.log("Failed to get image/png clipboard data:", e);
+ return false;
+ }
+ return data && initialClipboardData !== data;
+ });
+ return promiseChanged;
+ }
+
+ /**
+ * 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
+ */
+ getContentDimensions() {
+ return SpecialPowers.spawn(this.browser, [], async function () {
+ let { innerWidth, innerHeight, scrollMaxX, scrollMaxY } = 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,
+ };
+ });
+ }
+
+ getSelectionLayerDimensions() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ Assert.ok(screenshotsChild._overlay._initialized, "The overlay exists");
+
+ return screenshotsChild._overlay.screenshotsContainer.getSelectionLayerDimensions();
+ });
+ }
+
+ async waitForSelectionLayerDimensionChange(oldWidth, oldHeight) {
+ await ContentTask.spawn(
+ this.browser,
+ [oldWidth, oldHeight],
+ async ([prevWidth, prevHeight]) => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+
+ await ContentTaskUtils.waitForCondition(() => {
+ let dimensions =
+ screenshotsChild._overlay.screenshotsContainer.getSelectionLayerDimensions();
+ info(
+ `old height: ${prevHeight}. new height: ${dimensions.scrollHeight}.\nold width: ${prevWidth}. new width: ${dimensions.scrollWidth}`
+ );
+ return (
+ dimensions.scrollHeight !== prevHeight &&
+ dimensions.scrollWidth !== prevWidth
+ );
+ }, "Wait for selection box width change");
+ }
+ );
+ }
+
+ getSelectionBoxDimensions() {
+ return ContentTask.spawn(this.browser, null, async () => {
+ let screenshotsChild = content.windowGlobalChild.getActor(
+ "ScreenshotsComponent"
+ );
+ Assert.ok(screenshotsChild._overlay._initialized, "The overlay exists");
+
+ return screenshotsChild._overlay.screenshotsContainer.getSelectionLayerBoxDimensions();
+ });
+ }
+
+ /**
+ * Clicks an element on the screen
+ * @param eleSel The selector for the element to click
+ */
+ async clickUIElement(eleSel) {
+ await SpecialPowers.spawn(
+ this.browser,
+ [eleSel],
+ async function (eleSelector) {
+ info(
+ `in clickScreenshotsUIElement content function, eleSelector: ${eleSelector}`
+ );
+ const EventUtils = ContentTaskUtils.getEventUtils(content);
+ let ele = content.document.querySelector(eleSelector);
+ info(`Found the thing to click: ${eleSelector}: ${!!ele}`);
+
+ EventUtils.synthesizeMouseAtCenter(ele, {});
+ // wait a frame for the screenshots UI to finish any init
+ await new content.Promise(res => content.requestAnimationFrame(res));
+ }
+ );
+ }
+
+ /**
+ * 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);
+ ok(image, "screenshot data exists on the clipboard");
+
+ // 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);
+ is(
+ binaryStream.readArrayBuffer(available, buffer),
+ available,
+ "Read expected amount of data"
+ );
+
+ // 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);
+ 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
+ );
+ 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") {
+ 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: true },
+ { process }
+ );
+}
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/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.html b/browser/components/screenshots/tests/browser/test-page.html
new file mode 100644
index 0000000000..4a3f7b0304
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/test-page.html
@@ -0,0 +1,10 @@
+<!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>
+</body>
+</html>