-
Emily McMinn authored
Bug 1827971 - Remove OPEN_FIREFOX_VIEW_AND_COLORWAYS_MODAL SpecialMessageAction r=omc-reviewers,aminomancer Turns out both `OPEN_FIREFOX_VIEW` and `OPEN_FIREFOX_VIEW_AND_COLORWAYS_MODAL` just call `window.FirefoxViewHandler.openTab();`, so not only is this action kinda misleading now that the Colorways closet is gone, it's a duplicate action. Differential Revision: https://phabricator.services.mozilla.com/D175792
Emily McMinn authoredBug 1827971 - Remove OPEN_FIREFOX_VIEW_AND_COLORWAYS_MODAL SpecialMessageAction r=omc-reviewers,aminomancer Turns out both `OPEN_FIREFOX_VIEW` and `OPEN_FIREFOX_VIEW_AND_COLORWAYS_MODAL` just call `window.FirefoxViewHandler.openTab();`, so not only is this action kinda misleading now that the Colorways closet is gone, it's a duplicate action. Differential Revision: https://phabricator.services.mozilla.com/D175792
SpecialMessageActions.sys.mjs 13.61 KiB
/* 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/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const DOH_DOORHANGER_DECISION_PREF = "doh-rollout.doorhanger-decision";
const NETWORK_TRR_MODE_PREF = "network.trr.mode";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
UITour: "resource:///modules/UITour.sys.mjs",
});
XPCOMUtils.defineLazyModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
Spotlight: "resource://activity-stream/lib/Spotlight.jsm",
});
export const SpecialMessageActions = {
// This is overridden by ASRouter.init
blockMessageById() {
throw new Error("ASRouter not intialized yet");
},
/**
* loadAddonIconInURLBar - load addons-notification icon by displaying
* box containing addons icon in urlbar. See Bug 1513882
*
* @param {Browser} browser browser element for showing addons icon
*/
loadAddonIconInURLBar(browser) {
if (!browser) {
return;
}
const chromeDoc = browser.ownerDocument;
let notificationPopupBox = chromeDoc.getElementById(
"notification-popup-box"
);
if (!notificationPopupBox) {
return;
}
if (
notificationPopupBox.style.display === "none" ||
notificationPopupBox.style.display === ""
) {
notificationPopupBox.style.display = "block";
}
},
/**
*
* @param {Browser} browser The revelant Browser
* @param {string} url URL to look up install location
* @param {string} telemetrySource Telemetry information to pass to getInstallForURL
*/
async installAddonFromURL(browser, url, telemetrySource = "amo") {
try {
this.loadAddonIconInURLBar(browser);
const aUri = Services.io.newURI(url);
const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
// AddonManager installation source associated to the addons installed from activitystream's CFR
// and RTAMO (source is going to be "amo" if not configured explicitly in the message provider).
const telemetryInfo = { source: telemetrySource };
const install = await lazy.AddonManager.getInstallForURL(aUri.spec, {
telemetryInfo,
});
await lazy.AddonManager.installAddonFromWebpage(
"application/x-xpinstall",
browser,
systemPrincipal,
install
);
} catch (e) {
console.error(e);
}
},
/**
* Pin Firefox to taskbar.
*
* @param {Window} window Reference to a window object
* @param {boolean} pin Private Browsing Mode if true
*/
pinFirefoxToTaskbar(window, privateBrowsing = false) {
return window.getShellService().pinToTaskbar(privateBrowsing);
},
/**
* Set browser as the operating system default browser.
*
* @param {Window} window Reference to a window object
*/
setDefaultBrowser(window) {
window.getShellService().setAsDefault();
},
/**
* Set browser as the default PDF handler.
*
* @param {Window} window Reference to a window object
*/
setDefaultPDFHandler(window, onlyIfKnownBrowser = false) {
window.getShellService().setAsDefaultPDFHandler(onlyIfKnownBrowser);
},
/**
* Reset browser homepage and newtab to default with a certain section configuration
*
* @param {"default"|null} home Value to set for browser homepage
* @param {"default"|null} newtab Value to set for browser newtab
* @param {obj} layout Configuration options for newtab sections
* @returns {undefined}
*/
configureHomepage({ homePage = null, newtab = null, layout = null }) {
// Homepage can be default, blank or a custom url
if (homePage === "default") {
Services.prefs.clearUserPref("browser.startup.homepage");
}
// Newtab page can only be default or blank
if (newtab === "default") {
Services.prefs.clearUserPref("browser.newtabpage.enabled");
}
if (layout) {
// Existing prefs that interact with the newtab page layout, we default to true
// or payload configuration
let newtabConfigurations = [
[
// controls the search bar
"browser.newtabpage.activity-stream.showSearch",
layout.search,
],
[
// controls the topsites
"browser.newtabpage.activity-stream.feeds.topsites",
layout.topsites,
// User can control number of topsite rows
["browser.newtabpage.activity-stream.topSitesRows"],
],
[
// controls the highlights section
"browser.newtabpage.activity-stream.feeds.section.highlights",
layout.highlights,
// User can control number of rows and highlight sources
[
"browser.newtabpage.activity-stream.section.highlights.rows",
"browser.newtabpage.activity-stream.section.highlights.includeVisited",
"browser.newtabpage.activity-stream.section.highlights.includePocket",
"browser.newtabpage.activity-stream.section.highlights.includeDownloads",
"browser.newtabpage.activity-stream.section.highlights.includeBookmarks",
],
],
[
// controls the snippets section
"browser.newtabpage.activity-stream.feeds.snippets",
layout.snippets,
],
[
// controls the topstories section
"browser.newtabpage.activity-stream.feeds.system.topstories",
layout.topstories,
],
].filter(
// If a section has configs that the user changed we will skip that section
([, , sectionConfigs]) =>
!sectionConfigs ||
sectionConfigs.every(
prefName => !Services.prefs.prefHasUserValue(prefName)
)
);
for (let [prefName, prefValue] of newtabConfigurations) {
Services.prefs.setBoolPref(prefName, prefValue);
}
}
},
/**
* Set prefs with special message actions
*
* @param {Object} pref - A pref to be updated.
* @param {string} pref.name - The name of the pref to be updated
* @param {string} [pref.value] - The value of the pref to be updated. If not included, the pref will be reset.
*/
setPref(pref) {
// Array of prefs that are allowed to be edited by SET_PREF
const allowedPrefs = [
"browser.dataFeatureRecommendations.enabled",
"browser.startup.homepage",
"browser.privateWindowSeparation.enabled",
"browser.firefox-view.feature-tour",
"browser.pdfjs.feature-tour",
"cookiebanners.service.mode",
"cookiebanners.service.mode.privateBrowsing",
"cookiebanners.service.detectOnly",
];
if (!allowedPrefs.includes(pref.name)) {
pref.name = `messaging-system-action.${pref.name}`;
}
// If pref has no value, reset it, otherwise set it to desired value
switch (typeof pref.value) {
case "object":
case "undefined":
Services.prefs.clearUserPref(pref.name);
break;
case "string":
Services.prefs.setStringPref(pref.name, pref.value);
break;
case "number":
Services.prefs.setIntPref(pref.name, pref.value);
break;
case "boolean":
Services.prefs.setBoolPref(pref.name, pref.value);
break;
default:
throw new Error(
`Special message action with type SET_PREF, pref of "${pref.name}" is an unsupported type.`
);
}
},
/**
* Processes "Special Message Actions", which are definitions of behaviors such as opening tabs
* installing add-ons, or focusing the awesome bar that are allowed to can be triggered from
* Messaging System interactions.
*
* @param {{type: string, data?: any}} action User action defined in message JSON.
* @param browser {Browser} The browser most relevant to the message.
*/
async handleAction(action, browser) {
const window = browser.ownerGlobal;
switch (action.type) {
case "SHOW_MIGRATION_WIZARD":
Services.tm.dispatchToMainThread(() =>
lazy.MigrationUtils.showMigrationWizard(window, {
entrypoint: lazy.MigrationUtils.MIGRATION_ENTRYPOINTS.NEWTAB,
migratorKey: action.data?.source,
})
);
break;
case "OPEN_PRIVATE_BROWSER_WINDOW":
// Forcefully open about:privatebrowsing
window.OpenBrowserWindow({ private: true });
break;
case "OPEN_URL":
window.openLinkIn(
Services.urlFormatter.formatURL(action.data.args),
action.data.where || "current",
{
private: false,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
csp: null,
}
);
break;
case "OPEN_ABOUT_PAGE":
let aboutPageURL = new URL(`about:${action.data.args}`);
if (action.data.entrypoint) {
aboutPageURL.search = action.data.entrypoint;
}
window.openTrustedLinkIn(
aboutPageURL.toString(),
action.data.where || "tab"
);
break;
case "OPEN_FIREFOX_VIEW":
window.FirefoxViewHandler.openTab();
break;
case "OPEN_PREFERENCES_PAGE":
window.openPreferences(
action.data.category || action.data.args,
action.data.entrypoint && {
urlParams: { entrypoint: action.data.entrypoint },
}
);
break;
case "OPEN_APPLICATIONS_MENU":
lazy.UITour.showMenu(window, action.data.args);
break;
case "HIGHLIGHT_FEATURE":
const highlight = await lazy.UITour.getTarget(window, action.data.args);
if (highlight) {
await lazy.UITour.showHighlight(window, highlight, "none", {
autohide: true,
});
}
break;
case "INSTALL_ADDON_FROM_URL":
await this.installAddonFromURL(
browser,
action.data.url,
action.data.telemetrySource
);
break;
case "PIN_FIREFOX_TO_TASKBAR":
await this.pinFirefoxToTaskbar(window, action.data?.privatePin);
break;
case "PIN_AND_DEFAULT":
await this.pinFirefoxToTaskbar(window, action.data?.privatePin);
this.setDefaultBrowser(window);
break;
case "SET_DEFAULT_BROWSER":
this.setDefaultBrowser(window);
break;
case "SET_DEFAULT_PDF_HANDLER":
this.setDefaultPDFHandler(
window,
action.data?.onlyIfKnownBrowser ?? false
);
break;
case "PIN_CURRENT_TAB":
let tab = window.gBrowser.selectedTab;
window.gBrowser.pinTab(tab);
window.ConfirmationHint.show(tab, "confirmation-hint-pin-tab", {
descriptionId: "confirmation-hint-pin-tab-description",
});
break;
case "SHOW_FIREFOX_ACCOUNTS":
if (!(await lazy.FxAccounts.canConnectAccount())) {
break;
}
const data = action.data;
const url = await lazy.FxAccounts.config.promiseConnectAccountURI(
(data && data.entrypoint) || "snippets",
(data && data.extraParams) || {}
);
// Use location provided; if not specified, replace the current tab.
window.openLinkIn(url, data.where || "current", {
private: false,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
csp: null,
});
break;
case "OPEN_PROTECTION_PANEL":
let { gProtectionsHandler } = window;
gProtectionsHandler.showProtectionsPopup({});
break;
case "OPEN_PROTECTION_REPORT":
window.gProtectionsHandler.openProtections();
break;
case "OPEN_AWESOME_BAR":
window.gURLBar.search("");
break;
case "DISABLE_STP_DOORHANGERS":
await this.blockMessageById([
"SOCIAL_TRACKING_PROTECTION",
"FINGERPRINTERS_PROTECTION",
"CRYPTOMINERS_PROTECTION",
]);
break;
case "DISABLE_DOH":
Services.prefs.setStringPref(
DOH_DOORHANGER_DECISION_PREF,
"UIDisabled"
);
Services.prefs.setIntPref(NETWORK_TRR_MODE_PREF, 5);
break;
case "ACCEPT_DOH":
Services.prefs.setStringPref(DOH_DOORHANGER_DECISION_PREF, "UIOk");
break;
case "CANCEL":
// A no-op used by CFRs that minimizes the notification but does not
// trigger a dismiss or block (it keeps the notification around)
break;
case "CONFIGURE_HOMEPAGE":
this.configureHomepage(action.data);
break;
case "SHOW_SPOTLIGHT":
lazy.Spotlight.showSpotlightDialog(browser, action.data);
break;
case "BLOCK_MESSAGE":
await this.blockMessageById(action.data.id);
break;
case "SET_PREF":
this.setPref(action.data.pref);
break;
case "MULTI_ACTION":
await Promise.all(
action.data.actions.map(async action => {
try {
await this.handleAction(action, browser);
} catch (err) {
throw new Error(`Error in MULTI_ACTION event: ${err.message}`);
}
})
);
break;
default:
throw new Error(
`Special message action with type ${action.type} is unsupported.`
);
case "CLICK_ELEMENT":
const clickElement = window.document.querySelector(
action.data.selector
);
clickElement?.click();
break;
case "RELOAD_BROWSER":
browser.reload();
break;
}
},
};