diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /browser/components/screenshots | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-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')
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); } |