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/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
UrlbarProviderQuickActions:
});
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
if (AppConstants.MOZ_UPDATER) {
XPCOMUtils.defineLazyServiceGetter(
lazy,
"AUS",
"@mozilla.org/updates/update-service;1",
"nsIApplicationUpdateService"
);
}
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"SCREENSHOT_BROWSER_COMPONENT",
"screenshots.browser.component.enabled",
false
);
let openUrlFun = url => () => openUrl(url);
let openUrl = url => {
let window = lazy.BrowserWindowTracker.getTopWindow();
if (url.startsWith("about:")) {
window.switchToTabHavingURI(Services.io.newURI(url), true, {
ignoreFragment: "whenComparing",
});
} else {
window.gBrowser.addTab(url, {
inBackground: false,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
}
return { focusContent: true };
};
let openAddonsUrl = url => {
return () => {
let window = lazy.BrowserWindowTracker.getTopWindow();
window.BrowserAddonUI.openAddonsMgr(url, { selectTabByViewId: true });
};
};
let currentBrowser = () =>
lazy.BrowserWindowTracker.getTopWindow()?.gBrowser.selectedBrowser;
let currentTab = () =>
lazy.BrowserWindowTracker.getTopWindow()?.gBrowser.selectedTab;
ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
return new Localization(["branding/brand.ftl", "browser/browser.ftl"], true);
});
const DEFAULT_ACTIONS = {
addons: {
l10nCommands: ["quickactions-cmd-addons2", "quickactions-addons"],
label: "quickactions-addons",
onPick: openAddonsUrl("addons://discover/"),
},
bookmarks: {
l10nCommands: ["quickactions-cmd-bookmarks", "quickactions-bookmarks2"],
label: "quickactions-bookmarks2",
onPick: () => {
lazy.BrowserWindowTracker.getTopWindow().top.PlacesCommandHook.showPlacesOrganizer(
"BookmarksToolbar"
);
},
},
clear: {
l10nCommands: [
"quickactions-cmd-clearhistory",
"quickactions-clearhistory",
],
label: "quickactions-clearhistory",
onPick: () => {
lazy.BrowserWindowTracker.getTopWindow()
.document.getElementById("Tools:Sanitize")
.doCommand();
},
},
downloads: {
l10nCommands: ["quickactions-cmd-downloads", "quickactions-downloads2"],
label: "quickactions-downloads2",
onPick: openUrlFun("about:downloads"),
},
extensions: {
l10nCommands: ["quickactions-cmd-extensions", "quickactions-extensions"],
label: "quickactions-extensions",
onPick: openAddonsUrl("addons://list/extension"),
},
inspect: {
l10nCommands: ["quickactions-cmd-inspector", "quickactions-inspector2"],
label: "quickactions-inspector2",
isVisible: () =>
lazy.DevToolsShim.isEnabled() || lazy.DevToolsShim.isDevToolsUser(),
isActive: () => {
// The inspect action is available if:
// 1. DevTools is enabled.
// 2. The user can be considered as a DevTools user.
// 3. The url is not about:devtools-toolbox.
// 4. The inspector is not opened yet on the page.
return (
lazy.DevToolsShim.isEnabled() &&
lazy.DevToolsShim.isDevToolsUser() &&
!currentBrowser()?.currentURI.spec.startsWith(
"about:devtools-toolbox"
) &&
!lazy.DevToolsShim.hasToolboxForTab(currentTab())
);
},
onPick: openInspector,
},
logins: {
l10nCommands: ["quickactions-cmd-logins", "quickactions-logins2"],
label: "quickactions-logins2",
onPick: openUrlFun("about:logins"),
},
plugins: {
l10nCommands: ["quickactions-cmd-plugins", "quickactions-plugins"],
label: "quickactions-plugins",
onPick: openAddonsUrl("addons://list/plugin"),
},
print: {
l10nCommands: ["quickactions-cmd-print", "quickactions-print2"],
label: "quickactions-print2",
onPick: () => {
lazy.BrowserWindowTracker.getTopWindow()
.document.getElementById("cmd_print")
.doCommand();
},
},
private: {
l10nCommands: ["quickactions-cmd-private", "quickactions-private2"],
label: "quickactions-private2",
onPick: () => {
lazy.BrowserWindowTracker.getTopWindow().OpenBrowserWindow({
private: true,
});
},
},
refresh: {
l10nCommands: ["quickactions-cmd-refresh", "quickactions-refresh"],
label: "quickactions-refresh",
onPick: () => {
lazy.ResetProfile.openConfirmationDialog(
lazy.BrowserWindowTracker.getTopWindow()
);
},
},
restart: {
l10nCommands: ["quickactions-cmd-restart", "quickactions-restart"],
label: "quickactions-restart",
onPick: restartBrowser,
},
savepdf: {
l10nCommands: ["quickactions-cmd-savepdf"],
label: "quickactions-savepdf",
onPick: () => {
// This writes over the users last used printer which we
// should not do. Refactor to launch the print preview with
// custom settings.
let win = lazy.BrowserWindowTracker.getTopWindow();
Cc["@mozilla.org/gfx/printsettings-service;1"]
.getService(Ci.nsIPrintSettingsService)
.maybeSaveLastUsedPrinterNameToPrefs(
win.PrintUtils.SAVE_TO_PDF_PRINTER
);
win.PrintUtils.startPrintWindow(
win.gBrowser.selectedBrowser.browsingContext,
{}
);
},
},
screenshot: {
l10nCommands: ["quickactions-cmd-screenshot", "quickactions-screenshot3"],
label: "quickactions-screenshot3",
isActive: () => {
return !lazy.BrowserWindowTracker.getTopWindow().gScreenshots.shouldScreenshotsButtonBeDisabled();
},
onPick: () => {
if (lazy.SCREENSHOT_BROWSER_COMPONENT) {
Services.obs.notifyObservers(
lazy.BrowserWindowTracker.getTopWindow(),
"menuitem-screenshot",
"quick_actions"
);
} else {
Services.obs.notifyObservers(
null,
"menuitem-screenshot-extension",
"quickaction"
);
}
return { focusContent: true };
},
},
settings: {
l10nCommands: ["quickactions-cmd-settings", "quickactions-settings2"],
label: "quickactions-settings2",
onPick: openUrlFun("about:preferences"),
},
themes: {
l10nCommands: ["quickactions-cmd-themes", "quickactions-themes"],
label: "quickactions-themes",
onPick: openAddonsUrl("addons://list/theme"),
},
update: {
l10nCommands: ["quickactions-cmd-update", "quickactions-update"],
label: "quickactions-update",
isActive: () => {
if (!AppConstants.MOZ_UPDATER) {
return false;
}
return (
lazy.AUS.currentState == Ci.nsIApplicationUpdateService.STATE_PENDING
);
},
onPick: restartBrowser,
},
viewsource: {
l10nCommands: ["quickactions-cmd-viewsource", "quickactions-viewsource2"],
label: "quickactions-viewsource2",
isActive: () => currentBrowser()?.currentURI.scheme !== "view-source",
onPick: () => openUrl("view-source:" + currentBrowser().currentURI.spec),
},
};
function openInspector() {
lazy.DevToolsShim.showToolboxForTab(
lazy.BrowserWindowTracker.getTopWindow().gBrowser.selectedTab,
{ toolId: "inspector" }
);
}
// TODO: We likely want a prompt to confirm with the user that they want to restart
// the browser.
function restartBrowser() {
// Notify all windows that an application quit has been requested.
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
Ci.nsISupportsPRBool
);
Services.obs.notifyObservers(
cancelQuit,
"quit-application-requested",
"restart"
);
// Something aborted the quit process.
if (cancelQuit.data) {
return;
}
// If already in safe mode restart in safe mode.
if (Services.appinfo.inSafeMode) {
Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
} else {
Services.startup.quit(
Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
);
}
}
function random(seed) {
let x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
function shuffle(array, seed) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(random(seed) * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
/**
* Loads the default QuickActions.
*/
export class QuickActionsLoaderDefault {
// Track the loading of the QuickActions to ensure they aren't loaded multiple times.
static #loadedPromise = null;
static async load() {
let keys = Object.keys(DEFAULT_ACTIONS);
if (lazy.UrlbarPrefs.get("quickactions.randomOrderActions")) {
// We insert the actions in a random order which means they will be returned
// in a random but consistent order (the order of results for "view" and "views"
// should be the same).
// We use the Nimbus randomizationId as the seed as the order should not change
// for the user between restarts, it should be random between users but a user should
// see actions the same order.
let seed = [...lazy.ClientEnvironment.randomizationId]
.map(x => x.charCodeAt(0))
.reduce((sum, a) => sum + a, 0);
shuffle(keys, seed);
}
for (const key of keys) {
let actionData = DEFAULT_ACTIONS[key];
let messages = await lazy.gFluentStrings.formatMessages(
actionData.l10nCommands.map(id => ({ id }))
);
actionData.commands = messages
.map(({ value }) => value.split(",").map(x => x.trim().toLowerCase()))
.flat();
lazy.UrlbarProviderQuickActions.addAction(key, actionData);
}
}
static async ensureLoaded() {
if (!this.#loadedPromise) {
this.#loadedPromise = this.load();
}
await this.#loadedPromise;
}
}