|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /** |
|
8 * Provides functions to handle status and messages in the user interface. |
|
9 */ |
|
10 |
|
11 "use strict"; |
|
12 |
|
13 this.EXPORTED_SYMBOLS = [ |
|
14 "DownloadUIHelper", |
|
15 ]; |
|
16 |
|
17 //////////////////////////////////////////////////////////////////////////////// |
|
18 //// Globals |
|
19 |
|
20 const Cc = Components.classes; |
|
21 const Ci = Components.interfaces; |
|
22 const Cu = Components.utils; |
|
23 const Cr = Components.results; |
|
24 |
|
25 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
26 |
|
27 XPCOMUtils.defineLazyModuleGetter(this, "OS", |
|
28 "resource://gre/modules/osfile.jsm"); |
|
29 XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
|
30 "resource://gre/modules/Promise.jsm"); |
|
31 XPCOMUtils.defineLazyModuleGetter(this, "Services", |
|
32 "resource://gre/modules/Services.jsm"); |
|
33 |
|
34 const kStringBundleUrl = |
|
35 "chrome://mozapps/locale/downloads/downloads.properties"; |
|
36 |
|
37 const kStringsRequiringFormatting = { |
|
38 fileExecutableSecurityWarning: true, |
|
39 cancelDownloadsOKTextMultiple: true, |
|
40 quitCancelDownloadsAlertMsgMultiple: true, |
|
41 quitCancelDownloadsAlertMsgMacMultiple: true, |
|
42 offlineCancelDownloadsAlertMsgMultiple: true, |
|
43 leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple: true |
|
44 }; |
|
45 |
|
46 //////////////////////////////////////////////////////////////////////////////// |
|
47 //// DownloadUIHelper |
|
48 |
|
49 /** |
|
50 * Provides functions to handle status and messages in the user interface. |
|
51 */ |
|
52 this.DownloadUIHelper = { |
|
53 /** |
|
54 * Returns an object that can be used to display prompts related to downloads. |
|
55 * |
|
56 * The prompts may be either anchored to a specified window, or anchored to |
|
57 * the most recently active window, for example if the prompt is displayed in |
|
58 * response to global notifications that are not associated with any window. |
|
59 * |
|
60 * @param aParent |
|
61 * If specified, should reference the nsIDOMWindow to which the prompts |
|
62 * should be attached. If omitted, the prompts will be attached to the |
|
63 * most recently active window. |
|
64 * |
|
65 * @return A DownloadPrompter object. |
|
66 */ |
|
67 getPrompter: function (aParent) |
|
68 { |
|
69 return new DownloadPrompter(aParent || null); |
|
70 }, |
|
71 }; |
|
72 |
|
73 /** |
|
74 * Returns an object whose keys are the string names from the downloads string |
|
75 * bundle, and whose values are either the translated strings or functions |
|
76 * returning formatted strings. |
|
77 */ |
|
78 XPCOMUtils.defineLazyGetter(DownloadUIHelper, "strings", function () { |
|
79 let strings = {}; |
|
80 let sb = Services.strings.createBundle(kStringBundleUrl); |
|
81 let enumerator = sb.getSimpleEnumeration(); |
|
82 while (enumerator.hasMoreElements()) { |
|
83 let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); |
|
84 let stringName = string.key; |
|
85 if (stringName in kStringsRequiringFormatting) { |
|
86 strings[stringName] = function () { |
|
87 // Convert "arguments" to a real array before calling into XPCOM. |
|
88 return sb.formatStringFromName(stringName, |
|
89 Array.slice(arguments, 0), |
|
90 arguments.length); |
|
91 }; |
|
92 } else { |
|
93 strings[stringName] = string.value; |
|
94 } |
|
95 } |
|
96 return strings; |
|
97 }); |
|
98 |
|
99 //////////////////////////////////////////////////////////////////////////////// |
|
100 //// DownloadPrompter |
|
101 |
|
102 /** |
|
103 * Allows displaying prompts related to downloads. |
|
104 * |
|
105 * @param aParent |
|
106 * The nsIDOMWindow to which prompts should be attached, or null to |
|
107 * attach prompts to the most recently active window. |
|
108 */ |
|
109 this.DownloadPrompter = function (aParent) |
|
110 { |
|
111 #ifdef MOZ_B2G |
|
112 // On B2G there is no prompter implementation. |
|
113 this._prompter = null; |
|
114 #else |
|
115 this._prompter = Services.ww.getNewPrompter(aParent); |
|
116 #endif |
|
117 } |
|
118 |
|
119 this.DownloadPrompter.prototype = { |
|
120 /** |
|
121 * Constants with the different type of prompts. |
|
122 */ |
|
123 ON_QUIT: "prompt-on-quit", |
|
124 ON_OFFLINE: "prompt-on-offline", |
|
125 ON_LEAVE_PRIVATE_BROWSING: "prompt-on-leave-private-browsing", |
|
126 |
|
127 /** |
|
128 * nsIPrompt instance for displaying messages. |
|
129 */ |
|
130 _prompter: null, |
|
131 |
|
132 /** |
|
133 * Displays a warning message box that informs that the specified file is |
|
134 * executable, and asks whether the user wants to launch it. The user is |
|
135 * given the option of disabling future instances of this warning. |
|
136 * |
|
137 * @param aPath |
|
138 * String containing the full path to the file to be opened. |
|
139 * |
|
140 * @return {Promise} |
|
141 * @resolves Boolean indicating whether the launch operation can continue. |
|
142 * @rejects JavaScript exception. |
|
143 */ |
|
144 confirmLaunchExecutable: function (aPath) |
|
145 { |
|
146 const kPrefAlertOnEXEOpen = "browser.download.manager.alertOnEXEOpen"; |
|
147 |
|
148 try { |
|
149 // Always launch in case we have no prompter implementation. |
|
150 if (!this._prompter) { |
|
151 return Promise.resolve(true); |
|
152 } |
|
153 |
|
154 try { |
|
155 if (!Services.prefs.getBoolPref(kPrefAlertOnEXEOpen)) { |
|
156 return Promise.resolve(true); |
|
157 } |
|
158 } catch (ex) { |
|
159 // If the preference does not exist, continue with the prompt. |
|
160 } |
|
161 |
|
162 let leafName = OS.Path.basename(aPath); |
|
163 |
|
164 let s = DownloadUIHelper.strings; |
|
165 let checkState = { value: false }; |
|
166 let shouldLaunch = this._prompter.confirmCheck( |
|
167 s.fileExecutableSecurityWarningTitle, |
|
168 s.fileExecutableSecurityWarning(leafName, leafName), |
|
169 s.fileExecutableSecurityWarningDontAsk, |
|
170 checkState); |
|
171 |
|
172 if (shouldLaunch) { |
|
173 Services.prefs.setBoolPref(kPrefAlertOnEXEOpen, !checkState.value); |
|
174 } |
|
175 |
|
176 return Promise.resolve(shouldLaunch); |
|
177 } catch (ex) { |
|
178 return Promise.reject(ex); |
|
179 } |
|
180 }, |
|
181 |
|
182 /** |
|
183 * Displays a warning message box that informs that there are active |
|
184 * downloads, and asks whether the user wants to cancel them or not. |
|
185 * |
|
186 * @param aDownloadsCount |
|
187 * The current downloads count. |
|
188 * @param aPromptType |
|
189 * The type of prompt notification depending on the observer. |
|
190 * |
|
191 * @return False to cancel the downloads and continue, true to abort the |
|
192 * operation. |
|
193 */ |
|
194 confirmCancelDownloads: function DP_confirmCancelDownload(aDownloadsCount, |
|
195 aPromptType) |
|
196 { |
|
197 // Always continue in case we have no prompter implementation, or if there |
|
198 // are no active downloads. |
|
199 if (!this._prompter || aDownloadsCount <= 0) { |
|
200 return false; |
|
201 } |
|
202 |
|
203 let s = DownloadUIHelper.strings; |
|
204 let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) + |
|
205 (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1); |
|
206 let okButton = aDownloadsCount > 1 ? s.cancelDownloadsOKTextMultiple(aDownloadsCount) |
|
207 : s.cancelDownloadsOKText; |
|
208 let title, message, cancelButton; |
|
209 |
|
210 switch (aPromptType) { |
|
211 case this.ON_QUIT: |
|
212 title = s.quitCancelDownloadsAlertTitle; |
|
213 #ifndef XP_MACOSX |
|
214 message = aDownloadsCount > 1 |
|
215 ? s.quitCancelDownloadsAlertMsgMultiple(aDownloadsCount) |
|
216 : s.quitCancelDownloadsAlertMsg; |
|
217 cancelButton = s.dontQuitButtonWin; |
|
218 #else |
|
219 message = aDownloadsCount > 1 |
|
220 ? s.quitCancelDownloadsAlertMsgMacMultiple(aDownloadsCount) |
|
221 : s.quitCancelDownloadsAlertMsgMac; |
|
222 cancelButton = s.dontQuitButtonMac; |
|
223 #endif |
|
224 break; |
|
225 case this.ON_OFFLINE: |
|
226 title = s.offlineCancelDownloadsAlertTitle; |
|
227 message = aDownloadsCount > 1 |
|
228 ? s.offlineCancelDownloadsAlertMsgMultiple(aDownloadsCount) |
|
229 : s.offlineCancelDownloadsAlertMsg; |
|
230 cancelButton = s.dontGoOfflineButton; |
|
231 break; |
|
232 case this.ON_LEAVE_PRIVATE_BROWSING: |
|
233 title = s.leavePrivateBrowsingCancelDownloadsAlertTitle; |
|
234 message = aDownloadsCount > 1 |
|
235 ? s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple(aDownloadsCount) |
|
236 : s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsg; |
|
237 cancelButton = s.dontLeavePrivateBrowsingButton; |
|
238 break; |
|
239 } |
|
240 |
|
241 let rv = this._prompter.confirmEx(title, message, buttonFlags, okButton, |
|
242 cancelButton, null, null, {}); |
|
243 return (rv == 1); |
|
244 } |
|
245 }; |