mobile/android/components/PromptService.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial