summaryrefslogtreecommitdiffstats
path: root/browser/components/screenshots
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
commit40a355a42d4a9444dc753c04c6608dade2f06a23 (patch)
tree871fc667d2de662f171103ce5ec067014ef85e61 /browser/components/screenshots
parentAdding upstream version 124.0.1. (diff)
downloadfirefox-40a355a42d4a9444dc753c04c6608dade2f06a23.tar.xz
firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.zip
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/screenshots')
-rw-r--r--browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs3
-rw-r--r--browser/components/screenshots/ScreenshotsUtils.sys.mjs168
-rw-r--r--browser/components/screenshots/fileHelpers.mjs2
-rw-r--r--browser/components/screenshots/tests/browser/browser.toml10
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js17
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js2
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js105
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js97
-rw-r--r--browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js206
-rw-r--r--browser/components/screenshots/tests/browser/browser_test_moving_tab_to_new_window.js123
-rw-r--r--browser/components/screenshots/tests/browser/head.js39
11 files changed, 579 insertions, 193 deletions
diff --git a/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs b/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs
index 5d96a46c88..bcb3199902 100644
--- a/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs
+++ b/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs
@@ -817,7 +817,8 @@ export class ScreenshotsOverlay {
* @param {object} detail Extra details to send to the child actor
*/
#dispatchEvent(eventType, detail) {
- this.window.dispatchEvent(
+ this.window.windowUtils.dispatchEventToChromeOnly(
+ this.window,
new CustomEvent(eventType, {
bubbles: true,
detail,
diff --git a/browser/components/screenshots/ScreenshotsUtils.sys.mjs b/browser/components/screenshots/ScreenshotsUtils.sys.mjs
index 68e4f896bf..fc84facee3 100644
--- a/browser/components/screenshots/ScreenshotsUtils.sys.mjs
+++ b/browser/components/screenshots/ScreenshotsUtils.sys.mjs
@@ -65,11 +65,13 @@ export class ScreenshotsComponentParent extends JSWindowActorParent {
// otherwise looks like the UIPhases.CLOSED state.
return;
}
+
switch (message.name) {
- case "Screenshots:CancelScreenshot":
+ case "Screenshots:CancelScreenshot": {
let { reason } = message.data;
ScreenshotsUtils.cancel(browser, reason);
break;
+ }
case "Screenshots:CopyScreenshot":
ScreenshotsUtils.closePanel(browser);
({ region } = message.data);
@@ -112,11 +114,12 @@ export class ScreenshotsComponentParent extends JSWindowActorParent {
export class ScreenshotsHelperParent extends JSWindowActorParent {
receiveMessage(message) {
switch (message.name) {
- case "ScreenshotsHelper:GetElementRectFromPoint":
+ case "ScreenshotsHelper:GetElementRectFromPoint": {
let cxt = BrowsingContext.get(message.data.bcId);
return cxt.currentWindowGlobal
.getActor("ScreenshotsHelper")
.sendQuery("ScreenshotsHelper:GetElementRectFromPoint", message.data);
+ }
}
return null;
}
@@ -186,10 +189,87 @@ export var ScreenshotsUtils = {
},
handleEvent(event) {
- // Escape should cancel and exit
- if (event.type === "keydown" && event.key === "Escape") {
- let browser = event.view.gBrowser.selectedBrowser;
- this.cancel(browser, "escape");
+ switch (event.type) {
+ case "keydown":
+ if (event.key === "Escape") {
+ // Escape should cancel and exit
+ let browser = event.view.gBrowser.selectedBrowser;
+ this.cancel(browser, "escape");
+ }
+ break;
+ case "TabSelect":
+ this.handleTabSelect(event);
+ break;
+ case "SwapDocShells":
+ this.handleDocShellSwapEvent(event);
+ break;
+ case "EndSwapDocShells":
+ this.handleEndDocShellSwapEvent(event);
+ break;
+ }
+ },
+
+ /**
+ * When we swap docshells for a given screenshots browser, we need to update
+ * the browserToScreenshotsState WeakMap to the correct browser. If the old
+ * browser is in a state other than OVERLAYSELECTION, we will close
+ * screenshots.
+ *
+ * @param {Event} event The SwapDocShells event
+ */
+ handleDocShellSwapEvent(event) {
+ let oldBrowser = event.target;
+ let newBrowser = event.detail;
+
+ const currentUIPhase = this.getUIPhase(oldBrowser);
+ if (currentUIPhase === UIPhases.OVERLAYSELECTION) {
+ newBrowser.addEventListener("SwapDocShells", this);
+ newBrowser.addEventListener("EndSwapDocShells", this);
+ oldBrowser.removeEventListener("SwapDocShells", this);
+
+ let perBrowserState =
+ this.browserToScreenshotsState.get(oldBrowser) || {};
+ this.browserToScreenshotsState.set(newBrowser, perBrowserState);
+ this.browserToScreenshotsState.delete(oldBrowser);
+
+ this.getActor(oldBrowser).sendAsyncMessage(
+ "Screenshots:RemoveEventListeners"
+ );
+ } else {
+ this.cancel(oldBrowser, "navigation");
+ }
+ },
+
+ /**
+ * When we swap docshells for a given screenshots browser, we need to add the
+ * event listeners to the new browser because we removed event listeners in
+ * handleDocShellSwapEvent.
+ *
+ * We attach the overlay event listeners to this.docShell.chromeEventHandler
+ * in ScreenshotsComponentChild.sys.mjs which is the browser when the page is
+ * loaded via the parent process (about:config, about:robots, etc) and when
+ * this is the case, we lose the event listeners on the original browser.
+ * To fix this, we remove the event listeners on the old browser and add the
+ * event listeners to the new browser when a SwapDocShells occurs.
+ *
+ * @param {Event} event The EndSwapDocShells event
+ */
+ handleEndDocShellSwapEvent(event) {
+ let browser = event.target;
+ this.getActor(browser).sendAsyncMessage("Screenshots:AddEventListeners");
+ browser.removeEventListener("EndSwapDocShells", this);
+ },
+
+ /**
+ * When we receive a TabSelect event, we will close screenshots in the
+ * previous tab if the previous tab was in the initial state.
+ *
+ * @param {Event} event The TabSelect event
+ */
+ handleTabSelect(event) {
+ let previousTab = event.detail.previousTab;
+ if (this.getUIPhase(previousTab.linkedBrowser) === UIPhases.INITIAL) {
+ this.cancel(previousTab.linkedBrowser, "navigation");
}
},
@@ -249,10 +329,14 @@ export var ScreenshotsUtils = {
start(browser, reason = "") {
const uiPhase = this.getUIPhase(browser);
switch (uiPhase) {
- case UIPhases.CLOSED:
+ case UIPhases.CLOSED: {
this.captureFocusedElement(browser, "previousFocusRef");
this.showPanelAndOverlay(browser, reason);
+ browser.addEventListener("SwapDocShells", this);
+ let gBrowser = browser.getTabBrowser();
+ gBrowser.tabContainer.addEventListener("TabSelect", this);
break;
+ }
case UIPhases.INITIAL:
// nothing to do, panel & overlay are already open
break;
@@ -277,6 +361,10 @@ export var ScreenshotsUtils = {
this.resetMethodsUsed();
this.attemptToRestoreFocus(browser);
+ browser.removeEventListener("SwapDocShells", this);
+ const gBrowser = browser.getTabBrowser();
+ gBrowser.tabContainer.removeEventListener("TabSelect", this);
+
this.browserToScreenshotsState.delete(browser);
if (Cu.isInAutomation) {
Services.obs.notifyObservers(null, "screenshots-exit");
@@ -465,21 +553,15 @@ export var ScreenshotsUtils = {
},
/**
- * Returns the buttons panel for the given browser
+ * Returns the buttons panel for the given browser if the panel exists.
+ * Otherwise creates the buttons panel and returns the buttons panel.
* @param browser The current browser
* @returns The buttons panel
*/
panelForBrowser(browser) {
- return browser.ownerDocument.getElementById("screenshotsPagePanel");
- },
-
- /**
- * Create the buttons container from its template, for this browser
- * @param browser The current browser
- * @returns The buttons panel
- */
- createPanelForBrowser(browser) {
- let buttonsPanel = this.panelForBrowser(browser);
+ let buttonsPanel = browser.ownerDocument.getElementById(
+ "screenshotsPagePanel"
+ );
if (!buttonsPanel) {
let doc = browser.ownerDocument;
let template = doc.getElementById("screenshotsPagePanelTemplate");
@@ -491,7 +573,10 @@ export var ScreenshotsUtils = {
anchor.appendChild(buttonsPanel);
}
- return this.panelForBrowser(browser);
+ return (
+ buttonsPanel ??
+ browser.ownerDocument.getElementById("screenshotsPagePanel")
+ );
},
/**
@@ -533,7 +618,6 @@ export var ScreenshotsUtils = {
async showPanelAndOverlay(browser, data) {
let actor = this.getActor(browser);
actor.sendAsyncMessage("Screenshots:ShowOverlay");
- this.createPanelForBrowser(browser);
this.recordTelemetryEvent("started", data, {});
this.openPanel(browser);
},
@@ -544,7 +628,12 @@ export var ScreenshotsUtils = {
* @param browser The current browser.
*/
closeOverlay(browser, options = {}) {
- let actor = this.getActor(browser);
+ // If the actor has been unregistered (e.g. if the component enabled pref is flipped false)
+ // its possible getActor will throw an exception. That's ok.
+ let actor;
+ try {
+ actor = this.getActor(browser);
+ } catch (ex) {}
actor?.sendAsyncMessage("Screenshots:HideOverlay", options);
if (this.browserToScreenshotsState.has(browser)) {
@@ -806,6 +895,8 @@ export var ScreenshotsUtils = {
canvas.width = region.width * devicePixelRatio;
canvas.height = region.height * devicePixelRatio;
+ const snapshotSize = Math.floor(MAX_SNAPSHOT_DIMENSION * devicePixelRatio);
+
for (
let startLeft = region.left;
startLeft < region.right;
@@ -832,12 +923,20 @@ export var ScreenshotsUtils = {
"rgb(255,255,255)"
);
+ // The `left` and `top` need to be a multiple of the `snapshotSize` to
+ // prevent gaps/lines from appearing in the screenshot.
+ // If devicePixelRatio is 0.3, snapshotSize would be 307 after flooring
+ // from 307.2. Therefore every fifth snapshot would have a start of
+ // 307.2 * 5 or 1536 which is not a multiple of 307 and would cause a
+ // gap/line in the snapshot.
+ let left = Math.floor((startLeft - region.left) * devicePixelRatio);
+ let top = Math.floor((startTop - region.top) * devicePixelRatio);
context.drawImage(
snapshot,
- (startLeft - region.left) * devicePixelRatio,
- (startTop - region.top) * devicePixelRatio,
- width * devicePixelRatio,
- height * devicePixelRatio
+ left - (left % snapshotSize),
+ top - (top % snapshotSize),
+ Math.floor(width * devicePixelRatio),
+ Math.floor(height * devicePixelRatio)
);
snapshot.close();
@@ -891,8 +990,23 @@ export var ScreenshotsUtils = {
"@mozilla.org/widget/transferable;1"
].createInstance(Ci.nsITransferable);
transferable.init(null);
- transferable.addDataFlavor("image/png");
- transferable.setTransferData("image/png", imgDecoded);
+ // Internal consumers expect the image data to be stored as a
+ // nsIInputStream. On Linux and Windows, pasted data is directly
+ // retrieved from the system's native clipboard, and made available
+ // as a nsIInputStream.
+ //
+ // On macOS, nsClipboard::GetNativeClipboardData (nsClipboard.mm) uses
+ // a cached copy of nsITransferable if available, e.g. when the copy
+ // was initiated by the same browser instance. To make sure that a
+ // nsIInputStream is returned instead of the cached imgIContainer,
+ // the image is exported as as `kNativeImageMime`. Data associated
+ // with this type is converted to a platform-specific image format
+ // when written to the clipboard. The type is not used when images
+ // are read from the clipboard (on all platforms, not just macOS).
+ // This forces nsClipboard::GetNativeClipboardData to fall back to
+ // the native clipboard, and return the image as a nsITransferable.
+ transferable.addDataFlavor("application/x-moz-nativeimage");
+ transferable.setTransferData("application/x-moz-nativeimage", imgDecoded);
Services.clipboard.setData(
transferable,
diff --git a/browser/components/screenshots/fileHelpers.mjs b/browser/components/screenshots/fileHelpers.mjs
index 42cb868bea..4fd2e77561 100644
--- a/browser/components/screenshots/fileHelpers.mjs
+++ b/browser/components/screenshots/fileHelpers.mjs
@@ -238,7 +238,7 @@ function promiseTargetFile(aFpP, win) {
let fp = makeFilePicker();
let titleKey = aFpP.fpTitleKey;
fp.init(
- win,
+ win.browsingContext,
ContentAreaUtils.stringBundle.GetStringFromName(titleKey),
Ci.nsIFilePicker.modeSave
);
diff --git a/browser/components/screenshots/tests/browser/browser.toml b/browser/components/screenshots/tests/browser/browser.toml
index b363c14732..b27d28c677 100644
--- a/browser/components/screenshots/tests/browser/browser.toml
+++ b/browser/components/screenshots/tests/browser/browser.toml
@@ -16,14 +16,22 @@ prefs = [
]
["browser_iframe_test.js"]
+skip-if = ["os == 'linux'"]
["browser_overlay_keyboard_test.js"]
["browser_screenshots_drag_scroll_test.js"]
+skip-if = [
+ "apple_silicon && !debug", # Bug 1804441
+ "apple_catalina", # Bug 1804441
+]
["browser_screenshots_drag_test.js"]
["browser_screenshots_focus_test.js"]
+skip-if = [
+ "os == 'linux' && os_version == '18.04'", # Bug 1803618
+]
["browser_screenshots_overlay_panel_sync.js"]
@@ -52,4 +60,6 @@ skip-if = ["!crashreporter"]
["browser_test_element_picker.js"]
+["browser_test_moving_tab_to_new_window.js"]
+
["browser_test_resize.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
index 7fdb084ca6..757d721268 100644
--- a/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_drag_scroll_test.js
@@ -283,6 +283,23 @@ add_task(async function test_scrollingScreenshotsOpen() {
let { scrollWidth, scrollHeight } =
await helper.getScreenshotsOverlayDimensions();
+ info(
+ JSON.stringify(
+ {
+ left,
+ top,
+ right,
+ bottom,
+ width,
+ height,
+ scrollWidth,
+ scrollHeight,
+ },
+ null,
+ 2
+ )
+ );
+
is(left, startX, "The box left is 10");
is(top, startY, "The box top is 10");
is(
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
index 770a7ae06b..51d5b858b9 100644
--- a/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_downloads.js
@@ -29,7 +29,7 @@ add_setup(async function () {
],
});
- MockFilePicker.init(window);
+ MockFilePicker.init(window.browsingContext);
MockFilePicker.useAnyFile();
MockFilePicker.returnValue = MockFilePicker.returnOK;
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
index 51cda963d9..006a9819ed 100644
--- a/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_full_page.js
@@ -3,8 +3,11 @@
"use strict";
-function assertRange(lhs, rhsMin, rhsMax, msg) {
- Assert.ok(lhs >= rhsMin && lhs <= rhsMax, msg);
+function assertPixel(actual, expected, message) {
+ info(message);
+ isfuzzy(actual[0], expected[0], 1, "R color value");
+ isfuzzy(actual[1], expected[1], 1, "G color value");
+ isfuzzy(actual[2], expected[2], 1, "B color value");
}
add_task(async function test_fullpageScreenshot() {
@@ -65,25 +68,36 @@ add_task(async function test_fullpageScreenshot() {
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- assertRange(result.color.topLeft[0], 110, 111, "R color value");
- assertRange(result.color.topLeft[1], 110, 111, "G color value");
- assertRange(result.color.topLeft[2], 110, 111, "B color value");
-
- // top right
- assertRange(result.color.topRight[0], 55, 56, "R color value");
- assertRange(result.color.topRight[1], 155, 156, "G color value");
- assertRange(result.color.topRight[2], 155, 156, "B color value");
-
- // bottom left
- assertRange(result.color.bottomLeft[0], 105, 106, "R color value");
- assertRange(result.color.bottomLeft[1], 55, 56, "G color value");
- assertRange(result.color.bottomLeft[2], 105, 106, "B color value");
-
- // bottom right
- assertRange(result.color.bottomRight[0], 52, 53, "R color value");
- assertRange(result.color.bottomRight[1], 127, 128, "G color value");
- assertRange(result.color.bottomRight[2], 152, 153, "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [130, 130, 130], "Top left pixel");
+ assertPixel(result.color.topRight, [66, 170, 171], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [125, 75, 125],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [64, 145, 169],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [111, 111, 111], "Top left pixel");
+ assertPixel(result.color.topRight, [55, 155, 155], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [105, 55, 105],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [52, 127, 152],
+ "Bottom right pixel"
+ );
+ }
}
);
});
@@ -151,25 +165,36 @@ add_task(async function test_fullpageScreenshotScrolled() {
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- assertRange(result.color.topLeft[0], 110, 111, "R color value");
- assertRange(result.color.topLeft[1], 110, 111, "G color value");
- assertRange(result.color.topLeft[2], 110, 111, "B color value");
-
- // top right
- assertRange(result.color.topRight[0], 55, 56, "R color value");
- assertRange(result.color.topRight[1], 155, 156, "G color value");
- assertRange(result.color.topRight[2], 155, 156, "B color value");
-
- // bottom left
- assertRange(result.color.bottomLeft[0], 105, 106, "R color value");
- assertRange(result.color.bottomLeft[1], 55, 56, "G color value");
- assertRange(result.color.bottomLeft[2], 105, 106, "B color value");
-
- // bottom right
- assertRange(result.color.bottomRight[0], 52, 53, "R color value");
- assertRange(result.color.bottomRight[1], 127, 128, "G color value");
- assertRange(result.color.bottomRight[2], 152, 153, "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [130, 130, 130], "Top left pixel");
+ assertPixel(result.color.topRight, [66, 170, 171], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [125, 75, 125],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [64, 145, 169],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [111, 111, 111], "Top left pixel");
+ assertPixel(result.color.topRight, [55, 155, 155], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [105, 55, 105],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [52, 127, 152],
+ "Bottom right pixel"
+ );
+ }
}
);
});
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
index 0aafba8fb3..ad262a7e67 100644
--- a/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_toggle_pref.js
@@ -9,6 +9,7 @@ const { sinon } = ChromeUtils.importESModule(
ChromeUtils.defineESModuleGetters(this, {
ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
+ AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
});
ChromeUtils.defineLazyGetter(this, "ExtensionManagement", () => {
const { Management } = ChromeUtils.importESModule(
@@ -17,7 +18,11 @@ ChromeUtils.defineLazyGetter(this, "ExtensionManagement", () => {
return Management;
});
-add_task(async function test() {
+const COMPONENT_PREF = "screenshots.browser.component.enabled";
+const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
+const SCREENSHOT_EXTENSION = "screenshots@mozilla.org";
+
+add_task(async function test_toggling_screenshots_pref() {
let observerSpy = sinon.spy();
let notifierSpy = sinon.spy();
@@ -31,13 +36,24 @@ add_task(async function test() {
ScreenshotsUtils.notify.wrappedMethod.apply(this, arguments);
});
+ // wait for startup idle tasks to complete
+ await new Promise(resolve => ChromeUtils.idleDispatch(resolve));
+ ok(Services.prefs.getBoolPref(COMPONENT_PREF), "Component enabled");
+ ok(!Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots enabled");
+
+ let addon = await AddonManager.getAddonByID(SCREENSHOT_EXTENSION);
+ await BrowserTestUtils.waitForCondition(
+ () => !addon.isActive,
+ "The extension is not active when the component is prefd on"
+ );
+
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: SHORT_TEST_PAGE,
},
async browser => {
- function awaitExtensionEvent(eventName, id) {
+ function extensionEventPromise(eventName, id) {
return new Promise(resolve => {
let listener = (_eventName, ...args) => {
let extension = args[0];
@@ -49,9 +65,21 @@ add_task(async function test() {
ExtensionManagement.on(eventName, listener);
});
}
- const SCREENSHOT_EXTENSION = "screenshots@mozilla.org";
let helper = new ScreenshotsHelper(browser);
+ ok(
+ addon.userDisabled,
+ "The extension is disabled when the component is prefd on"
+ );
+ ok(
+ !addon.isActive,
+ "The extension is not initially active when the component is prefd on"
+ );
+ await BrowserTestUtils.waitForCondition(
+ () => ScreenshotsUtils.initialized,
+ "The component is initialized"
+ );
+ ok(ScreenshotsUtils.initialized, "The component is initialized");
ok(observerSpy.notCalled, "Observer not called");
helper.triggerUIFromToolbar();
@@ -80,12 +108,20 @@ add_task(async function test() {
Assert.equal(observerSpy.callCount, 3, "Observer function called thrice");
- const COMPONENT_PREF = "screenshots.browser.component.enabled";
- await SpecialPowers.pushPrefEnv({
- set: [[COMPONENT_PREF, false]],
- });
+ let extensionReadyPromise = extensionEventPromise(
+ "ready",
+ SCREENSHOT_EXTENSION
+ );
+ Services.prefs.setBoolPref(COMPONENT_PREF, false);
ok(!Services.prefs.getBoolPref(COMPONENT_PREF), "Extension enabled");
- await awaitExtensionEvent("ready", SCREENSHOT_EXTENSION);
+
+ info("Waiting for the extension ready event");
+ await extensionReadyPromise;
+ await BrowserTestUtils.waitForCondition(
+ () => !addon.userDisabled,
+ "The extension gets un-disabled when the component is prefd off"
+ );
+ ok(addon.isActive, "Extension is active");
helper.triggerUIFromToolbar();
Assert.equal(
@@ -94,6 +130,7 @@ add_task(async function test() {
"Observer function still called thrice"
);
+ info("Waiting for the extensions overlay");
await SpecialPowers.spawn(
browser,
["#firefox-screenshots-preselection-iframe"],
@@ -115,6 +152,7 @@ add_task(async function test() {
}
);
+ info("Waiting for the extensions overlay");
helper.triggerUIFromToolbar();
await SpecialPowers.spawn(
browser,
@@ -202,9 +240,7 @@ add_task(async function test() {
"screenshots-component-initialized"
);
- await SpecialPowers.pushPrefEnv({
- set: [[COMPONENT_PREF, true]],
- });
+ Services.prefs.setBoolPref(COMPONENT_PREF, true);
ok(Services.prefs.getBoolPref(COMPONENT_PREF), "Component enabled");
// Needed for component to initialize
await componentReady;
@@ -215,12 +251,6 @@ add_task(async function test() {
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");
}
);
@@ -230,7 +260,9 @@ add_task(async function test() {
url: SHORT_TEST_PAGE,
},
async browser => {
- const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
+ Services.prefs.setBoolPref(SCREENSHOTS_PREF, true);
+ Services.prefs.setBoolPref(COMPONENT_PREF, true);
+
ok(Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots disabled");
ok(
@@ -255,22 +287,23 @@ add_task(async function test() {
menu.hidePopup();
await popuphidden;
- await SpecialPowers.pushPrefEnv({
- set: [[SCREENSHOTS_PREF, false]],
- });
- ok(!Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots enabled");
- }
- );
+ let componentReady = TestUtils.topicObserved(
+ "screenshots-component-initialized"
+ );
+
+ Services.prefs.setBoolPref(SCREENSHOTS_PREF, false);
- await BrowserTestUtils.withNewTab(
- {
- gBrowser,
- url: SHORT_TEST_PAGE,
- },
- async browser => {
- const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
ok(!Services.prefs.getBoolPref(SCREENSHOTS_PREF), "Screenshots enabled");
+ await componentReady;
+
+ ok(ScreenshotsUtils.initialized, "The component is initialized");
+
+ ok(
+ !document.getElementById("screenshot-button").disabled,
+ "Toolbar button is enabled"
+ );
+
let helper = new ScreenshotsHelper(browser);
helper.triggerUIFromToolbar();
@@ -284,6 +317,4 @@ add_task(async function test() {
observerStub.restore();
notifierStub.restore();
-
- await SpecialPowers.popPrefEnv();
});
diff --git a/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js b/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
index 7b7a46f73d..c53b44d5ea 100644
--- a/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
+++ b/browser/components/screenshots/tests/browser/browser_screenshots_test_visible.js
@@ -3,6 +3,13 @@
"use strict";
+function assertPixel(actual, expected, message) {
+ info(message);
+ isfuzzy(actual[0], expected[0], 1, "R color value");
+ isfuzzy(actual[1], expected[1], 1, "G color value");
+ isfuzzy(actual[2], expected[2], 1, "B color value");
+}
+
add_task(async function test_visibleScreenshot() {
await BrowserTestUtils.withNewTab(
{
@@ -65,25 +72,36 @@ add_task(async function test_visibleScreenshot() {
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- Assert.equal(111, result.color.topLeft[0], "R color value");
- Assert.equal(111, result.color.topLeft[1], "G color value");
- Assert.equal(111, result.color.topLeft[2], "B color value");
-
- // top right
- Assert.equal(111, result.color.topRight[0], "R color value");
- Assert.equal(111, result.color.topRight[1], "G color value");
- Assert.equal(111, result.color.topRight[2], "B color value");
-
- // bottom left
- Assert.equal(111, result.color.bottomLeft[0], "R color value");
- Assert.equal(111, result.color.bottomLeft[1], "G color value");
- Assert.equal(111, result.color.bottomLeft[2], "B color value");
-
- // bottom right
- Assert.equal(111, result.color.bottomRight[0], "R color value");
- Assert.equal(111, result.color.bottomRight[1], "G color value");
- Assert.equal(111, result.color.bottomRight[2], "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [130, 130, 130], "Top left pixel");
+ assertPixel(result.color.topRight, [130, 130, 130], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [130, 130, 130],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [130, 130, 130],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [111, 111, 111], "Top left pixel");
+ assertPixel(result.color.topRight, [111, 111, 111], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [111, 111, 111],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [111, 111, 111],
+ "Bottom right pixel"
+ );
+ }
}
);
});
@@ -147,34 +165,42 @@ add_task(async function test_visibleScreenshotScrolledY() {
info("Waiting for clipboard change");
let result = await clipboardChanged;
- // let result = await helper.getImageSizeAndColorFromClipboard();
- // debugger;
-
info("result: " + JSON.stringify(result, null, 2));
info("contentInfo: " + JSON.stringify(contentInfo, null, 2));
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- Assert.equal(105, result.color.topLeft[0], "R color value");
- Assert.equal(55, result.color.topLeft[1], "G color value");
- Assert.equal(105, result.color.topLeft[2], "B color value");
-
- // top right
- Assert.equal(105, result.color.topRight[0], "R color value");
- Assert.equal(55, result.color.topRight[1], "G color value");
- Assert.equal(105, result.color.topRight[2], "B color value");
-
- // bottom left
- Assert.equal(105, result.color.bottomLeft[0], "R color value");
- Assert.equal(55, result.color.bottomLeft[1], "G color value");
- Assert.equal(105, result.color.bottomLeft[2], "B color value");
-
- // bottom right
- Assert.equal(105, result.color.bottomRight[0], "R color value");
- Assert.equal(55, result.color.bottomRight[1], "G color value");
- Assert.equal(105, result.color.bottomRight[2], "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [125, 75, 125], "Top left pixel");
+ assertPixel(result.color.topRight, [125, 75, 125], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [125, 75, 125],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [125, 75, 125],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [105, 55, 105], "Top left pixel");
+ assertPixel(result.color.topRight, [105, 55, 105], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [105, 55, 105],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [105, 55, 105],
+ "Bottom right pixel"
+ );
+ }
}
);
});
@@ -244,25 +270,36 @@ add_task(async function test_visibleScreenshotScrolledX() {
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- Assert.equal(55, result.color.topLeft[0], "R color value");
- Assert.equal(155, result.color.topLeft[1], "G color value");
- Assert.equal(155, result.color.topLeft[2], "B color value");
-
- // top right
- Assert.equal(55, result.color.topRight[0], "R color value");
- Assert.equal(155, result.color.topRight[1], "G color value");
- Assert.equal(155, result.color.topRight[2], "B color value");
-
- // bottom left
- Assert.equal(55, result.color.bottomLeft[0], "R color value");
- Assert.equal(155, result.color.bottomLeft[1], "G color value");
- Assert.equal(155, result.color.bottomLeft[2], "B color value");
-
- // bottom right
- Assert.equal(55, result.color.bottomRight[0], "R color value");
- Assert.equal(155, result.color.bottomRight[1], "G color value");
- Assert.equal(155, result.color.bottomRight[2], "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [66, 170, 171], "Top left pixel");
+ assertPixel(result.color.topRight, [66, 170, 171], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [66, 170, 171],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [66, 170, 171],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [55, 155, 155], "Top left pixel");
+ assertPixel(result.color.topRight, [55, 155, 155], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [55, 155, 155],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [55, 155, 155],
+ "Bottom right pixel"
+ );
+ }
}
);
});
@@ -332,25 +369,36 @@ add_task(async function test_visibleScreenshotScrolledXAndY() {
Assert.equal(result.width, expectedWidth, "Widths should be equal");
Assert.equal(result.height, expectedHeight, "Heights should be equal");
- // top left
- Assert.equal(52, result.color.topLeft[0], "R color value");
- Assert.equal(127, result.color.topLeft[1], "G color value");
- Assert.equal(152, result.color.topLeft[2], "B color value");
-
- // top right
- Assert.equal(52, result.color.topRight[0], "R color value");
- Assert.equal(127, result.color.topRight[1], "G color value");
- Assert.equal(152, result.color.topRight[2], "B color value");
-
- // bottom left
- Assert.equal(52, result.color.bottomLeft[0], "R color value");
- Assert.equal(127, result.color.bottomLeft[1], "G color value");
- Assert.equal(152, result.color.bottomLeft[2], "B color value");
-
- // bottom right
- Assert.equal(52, result.color.bottomRight[0], "R color value");
- Assert.equal(127, result.color.bottomRight[1], "G color value");
- Assert.equal(152, result.color.bottomRight[2], "B color value");
+ // Due to https://bugzil.la/1396587, the pasted image colors differ from
+ // the original image on macOS. Once that bug is fixed, we can remove the
+ // special check for macOS.
+ if (AppConstants.platform === "macosx") {
+ assertPixel(result.color.topLeft, [64, 145, 169], "Top left pixel");
+ assertPixel(result.color.topRight, [64, 145, 169], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [64, 145, 169],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [64, 145, 169],
+ "Bottom right pixel"
+ );
+ } else {
+ assertPixel(result.color.topLeft, [52, 127, 152], "Top left pixel");
+ assertPixel(result.color.topRight, [52, 127, 152], "Top right pixel");
+ assertPixel(
+ result.color.bottomLeft,
+ [52, 127, 152],
+ "Bottom left pixel"
+ );
+ assertPixel(
+ result.color.bottomRight,
+ [52, 127, 152],
+ "Bottom right pixel"
+ );
+ }
}
);
});
diff --git a/browser/components/screenshots/tests/browser/browser_test_moving_tab_to_new_window.js b/browser/components/screenshots/tests/browser/browser_test_moving_tab_to_new_window.js
new file mode 100644
index 0000000000..c3bb546f3a
--- /dev/null
+++ b/browser/components/screenshots/tests/browser/browser_test_moving_tab_to_new_window.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_movingTabToNewWindow() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+ let originalHelper = new ScreenshotsHelper(tab.linkedBrowser);
+ originalHelper.triggerUIFromToolbar();
+ await originalHelper.waitForOverlay();
+ await originalHelper.dragOverlay(10, 10, 300, 300);
+
+ let newWindow = gBrowser.replaceTabWithWindow(tab);
+ let swapDocShellPromise = BrowserTestUtils.waitForEvent(
+ tab.linkedBrowser,
+ "SwapDocShells"
+ );
+ await swapDocShellPromise;
+
+ let newtab = newWindow.gBrowser.selectedTab;
+ let newHelper = new ScreenshotsHelper(newtab.linkedBrowser);
+
+ let isInitialized = await newHelper.isOverlayInitialized();
+
+ ok(isInitialized, "Overlay is initialized after switching windows");
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(tab.linkedBrowser),
+ "The old browser is no longer in the ScreenshotsUtils weakmap"
+ );
+ ok(
+ ScreenshotsUtils.browserToScreenshotsState.has(newtab.linkedBrowser),
+ "The new browser is in the ScreenshotsUtils weakmap"
+ );
+
+ await newHelper.clickCancelButton();
+ await newHelper.assertStateChange("crosshairs");
+ await newHelper.waitForOverlay();
+
+ swapDocShellPromise = BrowserTestUtils.waitForEvent(
+ newtab.linkedBrowser,
+ "SwapDocShells"
+ );
+ gBrowser.adoptTab(newWindow.gBrowser.selectedTab, 1, true);
+ await swapDocShellPromise;
+
+ tab = gBrowser.selectedTab;
+ let helper = new ScreenshotsHelper(tab.linkedBrowser);
+
+ isInitialized = await helper.isOverlayInitialized();
+
+ ok(!isInitialized, "Overlay is not initialized");
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(tab.linkedBrowser),
+ "The old browser is no longer in the ScreenshotsUtils weakmap"
+ );
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(newtab.linkedBrowser),
+ "The new browser is no longer in the ScreenshotsUtils weakmap"
+ );
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_movingParentProcessTabToNewWindow() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:robots"
+ );
+ let originalHelper = new ScreenshotsHelper(tab.linkedBrowser);
+ originalHelper.triggerUIFromToolbar();
+ await originalHelper.waitForOverlay();
+ await originalHelper.dragOverlay(10, 10, 300, 300);
+
+ let newWindow = gBrowser.replaceTabWithWindow(tab);
+ let swapDocShellPromise = BrowserTestUtils.waitForEvent(
+ tab.linkedBrowser,
+ "SwapDocShells"
+ );
+ await swapDocShellPromise;
+
+ let newtab = newWindow.gBrowser.selectedTab;
+ let newHelper = new ScreenshotsHelper(newtab.linkedBrowser);
+
+ let isInitialized = await newHelper.isOverlayInitialized();
+
+ ok(isInitialized, "Overlay is initialized after switching windows");
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(tab.linkedBrowser),
+ "The old browser is no longer in the ScreenshotsUtils weakmap"
+ );
+ ok(
+ ScreenshotsUtils.browserToScreenshotsState.has(newtab.linkedBrowser),
+ "The new browser is in the ScreenshotsUtils weakmap"
+ );
+
+ await newHelper.clickCancelButton();
+ await newHelper.assertStateChange("crosshairs");
+ await newHelper.waitForOverlay();
+
+ swapDocShellPromise = BrowserTestUtils.waitForEvent(
+ newtab.linkedBrowser,
+ "SwapDocShells"
+ );
+ gBrowser.adoptTab(newWindow.gBrowser.selectedTab, 1, true);
+ await swapDocShellPromise;
+
+ tab = gBrowser.selectedTab;
+ let helper = new ScreenshotsHelper(tab.linkedBrowser);
+
+ isInitialized = await helper.isOverlayInitialized();
+
+ ok(!isInitialized, "Overlay is not initialized");
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(tab.linkedBrowser),
+ "The old browser is no longer in the ScreenshotsUtils weakmap"
+ );
+ ok(
+ !ScreenshotsUtils.browserToScreenshotsState.has(newtab.linkedBrowser),
+ "The new browser is no longer in the ScreenshotsUtils weakmap"
+ );
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/screenshots/tests/browser/head.js b/browser/components/screenshots/tests/browser/head.js
index 171e3b8c41..a36e955830 100644
--- a/browser/components/screenshots/tests/browser/head.js
+++ b/browser/components/screenshots/tests/browser/head.js
@@ -38,15 +38,21 @@ const MouseEvents = {
{},
{
get: (target, name) =>
- async function (x, y, options = {}) {
+ async function (x, y, options = {}, browser) {
if (name === "click") {
- this.down(x, y, options);
- this.up(x, y, options);
+ this.down(x, y, options, browser);
+ this.up(x, y, options, browser);
} else {
- await safeSynthesizeMouseEventInContentPage(":root", x, y, {
- type: "mouse" + name,
- ...options,
- });
+ await safeSynthesizeMouseEventInContentPage(
+ ":root",
+ x,
+ y,
+ {
+ type: "mouse" + name,
+ ...options,
+ },
+ browser
+ );
}
},
}
@@ -276,10 +282,15 @@ class ScreenshotsHelper {
this.waitForStateChange("resizing"),
]);
Assert.ok(true, "The overlay is in the dragging or resizing state");
-
+ // We intentionally turn off this a11y check, because the following mouse
+ // event is emitted at the end of the dragging event. Its keyboard
+ // accessible alternative is provided and tested elsewhere, therefore
+ // this rule check shall be ignored by a11y_checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
mouse.up(endX, endY);
await this.assertStateChange("selected");
+ AccessibilityUtils.resetEnv();
this.endX = endX;
this.endY = endY;
@@ -474,7 +485,7 @@ class ScreenshotsHelper {
);
info(`clicking cancel button at ${x}, ${y}`);
- mouse.click(x, y);
+ mouse.click(x, y, {}, this.browser);
}
async clickPreviewCancelButton() {
@@ -878,9 +889,15 @@ async function safeSynthesizeMouseEventInContentPage(
selector,
x,
y,
- options = {}
+ options = {},
+ browser
) {
- let context = gBrowser.selectedBrowser.browsingContext;
+ let context;
+ if (!browser) {
+ context = gBrowser.selectedBrowser.browsingContext;
+ } else {
+ context = browser.browsingContext;
+ }
BrowserTestUtils.synthesizeMouse(selector, x, y, options, context);
}