diff options
Diffstat (limited to 'browser/components/screenshots/tests/browser/head.js')
-rw-r--r-- | browser/components/screenshots/tests/browser/head.js | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/browser/components/screenshots/tests/browser/head.js b/browser/components/screenshots/tests/browser/head.js new file mode 100644 index 0000000000..171e3b8c41 --- /dev/null +++ b/browser/components/screenshots/tests/browser/head.js @@ -0,0 +1,951 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); +const { UrlbarTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/UrlbarTestUtils.sys.mjs" +); + +const TEST_ROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +const TEST_PAGE = TEST_ROOT + "test-page.html"; +const SHORT_TEST_PAGE = TEST_ROOT + "short-test-page.html"; +const LARGE_TEST_PAGE = TEST_ROOT + "large-test-page.html"; +const IFRAME_TEST_PAGE = TEST_ROOT + "iframe-test-page.html"; +const RESIZE_TEST_PAGE = TEST_ROOT + "test-page-resize.html"; + +const { MAX_CAPTURE_DIMENSION, MAX_CAPTURE_AREA } = ChromeUtils.importESModule( + "resource:///modules/ScreenshotsUtils.sys.mjs" +); + +const gScreenshotUISelectors = { + panel: "#screenshotsPagePanel", + fullPageButton: "button.full-page", + visiblePageButton: "button.visible-page", + copyButton: "button.#copy", +}; + +// MouseEvents is for the mouse events on the Anonymous content +const MouseEvents = { + mouse: new Proxy( + {}, + { + get: (target, name) => + async function (x, y, options = {}) { + if (name === "click") { + this.down(x, y, options); + this.up(x, y, options); + } else { + await safeSynthesizeMouseEventInContentPage(":root", x, y, { + type: "mouse" + name, + ...options, + }); + } + }, + } + ), +}; + +const { mouse } = MouseEvents; + +class ScreenshotsHelper { + constructor(browser) { + this.browser = browser; + this.selector = gScreenshotUISelectors; + } + + get toolbarButton() { + return this.browser.ownerDocument.getElementById("screenshot-button"); + } + + get panel() { + return this.browser.ownerDocument.querySelector(this.selector.panel); + } + + /** + * Click the screenshots button in the toolbar + */ + triggerUIFromToolbar() { + let button = this.toolbarButton; + ok( + BrowserTestUtils.isVisible(button), + "The screenshot toolbar button is visible" + ); + button.click(); + } + + async getPanelButton(selector) { + let panel = await this.waitForPanel(); + let screenshotsButtons = panel.querySelector("screenshots-buttons"); + ok(screenshotsButtons, "Found the screenshots-buttons"); + let button = screenshotsButtons.shadowRoot.querySelector(selector); + ok(button, `Found ${selector} button`); + return button; + } + + async waitForPanel() { + let panel = this.panel; + await BrowserTestUtils.waitForCondition(async () => { + if (!panel) { + panel = this.panel; + } + return panel && BrowserTestUtils.isVisible(panel); + }); + return panel; + } + + async waitForOverlay() { + const panel = await this.waitForPanel(); + ok(BrowserTestUtils.isVisible(panel), "Panel buttons are visible"); + + await BrowserTestUtils.waitForCondition(async () => { + let init = await this.isOverlayInitialized(); + return init; + }); + info("Overlay is visible"); + } + + async waitForPanelClosed() { + let panel = this.panel; + if (!panel) { + info("waitForPanelClosed: Panel doesnt exist"); + return; + } + if (panel.hidden) { + info("waitForPanelClosed: panel is already hidden"); + return; + } + info("waitForPanelClosed: waiting for the panel to become hidden"); + await BrowserTestUtils.waitForMutationCondition( + panel, + { attributes: true }, + () => { + return BrowserTestUtils.isHidden(panel); + } + ); + ok(BrowserTestUtils.isHidden(panel), "Panel buttons are hidden"); + info("waitForPanelClosed, panel is hidden: " + panel.hidden); + } + + async waitForOverlayClosed() { + await this.waitForPanelClosed(); + await BrowserTestUtils.waitForCondition(async () => { + let init = !(await this.isOverlayInitialized()); + info("Is overlay initialized: " + !init); + return init; + }); + info("Overlay is not visible"); + } + + async isOverlayInitialized() { + return SpecialPowers.spawn(this.browser, [], () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + return screenshotsChild?.overlay?.initialized; + }); + } + + waitForStateChange(newState) { + return SpecialPowers.spawn(this.browser, [newState], async state => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + + await ContentTaskUtils.waitForCondition(() => { + info(`got ${screenshotsChild.overlay.state}. expected ${state}`); + return screenshotsChild.overlay.state === state; + }, `Wait for overlay state to be ${state}`); + + return screenshotsChild.overlay.state; + }); + } + + async assertStateChange(newState) { + let currentState = await this.waitForStateChange(newState); + + is( + currentState, + newState, + `The current state is ${currentState}, expected ${newState}` + ); + } + + getHoverElementRect() { + return ContentTask.spawn(this.browser, null, async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + return screenshotsChild.overlay.hoverElementRegion.dimensions; + }); + } + + isHoverElementRegionValid() { + return ContentTask.spawn(this.browser, null, async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + return screenshotsChild.overlay.hoverElementRegion.isRegionValid; + }); + } + + async waitForHoverElementRect(expectedWidth, expectedHeight) { + return SpecialPowers.spawn( + this.browser, + [expectedWidth, expectedHeight], + async (width, height) => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + let dimensions; + await ContentTaskUtils.waitForCondition(() => { + dimensions = screenshotsChild.overlay.hoverElementRegion.dimensions; + return dimensions.width === width && dimensions.height === height; + }, "The hover element region is the expected width and height"); + return dimensions; + } + ); + } + + async waitForSelectionRegionSizeChange(currentWidth) { + await ContentTask.spawn( + this.browser, + [currentWidth], + async ([currWidth]) => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + + let dimensions = screenshotsChild.overlay.selectionRegion.dimensions; + await ContentTaskUtils.waitForCondition(() => { + dimensions = screenshotsChild.overlay.selectionRegion.dimensions; + return dimensions.width !== currWidth; + }, "Wait for selection box width change"); + } + ); + } + + /** + * This will drag an overlay starting at the given startX and startY coordinates and ending + * at the given endX and endY coordinates. + * + * endY should be at least 70px from the bottom of window and endX should be at least + * 265px from the left of the window. If these requirements are not met then the + * overlay buttons (cancel, copy, download) will be positioned different from the default + * and the methods to click the overlay buttons will not work unless the updated + * position coordinates are supplied. + * See https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs#1789,1798 + * for how the overlay buttons are positioned when the overlay rect is near the bottom or + * left edge of the window. + * + * Note: The distance of the rect should be greater than 40 to enter in the "dragging" state. + * See https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/browser/components/screenshots/ScreenshotsOverlayChild.sys.mjs#809 + * @param {Number} startX The starting X coordinate. The left edge of the overlay rect. + * @param {Number} startY The starting Y coordinate. The top edge of the overlay rect. + * @param {Number} endX The end X coordinate. The right edge of the overlay rect. + * @param {Number} endY The end Y coordinate. The bottom edge of the overlay rect. + */ + async dragOverlay( + startX, + startY, + endX, + endY, + expectedStartingState = "crosshairs" + ) { + await this.assertStateChange(expectedStartingState); + + mouse.down(startX, startY); + + await Promise.any([ + this.waitForStateChange("draggingReady"), + this.waitForStateChange("resizing"), + ]); + Assert.ok(true, "The overlay is in the draggingReady or resizing state"); + + mouse.move(endX, endY); + + await Promise.any([ + this.waitForStateChange("dragging"), + this.waitForStateChange("resizing"), + ]); + Assert.ok(true, "The overlay is in the dragging or resizing state"); + + mouse.up(endX, endY); + + await this.assertStateChange("selected"); + + this.endX = endX; + this.endY = endY; + } + + async moveOverlayViaKeyboard(mover, events) { + await SpecialPowers.spawn( + this.browser, + [mover, events], + async (moverToFocus, eventsArr) => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + + let overlay = screenshotsChild.overlay; + + switch (moverToFocus) { + case "highlight": + overlay.highlightEl.focus({ focusVisible: true }); + break; + case "mover-bottomLeft": + overlay.bottomLeftMover.focus({ focusVisible: true }); + break; + case "mover-bottomRight": + overlay.bottomRightMover.focus({ focusVisible: true }); + break; + case "mover-topLeft": + overlay.topLeftMover.focus({ focusVisible: true }); + break; + case "mover-topRight": + overlay.topRightMover.focus({ focusVisible: true }); + break; + } + screenshotsChild.overlay.highlightEl.focus(); + + for (let event of eventsArr) { + EventUtils.synthesizeKey( + event.key, + { type: "keydown", ...event.options }, + content + ); + + await ContentTaskUtils.waitForCondition( + () => overlay.state === "resizing", + "Wait for overlay state to be resizing" + ); + + EventUtils.synthesizeKey( + event.key, + { type: "keyup", ...event.options }, + content + ); + + await ContentTaskUtils.waitForCondition( + () => overlay.state === "selected", + "Wait for overlay state to be selected" + ); + } + } + ); + } + + async scrollContentWindow(x, y) { + let promise = BrowserTestUtils.waitForContentEvent(this.browser, "scroll"); + let contentDims = await this.getContentDimensions(); + await ContentTask.spawn( + this.browser, + [x, y, contentDims], + async ([xPos, yPos, cDims]) => { + content.window.scroll(xPos, yPos); + + info(JSON.stringify(cDims, null, 2)); + const scrollbarHeight = {}; + const scrollbarWidth = {}; + content.window.windowUtils.getScrollbarSize( + false, + scrollbarWidth, + scrollbarHeight + ); + + await ContentTaskUtils.waitForCondition(() => { + function isCloseEnough(a, b, diff) { + return Math.abs(a - b) <= diff; + } + + info( + `scrollbarWidth: ${scrollbarWidth.value}, scrollbarHeight: ${scrollbarHeight.value}` + ); + info( + `scrollX: ${content.window.scrollX}, scrollY: ${content.window.scrollY}, scrollMaxX: ${content.window.scrollMaxX}, scrollMaxY: ${content.window.scrollMaxY}` + ); + + // Sometimes (read intermittently) the scroll width/height will be + // off by the width/height of the scrollbar when we are expecting the + // page to be scrolled to the very end. To mitigate this, we check + // that the below differences are within the scrollbar width/height. + return ( + (content.window.scrollX === xPos || + isCloseEnough( + cDims.clientWidth + content.window.scrollX, + cDims.scrollWidth, + scrollbarWidth.value + 1 + )) && + (content.window.scrollY === yPos || + isCloseEnough( + cDims.clientHeight + content.window.scrollY, + cDims.scrollHeight, + scrollbarHeight.value + 1 + )) + ); + }, `Waiting for window to scroll to ${xPos}, ${yPos}`); + } + ); + await promise; + } + + async waitForScrollTo(x, y) { + await ContentTask.spawn(this.browser, [x, y], async ([xPos, yPos]) => { + await ContentTaskUtils.waitForCondition(() => { + info( + `Got scrollX: ${content.window.scrollX}. scrollY: ${content.window.scrollY}` + ); + return ( + content.window.scrollX === xPos && content.window.scrollY === yPos + ); + }, `Waiting for window to scroll to ${xPos}, ${yPos}`); + }); + } + + async resizeContentWindow(width, height) { + this.browser.ownerGlobal.resizeTo(width, height); + await TestUtils.waitForCondition( + () => window.outerHeight === height && window.outerWidth === width, + "Waiting for window to resize" + ); + } + + async clickDownloadButton() { + let { centerX: x, centerY: y } = await ContentTask.spawn( + this.browser, + null, + async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + let { left, top, width, height } = + screenshotsChild.overlay.downloadButton.getBoundingClientRect(); + let centerX = left + width / 2; + let centerY = top + height / 2; + return { centerX, centerY }; + } + ); + + info(`clicking download button at ${x}, ${y}`); + mouse.click(x, y); + } + + async clickCopyButton() { + let { centerX: x, centerY: y } = await ContentTask.spawn( + this.browser, + null, + async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + let { left, top, width, height } = + screenshotsChild.overlay.copyButton.getBoundingClientRect(); + let centerX = left + width / 2; + let centerY = top + height / 2; + return { centerX, centerY }; + } + ); + + info(`clicking copy button at ${x}, ${y}`); + mouse.click(x, y); + } + + async clickCancelButton() { + let { centerX: x, centerY: y } = await ContentTask.spawn( + this.browser, + null, + async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + let { left, top, width, height } = + screenshotsChild.overlay.cancelButton.getBoundingClientRect(); + let centerX = left + width / 2; + let centerY = top + height / 2; + return { centerX, centerY }; + } + ); + + info(`clicking cancel button at ${x}, ${y}`); + mouse.click(x, y); + } + + async clickPreviewCancelButton() { + let { centerX: x, centerY: y } = await ContentTask.spawn( + this.browser, + null, + async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + let { left, top, width, height } = + screenshotsChild.overlay.previewCancelButton.getBoundingClientRect(); + let centerX = left + width / 2; + let centerY = top + height / 2; + return { centerX, centerY }; + } + ); + + info(`clicking cancel button at ${x}, ${y}`); + mouse.click(x, y); + } + + escapeKeyInContent() { + return SpecialPowers.spawn(this.browser, [], () => { + EventUtils.synthesizeKey("KEY_Escape", {}, content); + }); + } + + getTestPageElementRect(elementId = "testPageElement") { + return ContentTask.spawn(this.browser, [elementId], async id => { + let ele = content.document.getElementById(id); + return ele.getBoundingClientRect(); + }); + } + + async clickTestPageElement(elementId = "testPageElement") { + let rect = await this.getTestPageElementRect(elementId); + let dims = await this.getContentDimensions(); + + let x = Math.floor(rect.x + dims.scrollX + rect.width / 2); + let y = Math.floor(rect.y + dims.scrollY + rect.height / 2); + + mouse.move(x, y); + await this.waitForHoverElementRect(rect.width, rect.height); + mouse.down(x, y); + await this.assertStateChange("draggingReady"); + mouse.up(x, y); + await this.assertStateChange("selected"); + } + + async zoomBrowser(zoom) { + let promise = BrowserTestUtils.waitForContentEvent(this.browser, "resize"); + await SpecialPowers.spawn(this.browser, [zoom], zoomLevel => { + const { Layout } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs" + ); + Layout.zoomDocument(content.document, zoomLevel); + }); + await promise; + } + + /** + * Gets the dialog box + * @returns The dialog box + */ + getDialog() { + let currDialogBox = this.browser.tabDialogBox; + let manager = currDialogBox.getTabDialogManager(); + let dialogs = manager.hasDialogs && manager.dialogs; + return dialogs[0]; + } + + assertPanelVisible() { + info("assertPanelVisible, panel.hidden:" + this.panel?.hidden); + Assert.ok( + BrowserTestUtils.isVisible(this.panel), + "Screenshots panel is visible" + ); + } + + assertPanelNotVisible() { + info("assertPanelNotVisible, panel.hidden:" + this.panel?.hidden); + Assert.ok( + !this.panel || BrowserTestUtils.isHidden(this.panel), + "Screenshots panel is not visible" + ); + } + + /** + * Copied from screenshots extension + * Returns a promise that resolves when the clipboard data has changed + * Otherwise rejects + */ + waitForRawClipboardChange(epectedWidth, expectedHeight) { + const initialClipboardData = Date.now().toString(); + SpecialPowers.clipboardCopyString(initialClipboardData); + + return TestUtils.waitForCondition( + async () => { + let data; + try { + data = await this.getImageSizeAndColorFromClipboard(); + } catch (e) { + console.log("Failed to get image/png clipboard data:", e); + return false; + } + if ( + data && + initialClipboardData !== data && + data.height === expectedHeight && + data.width === epectedWidth + ) { + return data; + } + return false; + }, + "Waiting for screenshot to copy to clipboard", + 200 + ); + } + + /** + * Gets the client and scroll demensions on the window + * @returns { Object } + * clientHeight The visible height + * clientWidth The visible width + * scrollHeight The scrollable height + * scrollWidth The scrollable width + * scrollX The scroll x position + * scrollY The scroll y position + */ + getContentDimensions() { + return SpecialPowers.spawn(this.browser, [], async function () { + let { + innerWidth, + innerHeight, + scrollMaxX, + scrollMaxY, + scrollX, + scrollY, + } = content.window; + let width = innerWidth + scrollMaxX; + let height = innerHeight + scrollMaxY; + + const scrollbarHeight = {}; + const scrollbarWidth = {}; + content.window.windowUtils.getScrollbarSize( + false, + scrollbarWidth, + scrollbarHeight + ); + width -= scrollbarWidth.value; + height -= scrollbarHeight.value; + innerWidth -= scrollbarWidth.value; + innerHeight -= scrollbarHeight.value; + + return { + clientHeight: innerHeight, + clientWidth: innerWidth, + scrollHeight: height, + scrollWidth: width, + scrollX, + scrollY, + }; + }); + } + + async getScreenshotsOverlayDimensions() { + return ContentTask.spawn(this.browser, null, async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + Assert.ok(screenshotsChild.overlay.initialized, "The overlay exists"); + + let screenshotsContainer = screenshotsChild.overlay.screenshotsContainer; + + await ContentTaskUtils.waitForCondition(() => { + return !screenshotsContainer.hasAttribute("resizing"); + }, "Waiting for overlay to be done resizing"); + + info( + `${screenshotsContainer.style.width} ${ + screenshotsContainer.style.height + } ${screenshotsContainer.hasAttribute("resizing")}` + ); + + return { + scrollWidth: screenshotsContainer.scrollWidth, + scrollHeight: screenshotsContainer.scrollHeight, + }; + }); + } + + async waitForSelectionLayerDimensionChange(oldWidth, oldHeight) { + await ContentTask.spawn( + this.browser, + [oldWidth, oldHeight], + async ([prevWidth, prevHeight]) => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + + await ContentTaskUtils.waitForCondition(() => { + let screenshotsContainer = + screenshotsChild.overlay.screenshotsContainer; + info( + `old height: ${prevHeight}. new height: ${screenshotsContainer.scrollHeight}.\nold width: ${prevWidth}. new width: ${screenshotsContainer.scrollWidth}` + ); + return ( + screenshotsContainer.scrollHeight !== prevHeight && + screenshotsContainer.scrollWidth !== prevWidth + ); + }, "Wait for selection box width change"); + } + ); + } + + waitForOverlaySizeChangeTo(width, height) { + return ContentTask.spawn( + this.browser, + [width, height], + async ([newWidth, newHeight]) => { + await ContentTaskUtils.waitForCondition(() => { + let { + innerHeight, + innerWidth, + scrollMaxY, + scrollMaxX, + scrollMinY, + scrollMinX, + } = content.window; + let scrollWidth = innerWidth + scrollMaxX - scrollMinX; + let scrollHeight = innerHeight + scrollMaxY - scrollMinY; + + const scrollbarHeight = {}; + const scrollbarWidth = {}; + content.window.windowUtils.getScrollbarSize( + false, + scrollbarWidth, + scrollbarHeight + ); + scrollWidth -= scrollbarWidth.value; + scrollHeight -= scrollbarHeight.value; + info( + `${scrollHeight}, ${newHeight}, ${scrollWidth}, ${newWidth}, ${content.window.scrollMaxX}` + ); + return scrollHeight === newHeight && scrollWidth === newWidth; + }, "Wait for document size change"); + } + ); + } + + getSelectionRegionDimensions() { + return ContentTask.spawn(this.browser, null, async () => { + let screenshotsChild = content.windowGlobalChild.getActor( + "ScreenshotsComponent" + ); + Assert.ok(screenshotsChild.overlay.initialized, "The overlay exists"); + + return screenshotsChild.overlay.selectionRegion.dimensions; + }); + } + + /** + * Copied from screenshots extension + * A helper that returns the size of the image that was just put into the clipboard by the + * :screenshot command. + * @return The {width, height, color} dimension and color object. + */ + async getImageSizeAndColorFromClipboard() { + let flavor = "image/png"; + let image = getRawClipboardData(flavor); + if (!image) { + return false; + } + + // Due to the differences in how images could be stored in the clipboard the + // checks below are needed. The clipboard could already provide the image as + // byte streams or as image container. If it's not possible obtain a + // byte stream, the function throws. + + if (image instanceof Ci.imgIContainer) { + image = Cc["@mozilla.org/image/tools;1"] + .getService(Ci.imgITools) + .encodeImage(image, flavor); + } + + if (!(image instanceof Ci.nsIInputStream)) { + throw new Error("Unable to read image data"); + } + + const binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance( + Ci.nsIBinaryInputStream + ); + binaryStream.setInputStream(image); + const available = binaryStream.available(); + const buffer = new ArrayBuffer(available); + info( + `${binaryStream.readArrayBuffer( + available, + buffer + )} read, ${available} available` + ); + + // We are going to load the image in the content page to measure its size. + // We don't want to insert the image directly in the browser's document + // which could mess all sorts of things up + return SpecialPowers.spawn( + this.browser, + [buffer], + async function (_buffer) { + const img = content.document.createElement("img"); + const loaded = new Promise(r => { + img.addEventListener("load", r, { once: true }); + }); + const url = content.URL.createObjectURL( + new Blob([_buffer], { type: "image/png" }) + ); + + img.src = url; + content.document.documentElement.appendChild(img); + + info("Waiting for the clipboard image to load in the content page"); + await loaded; + + let canvas = content.document.createElementNS( + "http://www.w3.org/1999/xhtml", + "html:canvas" + ); + let context = canvas.getContext("2d"); + canvas.width = img.width; + canvas.height = img.height; + context.drawImage(img, 0, 0); + let topLeft = context.getImageData(0, 0, 1, 1); + let topRight = context.getImageData(img.width - 1, 0, 1, 1); + let bottomLeft = context.getImageData(0, img.height - 1, 1, 1); + let bottomRight = context.getImageData( + img.width - 1, + img.height - 1, + 1, + 1 + ); + + img.remove(); + content.URL.revokeObjectURL(url); + + return { + width: img.width, + height: img.height, + color: { + topLeft: topLeft.data, + topRight: topRight.data, + bottomLeft: bottomLeft.data, + bottomRight: bottomRight.data, + }, + }; + } + ); + } +} + +/** + * Get the raw clipboard data + * @param flavor Type of data to get from clipboard + * @returns The data from the clipboard + */ +function getRawClipboardData(flavor) { + const whichClipboard = Services.clipboard.kGlobalClipboard; + const xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + xferable.init(null); + xferable.addDataFlavor(flavor); + Services.clipboard.getData( + xferable, + whichClipboard, + SpecialPowers.wrap(window).browsingContext.currentWindowContext + ); + let data = {}; + try { + // xferable.getTransferData(flavor, data); + xferable.getAnyTransferData({}, data); + info(JSON.stringify(data, null, 2)); + } catch (e) { + info(e); + } + data = data.value || null; + return data; +} + +/** + * Synthesize a mouse event on an element, after ensuring that it is visible + * in the viewport. + * + * @param {String} selector: The node selector to get the node target for the event. + * @param {number} x + * @param {number} y + * @param {object} options: Options that will be passed to BrowserTestUtils.synthesizeMouse + */ +async function safeSynthesizeMouseEventInContentPage( + selector, + x, + y, + options = {} +) { + let context = gBrowser.selectedBrowser.browsingContext; + BrowserTestUtils.synthesizeMouse(selector, x, y, options, context); +} + +add_setup(async () => { + CustomizableUI.addWidgetToArea( + "screenshot-button", + CustomizableUI.AREA_NAVBAR, + 0 + ); + let screenshotBtn = document.getElementById("screenshot-button"); + Assert.ok(screenshotBtn, "The screenshots button was added to the nav bar"); +}); + +function getContentDevicePixelRatio(browser) { + return SpecialPowers.spawn(browser, [], async function () { + return content.window.devicePixelRatio; + }); +} + +async function clearAllTelemetryEvents() { + // Clear everything. + info("Clearing all telemetry events"); + await TestUtils.waitForCondition(() => { + Services.telemetry.clearEvents(); + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ); + let content = events.content; + let parent = events.parent; + + return (!content && !parent) || (!content.length && !parent.length); + }); +} + +async function waitForScreenshotsEventCount(count, process = "parent") { + await TestUtils.waitForCondition( + () => { + let events = TelemetryTestUtils.getEvents( + { category: "screenshots" }, + { process } + ); + + info(`Got ${events?.length} event(s)`); + info(`Actual events: ${JSON.stringify(events, null, 2)}`); + return events.length === count ? events : null; + }, + `Waiting for ${count} ${process} event(s).`, + 200, + 100 + ); +} + +async function assertScreenshotsEvents( + expectedEvents, + process = "parent", + clearEvents = true +) { + info(`Expected events: ${JSON.stringify(expectedEvents, null, 2)}`); + // Make sure we have recorded the correct number of events + await waitForScreenshotsEventCount(expectedEvents.length, process); + + TelemetryTestUtils.assertEvents( + expectedEvents, + { category: "screenshots" }, + { clear: clearEvents, process } + ); +} |