/* 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/. */ /** * Provides functions to handle status and messages in the user interface. */ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; const lazy = {}; // BrowserWindowTracker and PrivateBrowsingUtils are only used when opening downloaded files into a browser window ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", }); XPCOMUtils.defineLazyModuleGetters(lazy, { BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm", }); XPCOMUtils.defineLazyGetter( lazy, "l10n", () => new Localization(["toolkit/downloads/downloadUI.ftl"], true) ); /** * Provides functions to handle status and messages in the user interface. */ export var DownloadUIHelper = { /** * Returns an object that can be used to display prompts related to downloads. * * The prompts may be either anchored to a specified window, or anchored to * the most recently active window, for example if the prompt is displayed in * response to global notifications that are not associated with any window. * * @param aParent * If specified, should reference the nsIDOMWindow to which the prompts * should be attached. If omitted, the prompts will be attached to the * most recently active window. * * @return A DownloadPrompter object. */ getPrompter(aParent) { return new DownloadPrompter(aParent || null); }, /** * Open the given file as a file: URI in the active window * * @param nsIFile file The downloaded file * @param options.chromeWindow Optional chrome window where we could open the file URI * @param options.openWhere String indicating how to open the URI. * One of "window", "tab", "tabshifted" * @param options.isPrivate Open in private window or not * @param options.browsingContextId BrowsingContext ID of the initiating document * @param options.userContextId UserContextID of the initiating document */ loadFileIn( file, { chromeWindow: browserWin, openWhere = "tab", isPrivate, userContextId = 0, browsingContextId = 0, } = {} ) { let fileURI = Services.io.newFileURI(file); let allowPrivate = isPrivate || lazy.PrivateBrowsingUtils.permanentPrivateBrowsing; if ( !browserWin || browserWin.document.documentElement.getAttribute("windowtype") !== "navigator:browser" ) { // we'll need a private window for a private download, or if we're in private-only mode // but otherwise we want to open files in a non-private window browserWin = lazy.BrowserWindowTracker.getTopWindow({ private: allowPrivate, }); } // if there is no suitable browser window, we'll need to open one and ignore any other `openWhere` value // this can happen if the library dialog is the only open window if (!browserWin) { // There is no browser window open, so open a new one. let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); let strURI = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); strURI.data = fileURI.spec; args.appendElement(strURI); let features = "chrome,dialog=no,all"; if (isPrivate) { features += ",private"; } browserWin = Services.ww.openWindow( null, AppConstants.BROWSER_CHROME_URL, null, features, args ); return; } // a browser window will have the helpers from utilityOverlay.js let browsingContext = browserWin?.BrowsingContext.get(browsingContextId); browserWin.openTrustedLinkIn(fileURI.spec, openWhere, { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), private: isPrivate, userContextId, openerBrowser: browsingContext?.top?.embedderElement, }); }, }; /** * Allows displaying prompts related to downloads. * * @param aParent * The nsIDOMWindow to which prompts should be attached, or null to * attach prompts to the most recently active window. */ var DownloadPrompter = function (aParent) { this._prompter = Services.ww.getNewPrompter(aParent); }; DownloadPrompter.prototype = { /** * Constants with the different type of prompts. */ ON_QUIT: "prompt-on-quit", ON_OFFLINE: "prompt-on-offline", ON_LEAVE_PRIVATE_BROWSING: "prompt-on-leave-private-browsing", /** * nsIPrompt instance for displaying messages. */ _prompter: null, /** * Displays a warning message box that informs that the specified file is * executable, and asks whether the user wants to launch it. * * @param path * String containing the full path to the file to be opened. * * @resolves Boolean indicating whether the launch operation can continue. */ async confirmLaunchExecutable(path) { const kPrefSkipConfirm = "browser.download.skipConfirmLaunchExecutable"; // Always launch in case we have no prompter implementation. if (!this._prompter) { return true; } try { if (Services.prefs.getBoolPref(kPrefSkipConfirm)) { return true; } } catch (ex) { // If the preference does not exist, continue with the prompt. } const title = lazy.l10n.formatValueSync( "download-ui-file-executable-security-warning-title" ); const message = lazy.l10n.formatValueSync( "download-ui-file-executable-security-warning", { executable: PathUtils.filename(path) } ); return this._prompter.confirm(title, message); }, /** * Displays a warning message box that informs that there are active * downloads, and asks whether the user wants to cancel them or not. * * @param aDownloadsCount * The current downloads count. * @param aPromptType * The type of prompt notification depending on the observer. * * @return False to cancel the downloads and continue, true to abort the * operation. */ confirmCancelDownloads: function DP_confirmCancelDownload( aDownloadsCount, aPromptType ) { // Always continue in case we have no prompter implementation, or if there // are no active downloads. if (!this._prompter || aDownloadsCount <= 0) { return false; } let message, cancelButton; switch (aPromptType) { case this.ON_QUIT: message = AppConstants.platform == "macosx" ? "download-ui-confirm-quit-cancel-downloads-mac" : "download-ui-confirm-quit-cancel-downloads"; cancelButton = "download-ui-dont-quit-button"; break; case this.ON_OFFLINE: message = "download-ui-confirm-offline-cancel-downloads"; cancelButton = "download-ui-dont-go-offline-button"; break; case this.ON_LEAVE_PRIVATE_BROWSING: message = "download-ui-confirm-leave-private-browsing-windows-cancel-downloads"; cancelButton = "download-ui-dont-leave-private-browsing-button"; break; } const buttonFlags = Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0 + Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1; let rv = this._prompter.confirmEx( lazy.l10n.formatValueSync("download-ui-confirm-title"), lazy.l10n.formatValueSync(message, { downloadsCount: aDownloadsCount }), buttonFlags, lazy.l10n.formatValueSync("download-ui-cancel-downloads-ok", { downloadsCount: aDownloadsCount, }), lazy.l10n.formatValueSync(cancelButton), null, null, {} ); return rv == 1; }, };