bug 866304. In metro, use the tab-modal prompt implementation that is available in toolkit. r=mbrubeck

This patch replaces metro's 'PromptService' (which implements nsIPromptService and nsIPrompt, among others) with an implementation that simply forwards all calls to toolkit's nsPrompter.js. Before forwarding each call, it writes the "allowTabModal" property on the object obtained from nsPrompter.js, causing the created prompt to be tab-modal.

It also adds functionality to browser.js for creating tab modal prompts. One of the changes made as part of adding this functionality is replacing the <browser> with a <stack> of <browser> objects.
This commit is contained in:
Tim Abraldes 2013-08-05 23:29:54 -07:00
parent 85de1c34a0
commit 56d9a5b8e7
17 changed files with 125 additions and 1383 deletions

View File

@ -2,6 +2,10 @@
* 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/. */
browser[tabmodalPromptShowing] {
-moz-user-focus: none !important;
}
browser[remote="false"] {
-moz-binding: url("chrome://browser/content/bindings/browser.xml#local-browser");
}
@ -195,4 +199,4 @@ textbox {
/* used in about:config */
textbox[type="search"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#search-textbox");
}
}

View File

@ -16,11 +16,18 @@ const kSetInactiveStateTimeout = 100;
const kDefaultMetadata = { autoSize: false, allowZoom: true, autoScale: true };
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// Override sizeToContent in the main window. It breaks things (bug 565887)
window.sizeToContent = function() {
Cu.reportError("window.sizeToContent is not allowed in this window");
}
function getTabModalPromptBox(aWindow) {
let browser = Browser.getBrowserForWindow(aWindow);
return Browser.getTabModalPromptBox(browser);
}
/*
* Returns the browser for the currently displayed tab.
*/
@ -410,13 +417,47 @@ var Browser = {
return this._tabs;
},
getTabForBrowser: function getTabForBrowser(aBrowser) {
let tabs = this._tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].browser == aBrowser)
return tabs[i];
}
return null;
getTabModalPromptBox: function(aBrowser) {
let browser = (aBrowser || getBrowser());
let stack = browser.parentNode;
let self = this;
let promptBox = {
appendPrompt : function(args, onCloseCallback) {
let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
stack.appendChild(newPrompt);
browser.setAttribute("tabmodalPromptShowing", true);
newPrompt.clientTop; // style flush to assure binding is attached
let tab = self.getTabForBrowser(browser);
tab = tab.chromeTab;
newPrompt.init(args, tab, onCloseCallback);
return newPrompt;
},
removePrompt : function(aPrompt) {
stack.removeChild(aPrompt);
let prompts = this.listPrompts();
if (prompts.length) {
let prompt = prompts[prompts.length - 1];
prompt.Dialog.setDefaultFocus();
} else {
browser.removeAttribute("tabmodalPromptShowing");
browser.focus();
}
},
listPrompts : function(aPrompt) {
let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
// NodeList --> real JS array
let prompts = Array.slice(els);
return prompts;
},
};
return promptBox;
},
getBrowserForWindowId: function getBrowserForWindowId(aWindowId) {
@ -427,6 +468,21 @@ var Browser = {
return null;
},
getBrowserForWindow: function(aWindow) {
let windowID = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
return this.getBrowserForWindowId(windowID);
},
getTabForBrowser: function getTabForBrowser(aBrowser) {
let tabs = this._tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].browser == aBrowser)
return tabs[i];
}
return null;
},
getTabAtIndex: function getTabAtIndex(index) {
if (index >= this._tabs.length || index < 0)
return null;
@ -612,7 +668,7 @@ var Browser = {
getNotificationBox: function getNotificationBox(aBrowser) {
let browser = aBrowser || this.selectedBrowser;
return browser.parentNode;
return browser.parentNode.parentNode;
},
/**
@ -1632,7 +1688,11 @@ Tab.prototype = {
browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false");
// Append the browser to the document, which should start the page load
notification.appendChild(browser);
let stack = document.createElementNS(XUL_NS, "stack");
stack.className = "browserStack";
stack.appendChild(browser);
stack.setAttribute("flex", "1");
notification.appendChild(stack);
Elements.browsers.insertBefore(notification, aInsertBefore);
// stop about:blank from loading

View File

@ -1,40 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-alert-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-button-ok').focus()"
onclose="this.PromptHelper.onCloseAlert(this);"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-alert-dialog').close()"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-alert-dialog').close()"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-alert-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-alert-message"/>
</scrollbox>
<checkbox id="prompt-alert-checkbox" collapsed="true" pack="start" flex="1" />
</vbox>
<hbox class="prompt-buttons">
<button id="prompt-button-ok" class="prompt-button button-default" label="&ok.label;" command="cmd_ok"/>
</hbox>
</vbox>
</dialog>

View File

@ -1,41 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-confirm-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementsByAttribute('command', 'cmd_ok')[0].focus()"
onclose="this.PromptHelper.onCloseConfirm(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-confirm-dialog').PromptHelper.closeConfirm(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-confirm-dialog').PromptHelper.closeConfirm(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-confirm-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-confirm-message"/>
</scrollbox>
<checkbox id="prompt-confirm-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox id="prompt-confirm-buttons-box" class="prompt-buttons">
<button class="button-default" label="&ok.label;" command="cmd_ok"/>
<button label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

View File

@ -1,63 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE prompt [
<!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
<!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
<!ENTITY % crashpromptDTD SYSTEM "chrome://browser/locale/crashprompt.dtd">
%crashpromptDTD;
%promptDTD;
%commonDialogDTD;
]>
<dialog id="crash-prompt-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="this.CrashPrompt.load();"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="CrashPrompt.accept();"/>
<command id="cmd_cancel" oncommand="CrashPrompt.refuse();"/>
</commandset>
<!-- user query and options -->
<vbox id="crash-privacy-options" class="prompt-inner">
<vbox class="prompt-header" flex="1">
<hbox class="prompt-title">
<description>&crashprompt.dialog.title;</description>
</hbox>
<description class="prompt-message" id="privacy-crash-blurb"/>
<hbox flex="1">
<description class="text-link crash-link" flex="1"
onclick="document.getElementById('crash-prompt-dialog').CrashPrompt.privacy();">&crashprompt.dialog.privacyLink;</description>
</hbox>
</vbox>
<hbox class="prompt-buttons">
<button id="crash-button-accept" class="button-default" default="true" command="cmd_ok">&crashprompt.dialog.acceptbutton;</button>
<separator/>
<button id="crash-button-refuse" command="cmd_cancel">&crashprompt.dialog.refusebutton;</button>
</hbox>
</vbox>
<!-- long winded privacy statement, hidden by default -->
<vbox id="crash-privacy-statement" class="prompt-inner-statement" hidden="true">
<hbox flex="1">
<vbox class="crash-privacy-back-button" onclick="document.getElementById('crash-prompt-dialog').CrashPrompt.privacyBack();"/>
<scrollbox orient="vertical" class="crash-privacy-statement-scroller" flex="1">
<hbox class="prompt-title">
<description>&crashprompt.dialog.title2;</description>
</hbox>
<description class="crash-message">&crashprompt.dialog.statement1;</description>
<separator/>
<description class="crash-message">&crashprompt.dialog.statement2;</description>
</scrollbox>
</hbox>
</vbox>
</dialog>

View File

@ -1,121 +0,0 @@
/* 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 PromptHelper = {
closeDialog: function(confirm, id) {
let dialog = document.getElementById(id);
if (typeof confirm == "boolean" && dialog.arguments && "defaultButton" in dialog.arguments)
// confirmEx always returns 1 when dismissed with "escape" (bug 345067).
dialog.arguments.result = confirm ? dialog.arguments.defaultButton : 1;
else
dialog.arguments.result = confirm;
dialog.close();
},
// Alert dialog
onCloseAlert: function(dialog) {
if (dialog.arguments)
dialog.arguments.value = document.getElementById("prompt-alert-checkbox").checked;
},
// Confirm dialog
closeConfirm: function(confirm) {
this.closeDialog(confirm, "prompt-confirm-dialog");
},
onCloseConfirm: function(dialog) {
if (dialog.arguments && ("checkbox" in dialog.arguments))
dialog.arguments.checkbox.value = document.getElementById("prompt-confirm-checkbox").checked;
},
// Prompt dialog
closePrompt: function(confirm) {
this.closeDialog(confirm, "prompt-prompt-dialog");
},
onClosePrompt: function(dialog) {
if (dialog.arguments) {
dialog.arguments.checkbox.value = document.getElementById("prompt-prompt-checkbox").checked;
dialog.arguments.value.value = document.getElementById("prompt-prompt-textbox").value;
}
},
// User / Password dialog
onLoadPassword: function onLoadPassword(dialog) {
let user = document.getElementById('prompt-password-user');
if (!user.value)
user.focus();
},
closePassword: function(confirm) {
this.closeDialog(confirm, "prompt-password-dialog");
},
onClosePassword: function(dialog) {
if (dialog.arguments) {
dialog.arguments.checkbox.value = document.getElementById("prompt-password-checkbox").checked;
dialog.arguments.user.value = document.getElementById("prompt-password-user").value;
dialog.arguments.password.value = document.getElementById("prompt-password-password").value;
}
},
// Select dialog
closeSelect: function(confirm) {
this.closeDialog(confirm, "prompt-select-dialog");
},
onCloseSelect: function(dialog) {
if (dialog.arguments)
dialog.arguments.selection.value = document.getElementById("prompt-select-list").selectedIndex;
}
};
var CrashPrompt = {
load: function () {
if (this._preventRecurse) {
throw new Exception("CrashPrompt recursion error!!");
return;
}
// populate the text blurb with localized text
let brandName = Services.strings.createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName");
let vendorName = Services.strings.createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("vendorShortName");
let crashBundle = Services.strings.createBundle("chrome://browser/locale/crashprompt.properties");
let message = crashBundle.formatStringFromName("crashprompt.messagebody2", [brandName, vendorName], 2);
let descElement = document.getElementById("privacy-crash-blurb");
descElement.textContent = message;
// focus the send button
document.getElementById('crash-button-accept').focus();
},
accept: function() {
document.getElementById("crash-prompt-dialog").close();
Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
Services.prefs.setBoolPref('app.crashreporter.prompted', true);
BrowserUI.crashReportingPrefChanged(true);
this._preventRecurse = true;
BrowserUI.startupCrashCheck();
this._preventRecurse = false;
},
refuse: function() {
document.getElementById("crash-prompt-dialog").close();
Services.prefs.setBoolPref('app.crashreporter.autosubmit', false);
Services.prefs.setBoolPref('app.crashreporter.prompted', true);
BrowserUI.crashReportingPrefChanged(false);
},
privacy: function() {
document.getElementById("crash-privacy-options").setAttribute("hidden", true);
document.getElementById("crash-privacy-statement").setAttribute("hidden", false);
},
privacyBack: function() {
document.getElementById("crash-privacy-options").setAttribute("hidden", false);
document.getElementById("crash-privacy-statement").setAttribute("hidden", true);
},
};

View File

@ -1,43 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-prompt-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-prompt-textbox').focus()"
onclose="this.PromptHelper.onClosePrompt(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-prompt-dialog').PromptHelper.closePrompt(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-prompt-dialog').PromptHelper.closePrompt(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-prompt-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-prompt-message"/>
</scrollbox>
<textbox id="prompt-prompt-textbox" class="prompt-edit"/>
<button id="prompt-prompt-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox class="prompt-buttons">
<button label="&ok.label;" command="cmd_ok"/>
<button label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

View File

@ -1,63 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE prompt [
<!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
<!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
%promptDTD;
%commonDialogDTD;
]>
<dialog id="prompt-password-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="this.PromptHelper.onLoadPassword(this)"
onclose="this.PromptHelper.onClosePassword(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-password-dialog').PromptHelper.closePassword(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-password-dialog').PromptHelper.closePassword(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-password-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-password-message"/>
</scrollbox>
<grid class="prompt-message">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row align="center">
<label value="&editfield0.label;"/>
<textbox id="prompt-password-user" class="prompt-edit"/>
</row>
<row align="center">
<label value="&editfield1.label;"/>
<textbox type="password" id="prompt-password-password" class="prompt-edit"/>
</row>
</rows>
</grid>
<checkbox id="prompt-password-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox class="prompt-buttons">
<button class="button-default" label="&ok.label;" command="cmd_ok"/>
<button label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

View File

@ -1,41 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-select-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-select-list').focus()"
onclose="this.PromptHelper.onCloseSelect(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-select-dialog').PromptHelper.closeSelect(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-select-dialog').PromptHelper.closeSelect(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-select-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-select-message"/>
</scrollbox>
<menulist id="prompt-select-list"/>
</vbox>
<hbox class="prompt-buttons">
<button class="button-default" label="&ok.label;" command="cmd_ok"/>
<button label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

View File

@ -33,14 +33,6 @@ chrome.jar:
* content/flyoutpanels/AboutFlyoutPanel.js (content/flyoutpanels/AboutFlyoutPanel.js)
content/flyoutpanels/PrefsFlyoutPanel.js (content/flyoutpanels/PrefsFlyoutPanel.js)
content/prompt/alert.xul (content/prompt/alert.xul)
content/prompt/confirm.xul (content/prompt/confirm.xul)
content/prompt/prompt.xul (content/prompt/prompt.xul)
content/prompt/promptPassword.xul (content/prompt/promptPassword.xul)
content/prompt/select.xul (content/prompt/select.xul)
content/prompt/prompt.js (content/prompt/prompt.js)
content/prompt/crash.xul (content/prompt/crash.xul)
content/helperui/AlertsHelper.js (content/helperui/AlertsHelper.js)
content/helperui/IndexedDB.js (content/helperui/IndexedDB.js)
content/helperui/MenuUI.js (content/helperui/MenuUI.js)

View File

@ -1,3 +1,4 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
@ -9,55 +10,8 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// Whitelist of methods we remote - to check against malicious data.
// For example, it would be dangerous to allow content to show auth prompts.
const REMOTABLE_METHODS = {
alert: { outParams: [] },
alertCheck: { outParams: [4] },
confirm: { outParams: [] },
prompt: { outParams: [3, 5] },
confirmEx: { outParams: [8] },
confirmCheck: { outParams: [4] },
select: { outParams: [5] }
};
var gPromptService = null;
function PromptService() {
// Depending on if we are in the parent or child, prepare to remote
// certain calls
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
if (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
// Parent process
this.inContentProcess = false;
// Used for wakeups service. FIXME: clean up with bug 593407
this.wrappedJSObject = this;
// Setup listener for child messages. We don't need to call
// addMessageListener as the wakeup service will do that for us.
this.receiveMessage = function(aMessage) {
var json = aMessage.json;
switch (aMessage.name) {
case "Prompt:Call":
var method = aMessage.json.method;
if (!REMOTABLE_METHODS.hasOwnProperty(method))
throw "PromptServiceRemoter received an invalid method "+method;
var arguments = aMessage.json.arguments;
var ret = this[method].apply(this, arguments);
// Return multiple return values in objects of form { value: ... },
// and also with the actual return value at the end
arguments.push(ret);
return arguments;
}
};
} else {
// Child process
this.inContentProcess = true;
}
gPromptService = this;
}
PromptService.prototype = {
@ -67,59 +21,43 @@ PromptService.prototype = {
/* ---------- nsIPromptFactory ---------- */
// XXX Copied from nsPrompter.js.
getPrompt: function getPrompt(domWin, iid) {
if (this.inContentProcess)
return ContentPrompt.QueryInterface(iid);
let factory =
Components.classesByID["{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"]
.createInstance(Ci.nsIPromptFactory);
let doc = this.getDocument();
if (!doc) {
let fallback = this._getFallbackService();
return fallback.getPrompt(domWin, iid);
let prompt = factory.getPrompt(domWin, iid);
try {
let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
bag.setPropertyAsBool("allowTabModal", true);
} catch(e) {
}
let p = new Prompt(domWin, doc);
p.QueryInterface(iid);
return p;
return prompt;
},
/* ---------- private memebers ---------- */
_getFallbackService: function _getFallbackService() {
return Components.classesByID["{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"]
.getService(Ci.nsIPromptService);
},
getDocument: function getDocument() {
let win = Services.wm.getMostRecentWindow("navigator:browser");
return win ? win.document : null;
},
// nsIPromptService and nsIPromptService2 methods proxy to our Prompt class
// if we can show in-document popups, or to the fallback service otherwise.
callProxy: function(aMethod, aArguments) {
let prompt;
if (this.inContentProcess) {
// Bring this tab to the front, so prompt appears on the right tab
var window = aArguments[0];
if (window && window.document) {
var event = window.document.createEvent("Events");
event.initEvent("DOMWillOpenModalDialog", true, true);
let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.dispatchEventToChromeOnly(window, event);
callProxy: function(aName, aArgs) {
let domWin = aArgs[0];
if (!domWin) {
let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
if (!chromeWin) {
let msg = "PromptService.js: Attempted create a prompt but no DOM Window specified and failed to find one";
Cu.reportError(msg);
throw(msg);
}
prompt = ContentPrompt;
} else {
let doc = this.getDocument();
if (!doc) {
let fallback = this._getFallbackService();
return fallback[aMethod].apply(fallback, aArguments);
}
let domWin = aArguments[0];
prompt = new Prompt(domWin, doc);
domWin = chromeWin.getBrowser().contentWindow;
}
return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1));
let prompt = this.getPrompt(domWin, Ci.nsIPrompt);
if (aName == 'promptAuth' || aName == 'promptAuthAsync') {
let adapterFactory =
Components.classesByID["{6e134924-6c3a-4d86-81ac-69432dd971dc}"]
.createInstance(Ci.nsIAuthPromptAdapterFactory);
prompt = adapterFactory.createAdapter(prompt);
}
return prompt[aName].apply(prompt, Array.prototype.slice.call(aArgs, 1));
},
/* ---------- nsIPromptService ---------- */
@ -162,755 +100,4 @@ PromptService.prototype = {
}
};
// Implementation of nsIPrompt that just forwards to the parent process.
let ContentPrompt = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
sendMessage: function sendMessage(aMethod) {
let args = Array.prototype.slice.call(arguments);
args[0] = null; // No need to pass "window" argument to the prompt service.
// We send all prompts as sync, even alert (which has no important
// return value), as otherwise program flow will continue, and the
// script can theoretically show several alerts at once. In particular
// this can lead to a bug where you cannot click the earlier one, which
// is now hidden by a new one (and Fennec is helplessly frozen).
var json = { method: aMethod, arguments: args };
var response = this.messageManager.sendSyncMessage("Prompt:Call", json)[0];
// Args copying - for methods that have out values
REMOTABLE_METHODS[aMethod].outParams.forEach(function(i) {
args[i].value = response[i].value;
});
return response.pop(); // final return value was given at the end
}
};
XPCOMUtils.defineLazyServiceGetter(ContentPrompt, "messageManager",
"@mozilla.org/childprocessmessagemanager;1", Ci.nsISyncMessageSender);
// Add remotable methods to ContentPrompt.
for (let [method, _] in Iterator(REMOTABLE_METHODS)) {
ContentPrompt[method] = ContentPrompt.sendMessage.bind(ContentPrompt, method);
}
function Prompt(aDomWin, aDocument) {
this._domWin = aDomWin;
this._doc = aDocument;
}
Prompt.prototype = {
_domWin: null,
_doc: null,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]),
/* ---------- internal methods ---------- */
openDialog: function openDialog(aSrc, aParams) {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
return browser.DialogUI.importModal(this._domWin, aSrc, aParams);
},
_setupPrompt: function setupPrompt(aDoc, aType, aTitle, aText, aCheck) {
aDoc.getElementById("prompt-" + aType + "-title").appendChild(aDoc.createTextNode(aTitle));
aDoc.getElementById("prompt-" + aType + "-message").appendChild(aDoc.createTextNode(aText));
if (aCheck && aCheck.msg) {
aDoc.getElementById("prompt-" + aType + "-checkbox").checked = aCheck.value;
this.setLabelForNode(aDoc.getElementById("prompt-" + aType + "-checkbox"), aCheck.msg);
aDoc.getElementById("prompt-" + aType + "-checkbox").removeAttribute("collapsed");
}
},
commonPrompt: function commonPrompt(aTitle, aText, aValue, aCheckMsg, aCheckState, isPassword) {
var params = new Object();
params.result = false;
params.checkbox = aCheckState;
params.value = aValue;
let dialog = this.openDialog("chrome://browser/content/prompt/prompt.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "prompt", aTitle, aText, {value: aCheckState.value, msg: aCheckMsg});
doc.getElementById("prompt-prompt-textbox").value = aValue.value;
if (isPassword)
doc.getElementById("prompt-prompt-textbox").type = "password";
dialog.waitForClose();
return params.result;
},
//
// Copied from chrome://global/content/commonDialog.js
//
setLabelForNode: function setLabelForNode(aNode, aLabel) {
// This is for labels which may contain embedded access keys.
// If we end in (&X) where X represents the access key, optionally preceded
// by spaces and/or followed by the ':' character, store the access key and
// remove the access key placeholder + leading spaces from the label.
// Otherwise a character preceded by one but not two &s is the access key.
// Store it and remove the &.
// Note that if you change the following code, see the comment of
// nsTextBoxFrame::UpdateAccessTitle.
if (!aLabel)
return;
var accessKey = null;
if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) {
aLabel = RegExp.leftContext + RegExp.$2;
accessKey = RegExp.$1;
} else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) {
aLabel = RegExp.$1 + RegExp.$2;
accessKey = RegExp.$3;
}
// && is the magic sequence to embed an & in your label.
aLabel = aLabel.replace(/\&\&/g, "&");
if (aNode instanceof Ci.nsIDOMXULLabelElement) {
aNode.setAttribute("value", aLabel);
} else if (aNode instanceof Ci.nsIDOMXULDescriptionElement) {
let text = aNode.ownerDocument.createTextNode(aLabel);
aNode.appendChild(text);
} else { // Set text for other xul elements
aNode.setAttribute("label", aLabel);
}
// XXXjag bug 325251
// Need to set this after aNode.setAttribute("value", aLabel);
if (accessKey)
aNode.setAttribute("accesskey", accessKey);
},
/*
* ---------- interface disambiguation ----------
*
* XXX Copied from nsPrompter.js.
*
* nsIPrompt and nsIAuthPrompt share 3 method names with slightly
* different arguments. All but prompt() have the same number of
* arguments, so look at the arg types to figure out how we're being
* called. :-(
*/
prompt: function prompt() {
if (gPromptService.inContentProcess)
return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments)));
// also, the nsIPrompt flavor has 5 args instead of 6.
if (typeof arguments[2] == "object")
return this.nsIPrompt_prompt.apply(this, arguments);
else
return this.nsIAuthPrompt_prompt.apply(this, arguments);
},
promptUsernameAndPassword: function promptUsernameAndPassword() {
// Both have 6 args, so use types.
if (typeof arguments[2] == "object")
return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
else
return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
},
promptPassword: function promptPassword() {
// Both have 5 args, so use types.
if (typeof arguments[2] == "object")
return this.nsIPrompt_promptPassword.apply(this, arguments);
else
return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
},
/* ---------- nsIPrompt ---------- */
alert: function alert(aTitle, aText) {
let dialog = this.openDialog("chrome://browser/content/prompt/alert.xul", null);
let doc = this._doc;
this._setupPrompt(doc, "alert", aTitle, aText);
dialog.waitForClose();
},
alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) {
let dialog = this.openDialog("chrome://browser/content/prompt/alert.xul", aCheckState);
let doc = this._doc;
this._setupPrompt(doc, "alert", aTitle, aText, {value: aCheckState.value, msg: aCheckMsg});
dialog.waitForClose();
},
confirm: function confirm(aTitle, aText) {
var params = new Object();
params.result = false;
let dialog = this.openDialog("chrome://browser/content/prompt/confirm.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "confirm", aTitle, aText);
dialog.waitForClose();
return params.result;
},
confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) {
var params = new Object();
params.result = false;
params.checkbox = aCheckState;
let dialog = this.openDialog("chrome://browser/content/prompt/confirm.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "confirm", aTitle, aText, {value: aCheckState.value, msg: aCheckMsg});
dialog.waitForClose();
return params.result;
},
confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0,
aButton1, aButton2, aCheckMsg, aCheckState) {
let numButtons = 0;
let titles = [aButton0, aButton1, aButton2];
let defaultButton = 0;
if (aButtonFlags & Ci.nsIPromptService.BUTTON_POS_1_DEFAULT)
defaultButton = 1;
if (aButtonFlags & Ci.nsIPromptService.BUTTON_POS_2_DEFAULT)
defaultButton = 2;
var params = {
result: false,
checkbox: aCheckState,
defaultButton: defaultButton
}
let dialog = this.openDialog("chrome://browser/content/prompt/confirm.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "confirm", aTitle, aText, {value: aCheckState.value, msg: aCheckMsg});
let bbox = doc.getElementById("prompt-confirm-buttons-box");
while (bbox.lastChild)
bbox.removeChild(bbox.lastChild);
for (let i = 0; i < 3; i++) {
let bTitle = null;
switch (aButtonFlags & 0xff) {
case Ci.nsIPromptService.BUTTON_TITLE_OK :
bTitle = PromptUtils.getLocaleString("OK");
break;
case Ci.nsIPromptService.BUTTON_TITLE_CANCEL :
bTitle = PromptUtils.getLocaleString("Cancel");
break;
case Ci.nsIPromptService.BUTTON_TITLE_YES :
bTitle = PromptUtils.getLocaleString("Yes");
break;
case Ci.nsIPromptService.BUTTON_TITLE_NO :
bTitle = PromptUtils.getLocaleString("No");
break;
case Ci.nsIPromptService.BUTTON_TITLE_SAVE :
bTitle = PromptUtils.getLocaleString("Save");
break;
case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE :
bTitle = PromptUtils.getLocaleString("DontSave");
break;
case Ci.nsIPromptService.BUTTON_TITLE_REVERT :
bTitle = PromptUtils.getLocaleString("Revert");
break;
case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING :
bTitle = titles[i];
break;
}
if (bTitle) {
let button = doc.createElement("button");
this.setLabelForNode(button, bTitle);
if (i == defaultButton) {
button.setAttribute("command", "cmd_ok");
}
else {
button.setAttribute("oncommand",
"document.getElementById('prompt-confirm-dialog').PromptHelper.closeConfirm(" + i + ")");
}
bbox.appendChild(button);
}
aButtonFlags >>= 8;
}
dialog.waitForClose();
return params.result;
},
nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) {
return this.commonPrompt(aTitle, aText, aValue, aCheckMsg, aCheckState, false);
},
nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
aTitle, aText, aPassword, aCheckMsg, aCheckState) {
return this.commonPrompt(aTitle, aText, aPassword, aCheckMsg, aCheckState, true);
},
nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword(
aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) {
var params = new Object();
params.result = false;
params.checkbox = aCheckState;
params.user = aUsername;
params.password = aPassword;
let dialog = this.openDialog("chrome://browser/content/prompt/promptPassword.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "password", aTitle, aText, {value: aCheckState.value, msg: aCheckMsg});
doc.getElementById("prompt-password-user").value = aUsername.value;
doc.getElementById("prompt-password-password").value = aPassword.value;
dialog.waitForClose();
return params.result;
},
select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) {
var params = new Object();
params.result = false;
params.selection = aOutSelection;
let dialog = this.openDialog("chrome://browser/content/prompt/select.xul", params);
let doc = this._doc;
this._setupPrompt(doc, "select", aTitle, aText);
let list = doc.getElementById("prompt-select-list");
for (let i = 0; i < aCount; i++)
list.appendItem(aSelectList[i], null, null);
// select the first one
list.selectedIndex = 0;
dialog.waitForClose();
return params.result;
},
/* ---------- nsIAuthPrompt ---------- */
nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
// TODO: Port functions from nsLoginManagerPrompter.js to here
if (defaultText)
result.value = defaultText;
return this.nsIPrompt_prompt(title, text, result, null, {});
},
nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) {
return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass);
},
nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) {
return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass);
},
nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) {
let checkMsg = null;
let check = { value: false };
let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm);
let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword);
if (canSave) {
// Look for existing logins.
let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm);
[checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass);
}
let ok = false;
if (aUser)
ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check);
else
ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check);
if (ok && canSave && check.value)
PromptUtils.savePassword(hostname, realm, aUser, aPass);
return ok; },
/* ---------- nsIAuthPrompt2 ---------- */
promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) {
let checkMsg = null;
let check = { value: false };
let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
let [username, password] = PromptUtils.getAuthInfo(aAuthInfo);
let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
let canSave = PromptUtils.canSaveLogin(hostname, null);
if (canSave)
[checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password);
if (username.value && password.value) {
PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
}
let canAutologin = false;
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY &&
!(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) &&
Services.prefs.getBoolPref("signon.autologin.proxy"))
canAutologin = true;
let ok = canAutologin;
if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check);
else if (!ok)
ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check);
PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
if (ok && canSave && check.value)
PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm);
return ok;
},
_asyncPrompts: {},
_asyncPromptInProgress: false,
_doAsyncPrompt : function() {
if (this._asyncPromptInProgress)
return;
// Find the first prompt key we have in the queue
let hashKey = null;
for (hashKey in this._asyncPrompts)
break;
if (!hashKey)
return;
// If login manger has logins for this host, defer prompting if we're
// already waiting on a master password entry.
let prompt = this._asyncPrompts[hashKey];
let prompter = prompt.prompter;
let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo);
let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy)
return;
this._asyncPromptInProgress = true;
prompt.inProgress = true;
let self = this;
let runnable = {
run: function() {
let ok = false;
try {
ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo);
} catch (e) {
Cu.reportError("_doAsyncPrompt:run: " + e + "\n");
}
delete self._asyncPrompts[hashKey];
prompt.inProgress = false;
self._asyncPromptInProgress = false;
for each (let consumer in prompt.consumers) {
if (!consumer.callback)
// Not having a callback means that consumer didn't provide it
// or canceled the notification
continue;
try {
if (ok)
consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo);
else
consumer.callback.onAuthCancelled(consumer.context, true);
} catch (e) { /* Throw away exceptions caused by callback */ }
}
self._doAsyncPrompt();
}
}
Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL);
},
asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) {
let cancelable = null;
try {
// If the user submits a login but it fails, we need to remove the
// notification bar that was displayed. Conveniently, the user will
// be prompted for authentication again, which brings us here.
//this._removeLoginNotifications();
cancelable = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
callback: aCallback,
context: aContext,
cancel: function() {
this.callback.onAuthCancelled(this.context, false);
this.callback = null;
this.context = null;
}
};
let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
let hashKey = aLevel + "|" + hostname + "|" + httpRealm;
let asyncPrompt = this._asyncPrompts[hashKey];
if (asyncPrompt) {
asyncPrompt.consumers.push(cancelable);
return cancelable;
}
asyncPrompt = {
consumers: [cancelable],
channel: aChannel,
authInfo: aAuthInfo,
level: aLevel,
inProgress : false,
prompter: this
}
this._asyncPrompts[hashKey] = asyncPrompt;
this._doAsyncPrompt();
} catch (e) {
Cu.reportError("PromptService: " + e + "\n");
throw e;
}
return cancelable;
}
};
let PromptUtils = {
getLocaleString: function pu_getLocaleString(aKey, aService) {
if (aService == "passwdmgr")
return this.passwdBundle.GetStringFromName(aKey);
return this.bundle.GetStringFromName(aKey);
},
get pwmgr() {
delete this.pwmgr;
return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
},
getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) {
let httpRealm = /^.+ \(.+\)$/;
if (httpRealm.test(aRealmString))
return [null, null, null];
let uri = Services.io.newURI(aRealmString, null, null);
let pathname = "";
if (uri.path != "/")
pathname = uri.path;
let formattedHostname = this._getFormattedHostname(uri);
return [formattedHostname, formattedHostname + pathname, uri.username];
},
canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) {
let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname)
if (aSavePassword)
canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY)
return canSave;
},
getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) {
let checkLabel = null;
let check = { value: false };
let selectedLogin;
checkLabel = this.getLocaleString("rememberPassword", "passwdmgr");
// XXX Like the original code, we can't deal with multiple
// account selection. (bug 227632)
if (aFoundLogins.length > 0) {
selectedLogin = aFoundLogins[0];
// If the caller provided a username, try to use it. If they
// provided only a password, this will try to find a password-only
// login (or return null if none exists).
if (aUser.value)
selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value);
if (selectedLogin) {
check.value = true;
aUser.value = selectedLogin.username;
// If the caller provided a password, prefer it.
if (!aPass.value)
aPass.value = selectedLogin.password;
}
}
return [checkLabel, check];
},
findLogin: function pu_findLogin(aLogins, aName, aValue) {
for (let i = 0; i < aLogins.length; i++)
if (aLogins[i][aName] == aValue)
return aLogins[i];
return null;
},
savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) {
let selectedLogin = this.findLogin(aLogins, "username", aUser.value);
// If we didn't find an existing login, or if the username
// changed, save as a new login.
if (!selectedLogin) {
// add as new
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", "");
this.pwmgr.addLogin(newLogin);
} else if (aPass.value != selectedLogin.password) {
// update password
this.updateLogin(selectedLogin, aPass.value);
} else {
this.updateLogin(selectedLogin);
}
},
updateLogin: function pu_updateLogin(aLogin, aPassword) {
let now = Date.now();
let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
if (aPassword) {
propBag.setProperty("password", aPassword);
// Explicitly set the password change time here (even though it would
// be changed automatically), to ensure that it's exactly the same
// value as timeLastUsed.
propBag.setProperty("timePasswordChanged", now);
}
propBag.setProperty("timeLastUsed", now);
propBag.setProperty("timesUsedIncrement", 1);
this.pwmgr.modifyLogin(aLogin, propBag);
},
// JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388
makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) {
let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
let username = aAuthInfo.username;
let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo);
// Suppress "the site says: $realm" when we synthesized a missing realm.
if (!aAuthInfo.realm && !isProxy)
realm = "";
// Trim obnoxiously long realms.
if (realm.length > 150) {
realm = realm.substring(0, 150);
// Append "..." (or localized equivalent).
realm += this.ellipsis;
}
let text;
if (isProxy)
text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2);
else if (isPassOnly)
text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
else if (!realm)
text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1);
else
text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2);
return text;
},
// JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89
getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) {
let uri = aChannel.URI;
let res = { host: null, port: -1 };
if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) {
let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel);
res.host = proxy.proxyInfo.host;
res.port = proxy.proxyInfo.port;
} else {
res.host = uri.host;
res.port = uri.port;
}
return res;
},
getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) {
let hostname, realm;
// If our proxy is demanding authentication, don't use the
// channel's actual destination.
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
if (!(aChannel instanceof Ci.nsIProxiedChannel))
throw "proxy auth needs nsIProxiedChannel";
let info = aChannel.proxyInfo;
if (!info)
throw "proxy auth needs nsIProxyInfo";
// Proxies don't have a scheme, but we'll use "moz-proxy://"
// so that it's more obvious what the login is for.
let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port;
realm = aAuthInfo.realm;
if (!realm)
realm = hostname;
return [hostname, realm];
}
hostname = this.getFormattedHostname(aChannel.URI);
// If a HTTP WWW-Authenticate header specified a realm, that value
// will be available here. If it wasn't set or wasn't HTTP, we'll use
// the formatted hostname instead.
realm = aAuthInfo.realm;
if (!realm)
realm = hostname;
return [hostname, realm];
},
getAuthInfo : function pu_getAuthInfo(aAuthInfo) {
let flags = aAuthInfo.flags;
let username = {value: ""};
let password = {value: ""};
if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain)
username.value = aAuthInfo.domain + "\\" + aAuthInfo.username;
else
username.value = aAuthInfo.username;
password.value = aAuthInfo.password
return [username, password];
},
setAuthInfo : function (aAuthInfo, username, password) {
var flags = aAuthInfo.flags;
if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
// Domain is separated from username by a backslash
var idx = username.indexOf("\\");
if (idx == -1) {
aAuthInfo.username = username;
} else {
aAuthInfo.domain = username.substring(0, idx);
aAuthInfo.username = username.substring(idx+1);
}
} else {
aAuthInfo.username = username;
}
aAuthInfo.password = password;
},
getFormattedHostname : function pu_getFormattedHostname(uri) {
let scheme = uri.scheme;
let hostname = scheme + "://" + uri.host;
// If the URI explicitly specified a port, only include it when
// it's not the default. (We never want "http://foo.com:80")
port = uri.port;
if (port != -1) {
let handler = Services.io.getProtocolHandler(scheme);
if (port != handler.defaultPort)
hostname += ":" + port;
}
return hostname;
},
};
XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () {
return Services.strings.createBundle("chrome://browser/locale/passwordmgr.properties");
});
XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () {
return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties");
});
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]);

View File

@ -1,12 +0,0 @@
<!-- 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/. -->
<!ENTITY crashprompt.dialog.title "Would you like to send Mozilla crash reports?">
<!ENTITY crashprompt.dialog.privacyLink "Privacy statement of crash-reporting feature">
<!ENTITY crashprompt.dialog.acceptbutton "Send reports">
<!ENTITY crashprompt.dialog.refusebutton "Don't send">
<!ENTITY crashprompt.dialog.title2 "Privacy statement of crash-reporting feature">
<!ENTITY crashprompt.dialog.statement1 "Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses information in crash reports to diagnose and correct problems in Firefox that cause crashes. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you are using.">
<!ENTITY crashprompt.dialog.statement2 "For current versions of Firefox, “Firefox Crash Reporter” is Firefoxs crash reporting feature. With this feature, you may have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at http://crash-stats.mozilla.com/.">

View File

@ -4,3 +4,10 @@
# LOCALIZATION NOTE (crashprompt.messagebody2): %1$S - short brand name, %2$S - vendor name
crashprompt.messagebody2=We are sorry, %1$S just recovered from a crash. Sending crash reports will help %2$S make %1$S more stable and secure. You can always change your preference in Settings/Options.
crashprompt.dialog.title=Would you like to send Mozilla crash reports?
crashprompt.dialog.privacyLink=Privacy statement of crash-reporting feature
crashprompt.dialog.acceptbutton=Send reports
crashprompt.dialog.refusebutton=Don't send
crashprompt.dialog.title2=Privacy statement of crash-reporting feature
crashprompt.dialog.statement1=Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses information in crash reports to diagnose and correct problems in Firefox that cause crashes. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you are using.
crashprompt.dialog.statement2=For current versions of Firefox, “Firefox Crash Reporter” is Firefoxs crash reporting feature. With this feature, you may have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at http://crash-stats.mozilla.com/.

View File

@ -1,6 +0,0 @@
<!-- 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/. -->
<!ENTITY ok.label "OK">
<!ENTITY cancel.label "Cancel">

View File

@ -19,7 +19,6 @@
locale/browser/sync.dtd (%chrome/sync.dtd)
locale/browser/sync.properties (%chrome/sync.properties)
locale/browser/passwordmgr.properties (%chrome/passwordmgr.properties)
locale/browser/prompt.dtd (%chrome/prompt.dtd)
locale/browser/phishing.dtd (%chrome/phishing.dtd)
locale/browser/crashprompt.dtd (%chrome/crashprompt.dtd)
locale/browser/crashprompt.properties (%chrome/crashprompt.properties)

View File

@ -23,6 +23,10 @@ pref("metro.debug.selection.displayRanges", false);
pref("metro.debug.selection.dumpRanges", false);
pref("metro.debug.selection.dumpEvents", false);
// Enable tab-modal prompts
pref("prompts.tab_modal.enabled", true);
// Enable off main thread compositing
pref("layers.offmainthreadcomposition.enabled", true);
pref("layers.async-pan-zoom.enabled", false);

View File

@ -498,107 +498,26 @@ notification {
-moz-box-flex: 9999;
}
/* Dialogs ----------------------------------------------------------------- */
/* Prompts ----------------------------------------------------------------- */
.modal-block,
.perm-modal-block {
background-color: rgba(0,0,0,.6);
-moz-box-align: center;
-moz-box-pack: center;
-moz-box-orient: horizontal;
.mainContainer {
/* TODO: These values should probably come from somewhere. Maybe the same as the width of our overlay buttons? */
padding-left: 60px;
padding-right: 60px;
}
.dialog-dark,
.panel-arrowcontent {
background: url("chrome://browser/skin/images/popup-bg-hdpi.png") left bottom repeat-x;
background-color: white;
border-radius: @border_radius_normal@;
box-shadow: black 0 @border_radius_tiny@ @border_radius_tiny@,
black 0 -@border_radius_tiny@ @border_radius_tiny@;
padding: @padding_normal@ 0; /* core spacing on top/bottom */
}
dialog {
-moz-box-orient: horizontal;
-moz-box-align: center;
-moz-box-pack: center;
}
dialog > .prompt-inner {
width: 600px;
}
.prompt-header > .prompt-message {
white-space: pre-wrap;
}
.prompt-header > .button-checkbox {
margin-left: @margin_large@;
}
.prompt-title {
padding-top: @metro_spacing_normal@;
text-align: start;
.info\.title {
font-size: @metro_font_large@;
}
/* Authentication dialogs do not have a title */
.prompt-title:empty {
display: none;
}
.prompt-message {
padding-top: @metro_spacing_normal@;
text-align: start;
.info\.body {
font-size: @metro_font_normal@;
}
.prompt-buttons {
-moz-box-pack: end;
text-align: end;
padding: @metro_spacing_normal@ 0;
}
.prompt-edit {
margin: @margin_xnormal@;
font-size: @font_normal@;
text-align: start;
}
/* additional styles for crash prompt dialog */
/* specific height and wider for the long privacy statement */
.prompt-inner-statement {
margin-top: @metro_spacing_xnormal@;
margin-bottom: @metro_spacing_xnormal@;
height: 450px;
width: 800px;
}
.crash-message {
text-align: justify;
margin-top: 5px;
margin-bottom: @metro_spacing_small@;
font-size: @metro_font_normal@;
}
.crash-link {
margin-top: @metro_spacing_small@;
}
/* temp - bug 887176 */
.crash-privacy-back-button {
width: 26px;
margin-top: 22px; /* align with title text */
-moz-margin-end: 15px;
background-image: url(chrome://browser/skin/images/appbar-stop.png);
background-origin: border-box;
background-position: right 0 top 0;
background-repeat: no-repeat;
}
.crash-privacy-statement-scroller {
overflow: auto;
.buttonContainer {
background-color: transparent;
padding: none;
border: none;
}
/* Arrowbox ---------------------------------------------------------------- */