Bug 1899747 - Add option to extend feature callout page_event_listener to all windows r=omc-reviewers,mviar,aminomancer
Differential Revision: https://phabricator.services.mozilla.com/D213239
This commit is contained in:
@@ -349,6 +349,9 @@ interface FeatureCallout {
|
||||
// don't set up real event listeners, but instead invoke the
|
||||
// action on a timer.
|
||||
interval?: number;
|
||||
// Extend addEventListener to all windows? Not compatible with
|
||||
// `interval`.
|
||||
every_window: boolean;
|
||||
};
|
||||
};
|
||||
action: {
|
||||
|
||||
@@ -1577,6 +1577,8 @@ export class FeatureCallout {
|
||||
* @property {Number} [interval] Used only for `timeout` and `interval` event
|
||||
* types. These don't set up real event listeners, but instead invoke the
|
||||
* action on a timer.
|
||||
* @property {Boolean} [every_window] Extend addEventListener to all windows?
|
||||
* Not compatible with `interval`.
|
||||
*
|
||||
* @typedef {Object} PageEventListenerAction Action sent to AboutWelcomeParent
|
||||
* @property {String} [type] Action type, e.g. `OPEN_URL`
|
||||
@@ -1657,7 +1659,8 @@ export class FeatureCallout {
|
||||
.map(attr => `[${attr.name}="${attr.value}"]`)
|
||||
.join("")}`;
|
||||
}
|
||||
if (this.doc.querySelectorAll(source).length > 1) {
|
||||
let doc = target.ownerDocument;
|
||||
if (doc.querySelectorAll(source).length > 1) {
|
||||
let uniqueAncestor = target.closest(`[id]:not(:scope, :root, body)`);
|
||||
if (uniqueAncestor) {
|
||||
source = `${this._getUniqueElementIdentifier(
|
||||
@@ -1665,6 +1668,12 @@ export class FeatureCallout {
|
||||
)} > ${source}`;
|
||||
}
|
||||
}
|
||||
if (doc !== this.doc) {
|
||||
let windowIndex = [
|
||||
...Services.wm.getEnumerator("navigator:browser"),
|
||||
].indexOf(target.ownerGlobal);
|
||||
source = `window${windowIndex + 1}: ${source}`;
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
EveryWindow: "resource:///modules/EveryWindow.sys.mjs",
|
||||
});
|
||||
/**
|
||||
* Methods for setting up and tearing down page event listeners. These are used
|
||||
* to dismiss Feature Callouts when the callout's anchor element is clicked.
|
||||
@@ -64,9 +67,28 @@ export class PageEventManager {
|
||||
passive: !options.preventDefault,
|
||||
signal: controller.signal,
|
||||
};
|
||||
const targets = this.doc.querySelectorAll(selectors);
|
||||
for (const target of targets) {
|
||||
target.addEventListener(type, callback, opt);
|
||||
if (options?.every_window) {
|
||||
// Using unique ID for each listener in case there are multiple
|
||||
// listeners with the same type
|
||||
let uuid = Services.uuid.generateUUID().number;
|
||||
lazy.EveryWindow.registerCallback(
|
||||
uuid,
|
||||
win => {
|
||||
for (const target of win.document.querySelectorAll(selectors)) {
|
||||
target.addEventListener(type, callback, opt);
|
||||
}
|
||||
},
|
||||
win => {
|
||||
for (const target of win.document.querySelectorAll(selectors)) {
|
||||
target.removeEventListener(type, callback, opt);
|
||||
}
|
||||
}
|
||||
);
|
||||
listener.uninit = () => lazy.EveryWindow.unregisterCallback(uuid, true);
|
||||
} else {
|
||||
for (const target of this.doc.querySelectorAll(selectors)) {
|
||||
target.addEventListener(type, callback, opt);
|
||||
}
|
||||
}
|
||||
listener.controller = controller;
|
||||
} else if (["timeout", "interval"].includes(type) && options.interval) {
|
||||
@@ -94,6 +116,7 @@ export class PageEventManager {
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
listener.uninit?.();
|
||||
listener.controller?.abort();
|
||||
this._listeners.delete(params);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,7 @@ add_task(async function feature_callout_dismiss_on_escape() {
|
||||
},
|
||||
message_id: "TEST_CALLOUT",
|
||||
});
|
||||
sandbox.restore();
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
@@ -423,3 +424,142 @@ add_task(async function feature_callout_pdfjs_theme() {
|
||||
});
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
add_task(async function page_event_listeners_every_window() {
|
||||
const testMessage2 = getTestMessage();
|
||||
testMessage2.content.screens[0].content.page_event_listeners = [
|
||||
{
|
||||
params: {
|
||||
type: "TabPinned",
|
||||
selectors: "#main-window",
|
||||
options: {
|
||||
every_window: true,
|
||||
},
|
||||
},
|
||||
action: {
|
||||
dismiss: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
const sandbox = sinon.createSandbox();
|
||||
const spy = new TelemetrySpy(sandbox);
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const doc = win.document;
|
||||
const browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
await showFeatureCallout(browser, testMessage2);
|
||||
info("Waiting for callout to render");
|
||||
|
||||
const newWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const newTab = await BrowserTestUtils.openNewForegroundTab(
|
||||
newWin.gBrowser,
|
||||
"about:mozilla"
|
||||
);
|
||||
|
||||
info("Waiting to pin tab in new window");
|
||||
let newTabPinned = BrowserTestUtils.waitForEvent(newTab, "TabPinned");
|
||||
newWin.gBrowser.pinTab(newTab);
|
||||
await newTabPinned;
|
||||
|
||||
ok(newTab.pinned, "Tab should be pinned");
|
||||
|
||||
info("Waiting to dismiss callout");
|
||||
await waitForCalloutRemoved(doc);
|
||||
ok(
|
||||
!doc.querySelector(calloutSelector),
|
||||
"Callout is removed from screen on page_event"
|
||||
);
|
||||
|
||||
//Check dismiss telemetry
|
||||
spy.assertCalledWith({
|
||||
event: "DISMISS",
|
||||
event_context: {
|
||||
source: sinon.match("tab.tabbrowser-tab"),
|
||||
page: "chrome",
|
||||
},
|
||||
message_id: "TEST_CALLOUT",
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function multiple_page_event_listeners_every_window() {
|
||||
const testMessage2 = getTestMessage();
|
||||
testMessage2.content.screens[0].content.page_event_listeners = [
|
||||
{
|
||||
params: {
|
||||
type: "click",
|
||||
selectors: "#PanelUI-menu-button",
|
||||
options: {
|
||||
every_window: true,
|
||||
},
|
||||
},
|
||||
action: {
|
||||
dismiss: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
params: {
|
||||
type: "click",
|
||||
selectors: "#unified-extensions-button",
|
||||
options: {
|
||||
every_window: true,
|
||||
},
|
||||
},
|
||||
action: {
|
||||
dismiss: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
params: {
|
||||
type: "click",
|
||||
selectors: "#fxa-toolbar-menu-button",
|
||||
options: {
|
||||
every_window: true,
|
||||
},
|
||||
},
|
||||
action: {
|
||||
dismiss: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
const sandbox = sinon.createSandbox();
|
||||
const spy = new TelemetrySpy(sandbox);
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const doc = win.document;
|
||||
const browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
await showFeatureCallout(browser, testMessage2);
|
||||
info("Waiting for callout to render");
|
||||
|
||||
const newWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const doc2 = newWin.document;
|
||||
|
||||
info("Clicking on button - second page event listener");
|
||||
doc2.querySelector("#unified-extensions-button").click();
|
||||
|
||||
info("Waiting to dismiss callout");
|
||||
await waitForCalloutRemoved(doc);
|
||||
ok(
|
||||
!doc.querySelector(calloutSelector),
|
||||
"Callout is removed from screen on page_event"
|
||||
);
|
||||
|
||||
//Check dismiss telemetry
|
||||
spy.assertCalledWith({
|
||||
event: "DISMISS",
|
||||
event_context: {
|
||||
source: sinon.match("#unified-extensions-button"),
|
||||
page: "chrome",
|
||||
},
|
||||
message_id: "TEST_CALLOUT",
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user