QuickActionsLoaderDefault.sys.mjs (13284B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 6 7 const lazy = {}; 8 9 ChromeUtils.defineESModuleGetters(lazy, { 10 ActionsProviderQuickActions: 11 "moz-src:///browser/components/urlbar/ActionsProviderQuickActions.sys.mjs", 12 BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", 13 DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.sys.mjs", 14 ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs", 15 ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs", 16 TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs", 17 UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs", 18 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", 19 }); 20 21 ChromeUtils.defineLazyGetter(lazy, "logger", () => 22 lazy.UrlbarUtils.getLogger({ prefix: "QuickActions" }) 23 ); 24 25 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 26 27 if (AppConstants.MOZ_UPDATER) { 28 XPCOMUtils.defineLazyServiceGetter( 29 lazy, 30 "AUS", 31 "@mozilla.org/updates/update-service;1", 32 Ci.nsIApplicationUpdateService 33 ); 34 } 35 36 let openUrlFun = url => () => openUrl(url); 37 let openUrl = url => { 38 let window = lazy.BrowserWindowTracker.getTopWindow({ 39 allowFromInactiveWorkspace: true, 40 }); 41 42 if (url.startsWith("about:")) { 43 window.switchToTabHavingURI(Services.io.newURI(url), true, { 44 ignoreFragment: "whenComparing", 45 }); 46 } else { 47 window.gBrowser.addTab(url, { 48 inBackground: false, 49 triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), 50 }); 51 } 52 return { focusContent: true }; 53 }; 54 55 let openAddonsUrl = url => { 56 return () => { 57 // bug 1983835 - should this only look for windows on the current 58 // workspace? 59 let window = lazy.BrowserWindowTracker.getTopWindow({ 60 allowFromInactiveWorkspace: true, 61 }); 62 window.BrowserAddonUI.openAddonsMgr(url, { selectTabByViewId: true }); 63 }; 64 }; 65 66 // bug 1983835 - should this only look for windows on the current 67 // workspace? 68 let currentBrowser = () => 69 lazy.BrowserWindowTracker.getTopWindow({ allowFromInactiveWorkspace: true }) 70 ?.gBrowser.selectedBrowser; 71 // bug 1983835 - should this only look for windows on the current 72 // workspace? 73 let currentTab = () => 74 lazy.BrowserWindowTracker.getTopWindow({ allowFromInactiveWorkspace: true }) 75 ?.gBrowser.selectedTab; 76 77 ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () { 78 return new Localization( 79 [ 80 "branding/brand.ftl", 81 "browser/browser.ftl", 82 "toolkit/branding/brandings.ftl", 83 ], 84 true 85 ); 86 }); 87 88 const DEFAULT_ACTIONS = { 89 addons: { 90 l10nCommands: ["quickactions-cmd-addons3"], 91 icon: "chrome://mozapps/skin/extensions/category-extensions.svg", 92 label: "quickactions-addons", 93 onPick: openAddonsUrl("addons://discover/"), 94 // Hide in base-browser, since we don't want to open extensions 95 // "recommendations" tab. tor-browser#43864. 96 disabled: () => true, 97 }, 98 bookmarks: { 99 l10nCommands: ["quickactions-cmd-bookmarks", "quickactions-bookmarks2"], 100 icon: "chrome://browser/skin/bookmark.svg", 101 label: "quickactions-bookmarks2", 102 onPick: () => { 103 lazy.BrowserWindowTracker.getTopWindow({ 104 allowFromInactiveWorkspace: true, 105 }).top.PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar"); 106 }, 107 }, 108 clear: { 109 l10nCommands: [ 110 "quickactions-cmd-clearrecenthistory", 111 "quickactions-clearrecenthistory", 112 ], 113 label: "quickactions-clearrecenthistory", 114 onPick: () => { 115 lazy.BrowserWindowTracker.getTopWindow({ 116 allowFromInactiveWorkspace: true, 117 }) 118 .document.getElementById("Tools:Sanitize") 119 .doCommand(); 120 }, 121 // Disable in permanent private browsing. tor-browser#43864. 122 // NOTE: This should also be disabled in private windows, but we don't have 123 // access to a Window element to check. See mozilla bug 1980912. 124 disabled: () => { 125 return lazy.PrivateBrowsingUtils.permanentPrivateBrowsing; 126 }, 127 }, 128 downloads: { 129 l10nCommands: ["quickactions-cmd-downloads"], 130 icon: "chrome://browser/skin/downloads/downloads.svg", 131 label: "quickactions-downloads2", 132 onPick: openUrlFun("about:downloads"), 133 }, 134 extensions: { 135 l10nCommands: ["quickactions-cmd-extensions2"], 136 icon: "chrome://mozapps/skin/extensions/category-extensions.svg", 137 label: "quickactions-extensions", 138 onPick: openAddonsUrl("addons://list/extension"), 139 // Hide in base-browser since we do not want to encourage users to change 140 // their extensions/addons. tor-browser#43864. 141 disabled: () => true, 142 }, 143 help: { 144 l10nCommands: ["quickactions-cmd-help"], 145 icon: "chrome://global/skin/icons/help.svg", 146 label: "quickactions-help", 147 // Open the base-browser support/help page, rather than Firefox's. 148 // tor-browser#43864. 149 onPick: openUrlFun( 150 Services.prefs.getStringPref("browser.base-browser-support-url", "") 151 ), 152 }, 153 firefoxview: { 154 l10nCommands: ["quickactions-cmd-firefoxview"], 155 icon: "chrome://browser/skin/firefox-view.svg", 156 label: "quickactions-firefoxview", 157 onPick: () => { 158 lazy.BrowserWindowTracker.getTopWindow({ 159 allowFromInactiveWorkspace: true, 160 }).FirefoxViewHandler.openTab(); 161 }, 162 // Hide in base-browser since firefoxview is disabled. 163 // tor-browser#43864 and tor-browser#42037. 164 disabled: () => true, 165 }, 166 inspect: { 167 l10nCommands: ["quickactions-cmd-inspector2"], 168 icon: "chrome://devtools/skin/images/open-inspector.svg", 169 label: "quickactions-inspector2", 170 isVisible: () => { 171 // The inspect action is available if: 172 // 1. DevTools is enabled. 173 // 2. The user can be considered as a DevTools user. 174 // 3. The url is not about:devtools-toolbox. 175 // 4. The inspector is not opened yet on the page. 176 return ( 177 lazy.DevToolsShim.isEnabled() && 178 lazy.DevToolsShim.isDevToolsUser() && 179 !currentBrowser()?.currentURI.spec.startsWith( 180 "about:devtools-toolbox" 181 ) && 182 !lazy.DevToolsShim.hasToolboxForTab(currentTab()) 183 ); 184 }, 185 onPick: openInspector, 186 }, 187 logins: { 188 l10nCommands: ["quickactions-cmd-logins"], 189 label: "quickactions-logins2", 190 onPick: openUrlFun("about:logins"), 191 // Disabled in base browser since saved passwords is not well supported in 192 // Tor Browser, and should be disabled in Mullvad Browser. 193 // tor-browser#44177. 194 disabled: () => true, 195 }, 196 print: { 197 l10nCommands: ["quickactions-cmd-print"], 198 label: "quickactions-print2", 199 icon: "chrome://global/skin/icons/print.svg", 200 isVisible: () => { 201 return Services.prefs.getBoolPref("print.enabled"); 202 }, 203 onPick: () => { 204 lazy.BrowserWindowTracker.getTopWindow({ 205 allowFromInactiveWorkspace: true, 206 }) 207 .document.getElementById("cmd_print") 208 .doCommand(); 209 }, 210 }, 211 private: { 212 l10nCommands: ["quickactions-cmd-private"], 213 label: "quickactions-private2", 214 icon: "chrome://global/skin/icons/indicator-private-browsing.svg", 215 onPick: () => { 216 lazy.BrowserWindowTracker.getTopWindow({ 217 allowFromInactiveWorkspace: true, 218 }).OpenBrowserWindow({ 219 private: true, 220 }); 221 }, 222 // Disable in permanent private browsing. tor-browser#44177. 223 disabled: () => { 224 return lazy.PrivateBrowsingUtils.permanentPrivateBrowsing; 225 }, 226 }, 227 refresh: { 228 l10nCommands: ["quickactions-cmd-refresh"], 229 label: "quickactions-refresh", 230 isVisible: () => lazy.ResetProfile.resetSupported(), 231 onPick: () => { 232 lazy.ResetProfile.openConfirmationDialog( 233 lazy.BrowserWindowTracker.getTopWindow({ 234 allowFromInactiveWorkspace: true, 235 }) 236 ); 237 }, 238 }, 239 restart: { 240 l10nCommands: ["quickactions-cmd-restart"], 241 label: "quickactions-restart", 242 onPick: restartBrowser, 243 }, 244 savepdf: { 245 l10nCommands: ["quickactions-cmd-savepdf2"], 246 label: "quickactions-savepdf", 247 icon: "chrome://global/skin/icons/print.svg", 248 isVisible: () => { 249 return Services.prefs.getBoolPref("print.enabled"); 250 }, 251 onPick: () => { 252 // This writes over the users last used printer which we 253 // should not do. Refactor to launch the print preview with 254 // custom settings. 255 let win = lazy.BrowserWindowTracker.getTopWindow({ 256 allowFromInactiveWorkspace: true, 257 }); 258 Cc["@mozilla.org/gfx/printsettings-service;1"] 259 .getService(Ci.nsIPrintSettingsService) 260 .maybeSaveLastUsedPrinterNameToPrefs( 261 win.PrintUtils.SAVE_TO_PDF_PRINTER 262 ); 263 win.PrintUtils.startPrintWindow( 264 win.gBrowser.selectedBrowser.browsingContext, 265 {} 266 ); 267 }, 268 }, 269 screenshot: { 270 l10nCommands: ["quickactions-cmd-screenshot2"], 271 label: "quickactions-screenshot3", 272 icon: "chrome://browser/skin/screenshot.svg", 273 isVisible: () => { 274 return lazy.ScreenshotsUtils.screenshotsEnabled; 275 }, 276 onPick: () => { 277 Services.obs.notifyObservers( 278 lazy.BrowserWindowTracker.getTopWindow({ 279 allowFromInactiveWorkspace: true, 280 }), 281 "menuitem-screenshot", 282 "QuickActions" 283 ); 284 return { focusContent: true }; 285 }, 286 }, 287 settings: { 288 l10nCommands: ["quickactions-cmd-settings2"], 289 icon: "chrome://global/skin/icons/settings.svg", 290 label: "quickactions-settings2", 291 onPick: openUrlFun("about:preferences"), 292 }, 293 themes: { 294 l10nCommands: ["quickactions-cmd-themes2"], 295 icon: "chrome://mozapps/skin/extensions/category-extensions.svg", 296 label: "quickactions-themes", 297 onPick: openAddonsUrl("addons://list/theme"), 298 }, 299 translate: { 300 l10nCommands: ["quickactions-cmd-translate"], 301 icon: "chrome://browser/skin/translations.svg", 302 label: "quickactions-translate", 303 isVisible: () => { 304 return Services.prefs.getBoolPref( 305 "browser.translations.quickAction.enabled", 306 false 307 ); 308 }, 309 onPick: async () => { 310 let url = "about:translations"; 311 let targetLanguage; 312 313 try { 314 targetLanguage = 315 await lazy.TranslationsParent.getTopPreferredSupportedToLang(); 316 } catch (error) { 317 lazy.logger.error(error); 318 } 319 320 if (targetLanguage) { 321 const urlObj = new URL(url); 322 const params = new URLSearchParams(); 323 params.set("trg", targetLanguage); 324 urlObj.hash = params.toString(); 325 url = urlObj.href; 326 } 327 328 return openUrl(url); 329 }, 330 }, 331 update: { 332 l10nCommands: ["quickactions-cmd-update"], 333 label: "quickactions-update", 334 isVisible: () => { 335 if (!AppConstants.MOZ_UPDATER) { 336 return false; 337 } 338 return ( 339 lazy.AUS.currentState == Ci.nsIApplicationUpdateService.STATE_PENDING 340 ); 341 }, 342 onPick: restartBrowser, 343 }, 344 viewsource: { 345 l10nCommands: ["quickactions-cmd-viewsource2"], 346 icon: "chrome://global/skin/icons/settings.svg", 347 label: "quickactions-viewsource2", 348 isVisible: () => currentBrowser()?.currentURI.scheme !== "view-source", 349 onPick: () => openUrl("view-source:" + currentBrowser().currentURI.spec), 350 }, 351 }; 352 353 function openInspector() { 354 lazy.DevToolsShim.showToolboxForTab( 355 lazy.BrowserWindowTracker.getTopWindow({ allowFromInactiveWorkspace: true }) 356 .gBrowser.selectedTab, 357 { toolId: "inspector" } 358 ); 359 } 360 361 // TODO: We likely want a prompt to confirm with the user that they want to restart 362 // the browser. 363 function restartBrowser() { 364 // Notify all windows that an application quit has been requested. 365 let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance( 366 Ci.nsISupportsPRBool 367 ); 368 Services.obs.notifyObservers( 369 cancelQuit, 370 "quit-application-requested", 371 "restart" 372 ); 373 // Something aborted the quit process. 374 if (cancelQuit.data) { 375 return; 376 } 377 // If already in safe mode restart in safe mode. 378 if (Services.appinfo.inSafeMode) { 379 Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit); 380 } else { 381 Services.startup.quit( 382 Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart 383 ); 384 } 385 } 386 387 /** 388 * Loads the default QuickActions. 389 */ 390 export class QuickActionsLoaderDefault { 391 // Track the loading of the QuickActions to ensure they aren't loaded multiple times. 392 static #loadedPromise = null; 393 394 static async load() { 395 let keys = Object.keys(DEFAULT_ACTIONS); 396 for (const key of keys) { 397 let actionData = DEFAULT_ACTIONS[key]; 398 if (actionData.disabled?.()) { 399 continue; 400 } 401 let messages = await lazy.gFluentStrings.formatMessages( 402 actionData.l10nCommands.map(id => ({ id })) 403 ); 404 actionData.commands = messages 405 .map(({ value }) => value.split(",").map(x => x.trim().toLowerCase())) 406 .flat(); 407 lazy.ActionsProviderQuickActions.addAction(key, actionData); 408 } 409 } 410 static async ensureLoaded() { 411 if (!this.#loadedPromise) { 412 this.#loadedPromise = this.load(); 413 } 414 await this.#loadedPromise; 415 } 416 }