|
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 |
|
6 const Cc = Components.classes; |
|
7 const Ci = Components.interfaces; |
|
8 const Cr = Components.results; |
|
9 const Cu = Components.utils; |
|
10 |
|
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
12 Cu.import("resource://gre/modules/Services.jsm"); |
|
13 Cu.import("resource://gre/modules/SharedPromptUtils.jsm"); |
|
14 |
|
15 function Prompter() { |
|
16 // Note that EmbedPrompter clones this implementation. |
|
17 } |
|
18 |
|
19 Prompter.prototype = { |
|
20 classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"), |
|
21 QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), |
|
22 |
|
23 |
|
24 /* ---------- private memebers ---------- */ |
|
25 |
|
26 pickPrompter : function (domWin) { |
|
27 return new ModalPrompter(domWin); |
|
28 }, |
|
29 |
|
30 |
|
31 /* ---------- nsIPromptFactory ---------- */ |
|
32 |
|
33 |
|
34 getPrompt : function (domWin, iid) { |
|
35 // This is still kind of dumb; the C++ code delegated to login manager |
|
36 // here, which in turn calls back into us via nsIPromptService2. |
|
37 if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) { |
|
38 try { |
|
39 let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"]. |
|
40 getService(Ci.nsIPromptFactory); |
|
41 return pwmgr.getPrompt(domWin, iid); |
|
42 } catch (e) { |
|
43 Cu.reportError("nsPrompter: Delegation to password manager failed: " + e); |
|
44 } |
|
45 } |
|
46 |
|
47 let p = new ModalPrompter(domWin); |
|
48 p.QueryInterface(iid); |
|
49 return p; |
|
50 }, |
|
51 |
|
52 |
|
53 /* ---------- nsIPromptService ---------- */ |
|
54 |
|
55 |
|
56 alert : function (domWin, title, text) { |
|
57 let p = this.pickPrompter(domWin); |
|
58 p.alert(title, text); |
|
59 }, |
|
60 |
|
61 alertCheck : function (domWin, title, text, checkLabel, checkValue) { |
|
62 let p = this.pickPrompter(domWin); |
|
63 p.alertCheck(title, text, checkLabel, checkValue); |
|
64 }, |
|
65 |
|
66 confirm : function (domWin, title, text) { |
|
67 let p = this.pickPrompter(domWin); |
|
68 return p.confirm(title, text); |
|
69 }, |
|
70 |
|
71 confirmCheck : function (domWin, title, text, checkLabel, checkValue) { |
|
72 let p = this.pickPrompter(domWin); |
|
73 return p.confirmCheck(title, text, checkLabel, checkValue); |
|
74 }, |
|
75 |
|
76 confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) { |
|
77 let p = this.pickPrompter(domWin); |
|
78 return p.confirmEx(title, text, flags, button0, button1, button2, checkLabel, checkValue); |
|
79 }, |
|
80 |
|
81 prompt : function (domWin, title, text, value, checkLabel, checkValue) { |
|
82 let p = this.pickPrompter(domWin); |
|
83 return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue); |
|
84 }, |
|
85 |
|
86 promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) { |
|
87 let p = this.pickPrompter(domWin); |
|
88 return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue); |
|
89 }, |
|
90 |
|
91 promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) { |
|
92 let p = this.pickPrompter(domWin); |
|
93 return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue); |
|
94 }, |
|
95 |
|
96 select : function (domWin, title, text, count, list, selected) { |
|
97 let p = this.pickPrompter(domWin); |
|
98 return p.select(title, text, count, list, selected); |
|
99 }, |
|
100 |
|
101 |
|
102 /* ---------- nsIPromptService2 ---------- */ |
|
103 |
|
104 |
|
105 promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) { |
|
106 let p = this.pickPrompter(domWin); |
|
107 return p.promptAuth(channel, level, authInfo, checkLabel, checkValue); |
|
108 }, |
|
109 |
|
110 asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) { |
|
111 let p = this.pickPrompter(domWin); |
|
112 return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue); |
|
113 }, |
|
114 |
|
115 }; |
|
116 |
|
117 |
|
118 // Common utils not specific to a particular prompter style. |
|
119 let PromptUtilsTemp = { |
|
120 __proto__ : PromptUtils, |
|
121 |
|
122 getLocalizedString : function (key, formatArgs) { |
|
123 if (formatArgs) |
|
124 return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length); |
|
125 else |
|
126 return this.strBundle.GetStringFromName(key); |
|
127 }, |
|
128 |
|
129 confirmExHelper : function (flags, button0, button1, button2) { |
|
130 const BUTTON_DEFAULT_MASK = 0x03000000; |
|
131 let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24; |
|
132 let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE); |
|
133 |
|
134 // Flags can be used to select a specific pre-defined button label or |
|
135 // a caller-supplied string (button0/button1/button2). If no flags are |
|
136 // set for a button, then the button won't be shown. |
|
137 let argText = [button0, button1, button2]; |
|
138 let buttonLabels = [null, null, null]; |
|
139 for (let i = 0; i < 3; i++) { |
|
140 let buttonLabel; |
|
141 switch (flags & 0xff) { |
|
142 case Ci.nsIPrompt.BUTTON_TITLE_OK: |
|
143 buttonLabel = PromptUtils.getLocalizedString("OK"); |
|
144 break; |
|
145 case Ci.nsIPrompt.BUTTON_TITLE_CANCEL: |
|
146 buttonLabel = PromptUtils.getLocalizedString("Cancel"); |
|
147 break; |
|
148 case Ci.nsIPrompt.BUTTON_TITLE_YES: |
|
149 buttonLabel = PromptUtils.getLocalizedString("Yes"); |
|
150 break; |
|
151 case Ci.nsIPrompt.BUTTON_TITLE_NO: |
|
152 buttonLabel = PromptUtils.getLocalizedString("No"); |
|
153 break; |
|
154 case Ci.nsIPrompt.BUTTON_TITLE_SAVE: |
|
155 buttonLabel = PromptUtils.getLocalizedString("Save"); |
|
156 break; |
|
157 case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE: |
|
158 buttonLabel = PromptUtils.getLocalizedString("DontSave"); |
|
159 break; |
|
160 case Ci.nsIPrompt.BUTTON_TITLE_REVERT: |
|
161 buttonLabel = PromptUtils.getLocalizedString("Revert"); |
|
162 break; |
|
163 case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING: |
|
164 buttonLabel = argText[i]; |
|
165 break; |
|
166 } |
|
167 if (buttonLabel) |
|
168 buttonLabels[i] = buttonLabel; |
|
169 flags >>= 8; |
|
170 } |
|
171 |
|
172 return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled]; |
|
173 }, |
|
174 |
|
175 getAuthInfo : function (authInfo) { |
|
176 let username, password; |
|
177 |
|
178 let flags = authInfo.flags; |
|
179 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain) |
|
180 username = authInfo.domain + "\\" + authInfo.username; |
|
181 else |
|
182 username = authInfo.username; |
|
183 |
|
184 password = authInfo.password; |
|
185 |
|
186 return [username, password]; |
|
187 }, |
|
188 |
|
189 setAuthInfo : function (authInfo, username, password) { |
|
190 let flags = authInfo.flags; |
|
191 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { |
|
192 // Domain is separated from username by a backslash |
|
193 let idx = username.indexOf("\\"); |
|
194 if (idx == -1) { |
|
195 authInfo.username = username; |
|
196 } else { |
|
197 authInfo.domain = username.substring(0, idx); |
|
198 authInfo.username = username.substring(idx+1); |
|
199 } |
|
200 } else { |
|
201 authInfo.username = username; |
|
202 } |
|
203 authInfo.password = password; |
|
204 }, |
|
205 |
|
206 // Copied from login manager |
|
207 getFormattedHostname : function (uri) { |
|
208 let scheme = uri.scheme; |
|
209 let hostname = scheme + "://" + uri.host; |
|
210 |
|
211 // If the URI explicitly specified a port, only include it when |
|
212 // it's not the default. (We never want "http://foo.com:80") |
|
213 port = uri.port; |
|
214 if (port != -1) { |
|
215 let handler = Services.io.getProtocolHandler(scheme); |
|
216 if (port != handler.defaultPort) |
|
217 hostname += ":" + port; |
|
218 } |
|
219 |
|
220 return hostname; |
|
221 }, |
|
222 |
|
223 // Copied from login manager |
|
224 getAuthTarget : function (aChannel, aAuthInfo) { |
|
225 let hostname, realm; |
|
226 |
|
227 // If our proxy is demanding authentication, don't use the |
|
228 // channel's actual destination. |
|
229 if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { |
|
230 if (!(aChannel instanceof Ci.nsIProxiedChannel)) |
|
231 throw "proxy auth needs nsIProxiedChannel"; |
|
232 |
|
233 let info = aChannel.proxyInfo; |
|
234 if (!info) |
|
235 throw "proxy auth needs nsIProxyInfo"; |
|
236 |
|
237 // Proxies don't have a scheme, but we'll use "moz-proxy://" |
|
238 // so that it's more obvious what the login is for. |
|
239 let idnService = Cc["@mozilla.org/network/idn-service;1"]. |
|
240 getService(Ci.nsIIDNService); |
|
241 hostname = "moz-proxy://" + |
|
242 idnService.convertUTF8toACE(info.host) + |
|
243 ":" + info.port; |
|
244 realm = aAuthInfo.realm; |
|
245 if (!realm) |
|
246 realm = hostname; |
|
247 |
|
248 return [hostname, realm]; |
|
249 } |
|
250 |
|
251 hostname = this.getFormattedHostname(aChannel.URI); |
|
252 |
|
253 // If a HTTP WWW-Authenticate header specified a realm, that value |
|
254 // will be available here. If it wasn't set or wasn't HTTP, we'll use |
|
255 // the formatted hostname instead. |
|
256 realm = aAuthInfo.realm; |
|
257 if (!realm) |
|
258 realm = hostname; |
|
259 |
|
260 return [hostname, realm]; |
|
261 }, |
|
262 |
|
263 |
|
264 makeAuthMessage : function (channel, authInfo) { |
|
265 let isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); |
|
266 let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); |
|
267 |
|
268 let username = authInfo.username; |
|
269 let [displayHost, realm] = this.getAuthTarget(channel, authInfo); |
|
270 |
|
271 // Suppress "the site says: $realm" when we synthesized a missing realm. |
|
272 if (!authInfo.realm && !isProxy) |
|
273 realm = ""; |
|
274 |
|
275 // Trim obnoxiously long realms. |
|
276 if (realm.length > 150) { |
|
277 realm = realm.substring(0, 150); |
|
278 // Append "..." (or localized equivalent). |
|
279 realm += this.ellipsis; |
|
280 } |
|
281 |
|
282 let text; |
|
283 if (isProxy) |
|
284 text = PromptUtils.getLocalizedString("EnterLoginForProxy", [realm, displayHost]); |
|
285 else if (isPassOnly) |
|
286 text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]); |
|
287 else if (!realm) |
|
288 text = PromptUtils.getLocalizedString("EnterUserPasswordFor", [displayHost]); |
|
289 else |
|
290 text = PromptUtils.getLocalizedString("EnterLoginForRealm", [realm, displayHost]); |
|
291 |
|
292 return text; |
|
293 }, |
|
294 |
|
295 getTabModalPrompt : function (domWin) { |
|
296 var promptBox = null; |
|
297 |
|
298 try { |
|
299 // Get the topmost window, in case we're in a frame. |
|
300 var promptWin = domWin.top; |
|
301 |
|
302 // Get the chrome window for the content window we're using. |
|
303 // (Unwrap because we need a non-IDL property below.) |
|
304 var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
305 .getInterface(Ci.nsIWebNavigation) |
|
306 .QueryInterface(Ci.nsIDocShell) |
|
307 .chromeEventHandler.ownerDocument |
|
308 .defaultView.wrappedJSObject; |
|
309 |
|
310 if (chromeWin.getTabModalPromptBox) |
|
311 promptBox = chromeWin.getTabModalPromptBox(promptWin); |
|
312 } catch (e) { |
|
313 // If any errors happen, just assume no tabmodal prompter. |
|
314 } |
|
315 |
|
316 return promptBox; |
|
317 }, |
|
318 }; |
|
319 |
|
320 PromptUtils = PromptUtilsTemp; |
|
321 |
|
322 XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () { |
|
323 let bunService = Cc["@mozilla.org/intl/stringbundle;1"]. |
|
324 getService(Ci.nsIStringBundleService); |
|
325 let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties"); |
|
326 if (!bundle) |
|
327 throw "String bundle for Prompter not present!"; |
|
328 return bundle; |
|
329 }); |
|
330 |
|
331 XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () { |
|
332 let ellipsis = "\u2026"; |
|
333 try { |
|
334 ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; |
|
335 } catch (e) { } |
|
336 return ellipsis; |
|
337 }); |
|
338 |
|
339 |
|
340 |
|
341 function openModalWindow(domWin, uri, args) { |
|
342 // There's an implied contract that says modal prompts should still work |
|
343 // when no "parent" window is passed for the dialog (eg, the "Master |
|
344 // Password" dialog does this). These prompts must be shown even if there |
|
345 // are *no* visible windows at all. |
|
346 // There's also a requirement for prompts to be blocked if a window is |
|
347 // passed and that window is hidden (eg, auth prompts are supressed if the |
|
348 // passed window is the hidden window). |
|
349 // See bug 875157 comment 30 for more... |
|
350 if (domWin) { |
|
351 // a domWin was passed, so we can apply the check for it being hidden. |
|
352 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
353 .getInterface(Ci.nsIDOMWindowUtils); |
|
354 |
|
355 if (winUtils && !winUtils.isParentWindowMainWidgetVisible) { |
|
356 throw Components.Exception("Cannot call openModalWindow on a hidden window", |
|
357 Cr.NS_ERROR_NOT_AVAILABLE); |
|
358 } |
|
359 } else { |
|
360 // We try and find a window to use as the parent, but don't consider |
|
361 // if that is visible before showing the prompt. |
|
362 domWin = Services.ww.activeWindow; |
|
363 // domWin may still be null here if there are _no_ windows open. |
|
364 } |
|
365 // Note that we don't need to fire DOMWillOpenModalDialog and |
|
366 // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal |
|
367 // will do that. Similarly for enterModalState / leaveModalState. |
|
368 |
|
369 Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args); |
|
370 } |
|
371 |
|
372 function openTabPrompt(domWin, tabPrompt, args) { |
|
373 PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); |
|
374 |
|
375 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
376 .getInterface(Ci.nsIDOMWindowUtils); |
|
377 winUtils.enterModalState(); |
|
378 |
|
379 // We provide a callback so the prompt can close itself. We don't want to |
|
380 // wait for this event loop to return... Otherwise the presence of other |
|
381 // prompts on the call stack would in this dialog appearing unresponsive |
|
382 // until the other prompts had been closed. |
|
383 let callbackInvoked = false; |
|
384 let newPrompt; |
|
385 function onPromptClose(forceCleanup) { |
|
386 if (!newPrompt && !forceCleanup) |
|
387 return; |
|
388 callbackInvoked = true; |
|
389 if (newPrompt) |
|
390 tabPrompt.removePrompt(newPrompt); |
|
391 |
|
392 domWin.removeEventListener("pagehide", pagehide); |
|
393 |
|
394 winUtils.leaveModalState(); |
|
395 |
|
396 PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); |
|
397 } |
|
398 |
|
399 domWin.addEventListener("pagehide", pagehide); |
|
400 function pagehide() { |
|
401 domWin.removeEventListener("pagehide", pagehide); |
|
402 |
|
403 if (newPrompt) { |
|
404 newPrompt.abortPrompt(); |
|
405 } |
|
406 } |
|
407 |
|
408 try { |
|
409 let topPrincipal = domWin.top.document.nodePrincipal; |
|
410 let promptPrincipal = domWin.document.nodePrincipal; |
|
411 args.showAlertOrigin = topPrincipal.equals(promptPrincipal); |
|
412 args.promptActive = true; |
|
413 |
|
414 newPrompt = tabPrompt.appendPrompt(args, onPromptClose); |
|
415 |
|
416 // TODO since we don't actually open a window, need to check if |
|
417 // there's other stuff in nsWindowWatcher::OpenWindowInternal |
|
418 // that we might need to do here as well. |
|
419 |
|
420 let thread = Services.tm.currentThread; |
|
421 while (args.promptActive) |
|
422 thread.processNextEvent(true); |
|
423 delete args.promptActive; |
|
424 |
|
425 if (args.promptAborted) |
|
426 throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE); |
|
427 } finally { |
|
428 // If the prompt unexpectedly failed to invoke the callback, do so here. |
|
429 if (!callbackInvoked) |
|
430 onPromptClose(true); |
|
431 } |
|
432 } |
|
433 |
|
434 function openRemotePrompt(domWin, args, tabPrompt) { |
|
435 let messageManager = domWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
436 .getInterface(Ci.nsIWebNavigation) |
|
437 .QueryInterface(Ci.nsIDocShell) |
|
438 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
439 .getInterface(Ci.nsITabChild) |
|
440 .messageManager; |
|
441 |
|
442 PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); |
|
443 |
|
444 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
445 .getInterface(Ci.nsIDOMWindowUtils); |
|
446 winUtils.enterModalState(); |
|
447 let closed = false; |
|
448 |
|
449 // It should be hard or impossible to cause a window to create multiple |
|
450 // prompts, but just in case, give our prompt an ID. |
|
451 let id = "id" + Cc["@mozilla.org/uuid-generator;1"] |
|
452 .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); |
|
453 |
|
454 messageManager.addMessageListener("Prompt:Close", function listener(message) { |
|
455 if (message.data._remoteId !== id) { |
|
456 return; |
|
457 } |
|
458 |
|
459 messageManager.removeMessageListener("Prompt:Close", listener); |
|
460 domWin.removeEventListener("pagehide", pagehide); |
|
461 |
|
462 winUtils.leaveModalState(); |
|
463 PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); |
|
464 |
|
465 // Copy the response from the closed prompt into our args, it will be |
|
466 // read by our caller. |
|
467 if (message.data) { |
|
468 for (let key in message.data) { |
|
469 args[key] = message.data[key]; |
|
470 } |
|
471 } |
|
472 |
|
473 // Exit our nested event loop when we unwind. |
|
474 closed = true; |
|
475 }); |
|
476 |
|
477 domWin.addEventListener("pagehide", pagehide); |
|
478 function pagehide() { |
|
479 domWin.removeEventListener("pagehide", pagehide); |
|
480 messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id }); |
|
481 } |
|
482 |
|
483 let topPrincipal = domWin.top.document.nodePrincipal; |
|
484 let promptPrincipal = domWin.document.nodePrincipal; |
|
485 args.showAlertOrigin = topPrincipal.equals(promptPrincipal); |
|
486 |
|
487 args._remoteId = id; |
|
488 |
|
489 messageManager.sendAsyncMessage("Prompt:Open", args, {}); |
|
490 |
|
491 let thread = Services.tm.currentThread; |
|
492 while (!closed) { |
|
493 thread.processNextEvent(true); |
|
494 } |
|
495 } |
|
496 |
|
497 function ModalPrompter(domWin) { |
|
498 this.domWin = domWin; |
|
499 } |
|
500 ModalPrompter.prototype = { |
|
501 domWin : null, |
|
502 /* |
|
503 * Default to not using a tab-modal prompt, unless the caller opts in by |
|
504 * QIing to nsIWritablePropertyBag and setting the value of this property |
|
505 * to true. |
|
506 */ |
|
507 allowTabModal : false, |
|
508 |
|
509 QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, |
|
510 Ci.nsIAuthPrompt2, |
|
511 Ci.nsIWritablePropertyBag2]), |
|
512 |
|
513 |
|
514 /* ---------- internal methods ---------- */ |
|
515 |
|
516 |
|
517 openPrompt : function (args) { |
|
518 // Check pref, if false/missing do not ever allow tab-modal prompts. |
|
519 const prefName = "prompts.tab_modal.enabled"; |
|
520 let prefValue = false; |
|
521 if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL) |
|
522 prefValue = Services.prefs.getBoolPref(prefName); |
|
523 |
|
524 let allowTabModal = this.allowTabModal && prefValue; |
|
525 |
|
526 if (allowTabModal && this.domWin) { |
|
527 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { |
|
528 openRemotePrompt(this.domWin, args, true); |
|
529 return; |
|
530 } |
|
531 |
|
532 let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin); |
|
533 if (tabPrompt) { |
|
534 openTabPrompt(this.domWin, tabPrompt, args); |
|
535 return; |
|
536 } |
|
537 } |
|
538 |
|
539 // If we can't do a tab modal prompt, fallback to using a window-modal dialog. |
|
540 const COMMON_DIALOG = "chrome://global/content/commonDialog.xul"; |
|
541 const SELECT_DIALOG = "chrome://global/content/selectDialog.xul"; |
|
542 |
|
543 let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG; |
|
544 |
|
545 if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { |
|
546 args.uri = uri; |
|
547 openRemotePrompt(this.domWin, args); |
|
548 return; |
|
549 } |
|
550 |
|
551 let propBag = PromptUtils.objectToPropBag(args); |
|
552 openModalWindow(this.domWin, uri, propBag); |
|
553 PromptUtils.propBagToObject(propBag, args); |
|
554 }, |
|
555 |
|
556 |
|
557 |
|
558 /* |
|
559 * ---------- interface disambiguation ---------- |
|
560 * |
|
561 * nsIPrompt and nsIAuthPrompt share 3 method names with slightly |
|
562 * different arguments. All but prompt() have the same number of |
|
563 * arguments, so look at the arg types to figure out how we're being |
|
564 * called. :-( |
|
565 */ |
|
566 prompt : function() { |
|
567 // also, the nsIPrompt flavor has 5 args instead of 6. |
|
568 if (typeof arguments[2] == "object") |
|
569 return this.nsIPrompt_prompt.apply(this, arguments); |
|
570 else |
|
571 return this.nsIAuthPrompt_prompt.apply(this, arguments); |
|
572 }, |
|
573 |
|
574 promptUsernameAndPassword : function() { |
|
575 // Both have 6 args, so use types. |
|
576 if (typeof arguments[2] == "object") |
|
577 return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); |
|
578 else |
|
579 return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); |
|
580 }, |
|
581 |
|
582 promptPassword : function() { |
|
583 // Both have 5 args, so use types. |
|
584 if (typeof arguments[2] == "object") |
|
585 return this.nsIPrompt_promptPassword.apply(this, arguments); |
|
586 else |
|
587 return this.nsIAuthPrompt_promptPassword.apply(this, arguments); |
|
588 }, |
|
589 |
|
590 |
|
591 /* ---------- nsIPrompt ---------- */ |
|
592 |
|
593 |
|
594 alert : function (title, text) { |
|
595 if (!title) |
|
596 title = PromptUtils.getLocalizedString("Alert"); |
|
597 |
|
598 let args = { |
|
599 promptType: "alert", |
|
600 title: title, |
|
601 text: text, |
|
602 }; |
|
603 |
|
604 this.openPrompt(args); |
|
605 }, |
|
606 |
|
607 alertCheck : function (title, text, checkLabel, checkValue) { |
|
608 if (!title) |
|
609 title = PromptUtils.getLocalizedString("Alert"); |
|
610 |
|
611 let args = { |
|
612 promptType: "alertCheck", |
|
613 title: title, |
|
614 text: text, |
|
615 checkLabel: checkLabel, |
|
616 checked: checkValue.value, |
|
617 }; |
|
618 |
|
619 this.openPrompt(args); |
|
620 |
|
621 // Checkbox state always returned, even if cancel clicked. |
|
622 checkValue.value = args.checked; |
|
623 }, |
|
624 |
|
625 confirm : function (title, text) { |
|
626 if (!title) |
|
627 title = PromptUtils.getLocalizedString("Confirm"); |
|
628 |
|
629 let args = { |
|
630 promptType: "confirm", |
|
631 title: title, |
|
632 text: text, |
|
633 ok: false, |
|
634 }; |
|
635 |
|
636 this.openPrompt(args); |
|
637 |
|
638 // Did user click Ok or Cancel? |
|
639 return args.ok; |
|
640 }, |
|
641 |
|
642 confirmCheck : function (title, text, checkLabel, checkValue) { |
|
643 if (!title) |
|
644 title = PromptUtils.getLocalizedString("ConfirmCheck"); |
|
645 |
|
646 let args = { |
|
647 promptType: "confirmCheck", |
|
648 title: title, |
|
649 text: text, |
|
650 checkLabel: checkLabel, |
|
651 checked: checkValue.value, |
|
652 ok: false, |
|
653 }; |
|
654 |
|
655 this.openPrompt(args); |
|
656 |
|
657 // Checkbox state always returned, even if cancel clicked. |
|
658 checkValue.value = args.checked; |
|
659 |
|
660 // Did user click Ok or Cancel? |
|
661 return args.ok; |
|
662 }, |
|
663 |
|
664 confirmEx : function (title, text, flags, button0, button1, button2, |
|
665 checkLabel, checkValue) { |
|
666 |
|
667 if (!title) |
|
668 title = PromptUtils.getLocalizedString("Confirm"); |
|
669 |
|
670 let args = { |
|
671 promptType: "confirmEx", |
|
672 title: title, |
|
673 text: text, |
|
674 checkLabel: checkLabel, |
|
675 checked: checkValue.value, |
|
676 ok: false, |
|
677 buttonNumClicked: 1, |
|
678 }; |
|
679 |
|
680 let [label0, label1, label2, defaultButtonNum, isDelayEnabled] = |
|
681 PromptUtils.confirmExHelper(flags, button0, button1, button2); |
|
682 |
|
683 args.defaultButtonNum = defaultButtonNum; |
|
684 args.enableDelay = isDelayEnabled; |
|
685 |
|
686 if (label0) { |
|
687 args.button0Label = label0; |
|
688 if (label1) { |
|
689 args.button1Label = label1; |
|
690 if (label2) { |
|
691 args.button2Label = label2; |
|
692 } |
|
693 } |
|
694 } |
|
695 |
|
696 this.openPrompt(args); |
|
697 |
|
698 // Checkbox state always returned, even if cancel clicked. |
|
699 checkValue.value = args.checked; |
|
700 |
|
701 // Get the number of the button the user clicked. |
|
702 return args.buttonNumClicked; |
|
703 }, |
|
704 |
|
705 nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) { |
|
706 if (!title) |
|
707 title = PromptUtils.getLocalizedString("Prompt"); |
|
708 |
|
709 let args = { |
|
710 promptType: "prompt", |
|
711 title: title, |
|
712 text: text, |
|
713 value: value.value, |
|
714 checkLabel: checkLabel, |
|
715 checked: checkValue.value, |
|
716 ok: false, |
|
717 }; |
|
718 |
|
719 this.openPrompt(args); |
|
720 |
|
721 // Did user click Ok or Cancel? |
|
722 let ok = args.ok; |
|
723 if (ok) { |
|
724 checkValue.value = args.checked; |
|
725 value.value = args.value; |
|
726 } |
|
727 |
|
728 return ok; |
|
729 }, |
|
730 |
|
731 nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) { |
|
732 if (!title) |
|
733 title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2"); |
|
734 |
|
735 let args = { |
|
736 promptType: "promptUserAndPass", |
|
737 title: title, |
|
738 text: text, |
|
739 user: user.value, |
|
740 pass: pass.value, |
|
741 checkLabel: checkLabel, |
|
742 checked: checkValue.value, |
|
743 ok: false, |
|
744 }; |
|
745 |
|
746 this.openPrompt(args); |
|
747 |
|
748 // Did user click Ok or Cancel? |
|
749 let ok = args.ok; |
|
750 if (ok) { |
|
751 checkValue.value = args.checked; |
|
752 user.value = args.user; |
|
753 pass.value = args.pass; |
|
754 } |
|
755 |
|
756 return ok; |
|
757 }, |
|
758 |
|
759 nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) { |
|
760 if (!title) |
|
761 title = PromptUtils.getLocalizedString("PromptPassword2"); |
|
762 |
|
763 let args = { |
|
764 promptType: "promptPassword", |
|
765 title: title, |
|
766 text: text, |
|
767 pass: pass.value, |
|
768 checkLabel: checkLabel, |
|
769 checked: checkValue.value, |
|
770 ok: false, |
|
771 } |
|
772 |
|
773 this.openPrompt(args); |
|
774 |
|
775 // Did user click Ok or Cancel? |
|
776 let ok = args.ok; |
|
777 if (ok) { |
|
778 checkValue.value = args.checked; |
|
779 pass.value = args.pass; |
|
780 } |
|
781 |
|
782 return ok; |
|
783 }, |
|
784 |
|
785 select : function (title, text, count, list, selected) { |
|
786 if (!title) |
|
787 title = PromptUtils.getLocalizedString("Select"); |
|
788 |
|
789 let args = { |
|
790 promptType: "select", |
|
791 title: title, |
|
792 text: text, |
|
793 list: list, |
|
794 selected: -1, |
|
795 ok: false, |
|
796 }; |
|
797 |
|
798 this.openPrompt(args); |
|
799 |
|
800 // Did user click Ok or Cancel? |
|
801 let ok = args.ok; |
|
802 if (ok) |
|
803 selected.value = args.selected; |
|
804 |
|
805 return ok; |
|
806 }, |
|
807 |
|
808 |
|
809 /* ---------- nsIAuthPrompt ---------- */ |
|
810 |
|
811 |
|
812 nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { |
|
813 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp |
|
814 if (defaultText) |
|
815 result.value = defaultText; |
|
816 return this.nsIPrompt_prompt(title, text, result, null, {}); |
|
817 }, |
|
818 |
|
819 nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) { |
|
820 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp |
|
821 return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {}); |
|
822 }, |
|
823 |
|
824 nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) { |
|
825 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp |
|
826 return this.nsIPrompt_promptPassword(title, text, pass, null, {}); |
|
827 }, |
|
828 |
|
829 |
|
830 /* ---------- nsIAuthPrompt2 ---------- */ |
|
831 |
|
832 |
|
833 promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { |
|
834 let message = PromptUtils.makeAuthMessage(channel, authInfo); |
|
835 |
|
836 let [username, password] = PromptUtils.getAuthInfo(authInfo); |
|
837 |
|
838 let userParam = { value: username }; |
|
839 let passParam = { value: password }; |
|
840 |
|
841 let ok; |
|
842 if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) |
|
843 ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue); |
|
844 else |
|
845 ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue); |
|
846 |
|
847 if (ok) |
|
848 PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); |
|
849 return ok; |
|
850 }, |
|
851 |
|
852 asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { |
|
853 // Nothing calls this directly; netwerk ends up going through |
|
854 // nsIPromptService::GetPrompt, which delegates to login manager. |
|
855 // Login manger handles the async bits itself, and only calls out |
|
856 // promptAuth, never asyncPromptAuth. |
|
857 // |
|
858 // Bug 565582 will change this. |
|
859 throw Cr.NS_ERROR_NOT_IMPLEMENTED; |
|
860 }, |
|
861 |
|
862 /* ---------- nsIWritablePropertyBag2 ---------- */ |
|
863 |
|
864 // Only a partial implementation, for one specific use case... |
|
865 |
|
866 setPropertyAsBool : function(name, value) { |
|
867 if (name == "allowTabModal") |
|
868 this.allowTabModal = value; |
|
869 else |
|
870 throw Cr.NS_ERROR_ILLEGAL_VALUE; |
|
871 }, |
|
872 }; |
|
873 |
|
874 |
|
875 function AuthPromptAdapterFactory() { |
|
876 } |
|
877 AuthPromptAdapterFactory.prototype = { |
|
878 classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"), |
|
879 QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]), |
|
880 |
|
881 /* ---------- nsIAuthPromptAdapterFactory ---------- */ |
|
882 |
|
883 createAdapter : function (oldPrompter) { |
|
884 return new AuthPromptAdapter(oldPrompter); |
|
885 } |
|
886 }; |
|
887 |
|
888 |
|
889 // Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell. |
|
890 function AuthPromptAdapter(oldPrompter) { |
|
891 this.oldPrompter = oldPrompter; |
|
892 } |
|
893 AuthPromptAdapter.prototype = { |
|
894 QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]), |
|
895 oldPrompter : null, |
|
896 |
|
897 /* ---------- nsIAuthPrompt2 ---------- */ |
|
898 |
|
899 promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { |
|
900 let message = PromptUtils.makeAuthMessage(channel, authInfo); |
|
901 |
|
902 let [username, password] = PromptUtils.getAuthInfo(authInfo); |
|
903 let userParam = { value: username }; |
|
904 let passParam = { value: password }; |
|
905 |
|
906 let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo); |
|
907 let authTarget = host + " (" + realm + ")"; |
|
908 |
|
909 let ok; |
|
910 if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) |
|
911 ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam); |
|
912 else |
|
913 ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam); |
|
914 |
|
915 if (ok) |
|
916 PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); |
|
917 return ok; |
|
918 }, |
|
919 |
|
920 asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { |
|
921 throw Cr.NS_ERROR_NOT_IMPLEMENTED; |
|
922 } |
|
923 }; |
|
924 |
|
925 |
|
926 // Wrapper using the old embedding contractID, since it's already common in |
|
927 // the addon ecosystem. |
|
928 function EmbedPrompter() { |
|
929 } |
|
930 EmbedPrompter.prototype = new Prompter(); |
|
931 EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"); |
|
932 |
|
933 var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory]; |
|
934 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |