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.

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

mercurial