michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Provides functions to handle status and messages in the user interface. michael@0: */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = [ michael@0: "DownloadUIHelper", michael@0: ]; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Globals michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: const Cr = Components.results; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "OS", michael@0: "resource://gre/modules/osfile.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Promise", michael@0: "resource://gre/modules/Promise.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Services", michael@0: "resource://gre/modules/Services.jsm"); michael@0: michael@0: const kStringBundleUrl = michael@0: "chrome://mozapps/locale/downloads/downloads.properties"; michael@0: michael@0: const kStringsRequiringFormatting = { michael@0: fileExecutableSecurityWarning: true, michael@0: cancelDownloadsOKTextMultiple: true, michael@0: quitCancelDownloadsAlertMsgMultiple: true, michael@0: quitCancelDownloadsAlertMsgMacMultiple: true, michael@0: offlineCancelDownloadsAlertMsgMultiple: true, michael@0: leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple: true michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// DownloadUIHelper michael@0: michael@0: /** michael@0: * Provides functions to handle status and messages in the user interface. michael@0: */ michael@0: this.DownloadUIHelper = { michael@0: /** michael@0: * Returns an object that can be used to display prompts related to downloads. michael@0: * michael@0: * The prompts may be either anchored to a specified window, or anchored to michael@0: * the most recently active window, for example if the prompt is displayed in michael@0: * response to global notifications that are not associated with any window. michael@0: * michael@0: * @param aParent michael@0: * If specified, should reference the nsIDOMWindow to which the prompts michael@0: * should be attached. If omitted, the prompts will be attached to the michael@0: * most recently active window. michael@0: * michael@0: * @return A DownloadPrompter object. michael@0: */ michael@0: getPrompter: function (aParent) michael@0: { michael@0: return new DownloadPrompter(aParent || null); michael@0: }, michael@0: }; michael@0: michael@0: /** michael@0: * Returns an object whose keys are the string names from the downloads string michael@0: * bundle, and whose values are either the translated strings or functions michael@0: * returning formatted strings. michael@0: */ michael@0: XPCOMUtils.defineLazyGetter(DownloadUIHelper, "strings", function () { michael@0: let strings = {}; michael@0: let sb = Services.strings.createBundle(kStringBundleUrl); michael@0: let enumerator = sb.getSimpleEnumeration(); michael@0: while (enumerator.hasMoreElements()) { michael@0: let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); michael@0: let stringName = string.key; michael@0: if (stringName in kStringsRequiringFormatting) { michael@0: strings[stringName] = function () { michael@0: // Convert "arguments" to a real array before calling into XPCOM. michael@0: return sb.formatStringFromName(stringName, michael@0: Array.slice(arguments, 0), michael@0: arguments.length); michael@0: }; michael@0: } else { michael@0: strings[stringName] = string.value; michael@0: } michael@0: } michael@0: return strings; michael@0: }); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// DownloadPrompter michael@0: michael@0: /** michael@0: * Allows displaying prompts related to downloads. michael@0: * michael@0: * @param aParent michael@0: * The nsIDOMWindow to which prompts should be attached, or null to michael@0: * attach prompts to the most recently active window. michael@0: */ michael@0: this.DownloadPrompter = function (aParent) michael@0: { michael@0: #ifdef MOZ_B2G michael@0: // On B2G there is no prompter implementation. michael@0: this._prompter = null; michael@0: #else michael@0: this._prompter = Services.ww.getNewPrompter(aParent); michael@0: #endif michael@0: } michael@0: michael@0: this.DownloadPrompter.prototype = { michael@0: /** michael@0: * Constants with the different type of prompts. michael@0: */ michael@0: ON_QUIT: "prompt-on-quit", michael@0: ON_OFFLINE: "prompt-on-offline", michael@0: ON_LEAVE_PRIVATE_BROWSING: "prompt-on-leave-private-browsing", michael@0: michael@0: /** michael@0: * nsIPrompt instance for displaying messages. michael@0: */ michael@0: _prompter: null, michael@0: michael@0: /** michael@0: * Displays a warning message box that informs that the specified file is michael@0: * executable, and asks whether the user wants to launch it. The user is michael@0: * given the option of disabling future instances of this warning. michael@0: * michael@0: * @param aPath michael@0: * String containing the full path to the file to be opened. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves Boolean indicating whether the launch operation can continue. michael@0: * @rejects JavaScript exception. michael@0: */ michael@0: confirmLaunchExecutable: function (aPath) michael@0: { michael@0: const kPrefAlertOnEXEOpen = "browser.download.manager.alertOnEXEOpen"; michael@0: michael@0: try { michael@0: // Always launch in case we have no prompter implementation. michael@0: if (!this._prompter) { michael@0: return Promise.resolve(true); michael@0: } michael@0: michael@0: try { michael@0: if (!Services.prefs.getBoolPref(kPrefAlertOnEXEOpen)) { michael@0: return Promise.resolve(true); michael@0: } michael@0: } catch (ex) { michael@0: // If the preference does not exist, continue with the prompt. michael@0: } michael@0: michael@0: let leafName = OS.Path.basename(aPath); michael@0: michael@0: let s = DownloadUIHelper.strings; michael@0: let checkState = { value: false }; michael@0: let shouldLaunch = this._prompter.confirmCheck( michael@0: s.fileExecutableSecurityWarningTitle, michael@0: s.fileExecutableSecurityWarning(leafName, leafName), michael@0: s.fileExecutableSecurityWarningDontAsk, michael@0: checkState); michael@0: michael@0: if (shouldLaunch) { michael@0: Services.prefs.setBoolPref(kPrefAlertOnEXEOpen, !checkState.value); michael@0: } michael@0: michael@0: return Promise.resolve(shouldLaunch); michael@0: } catch (ex) { michael@0: return Promise.reject(ex); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Displays a warning message box that informs that there are active michael@0: * downloads, and asks whether the user wants to cancel them or not. michael@0: * michael@0: * @param aDownloadsCount michael@0: * The current downloads count. michael@0: * @param aPromptType michael@0: * The type of prompt notification depending on the observer. michael@0: * michael@0: * @return False to cancel the downloads and continue, true to abort the michael@0: * operation. michael@0: */ michael@0: confirmCancelDownloads: function DP_confirmCancelDownload(aDownloadsCount, michael@0: aPromptType) michael@0: { michael@0: // Always continue in case we have no prompter implementation, or if there michael@0: // are no active downloads. michael@0: if (!this._prompter || aDownloadsCount <= 0) { michael@0: return false; michael@0: } michael@0: michael@0: let s = DownloadUIHelper.strings; michael@0: let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) + michael@0: (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1); michael@0: let okButton = aDownloadsCount > 1 ? s.cancelDownloadsOKTextMultiple(aDownloadsCount) michael@0: : s.cancelDownloadsOKText; michael@0: let title, message, cancelButton; michael@0: michael@0: switch (aPromptType) { michael@0: case this.ON_QUIT: michael@0: title = s.quitCancelDownloadsAlertTitle; michael@0: #ifndef XP_MACOSX michael@0: message = aDownloadsCount > 1 michael@0: ? s.quitCancelDownloadsAlertMsgMultiple(aDownloadsCount) michael@0: : s.quitCancelDownloadsAlertMsg; michael@0: cancelButton = s.dontQuitButtonWin; michael@0: #else michael@0: message = aDownloadsCount > 1 michael@0: ? s.quitCancelDownloadsAlertMsgMacMultiple(aDownloadsCount) michael@0: : s.quitCancelDownloadsAlertMsgMac; michael@0: cancelButton = s.dontQuitButtonMac; michael@0: #endif michael@0: break; michael@0: case this.ON_OFFLINE: michael@0: title = s.offlineCancelDownloadsAlertTitle; michael@0: message = aDownloadsCount > 1 michael@0: ? s.offlineCancelDownloadsAlertMsgMultiple(aDownloadsCount) michael@0: : s.offlineCancelDownloadsAlertMsg; michael@0: cancelButton = s.dontGoOfflineButton; michael@0: break; michael@0: case this.ON_LEAVE_PRIVATE_BROWSING: michael@0: title = s.leavePrivateBrowsingCancelDownloadsAlertTitle; michael@0: message = aDownloadsCount > 1 michael@0: ? s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple(aDownloadsCount) michael@0: : s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsg; michael@0: cancelButton = s.dontLeavePrivateBrowsingButton; michael@0: break; michael@0: } michael@0: michael@0: let rv = this._prompter.confirmEx(title, message, buttonFlags, okButton, michael@0: cancelButton, null, null, {}); michael@0: return (rv == 1); michael@0: } michael@0: };