Source code

Revision control

Copy as Markdown

Other Tools

/* 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, {
AboutWelcomeTelemetry:
"resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
MessagingSystemAllowlists:
});
ChromeUtils.defineLazyGetter(
lazy,
"AWTelemetry",
() => new lazy.AboutWelcomeTelemetry()
);
export const Spotlight = {
_dialog: null,
_dialogWindow: null,
get isOpen() {
return !!this._dialog;
},
close(window) {
if (!this._dialog) {
return;
}
// Only close if no window specified or if the window owns the dialog
if (!window || this._dialogWindow === window) {
let dialog = this._dialog;
this._dialog = null;
this._dialogWindow = null;
dialog.close();
}
},
sendUserEventTelemetry(event, message, dispatch) {
if (message.content?.metrics === "block") {
return;
}
const data = {
action: "spotlight_user_event",
message_id: message.content.id,
event,
event_context: {},
};
if (message.content.write_in_microsurvey) {
data.event_context.write_in_microsurvey = true;
}
dispatch({ type: "SPOTLIGHT_TELEMETRY", data });
},
defaultDispatch(message) {
if (message.type === "SPOTLIGHT_TELEMETRY") {
const { message_id, event } = message.data;
lazy.AWTelemetry.sendTelemetry({ message_id, event });
}
},
/**
* Shows spotlight tab or window modal specific to the given browser
*
* @param browser The browser for spotlight display
* @param message Message containing content to show
* @param dispatchCFRAction A function to dispatch resulting actions
* @return boolean value capturing if spotlight was displayed
*/
async showSpotlightDialog(browser, message, dispatch = this.defaultDispatch) {
const win = browser?.documentGlobal;
if (!win) {
return false;
}
// Warm the Remote Settings-backed SET_PREF allowlist cache as early as
// possible as a spotlight dialog can be opened directly via the SET_PREF
// action's own handleAction dispatch (SHOW_SPOTLIGHT), which does not go
// through ASRouter's own init sequence. This isn't awaited, so it only
// shrinks the window for a possible race condition.
lazy.MessagingSystemAllowlists.ensureInit();
if (message.trigger?.id === "lastWindowClose") {
win.gDialogBox.replaceDialogIfOpen();
// We do this for the selected browser, not the triggering browser, since
// even a global modal is prevented by a tab modal in the selected
// browser, and we want lastWindowClose modals to take priority: this is
// the user's last chance to see it before the window actually closes.
win.gBrowser
.getTabDialogBox(win.gBrowser.selectedBrowser)
.abortAllDialogs();
} else if (win.gDialogBox.isOpen) {
return false;
}
const spotlight_url = "chrome://browser/content/spotlight.html";
const dispatchCFRAction =
// This also blocks CFR impressions, which is fine for current use cases.
message.content?.metrics === "block" ? () => {} : dispatch;
// This handles `IMPRESSION` events used by ASRouter for frequency caps.
// AboutWelcome handles `IMPRESSION` events for telemetry.
this.sendUserEventTelemetry("IMPRESSION", message, dispatchCFRAction);
dispatchCFRAction({ type: "IMPRESSION", data: message });
let unloadHandler = () => {
this._dialog = null;
this._dialogWindow = null;
};
win.addEventListener("unload", unloadHandler, { once: true });
try {
if (message.content?.modal === "tab") {
let { closedPromise, dialog } = win.gBrowser
.getTabDialogBox(browser)
.open(
spotlight_url,
{
features: "resizable=no",
allowDuplicateDialogs: false,
},
message.content
);
this._dialog = dialog;
this._dialogWindow = win;
await closedPromise;
} else {
let closedPromise = win.gDialogBox.open(spotlight_url, message.content);
this._dialog = win.gDialogBox.dialog;
this._dialogWindow = win;
await closedPromise;
}
} finally {
win.removeEventListener("unload", unloadHandler);
this._dialog = null;
this._dialogWindow = null;
}
// If dismissed report telemetry and exit
this.sendUserEventTelemetry("DISMISS", message, dispatchCFRAction);
return true;
},
};