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