mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 23:30:46 +00:00
Bug 1873947 - Fix dragging screenshots tab to new window. r=sfoster,mconley
Differential Revision: https://phabricator.services.mozilla.com/D198470
This commit is contained in:
parent
76b1e7a644
commit
3e4d27dae4
@ -44,6 +44,10 @@ export class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
return this.getDocumentTitle();
|
||||
case "Screenshots:GetMethodsUsed":
|
||||
return this.getMethodsUsed();
|
||||
case "Screenshots:RemoveEventListeners":
|
||||
return this.removeEventListeners();
|
||||
case "Screenshots:AddEventListeners":
|
||||
return this.addEventListeners();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -84,14 +88,6 @@ export class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
}
|
||||
this.#scrollTask.arm();
|
||||
break;
|
||||
case "visibilitychange":
|
||||
if (
|
||||
event.target.visibilityState === "hidden" &&
|
||||
this.overlay?.state === "crosshairs"
|
||||
) {
|
||||
this.requestCancelScreenshot("navigation");
|
||||
}
|
||||
break;
|
||||
case "Screenshots:Close":
|
||||
this.requestCancelScreenshot(event.detail.reason);
|
||||
break;
|
||||
@ -210,6 +206,13 @@ export class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
});
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.contentWindow.addEventListener("beforeunload", this);
|
||||
this.contentWindow.addEventListener("resize", this);
|
||||
this.contentWindow.addEventListener("scroll", this);
|
||||
this.addOverlayEventListeners();
|
||||
}
|
||||
|
||||
addOverlayEventListeners() {
|
||||
let chromeEventHandler = this.docShell.chromeEventHandler;
|
||||
for (let event of ScreenshotsComponentChild.OVERLAY_EVENTS) {
|
||||
@ -234,16 +237,19 @@ export class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
let overlay =
|
||||
this.overlay ||
|
||||
(this.#overlay = new lazy.ScreenshotsOverlay(this.document));
|
||||
this.document.ownerGlobal.addEventListener("beforeunload", this);
|
||||
this.contentWindow.addEventListener("resize", this);
|
||||
this.contentWindow.addEventListener("scroll", this);
|
||||
this.contentWindow.addEventListener("visibilitychange", this);
|
||||
this.addOverlayEventListeners();
|
||||
this.addEventListeners();
|
||||
|
||||
overlay.initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
removeEventListeners() {
|
||||
this.contentWindow.removeEventListener("beforeunload", this);
|
||||
this.contentWindow.removeEventListener("resize", this);
|
||||
this.contentWindow.removeEventListener("scroll", this);
|
||||
this.removeOverlayEventListeners();
|
||||
}
|
||||
|
||||
removeOverlayEventListeners() {
|
||||
let chromeEventHandler = this.docShell.chromeEventHandler;
|
||||
for (let event of ScreenshotsComponentChild.OVERLAY_EVENTS) {
|
||||
@ -255,11 +261,7 @@ export class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
* Removes event listeners and the screenshots overlay.
|
||||
*/
|
||||
endScreenshotsOverlay(options = {}) {
|
||||
this.document.ownerGlobal.removeEventListener("beforeunload", this);
|
||||
this.contentWindow.removeEventListener("resize", this);
|
||||
this.contentWindow.removeEventListener("scroll", this);
|
||||
this.contentWindow.removeEventListener("visibilitychange", this);
|
||||
this.removeOverlayEventListeners();
|
||||
this.removeEventListeners();
|
||||
|
||||
this.overlay?.tearDown(options);
|
||||
this.#resizeTask?.disarm();
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -60,4 +60,6 @@ skip-if = ["!crashreporter"]
|
||||
|
||||
["browser_test_element_picker.js"]
|
||||
|
||||
["browser_test_moving_tab_to_new_window.js"]
|
||||
|
||||
["browser_test_resize.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(
|
||||
|
@ -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);
|
||||
});
|
@ -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
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -474,7 +480,7 @@ class ScreenshotsHelper {
|
||||
);
|
||||
|
||||
info(`clicking cancel button at ${x}, ${y}`);
|
||||
mouse.click(x, y);
|
||||
mouse.click(x, y, {}, this.browser);
|
||||
}
|
||||
|
||||
async clickPreviewCancelButton() {
|
||||
@ -878,9 +884,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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user