toolkit/components/prompts/src/CommonDialog.jsm

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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
michael@0 5 this.EXPORTED_SYMBOLS = ["CommonDialog"];
michael@0 6
michael@0 7 const Ci = Components.interfaces;
michael@0 8 const Cr = Components.results;
michael@0 9 const Cc = Components.classes;
michael@0 10 const Cu = Components.utils;
michael@0 11
michael@0 12 Cu.import("resource://gre/modules/Services.jsm");
michael@0 13
michael@0 14
michael@0 15 this.CommonDialog = function CommonDialog(args, ui) {
michael@0 16 this.args = args;
michael@0 17 this.ui = ui;
michael@0 18 }
michael@0 19
michael@0 20 CommonDialog.prototype = {
michael@0 21 args : null,
michael@0 22 ui : null,
michael@0 23
michael@0 24 hasInputField : true,
michael@0 25 numButtons : undefined,
michael@0 26 iconClass : undefined,
michael@0 27 soundID : undefined,
michael@0 28 focusTimer : null,
michael@0 29
michael@0 30 onLoad : function(xulDialog) {
michael@0 31 switch (this.args.promptType) {
michael@0 32 case "alert":
michael@0 33 case "alertCheck":
michael@0 34 this.hasInputField = false;
michael@0 35 this.numButtons = 1;
michael@0 36 this.iconClass = ["alert-icon"];
michael@0 37 this.soundID = Ci.nsISound.EVENT_ALERT_DIALOG_OPEN;
michael@0 38 break;
michael@0 39 case "confirmCheck":
michael@0 40 case "confirm":
michael@0 41 this.hasInputField = false;
michael@0 42 this.numButtons = 2;
michael@0 43 this.iconClass = ["question-icon"];
michael@0 44 this.soundID = Ci.nsISound.EVENT_CONFIRM_DIALOG_OPEN;
michael@0 45 break;
michael@0 46 case "confirmEx":
michael@0 47 var numButtons = 0;
michael@0 48 if (this.args.button0Label)
michael@0 49 numButtons++;
michael@0 50 if (this.args.button1Label)
michael@0 51 numButtons++;
michael@0 52 if (this.args.button2Label)
michael@0 53 numButtons++;
michael@0 54 if (this.args.button3Label)
michael@0 55 numButtons++;
michael@0 56 if (numButtons == 0)
michael@0 57 throw "A dialog with no buttons? Can not haz.";
michael@0 58 this.numButtons = numButtons;
michael@0 59 this.hasInputField = false;
michael@0 60 this.iconClass = ["question-icon"];
michael@0 61 this.soundID = Ci.nsISound.EVENT_CONFIRM_DIALOG_OPEN;
michael@0 62 break;
michael@0 63 case "prompt":
michael@0 64 this.numButtons = 2;
michael@0 65 this.iconClass = ["question-icon"];
michael@0 66 this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
michael@0 67 this.initTextbox("login", this.args.value);
michael@0 68 // Clear the label, since this isn't really a username prompt.
michael@0 69 this.ui.loginLabel.setAttribute("value", "");
michael@0 70 break;
michael@0 71 case "promptUserAndPass":
michael@0 72 this.numButtons = 2;
michael@0 73 this.iconClass = ["authentication-icon", "question-icon"];
michael@0 74 this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
michael@0 75 this.initTextbox("login", this.args.user);
michael@0 76 this.initTextbox("password1", this.args.pass);
michael@0 77 break;
michael@0 78 case "promptPassword":
michael@0 79 this.numButtons = 2;
michael@0 80 this.iconClass = ["authentication-icon", "question-icon"];
michael@0 81 this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
michael@0 82 this.initTextbox("password1", this.args.pass);
michael@0 83 // Clear the label, since the message presumably indicates its purpose.
michael@0 84 this.ui.password1Label.setAttribute("value", "");
michael@0 85 break;
michael@0 86 default:
michael@0 87 Cu.reportError("commonDialog opened for unknown type: " + this.args.promptType);
michael@0 88 throw "unknown dialog type";
michael@0 89 }
michael@0 90
michael@0 91 // set the document title
michael@0 92 let title = this.args.title;
michael@0 93 // OS X doesn't have a title on modal dialogs, this is hidden on other platforms.
michael@0 94 let infoTitle = this.ui.infoTitle;
michael@0 95 infoTitle.appendChild(infoTitle.ownerDocument.createTextNode(title));
michael@0 96 if (xulDialog)
michael@0 97 xulDialog.ownerDocument.title = title;
michael@0 98
michael@0 99 // Set button labels and visibility
michael@0 100 //
michael@0 101 // This assumes that button0 defaults to a visible "ok" button, and
michael@0 102 // button1 defaults to a visible "cancel" button. The other 2 buttons
michael@0 103 // have no default labels (and are hidden).
michael@0 104 switch (this.numButtons) {
michael@0 105 case 4:
michael@0 106 this.setLabelForNode(this.ui.button3, this.args.button3Label);
michael@0 107 this.ui.button3.hidden = false;
michael@0 108 // fall through
michael@0 109 case 3:
michael@0 110 this.setLabelForNode(this.ui.button2, this.args.button2Label);
michael@0 111 this.ui.button2.hidden = false;
michael@0 112 // fall through
michael@0 113 case 2:
michael@0 114 // Defaults to a visible "cancel" button
michael@0 115 if (this.args.button1Label)
michael@0 116 this.setLabelForNode(this.ui.button1, this.args.button1Label);
michael@0 117 break;
michael@0 118
michael@0 119 case 1:
michael@0 120 this.ui.button1.hidden = true;
michael@0 121 break;
michael@0 122 }
michael@0 123 // Defaults to a visible "ok" button
michael@0 124 if (this.args.button0Label)
michael@0 125 this.setLabelForNode(this.ui.button0, this.args.button0Label);
michael@0 126
michael@0 127 // display the main text
michael@0 128 // Bug 317334 - crop string length as a workaround.
michael@0 129 let croppedMessage = this.args.text.substr(0, 10000);
michael@0 130 let infoBody = this.ui.infoBody;
michael@0 131 infoBody.appendChild(infoBody.ownerDocument.createTextNode(croppedMessage));
michael@0 132
michael@0 133 let label = this.args.checkLabel;
michael@0 134 if (label) {
michael@0 135 // Only show the checkbox if label has a value.
michael@0 136 this.ui.checkboxContainer.hidden = false;
michael@0 137 this.setLabelForNode(this.ui.checkbox, label);
michael@0 138 this.ui.checkbox.checked = this.args.checked;
michael@0 139 }
michael@0 140
michael@0 141 // set the icon
michael@0 142 let icon = this.ui.infoIcon;
michael@0 143 if (icon)
michael@0 144 this.iconClass.forEach(function(el,idx,arr) icon.classList.add(el));
michael@0 145
michael@0 146 // set default result to cancelled
michael@0 147 this.args.ok = false;
michael@0 148 this.args.buttonNumClicked = 1;
michael@0 149
michael@0 150
michael@0 151 // Set the default button
michael@0 152 let b = (this.args.defaultButtonNum || 0);
michael@0 153 let button = this.ui["button" + b];
michael@0 154
michael@0 155 if (xulDialog)
michael@0 156 xulDialog.defaultButton = ['accept', 'cancel', 'extra1', 'extra2'][b];
michael@0 157 else
michael@0 158 button.setAttribute("default", "true");
michael@0 159
michael@0 160 // Set default focus / selection.
michael@0 161 this.setDefaultFocus(true);
michael@0 162
michael@0 163 if (this.args.enableDelay) {
michael@0 164 this.setButtonsEnabledState(false);
michael@0 165 // Use a longer, pref-controlled delay when the dialog is first opened.
michael@0 166 let delayTime = Services.prefs.getIntPref("security.dialog_enable_delay");
michael@0 167 this.startOnFocusDelay(delayTime);
michael@0 168 let self = this;
michael@0 169 this.ui.focusTarget.addEventListener("blur", function(e) { self.onBlur(e); }, false);
michael@0 170 this.ui.focusTarget.addEventListener("focus", function(e) { self.onFocus(e); }, false);
michael@0 171 }
michael@0 172
michael@0 173 // Play a sound (unless we're tab-modal -- don't want those to feel like OS prompts).
michael@0 174 try {
michael@0 175 if (xulDialog && this.soundID) {
michael@0 176 Cc["@mozilla.org/sound;1"].
michael@0 177 createInstance(Ci.nsISound).
michael@0 178 playEventSound(this.soundID);
michael@0 179 }
michael@0 180 } catch (e) {
michael@0 181 Cu.reportError("Couldn't play common dialog event sound: " + e);
michael@0 182 }
michael@0 183
michael@0 184 let topic = "common-dialog-loaded";
michael@0 185 if (!xulDialog)
michael@0 186 topic = "tabmodal-dialog-loaded";
michael@0 187 Services.obs.notifyObservers(this.ui.prompt, topic, null);
michael@0 188 },
michael@0 189
michael@0 190 setLabelForNode: function(aNode, aLabel) {
michael@0 191 // This is for labels which may contain embedded access keys.
michael@0 192 // If we end in (&X) where X represents the access key, optionally preceded
michael@0 193 // by spaces and/or followed by the ':' character, store the access key and
michael@0 194 // remove the access key placeholder + leading spaces from the label.
michael@0 195 // Otherwise a character preceded by one but not two &s is the access key.
michael@0 196 // Store it and remove the &.
michael@0 197
michael@0 198 // Note that if you change the following code, see the comment of
michael@0 199 // nsTextBoxFrame::UpdateAccessTitle.
michael@0 200 var accessKey = null;
michael@0 201 if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) {
michael@0 202 aLabel = RegExp.leftContext + RegExp.$2;
michael@0 203 accessKey = RegExp.$1;
michael@0 204 } else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) {
michael@0 205 aLabel = RegExp.$1 + RegExp.$2;
michael@0 206 accessKey = RegExp.$3;
michael@0 207 }
michael@0 208
michael@0 209 // && is the magic sequence to embed an & in your label.
michael@0 210 aLabel = aLabel.replace(/\&\&/g, "&");
michael@0 211 aNode.label = aLabel;
michael@0 212
michael@0 213 // XXXjag bug 325251
michael@0 214 // Need to set this after aNode.setAttribute("value", aLabel);
michael@0 215 if (accessKey)
michael@0 216 aNode.accessKey = accessKey;
michael@0 217 },
michael@0 218
michael@0 219
michael@0 220 initTextbox : function (aName, aValue) {
michael@0 221 this.ui[aName + "Container"].hidden = false;
michael@0 222 this.ui[aName + "Textbox"].setAttribute("value",
michael@0 223 aValue !== null ? aValue : "");
michael@0 224 },
michael@0 225
michael@0 226 setButtonsEnabledState : function(enabled) {
michael@0 227 this.ui.button0.disabled = !enabled;
michael@0 228 // button1 (cancel) remains enabled.
michael@0 229 this.ui.button2.disabled = !enabled;
michael@0 230 this.ui.button3.disabled = !enabled;
michael@0 231 },
michael@0 232
michael@0 233 onBlur : function (aEvent) {
michael@0 234 if (aEvent.target != this.ui.focusTarget)
michael@0 235 return;
michael@0 236 this.setButtonsEnabledState(false);
michael@0 237
michael@0 238 // If we blur while waiting to enable the buttons, just cancel the
michael@0 239 // timer to ensure the delay doesn't fire while not focused.
michael@0 240 if (this.focusTimer) {
michael@0 241 this.focusTimer.cancel();
michael@0 242 this.focusTimer = null;
michael@0 243 }
michael@0 244 },
michael@0 245
michael@0 246 onFocus : function (aEvent) {
michael@0 247 if (aEvent.target != this.ui.focusTarget)
michael@0 248 return;
michael@0 249 this.startOnFocusDelay();
michael@0 250 },
michael@0 251
michael@0 252 startOnFocusDelay : function(delayTime) {
michael@0 253 // Shouldn't already have a timer, but just in case...
michael@0 254 if (this.focusTimer)
michael@0 255 return;
michael@0 256 // If no delay specified, use 250ms. (This is the normal case for when
michael@0 257 // after the dialog has been opened and focus shifts.)
michael@0 258 if (!delayTime)
michael@0 259 delayTime = 250;
michael@0 260 let self = this;
michael@0 261 this.focusTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 262 this.focusTimer.initWithCallback(function() { self.onFocusTimeout(); },
michael@0 263 delayTime, Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 264 },
michael@0 265
michael@0 266 onFocusTimeout : function() {
michael@0 267 this.focusTimer = null;
michael@0 268 this.setButtonsEnabledState(true);
michael@0 269 },
michael@0 270
michael@0 271 setDefaultFocus : function(isInitialLoad) {
michael@0 272 let b = (this.args.defaultButtonNum || 0);
michael@0 273 let button = this.ui["button" + b];
michael@0 274
michael@0 275 if (!this.hasInputField) {
michael@0 276 let isOSX = ("nsILocalFileMac" in Components.interfaces);
michael@0 277 if (isOSX)
michael@0 278 this.ui.infoBody.focus();
michael@0 279 else
michael@0 280 button.focus();
michael@0 281 } else {
michael@0 282 // When the prompt is initialized, focus and select the textbox
michael@0 283 // contents. Afterwards, only focus the textbox.
michael@0 284 if (this.args.promptType == "promptPassword") {
michael@0 285 if (isInitialLoad)
michael@0 286 this.ui.password1Textbox.select();
michael@0 287 else
michael@0 288 this.ui.password1Textbox.focus();
michael@0 289 } else {
michael@0 290 if (isInitialLoad)
michael@0 291 this.ui.loginTextbox.select();
michael@0 292 else
michael@0 293 this.ui.loginTextbox.focus();
michael@0 294 }
michael@0 295 }
michael@0 296 },
michael@0 297
michael@0 298 onCheckbox : function() {
michael@0 299 this.args.checked = this.ui.checkbox.checked;
michael@0 300 },
michael@0 301
michael@0 302 onButton0 : function() {
michael@0 303 this.args.promptActive = false;
michael@0 304 this.args.ok = true;
michael@0 305 this.args.buttonNumClicked = 0;
michael@0 306
michael@0 307 let username = this.ui.loginTextbox.value;
michael@0 308 let password = this.ui.password1Textbox.value;
michael@0 309
michael@0 310 // Return textfield values
michael@0 311 switch (this.args.promptType) {
michael@0 312 case "prompt":
michael@0 313 this.args.value = username;
michael@0 314 break;
michael@0 315 case "promptUserAndPass":
michael@0 316 this.args.user = username;
michael@0 317 this.args.pass = password;
michael@0 318 break;
michael@0 319 case "promptPassword":
michael@0 320 this.args.pass = password;
michael@0 321 break;
michael@0 322 }
michael@0 323 },
michael@0 324
michael@0 325 onButton1 : function() {
michael@0 326 this.args.promptActive = false;
michael@0 327 this.args.buttonNumClicked = 1;
michael@0 328 },
michael@0 329
michael@0 330 onButton2 : function() {
michael@0 331 this.args.promptActive = false;
michael@0 332 this.args.buttonNumClicked = 2;
michael@0 333 },
michael@0 334
michael@0 335 onButton3 : function() {
michael@0 336 this.args.promptActive = false;
michael@0 337 this.args.buttonNumClicked = 3;
michael@0 338 },
michael@0 339
michael@0 340 abortPrompt : function() {
michael@0 341 this.args.promptActive = false;
michael@0 342 this.args.promptAborted = true;
michael@0 343 },
michael@0 344
michael@0 345 };

mercurial