Bug 1091287: Switch to the right browser before displaying a login prompt. r=mrbkap

This commit is contained in:
Dave Townsend 2014-11-04 11:51:06 -08:00
parent 544e2bacbb
commit dadb2c6ca9
9 changed files with 188 additions and 15 deletions

View File

@ -123,7 +123,7 @@ LoginManagerPrompter.prototype = {
},
setE10sData : function (aBrowser) {
setE10sData : function (aBrowser, aOpener) {
// XXX Implement me!
throw new Error("Not Yet Implemented");
},

View File

@ -71,6 +71,7 @@
#include "nsAuthInformationHolder.h"
#include "nsICancelable.h"
#include "gfxPrefs.h"
#include "nsILoginManagerPrompter.h"
#include <algorithm>
using namespace mozilla::dom;
@ -1880,8 +1881,18 @@ TabParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
// Get an auth prompter for our window so that the parenting
// of the dialogs works as it should when using tabs.
return wwatch->GetPrompt(window, iid,
reinterpret_cast<void**>(aResult));
nsCOMPtr<nsISupports> prompt;
rv = wwatch->GetPrompt(window, iid, getter_AddRefs(prompt));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoginManagerPrompter> prompter = do_QueryInterface(prompt);
if (prompter) {
nsCOMPtr<nsIDOMElement> browser = do_QueryInterface(mFrameElement);
prompter->SetE10sData(browser, nullptr);
}
*aResult = prompt.forget().take();
return NS_OK;
}
PColorPickerParent*

View File

@ -108,7 +108,7 @@ LoginManagerPrompter.prototype = {
this.log("===== initialized =====");
},
setE10sData : function (aBrowser) {
setE10sData : function (aBrowser, aOpener) {
throw new Error("This should be filled in when Android is multiprocess");
},

View File

@ -216,7 +216,7 @@ var LoginManagerParent = {
target.ownerDocument.defaultView :
target.contentWindow);
if (target.isRemoteBrowser)
prompterSvc.setE10sData({ browser: target, opener: opener });
prompterSvc.setE10sData(target, opener);
return prompterSvc;
}

View File

@ -6,9 +6,10 @@
#include "nsISupports.idl"
interface nsILoginInfo;
interface nsIDOMElement;
interface nsIDOMWindow;
[scriptable, uuid(88b75787-a78c-43aa-bfe8-52c3248b8dfd)]
[scriptable, uuid(425f73b9-b2db-4e8a-88c5-9ac2512934ce)]
interface nsILoginManagerPrompter : nsISupports {
/**
* Initialize the prompter. Must be called before using other interfaces.
@ -27,8 +28,9 @@ interface nsILoginManagerPrompter : nsISupports {
* window passed to init.
*
* @param aBrowser the <browser> to use for this prompter.
* @param aOpener the opener to use for this prompter.
*/
void setE10sData(in jsval aData);
void setE10sData(in nsIDOMElement aBrowser, in nsIDOMWindow aOpener);
/**
* Ask the user if they want to save a login (Yes, Never, Not Now)

View File

@ -11,6 +11,7 @@ const Cr = Components.results;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource://gre/modules/SharedPromptUtils.jsm");
/*
* LoginManagerPromptFactory
@ -577,10 +578,14 @@ LoginManagerPrompter.prototype = {
"Epic fail in promptAuth: " + e + "\n");
}
var ok = canAutologin ||
this._promptService.promptAuth(this._window,
aChannel, aLevel, aAuthInfo,
checkboxLabel, checkbox);
var ok = canAutologin;
if (!ok) {
if (this._window)
PromptUtils.fireDialogEvent(this._window, "DOMWillOpenModalDialog", this._browser);
ok = this._promptService.promptAuth(this._window,
aChannel, aLevel, aAuthInfo,
checkboxLabel, checkbox);
}
// If there's a notification box, use it to allow the user to
// determine if the login should be saved. If there isn't a
@ -713,11 +718,11 @@ LoginManagerPrompter.prototype = {
this.log("===== initialized =====");
},
setE10sData : function (aData) {
setE10sData : function (aBrowser, aOpener) {
if (!(this._window instanceof Ci.nsIDOMChromeWindow))
throw new Error("Unexpected call");
this._browser = aData.browser;
this._opener = aData.opener;
this._browser = aBrowser;
this._opener = aOpener;
},
@ -1224,7 +1229,7 @@ LoginManagerPrompter.prototype = {
}
let browser;
if (useOpener && isE10s) {
if (useOpener && this._opener && isE10s) {
// In e10s, we have to reconstruct the opener browser from
// the CPOW passed in the message (and then passed to us in
// setE10sData).

View File

@ -0,0 +1,110 @@
function handleRequest(request, response)
{
var match;
var requestAuth = true;
// Allow the caller to drive how authentication is processed via the query.
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
// The extra ? allows the user/pass/realm checks to succeed if the name is
// at the beginning of the query string.
var query = "?" + request.queryString;
var expected_user = "test", expected_pass = "testpass", realm = "mochitest";
// Look for an authentication header, if any, in the request.
//
// EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
//
// This test only supports Basic auth. The value sent by the client is
// "username:password", obscured with base64 encoding.
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
if (request.hasHeader("Authorization")) {
authPresent = true;
authHeader = request.getHeader("Authorization");
match = /Basic (.+)/.exec(authHeader);
if (match.length != 2)
throw "Couldn't parse auth header: " + authHeader;
var userpass = base64ToString(match[1]); // no atob() :-(
match = /(.*):(.*)/.exec(userpass);
if (match.length != 3)
throw "Couldn't decode auth header: " + userpass;
actual_user = match[1];
actual_pass = match[2];
}
// Don't request authentication if the credentials we got were what we
// expected.
if (expected_user == actual_user &&
expected_pass == actual_pass) {
requestAuth = false;
}
if (requestAuth) {
response.setStatusLine("1.0", 401, "Authentication required");
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
} else {
response.setStatusLine("1.0", 200, "OK");
}
response.setHeader("Content-Type", "application/xhtml+xml", false);
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
response.write("</html>");
}
// base64 decoder
//
// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
// doesn't seem to exist. :-(
/* Convert Base64 data to a string */
const toBinaryTable = [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
];
const base64Pad = '=';
function base64ToString(data) {
var result = '';
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
// Convert one by one.
for (var i = 0; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data[i] == base64Pad);
// Skip illegal characters and whitespace
if (c == -1) continue;
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding)
result += String.fromCharCode((leftdata >> leftbits) & 0xff);
leftdata &= (1 << leftbits) - 1;
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits)
throw Components.Exception('Corrupted base64 string');
return result;
}

View File

@ -1,7 +1,10 @@
[DEFAULT]
support-files =
authenticate.sjs
[browser_passwordmgr_fields.js]
[browser_passwordmgr_observers.js]
[browser_passwordmgr_sort.js]
[browser_passwordmgr_switchtab.js]
[browser_passwordmgrcopypwd.js]
[browser_passwordmgrdlg.js]

View File

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const PROMPT_URL = "chrome://global/content/commonDialog.xul";
const { interfaces: Ci } = Components;
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
isnot(tab, gBrowser.selectedTab, "New tab shouldn't be selected");
let listener = {
onOpenWindow: function(window) {
var domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
waitForFocus(() => {
is(domwindow.document.location.href, PROMPT_URL, "Should have seen a prompt window");
is(domwindow.args.promptType, "promptUserAndPass", "Should be an authenticate prompt");
is(gBrowser.selectedTab, tab, "Should have selected the new tab");
domwindow.document.documentElement.cancelDialog()
}, domwindow);
},
onCloseWindow: function() {
}
}
Services.wm.addListener(listener);
registerCleanupFunction(() => {
Services.wm.removeListener(listener);
gBrowser.removeTab(tab);
})
tab.linkedBrowser.addEventListener("load", () => {
finish();
}, true);
tab.linkedBrowser.loadURI("http://example.com/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs");
}