|
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 const Ci = Components.interfaces; |
|
5 const Cc = Components.classes; |
|
6 const Cr = Components.results; |
|
7 const Cu = Components.utils; |
|
8 |
|
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
10 Cu.import("resource://gre/modules/Services.jsm"); |
|
11 Cu.import("resource://gre/modules/Prompt.jsm"); |
|
12 |
|
13 var gPromptService = null; |
|
14 |
|
15 function PromptService() { |
|
16 gPromptService = this; |
|
17 } |
|
18 |
|
19 PromptService.prototype = { |
|
20 classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"), |
|
21 |
|
22 QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), |
|
23 |
|
24 /* ---------- nsIPromptFactory ---------- */ |
|
25 // XXX Copied from nsPrompter.js. |
|
26 getPrompt: function getPrompt(domWin, iid) { |
|
27 let doc = this.getDocument(); |
|
28 if (!doc) { |
|
29 let fallback = this._getFallbackService(); |
|
30 return fallback.QueryInterface(Ci.nsIPromptFactory).getPrompt(domWin, iid); |
|
31 } |
|
32 |
|
33 let p = new InternalPrompt(domWin, doc); |
|
34 p.QueryInterface(iid); |
|
35 return p; |
|
36 }, |
|
37 |
|
38 /* ---------- private memebers ---------- */ |
|
39 |
|
40 _getFallbackService: function _getFallbackService() { |
|
41 return Components.classesByID["{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"] |
|
42 .getService(Ci.nsIPromptService); |
|
43 }, |
|
44 |
|
45 getDocument: function getDocument() { |
|
46 let win = Services.wm.getMostRecentWindow("navigator:browser"); |
|
47 return win ? win.document : null; |
|
48 }, |
|
49 |
|
50 // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class |
|
51 // if we can show in-document popups, or to the fallback service otherwise. |
|
52 callProxy: function(aMethod, aArguments) { |
|
53 let prompt; |
|
54 let doc = this.getDocument(); |
|
55 if (!doc) { |
|
56 let fallback = this._getFallbackService(); |
|
57 return fallback[aMethod].apply(fallback, aArguments); |
|
58 } |
|
59 let domWin = aArguments[0]; |
|
60 prompt = new InternalPrompt(domWin, doc); |
|
61 return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1)); |
|
62 }, |
|
63 |
|
64 /* ---------- nsIPromptService ---------- */ |
|
65 |
|
66 alert: function() { |
|
67 return this.callProxy("alert", arguments); |
|
68 }, |
|
69 alertCheck: function() { |
|
70 return this.callProxy("alertCheck", arguments); |
|
71 }, |
|
72 confirm: function() { |
|
73 return this.callProxy("confirm", arguments); |
|
74 }, |
|
75 confirmCheck: function() { |
|
76 return this.callProxy("confirmCheck", arguments); |
|
77 }, |
|
78 confirmEx: function() { |
|
79 return this.callProxy("confirmEx", arguments); |
|
80 }, |
|
81 prompt: function() { |
|
82 return this.callProxy("prompt", arguments); |
|
83 }, |
|
84 promptUsernameAndPassword: function() { |
|
85 return this.callProxy("promptUsernameAndPassword", arguments); |
|
86 }, |
|
87 promptPassword: function() { |
|
88 return this.callProxy("promptPassword", arguments); |
|
89 }, |
|
90 select: function() { |
|
91 return this.callProxy("select", arguments); |
|
92 }, |
|
93 |
|
94 /* ---------- nsIPromptService2 ---------- */ |
|
95 promptAuth: function() { |
|
96 return this.callProxy("promptAuth", arguments); |
|
97 }, |
|
98 asyncPromptAuth: function() { |
|
99 return this.callProxy("asyncPromptAuth", arguments); |
|
100 } |
|
101 }; |
|
102 |
|
103 function InternalPrompt(aDomWin, aDocument) { |
|
104 this._domWin = aDomWin; |
|
105 this._doc = aDocument; |
|
106 } |
|
107 |
|
108 InternalPrompt.prototype = { |
|
109 _domWin: null, |
|
110 _doc: null, |
|
111 |
|
112 QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]), |
|
113 |
|
114 /* ---------- internal methods ---------- */ |
|
115 _getPrompt: function _getPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState) { |
|
116 let p = new Prompt({ |
|
117 window: this._domWin, |
|
118 title: aTitle, |
|
119 message: aText, |
|
120 buttons: aButtons || [ |
|
121 PromptUtils.getLocaleString("OK"), |
|
122 PromptUtils.getLocaleString("Cancel") |
|
123 ] |
|
124 }); |
|
125 return p; |
|
126 }, |
|
127 |
|
128 addCheckbox: function addCheckbox(aPrompt, aCheckMsg, aCheckState) { |
|
129 // Don't bother to check for aCheckSate. For nsIPomptService interfaces, aCheckState is an |
|
130 // out param and is required to be defined. If we've gotten here without it, something |
|
131 // has probably gone wrong and we should fail |
|
132 if (aCheckMsg) { |
|
133 aPrompt.addCheckbox({ |
|
134 label: PromptUtils.cleanUpLabel(aCheckMsg), |
|
135 checked: aCheckState.value |
|
136 }); |
|
137 } |
|
138 |
|
139 return aPrompt; |
|
140 }, |
|
141 |
|
142 addTextbox: function(prompt, value, autofocus, hint) { |
|
143 prompt.addTextbox({ |
|
144 value: (value !== null) ? value : "", |
|
145 autofocus: autofocus, |
|
146 hint: hint |
|
147 }); |
|
148 }, |
|
149 |
|
150 addPassword: function(prompt, value, autofocus, hint) { |
|
151 prompt.addPassword({ |
|
152 value: (value !== null) ? value : "", |
|
153 autofocus: autofocus, |
|
154 hint: hint |
|
155 }); |
|
156 }, |
|
157 |
|
158 /* Shows a native prompt, and then spins the event loop for this thread while we wait |
|
159 * for a response |
|
160 */ |
|
161 showPrompt: function showPrompt(aPrompt) { |
|
162 if (this._domWin) { |
|
163 PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); |
|
164 let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
|
165 winUtils.enterModalState(); |
|
166 } |
|
167 |
|
168 let retval = null; |
|
169 aPrompt.show(function(data) { |
|
170 retval = data; |
|
171 }); |
|
172 |
|
173 // Spin this thread while we wait for a result |
|
174 let thread = Services.tm.currentThread; |
|
175 while (retval == null) |
|
176 thread.processNextEvent(true); |
|
177 |
|
178 if (this._domWin) { |
|
179 let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
|
180 winUtils.leaveModalState(); |
|
181 PromptUtils.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); |
|
182 } |
|
183 |
|
184 return retval; |
|
185 }, |
|
186 |
|
187 /* |
|
188 * ---------- interface disambiguation ---------- |
|
189 * |
|
190 * XXX Copied from nsPrompter.js. |
|
191 * |
|
192 * nsIPrompt and nsIAuthPrompt share 3 method names with slightly |
|
193 * different arguments. All but prompt() have the same number of |
|
194 * arguments, so look at the arg types to figure out how we're being |
|
195 * called. :-( |
|
196 */ |
|
197 prompt: function prompt() { |
|
198 if (gPromptService.inContentProcess) |
|
199 return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments))); |
|
200 |
|
201 // also, the nsIPrompt flavor has 5 args instead of 6. |
|
202 if (typeof arguments[2] == "object") |
|
203 return this.nsIPrompt_prompt.apply(this, arguments); |
|
204 else |
|
205 return this.nsIAuthPrompt_prompt.apply(this, arguments); |
|
206 }, |
|
207 |
|
208 promptUsernameAndPassword: function promptUsernameAndPassword() { |
|
209 // Both have 6 args, so use types. |
|
210 if (typeof arguments[2] == "object") |
|
211 return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); |
|
212 else |
|
213 return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); |
|
214 }, |
|
215 |
|
216 promptPassword: function promptPassword() { |
|
217 // Both have 5 args, so use types. |
|
218 if (typeof arguments[2] == "object") |
|
219 return this.nsIPrompt_promptPassword.apply(this, arguments); |
|
220 else |
|
221 return this.nsIAuthPrompt_promptPassword.apply(this, arguments); |
|
222 }, |
|
223 |
|
224 /* ---------- nsIPrompt ---------- */ |
|
225 |
|
226 alert: function alert(aTitle, aText) { |
|
227 let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); |
|
228 p.setHint("alert"); |
|
229 this.showPrompt(p); |
|
230 }, |
|
231 |
|
232 alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) { |
|
233 let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); |
|
234 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
235 let data = this.showPrompt(p); |
|
236 if (aCheckState && data.button > -1) |
|
237 aCheckState.value = data.checkbox0; |
|
238 }, |
|
239 |
|
240 confirm: function confirm(aTitle, aText) { |
|
241 let p = this._getPrompt(aTitle, aText); |
|
242 p.setHint("confirm"); |
|
243 let data = this.showPrompt(p); |
|
244 return (data.button == 0); |
|
245 }, |
|
246 |
|
247 confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) { |
|
248 let p = this._getPrompt(aTitle, aText, null); |
|
249 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
250 let data = this.showPrompt(p); |
|
251 let ok = data.button == 0; |
|
252 if (aCheckState && data.button > -1) |
|
253 aCheckState.value = data.checkbox0; |
|
254 return ok; |
|
255 }, |
|
256 |
|
257 confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0, |
|
258 aButton1, aButton2, aCheckMsg, aCheckState) { |
|
259 let buttons = []; |
|
260 let titles = [aButton0, aButton1, aButton2]; |
|
261 for (let i = 0; i < 3; i++) { |
|
262 let bTitle = null; |
|
263 switch (aButtonFlags & 0xff) { |
|
264 case Ci.nsIPromptService.BUTTON_TITLE_OK : |
|
265 bTitle = PromptUtils.getLocaleString("OK"); |
|
266 break; |
|
267 case Ci.nsIPromptService.BUTTON_TITLE_CANCEL : |
|
268 bTitle = PromptUtils.getLocaleString("Cancel"); |
|
269 break; |
|
270 case Ci.nsIPromptService.BUTTON_TITLE_YES : |
|
271 bTitle = PromptUtils.getLocaleString("Yes"); |
|
272 break; |
|
273 case Ci.nsIPromptService.BUTTON_TITLE_NO : |
|
274 bTitle = PromptUtils.getLocaleString("No"); |
|
275 break; |
|
276 case Ci.nsIPromptService.BUTTON_TITLE_SAVE : |
|
277 bTitle = PromptUtils.getLocaleString("Save"); |
|
278 break; |
|
279 case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE : |
|
280 bTitle = PromptUtils.getLocaleString("DontSave"); |
|
281 break; |
|
282 case Ci.nsIPromptService.BUTTON_TITLE_REVERT : |
|
283 bTitle = PromptUtils.getLocaleString("Revert"); |
|
284 break; |
|
285 case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING : |
|
286 bTitle = PromptUtils.cleanUpLabel(titles[i]); |
|
287 break; |
|
288 } |
|
289 |
|
290 if (bTitle) |
|
291 buttons.push(bTitle); |
|
292 |
|
293 aButtonFlags >>= 8; |
|
294 } |
|
295 |
|
296 let p = this._getPrompt(aTitle, aText, buttons); |
|
297 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
298 let data = this.showPrompt(p); |
|
299 if (aCheckState && data.button > -1) |
|
300 aCheckState.value = data.checkbox0; |
|
301 return data.button; |
|
302 }, |
|
303 |
|
304 nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) { |
|
305 let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState); |
|
306 p.setHint("prompt"); |
|
307 this.addTextbox(p, aValue.value, true); |
|
308 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
309 let data = this.showPrompt(p); |
|
310 |
|
311 let ok = data.button == 0; |
|
312 if (aCheckState && data.button > -1) |
|
313 aCheckState.value = data.checkbox0; |
|
314 if (ok) |
|
315 aValue.value = data.textbox0; |
|
316 return ok; |
|
317 }, |
|
318 |
|
319 nsIPrompt_promptPassword: function nsIPrompt_promptPassword( |
|
320 aTitle, aText, aPassword, aCheckMsg, aCheckState) { |
|
321 let p = this._getPrompt(aTitle, aText, null); |
|
322 this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr")); |
|
323 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
324 let data = this.showPrompt(p); |
|
325 |
|
326 let ok = data.button == 0; |
|
327 if (aCheckState && data.button > -1) |
|
328 aCheckState.value = data.checkbox0; |
|
329 if (ok) |
|
330 aPassword.value = data.password0; |
|
331 return ok; |
|
332 }, |
|
333 |
|
334 nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword( |
|
335 aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) { |
|
336 let p = this._getPrompt(aTitle, aText, null); |
|
337 this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr")); |
|
338 this.addPassword(p, aPassword.value, false, PromptUtils.getLocaleString("password", "passwdmgr")); |
|
339 this.addCheckbox(p, aCheckMsg, aCheckState); |
|
340 let data = this.showPrompt(p); |
|
341 |
|
342 let ok = data.button == 0; |
|
343 if (aCheckState && data.button > -1) |
|
344 aCheckState.value = data.checkbox0; |
|
345 |
|
346 if (ok) { |
|
347 aUsername.value = data.textbox0; |
|
348 aPassword.value = data.password0; |
|
349 } |
|
350 return ok; |
|
351 }, |
|
352 |
|
353 select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) { |
|
354 let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); |
|
355 p.addMenulist({ values: aSelectList }); |
|
356 let data = this.showPrompt(p); |
|
357 |
|
358 let ok = data.button == 0; |
|
359 if (ok) |
|
360 aOutSelection.value = data.menulist0; |
|
361 |
|
362 return ok; |
|
363 }, |
|
364 |
|
365 /* ---------- nsIAuthPrompt ---------- */ |
|
366 |
|
367 nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { |
|
368 // TODO: Port functions from nsLoginManagerPrompter.js to here |
|
369 if (defaultText) |
|
370 result.value = defaultText; |
|
371 return this.nsIPrompt_prompt(title, text, result, null, {}); |
|
372 }, |
|
373 |
|
374 nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) { |
|
375 return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass); |
|
376 }, |
|
377 |
|
378 nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) { |
|
379 return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass); |
|
380 }, |
|
381 |
|
382 nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) { |
|
383 let checkMsg = null; |
|
384 let check = { value: false }; |
|
385 let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm); |
|
386 |
|
387 let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword); |
|
388 if (canSave) { |
|
389 // Look for existing logins. |
|
390 let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm); |
|
391 [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass); |
|
392 } |
|
393 |
|
394 let ok = false; |
|
395 if (aUser) |
|
396 ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check); |
|
397 else |
|
398 ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check); |
|
399 |
|
400 if (ok && canSave && check.value) |
|
401 PromptUtils.savePassword(hostname, realm, aUser, aPass); |
|
402 |
|
403 return ok; |
|
404 }, |
|
405 |
|
406 /* ---------- nsIAuthPrompt2 ---------- */ |
|
407 |
|
408 promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) { |
|
409 let checkMsg = null; |
|
410 let check = { value: false }; |
|
411 let message = PromptUtils.makeDialogText(aChannel, aAuthInfo); |
|
412 let [username, password] = PromptUtils.getAuthInfo(aAuthInfo); |
|
413 let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); |
|
414 let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); |
|
415 |
|
416 let canSave = PromptUtils.canSaveLogin(hostname, null); |
|
417 if (canSave) |
|
418 [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password); |
|
419 |
|
420 if (username.value && password.value) { |
|
421 PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); |
|
422 } |
|
423 |
|
424 let canAutologin = false; |
|
425 if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY && |
|
426 !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) && |
|
427 Services.prefs.getBoolPref("signon.autologin.proxy")) |
|
428 canAutologin = true; |
|
429 |
|
430 let ok = canAutologin; |
|
431 if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) |
|
432 ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check); |
|
433 else if (!ok) |
|
434 ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check); |
|
435 |
|
436 PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); |
|
437 |
|
438 if (ok && canSave && check.value) |
|
439 PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm); |
|
440 |
|
441 return ok; |
|
442 }, |
|
443 |
|
444 _asyncPrompts: {}, |
|
445 _asyncPromptInProgress: false, |
|
446 |
|
447 _doAsyncPrompt : function() { |
|
448 if (this._asyncPromptInProgress) |
|
449 return; |
|
450 |
|
451 // Find the first prompt key we have in the queue |
|
452 let hashKey = null; |
|
453 for (hashKey in this._asyncPrompts) |
|
454 break; |
|
455 |
|
456 if (!hashKey) |
|
457 return; |
|
458 |
|
459 // If login manger has logins for this host, defer prompting if we're |
|
460 // already waiting on a master password entry. |
|
461 let prompt = this._asyncPrompts[hashKey]; |
|
462 let prompter = prompt.prompter; |
|
463 let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo); |
|
464 let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); |
|
465 if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy) |
|
466 return; |
|
467 |
|
468 this._asyncPromptInProgress = true; |
|
469 prompt.inProgress = true; |
|
470 |
|
471 let self = this; |
|
472 |
|
473 let runnable = { |
|
474 run: function() { |
|
475 let ok = false; |
|
476 try { |
|
477 ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo); |
|
478 } catch (e) { |
|
479 Cu.reportError("_doAsyncPrompt:run: " + e + "\n"); |
|
480 } |
|
481 |
|
482 delete self._asyncPrompts[hashKey]; |
|
483 prompt.inProgress = false; |
|
484 self._asyncPromptInProgress = false; |
|
485 |
|
486 for (let consumer of prompt.consumers) { |
|
487 if (!consumer.callback) |
|
488 // Not having a callback means that consumer didn't provide it |
|
489 // or canceled the notification |
|
490 continue; |
|
491 |
|
492 try { |
|
493 if (ok) |
|
494 consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo); |
|
495 else |
|
496 consumer.callback.onAuthCancelled(consumer.context, true); |
|
497 } catch (e) { /* Throw away exceptions caused by callback */ } |
|
498 } |
|
499 self._doAsyncPrompt(); |
|
500 } |
|
501 } |
|
502 |
|
503 Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL); |
|
504 }, |
|
505 |
|
506 asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) { |
|
507 let cancelable = null; |
|
508 try { |
|
509 // If the user submits a login but it fails, we need to remove the |
|
510 // notification bar that was displayed. Conveniently, the user will |
|
511 // be prompted for authentication again, which brings us here. |
|
512 //this._removeLoginNotifications(); |
|
513 |
|
514 cancelable = { |
|
515 QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), |
|
516 callback: aCallback, |
|
517 context: aContext, |
|
518 cancel: function() { |
|
519 this.callback.onAuthCancelled(this.context, false); |
|
520 this.callback = null; |
|
521 this.context = null; |
|
522 } |
|
523 }; |
|
524 let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); |
|
525 let hashKey = aLevel + "|" + hostname + "|" + httpRealm; |
|
526 let asyncPrompt = this._asyncPrompts[hashKey]; |
|
527 if (asyncPrompt) { |
|
528 asyncPrompt.consumers.push(cancelable); |
|
529 return cancelable; |
|
530 } |
|
531 |
|
532 asyncPrompt = { |
|
533 consumers: [cancelable], |
|
534 channel: aChannel, |
|
535 authInfo: aAuthInfo, |
|
536 level: aLevel, |
|
537 inProgress : false, |
|
538 prompter: this |
|
539 } |
|
540 |
|
541 this._asyncPrompts[hashKey] = asyncPrompt; |
|
542 this._doAsyncPrompt(); |
|
543 } catch (e) { |
|
544 Cu.reportError("PromptService: " + e + "\n"); |
|
545 throw e; |
|
546 } |
|
547 return cancelable; |
|
548 } |
|
549 }; |
|
550 |
|
551 let PromptUtils = { |
|
552 getLocaleString: function pu_getLocaleString(aKey, aService) { |
|
553 if (aService == "passwdmgr") |
|
554 return this.cleanUpLabel(this.passwdBundle.GetStringFromName(aKey)); |
|
555 |
|
556 return this.cleanUpLabel(this.bundle.GetStringFromName(aKey)); |
|
557 }, |
|
558 |
|
559 // |
|
560 // Copied from chrome://global/content/commonDialog.js |
|
561 // |
|
562 cleanUpLabel: function cleanUpLabel(aLabel) { |
|
563 // This is for labels which may contain embedded access keys. |
|
564 // If we end in (&X) where X represents the access key, optionally preceded |
|
565 // by spaces and/or followed by the ':' character, |
|
566 // remove the access key placeholder + leading spaces from the label. |
|
567 // Otherwise a character preceded by one but not two &s is the access key. |
|
568 |
|
569 // Note that if you change the following code, see the comment of |
|
570 // nsTextBoxFrame::UpdateAccessTitle. |
|
571 if (!aLabel) |
|
572 return ""; |
|
573 |
|
574 if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) { |
|
575 aLabel = RegExp.leftContext + RegExp.$2; |
|
576 } else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) { |
|
577 aLabel = RegExp.$1 + RegExp.$2; |
|
578 } |
|
579 |
|
580 // Special code for using that & symbol |
|
581 aLabel = aLabel.replace(/\&\&/g, "&"); |
|
582 |
|
583 return aLabel; |
|
584 }, |
|
585 |
|
586 get pwmgr() { |
|
587 delete this.pwmgr; |
|
588 return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); |
|
589 }, |
|
590 |
|
591 getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) { |
|
592 let httpRealm = /^.+ \(.+\)$/; |
|
593 if (httpRealm.test(aRealmString)) |
|
594 return [null, null, null]; |
|
595 |
|
596 let uri = Services.io.newURI(aRealmString, null, null); |
|
597 let pathname = ""; |
|
598 |
|
599 if (uri.path != "/") |
|
600 pathname = uri.path; |
|
601 |
|
602 let formattedHostname = this._getFormattedHostname(uri); |
|
603 return [formattedHostname, formattedHostname + pathname, uri.username]; |
|
604 }, |
|
605 |
|
606 canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) { |
|
607 let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname) |
|
608 if (aSavePassword) |
|
609 canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) |
|
610 return canSave; |
|
611 }, |
|
612 |
|
613 getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) { |
|
614 let checkLabel = null; |
|
615 let check = { value: false }; |
|
616 let selectedLogin; |
|
617 |
|
618 checkLabel = this.getLocaleString("saveButton", "passwdmgr"); |
|
619 |
|
620 // XXX Like the original code, we can't deal with multiple |
|
621 // account selection. (bug 227632) |
|
622 if (aFoundLogins.length > 0) { |
|
623 selectedLogin = aFoundLogins[0]; |
|
624 |
|
625 // If the caller provided a username, try to use it. If they |
|
626 // provided only a password, this will try to find a password-only |
|
627 // login (or return null if none exists). |
|
628 if (aUser.value) |
|
629 selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value); |
|
630 |
|
631 if (selectedLogin) { |
|
632 check.value = true; |
|
633 aUser.value = selectedLogin.username; |
|
634 // If the caller provided a password, prefer it. |
|
635 if (!aPass.value) |
|
636 aPass.value = selectedLogin.password; |
|
637 } |
|
638 } |
|
639 |
|
640 return [checkLabel, check]; |
|
641 }, |
|
642 |
|
643 findLogin: function pu_findLogin(aLogins, aName, aValue) { |
|
644 for (let i = 0; i < aLogins.length; i++) |
|
645 if (aLogins[i][aName] == aValue) |
|
646 return aLogins[i]; |
|
647 return null; |
|
648 }, |
|
649 |
|
650 savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) { |
|
651 let selectedLogin = this.findLogin(aLogins, "username", aUser.value); |
|
652 |
|
653 // If we didn't find an existing login, or if the username |
|
654 // changed, save as a new login. |
|
655 if (!selectedLogin) { |
|
656 // add as new |
|
657 var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); |
|
658 newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", ""); |
|
659 this.pwmgr.addLogin(newLogin); |
|
660 } else if (aPass.value != selectedLogin.password) { |
|
661 // update password |
|
662 this.updateLogin(selectedLogin, aPass.value); |
|
663 } else { |
|
664 this.updateLogin(selectedLogin); |
|
665 } |
|
666 }, |
|
667 |
|
668 updateLogin: function pu_updateLogin(aLogin, aPassword) { |
|
669 let now = Date.now(); |
|
670 let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag); |
|
671 if (aPassword) { |
|
672 propBag.setProperty("password", aPassword); |
|
673 // Explicitly set the password change time here (even though it would |
|
674 // be changed automatically), to ensure that it's exactly the same |
|
675 // value as timeLastUsed. |
|
676 propBag.setProperty("timePasswordChanged", now); |
|
677 } |
|
678 propBag.setProperty("timeLastUsed", now); |
|
679 propBag.setProperty("timesUsedIncrement", 1); |
|
680 |
|
681 this.pwmgr.modifyLogin(aLogin, propBag); |
|
682 }, |
|
683 |
|
684 // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388 |
|
685 makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) { |
|
686 let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); |
|
687 let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); |
|
688 |
|
689 let username = aAuthInfo.username; |
|
690 let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo); |
|
691 |
|
692 // Suppress "the site says: $realm" when we synthesized a missing realm. |
|
693 if (!aAuthInfo.realm && !isProxy) |
|
694 realm = ""; |
|
695 |
|
696 // Trim obnoxiously long realms. |
|
697 if (realm.length > 150) { |
|
698 realm = realm.substring(0, 150); |
|
699 // Append "..." (or localized equivalent). |
|
700 realm += this.ellipsis; |
|
701 } |
|
702 |
|
703 let text; |
|
704 if (isProxy) |
|
705 text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2); |
|
706 else if (isPassOnly) |
|
707 text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2); |
|
708 else if (!realm) |
|
709 text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1); |
|
710 else |
|
711 text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2); |
|
712 |
|
713 return text; |
|
714 }, |
|
715 |
|
716 // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89 |
|
717 getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) { |
|
718 let uri = aChannel.URI; |
|
719 let res = { host: null, port: -1 }; |
|
720 if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) { |
|
721 let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel); |
|
722 res.host = proxy.proxyInfo.host; |
|
723 res.port = proxy.proxyInfo.port; |
|
724 } else { |
|
725 res.host = uri.host; |
|
726 res.port = uri.port; |
|
727 } |
|
728 return res; |
|
729 }, |
|
730 |
|
731 getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) { |
|
732 let hostname, realm; |
|
733 // If our proxy is demanding authentication, don't use the |
|
734 // channel's actual destination. |
|
735 if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { |
|
736 if (!(aChannel instanceof Ci.nsIProxiedChannel)) |
|
737 throw "proxy auth needs nsIProxiedChannel"; |
|
738 |
|
739 let info = aChannel.proxyInfo; |
|
740 if (!info) |
|
741 throw "proxy auth needs nsIProxyInfo"; |
|
742 |
|
743 // Proxies don't have a scheme, but we'll use "moz-proxy://" |
|
744 // so that it's more obvious what the login is for. |
|
745 let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService); |
|
746 hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port; |
|
747 realm = aAuthInfo.realm; |
|
748 if (!realm) |
|
749 realm = hostname; |
|
750 |
|
751 return [hostname, realm]; |
|
752 } |
|
753 hostname = this.getFormattedHostname(aChannel.URI); |
|
754 |
|
755 // If a HTTP WWW-Authenticate header specified a realm, that value |
|
756 // will be available here. If it wasn't set or wasn't HTTP, we'll use |
|
757 // the formatted hostname instead. |
|
758 realm = aAuthInfo.realm; |
|
759 if (!realm) |
|
760 realm = hostname; |
|
761 |
|
762 return [hostname, realm]; |
|
763 }, |
|
764 |
|
765 getAuthInfo : function pu_getAuthInfo(aAuthInfo) { |
|
766 let flags = aAuthInfo.flags; |
|
767 let username = {value: ""}; |
|
768 let password = {value: ""}; |
|
769 |
|
770 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain) |
|
771 username.value = aAuthInfo.domain + "\\" + aAuthInfo.username; |
|
772 else |
|
773 username.value = aAuthInfo.username; |
|
774 |
|
775 password.value = aAuthInfo.password |
|
776 |
|
777 return [username, password]; |
|
778 }, |
|
779 |
|
780 setAuthInfo : function (aAuthInfo, username, password) { |
|
781 var flags = aAuthInfo.flags; |
|
782 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { |
|
783 // Domain is separated from username by a backslash |
|
784 var idx = username.indexOf("\\"); |
|
785 if (idx == -1) { |
|
786 aAuthInfo.username = username; |
|
787 } else { |
|
788 aAuthInfo.domain = username.substring(0, idx); |
|
789 aAuthInfo.username = username.substring(idx+1); |
|
790 } |
|
791 } else { |
|
792 aAuthInfo.username = username; |
|
793 } |
|
794 aAuthInfo.password = password; |
|
795 }, |
|
796 |
|
797 getFormattedHostname : function pu_getFormattedHostname(uri) { |
|
798 let scheme = uri.scheme; |
|
799 let hostname = scheme + "://" + uri.host; |
|
800 |
|
801 // If the URI explicitly specified a port, only include it when |
|
802 // it's not the default. (We never want "http://foo.com:80") |
|
803 port = uri.port; |
|
804 if (port != -1) { |
|
805 let handler = Services.io.getProtocolHandler(scheme); |
|
806 if (port != handler.defaultPort) |
|
807 hostname += ":" + port; |
|
808 } |
|
809 return hostname; |
|
810 }, |
|
811 |
|
812 fireDialogEvent: function(aDomWin, aEventName) { |
|
813 // accessing the document object can throw if this window no longer exists. See bug 789888. |
|
814 try { |
|
815 if (!aDomWin.document) |
|
816 return; |
|
817 let event = aDomWin.document.createEvent("Events"); |
|
818 event.initEvent(aEventName, true, true); |
|
819 let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
820 .getInterface(Ci.nsIDOMWindowUtils); |
|
821 winUtils.dispatchEventToChromeOnly(aDomWin, event); |
|
822 } catch(ex) { |
|
823 } |
|
824 } |
|
825 }; |
|
826 |
|
827 XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () { |
|
828 return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"); |
|
829 }); |
|
830 |
|
831 XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () { |
|
832 return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties"); |
|
833 }); |
|
834 |
|
835 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]); |