toolkit/components/prompts/src/nsPrompter.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/prompts/src/nsPrompter.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,934 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +
     1.9 +const Cc = Components.classes;
    1.10 +const Ci = Components.interfaces;
    1.11 +const Cr = Components.results;
    1.12 +const Cu = Components.utils;
    1.13 +
    1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.15 +Cu.import("resource://gre/modules/Services.jsm");
    1.16 +Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
    1.17 +
    1.18 +function Prompter() {
    1.19 +    // Note that EmbedPrompter clones this implementation.
    1.20 +}
    1.21 +
    1.22 +Prompter.prototype = {
    1.23 +    classID          : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"),
    1.24 +    QueryInterface   : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
    1.25 +
    1.26 +
    1.27 +    /* ----------  private memebers  ---------- */
    1.28 +
    1.29 +    pickPrompter : function (domWin) {
    1.30 +        return new ModalPrompter(domWin);
    1.31 +    },
    1.32 +
    1.33 +
    1.34 +    /* ----------  nsIPromptFactory  ---------- */
    1.35 +
    1.36 +
    1.37 +    getPrompt : function (domWin, iid) {
    1.38 +        // This is still kind of dumb; the C++ code delegated to login manager
    1.39 +        // here, which in turn calls back into us via nsIPromptService2.
    1.40 +        if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) {
    1.41 +            try {
    1.42 +                let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
    1.43 +                            getService(Ci.nsIPromptFactory);
    1.44 +                return pwmgr.getPrompt(domWin, iid);
    1.45 +            } catch (e) {
    1.46 +                Cu.reportError("nsPrompter: Delegation to password manager failed: " + e);
    1.47 +            }
    1.48 +        }
    1.49 +
    1.50 +        let p = new ModalPrompter(domWin);
    1.51 +        p.QueryInterface(iid);
    1.52 +        return p;
    1.53 +    },
    1.54 +
    1.55 +
    1.56 +    /* ----------  nsIPromptService  ---------- */
    1.57 +
    1.58 +
    1.59 +    alert : function (domWin, title, text) {
    1.60 +        let p = this.pickPrompter(domWin);
    1.61 +        p.alert(title, text);
    1.62 +    },
    1.63 +
    1.64 +    alertCheck : function (domWin, title, text, checkLabel, checkValue) {
    1.65 +        let p = this.pickPrompter(domWin);
    1.66 +        p.alertCheck(title, text, checkLabel, checkValue);
    1.67 +    },
    1.68 +
    1.69 +    confirm : function (domWin, title, text) {
    1.70 +        let p = this.pickPrompter(domWin);
    1.71 +        return p.confirm(title, text);
    1.72 +    },
    1.73 +
    1.74 +    confirmCheck : function (domWin, title, text, checkLabel, checkValue) {
    1.75 +        let p = this.pickPrompter(domWin);
    1.76 +        return p.confirmCheck(title, text, checkLabel, checkValue);
    1.77 +    },
    1.78 +
    1.79 +    confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) {
    1.80 +        let p = this.pickPrompter(domWin);
    1.81 +        return p.confirmEx(title, text,  flags, button0, button1, button2, checkLabel, checkValue);
    1.82 +    },
    1.83 +
    1.84 +    prompt : function (domWin, title, text, value, checkLabel, checkValue) {
    1.85 +        let p = this.pickPrompter(domWin);
    1.86 +        return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue);
    1.87 +    },
    1.88 +
    1.89 +    promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) {
    1.90 +        let p = this.pickPrompter(domWin);
    1.91 +        return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue);
    1.92 +    },
    1.93 +
    1.94 +    promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) {
    1.95 +        let p = this.pickPrompter(domWin);
    1.96 +        return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue);
    1.97 +    },
    1.98 +
    1.99 +    select : function (domWin, title, text, count, list, selected) {
   1.100 +        let p = this.pickPrompter(domWin);
   1.101 +        return p.select(title, text, count, list, selected);
   1.102 +    },
   1.103 +
   1.104 +
   1.105 +    /* ----------  nsIPromptService2  ---------- */
   1.106 +
   1.107 +
   1.108 +    promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) {
   1.109 +        let p = this.pickPrompter(domWin);
   1.110 +        return p.promptAuth(channel, level, authInfo, checkLabel, checkValue);
   1.111 +    },
   1.112 +
   1.113 +    asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) {
   1.114 +        let p = this.pickPrompter(domWin);
   1.115 +        return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue);
   1.116 +    },
   1.117 +
   1.118 +};
   1.119 +
   1.120 +
   1.121 +// Common utils not specific to a particular prompter style.
   1.122 +let PromptUtilsTemp = {
   1.123 +    __proto__ : PromptUtils,
   1.124 +
   1.125 +    getLocalizedString : function (key, formatArgs) {
   1.126 +        if (formatArgs)
   1.127 +            return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length);
   1.128 +        else
   1.129 +            return this.strBundle.GetStringFromName(key);
   1.130 +    },
   1.131 +
   1.132 +    confirmExHelper : function (flags, button0, button1, button2) {
   1.133 +        const BUTTON_DEFAULT_MASK = 0x03000000;
   1.134 +        let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24;
   1.135 +        let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE);
   1.136 +
   1.137 +        // Flags can be used to select a specific pre-defined button label or
   1.138 +        // a caller-supplied string (button0/button1/button2). If no flags are
   1.139 +        // set for a button, then the button won't be shown.
   1.140 +        let argText = [button0, button1, button2];
   1.141 +        let buttonLabels = [null, null, null];
   1.142 +        for (let i = 0; i < 3; i++) {
   1.143 +            let buttonLabel;
   1.144 +            switch (flags & 0xff) {
   1.145 +              case Ci.nsIPrompt.BUTTON_TITLE_OK:
   1.146 +                buttonLabel = PromptUtils.getLocalizedString("OK");
   1.147 +                break;
   1.148 +              case Ci.nsIPrompt.BUTTON_TITLE_CANCEL:
   1.149 +                buttonLabel = PromptUtils.getLocalizedString("Cancel");
   1.150 +                break;
   1.151 +              case Ci.nsIPrompt.BUTTON_TITLE_YES:
   1.152 +                buttonLabel = PromptUtils.getLocalizedString("Yes");
   1.153 +                break;
   1.154 +              case Ci.nsIPrompt.BUTTON_TITLE_NO:
   1.155 +                buttonLabel = PromptUtils.getLocalizedString("No");
   1.156 +                break;
   1.157 +              case Ci.nsIPrompt.BUTTON_TITLE_SAVE:
   1.158 +                buttonLabel = PromptUtils.getLocalizedString("Save");
   1.159 +                break;
   1.160 +              case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE:
   1.161 +                buttonLabel = PromptUtils.getLocalizedString("DontSave");
   1.162 +                break;
   1.163 +              case Ci.nsIPrompt.BUTTON_TITLE_REVERT:
   1.164 +                buttonLabel = PromptUtils.getLocalizedString("Revert");
   1.165 +                break;
   1.166 +              case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING:
   1.167 +                buttonLabel = argText[i];
   1.168 +                break;
   1.169 +            }
   1.170 +            if (buttonLabel)
   1.171 +                buttonLabels[i] = buttonLabel;
   1.172 +            flags >>= 8;
   1.173 +        }
   1.174 +
   1.175 +        return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled];
   1.176 +    },
   1.177 +
   1.178 +    getAuthInfo : function (authInfo) {
   1.179 +        let username, password;
   1.180 +
   1.181 +        let flags = authInfo.flags;
   1.182 +        if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain)
   1.183 +            username = authInfo.domain + "\\" + authInfo.username;
   1.184 +        else
   1.185 +            username = authInfo.username;
   1.186 +
   1.187 +        password = authInfo.password;
   1.188 +
   1.189 +        return [username, password];
   1.190 +    },
   1.191 +
   1.192 +    setAuthInfo : function (authInfo, username, password) {
   1.193 +        let flags = authInfo.flags;
   1.194 +        if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
   1.195 +            // Domain is separated from username by a backslash
   1.196 +            let idx = username.indexOf("\\");
   1.197 +            if (idx == -1) {
   1.198 +                authInfo.username = username;
   1.199 +            } else {
   1.200 +                authInfo.domain   =  username.substring(0, idx);
   1.201 +                authInfo.username =  username.substring(idx+1);
   1.202 +            }
   1.203 +        } else {
   1.204 +            authInfo.username = username;
   1.205 +        }
   1.206 +        authInfo.password = password;
   1.207 +    },
   1.208 +
   1.209 +    // Copied from login manager
   1.210 +    getFormattedHostname : function (uri) {
   1.211 +        let scheme = uri.scheme;
   1.212 +        let hostname = scheme + "://" + uri.host;
   1.213 +
   1.214 +        // If the URI explicitly specified a port, only include it when
   1.215 +        // it's not the default. (We never want "http://foo.com:80")
   1.216 +        port = uri.port;
   1.217 +        if (port != -1) {
   1.218 +            let handler = Services.io.getProtocolHandler(scheme);
   1.219 +            if (port != handler.defaultPort)
   1.220 +                hostname += ":" + port;
   1.221 +        }
   1.222 +
   1.223 +        return hostname;
   1.224 +    },
   1.225 +
   1.226 +    // Copied from login manager
   1.227 +    getAuthTarget : function (aChannel, aAuthInfo) {
   1.228 +        let hostname, realm;
   1.229 +
   1.230 +        // If our proxy is demanding authentication, don't use the
   1.231 +        // channel's actual destination.
   1.232 +        if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
   1.233 +            if (!(aChannel instanceof Ci.nsIProxiedChannel))
   1.234 +                throw "proxy auth needs nsIProxiedChannel";
   1.235 +
   1.236 +            let info = aChannel.proxyInfo;
   1.237 +            if (!info)
   1.238 +                throw "proxy auth needs nsIProxyInfo";
   1.239 +
   1.240 +            // Proxies don't have a scheme, but we'll use "moz-proxy://"
   1.241 +            // so that it's more obvious what the login is for.
   1.242 +            let idnService = Cc["@mozilla.org/network/idn-service;1"].
   1.243 +                             getService(Ci.nsIIDNService);
   1.244 +            hostname = "moz-proxy://" +
   1.245 +                        idnService.convertUTF8toACE(info.host) +
   1.246 +                        ":" + info.port;
   1.247 +            realm = aAuthInfo.realm;
   1.248 +            if (!realm)
   1.249 +                realm = hostname;
   1.250 +
   1.251 +            return [hostname, realm];
   1.252 +        }
   1.253 +
   1.254 +        hostname = this.getFormattedHostname(aChannel.URI);
   1.255 +
   1.256 +        // If a HTTP WWW-Authenticate header specified a realm, that value
   1.257 +        // will be available here. If it wasn't set or wasn't HTTP, we'll use
   1.258 +        // the formatted hostname instead.
   1.259 +        realm = aAuthInfo.realm;
   1.260 +        if (!realm)
   1.261 +            realm = hostname;
   1.262 +
   1.263 +        return [hostname, realm];
   1.264 +    },
   1.265 +
   1.266 +
   1.267 +    makeAuthMessage : function (channel, authInfo) {
   1.268 +        let isProxy    = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
   1.269 +        let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
   1.270 +
   1.271 +        let username = authInfo.username;
   1.272 +        let [displayHost, realm] = this.getAuthTarget(channel, authInfo);
   1.273 +
   1.274 +        // Suppress "the site says: $realm" when we synthesized a missing realm.
   1.275 +        if (!authInfo.realm && !isProxy)
   1.276 +            realm = "";
   1.277 +
   1.278 +        // Trim obnoxiously long realms.
   1.279 +        if (realm.length > 150) {
   1.280 +            realm = realm.substring(0, 150);
   1.281 +            // Append "..." (or localized equivalent).
   1.282 +            realm += this.ellipsis;
   1.283 +        }
   1.284 +
   1.285 +        let text;
   1.286 +        if (isProxy)
   1.287 +            text = PromptUtils.getLocalizedString("EnterLoginForProxy", [realm, displayHost]);
   1.288 +        else if (isPassOnly)
   1.289 +            text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]);
   1.290 +        else if (!realm)
   1.291 +            text = PromptUtils.getLocalizedString("EnterUserPasswordFor", [displayHost]);
   1.292 +        else
   1.293 +            text = PromptUtils.getLocalizedString("EnterLoginForRealm", [realm, displayHost]);
   1.294 +
   1.295 +        return text;
   1.296 +    },
   1.297 +
   1.298 +    getTabModalPrompt : function (domWin) {
   1.299 +        var promptBox = null;
   1.300 +
   1.301 +        try {
   1.302 +            // Get the topmost window, in case we're in a frame.
   1.303 +            var promptWin = domWin.top;
   1.304 +
   1.305 +            // Get the chrome window for the content window we're using.
   1.306 +            // (Unwrap because we need a non-IDL property below.)
   1.307 +            var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor)
   1.308 +                                     .getInterface(Ci.nsIWebNavigation)
   1.309 +                                     .QueryInterface(Ci.nsIDocShell)
   1.310 +                                     .chromeEventHandler.ownerDocument
   1.311 +                                     .defaultView.wrappedJSObject;
   1.312 +
   1.313 +            if (chromeWin.getTabModalPromptBox)
   1.314 +                promptBox = chromeWin.getTabModalPromptBox(promptWin);
   1.315 +        } catch (e) {
   1.316 +            // If any errors happen, just assume no tabmodal prompter.
   1.317 +        }
   1.318 +
   1.319 +        return promptBox;
   1.320 +    },
   1.321 +};
   1.322 +
   1.323 +PromptUtils = PromptUtilsTemp;
   1.324 +
   1.325 +XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () {
   1.326 +    let bunService = Cc["@mozilla.org/intl/stringbundle;1"].
   1.327 +                     getService(Ci.nsIStringBundleService);
   1.328 +    let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties");
   1.329 +    if (!bundle)
   1.330 +        throw "String bundle for Prompter not present!";
   1.331 +    return bundle;
   1.332 +});
   1.333 +
   1.334 +XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () {
   1.335 +    let ellipsis = "\u2026";
   1.336 +    try {
   1.337 +        ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
   1.338 +    } catch (e) { }
   1.339 +    return ellipsis;
   1.340 +});
   1.341 +
   1.342 +
   1.343 +
   1.344 +function openModalWindow(domWin, uri, args) {
   1.345 +    // There's an implied contract that says modal prompts should still work
   1.346 +    // when no "parent" window is passed for the dialog (eg, the "Master
   1.347 +    // Password" dialog does this).  These prompts must be shown even if there
   1.348 +    // are *no* visible windows at all.
   1.349 +    // There's also a requirement for prompts to be blocked if a window is
   1.350 +    // passed and that window is hidden (eg, auth prompts are supressed if the
   1.351 +    // passed window is the hidden window).
   1.352 +    // See bug 875157 comment 30 for more...
   1.353 +    if (domWin) {
   1.354 +        // a domWin was passed, so we can apply the check for it being hidden.
   1.355 +        let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
   1.356 +                             .getInterface(Ci.nsIDOMWindowUtils);
   1.357 +
   1.358 +        if (winUtils && !winUtils.isParentWindowMainWidgetVisible) {
   1.359 +            throw Components.Exception("Cannot call openModalWindow on a hidden window",
   1.360 +                                       Cr.NS_ERROR_NOT_AVAILABLE);
   1.361 +        }
   1.362 +    } else {
   1.363 +        // We try and find a window to use as the parent, but don't consider
   1.364 +        // if that is visible before showing the prompt.
   1.365 +        domWin = Services.ww.activeWindow;
   1.366 +        // domWin may still be null here if there are _no_ windows open.
   1.367 +    }
   1.368 +    // Note that we don't need to fire DOMWillOpenModalDialog and
   1.369 +    // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal
   1.370 +    // will do that. Similarly for enterModalState / leaveModalState.
   1.371 +
   1.372 +    Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args);
   1.373 +}
   1.374 +
   1.375 +function openTabPrompt(domWin, tabPrompt, args) {
   1.376 +    PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog");
   1.377 +
   1.378 +    let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
   1.379 +                         .getInterface(Ci.nsIDOMWindowUtils);
   1.380 +    winUtils.enterModalState();
   1.381 +
   1.382 +    // We provide a callback so the prompt can close itself. We don't want to
   1.383 +    // wait for this event loop to return... Otherwise the presence of other
   1.384 +    // prompts on the call stack would in this dialog appearing unresponsive
   1.385 +    // until the other prompts had been closed.
   1.386 +    let callbackInvoked = false;
   1.387 +    let newPrompt;
   1.388 +    function onPromptClose(forceCleanup) {
   1.389 +        if (!newPrompt && !forceCleanup)
   1.390 +            return;
   1.391 +        callbackInvoked = true;
   1.392 +        if (newPrompt)
   1.393 +            tabPrompt.removePrompt(newPrompt);
   1.394 +
   1.395 +        domWin.removeEventListener("pagehide", pagehide);
   1.396 +
   1.397 +        winUtils.leaveModalState();
   1.398 +
   1.399 +        PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
   1.400 +    }
   1.401 +
   1.402 +    domWin.addEventListener("pagehide", pagehide);
   1.403 +    function pagehide() {
   1.404 +        domWin.removeEventListener("pagehide", pagehide);
   1.405 +
   1.406 +        if (newPrompt) {
   1.407 +            newPrompt.abortPrompt();
   1.408 +        }
   1.409 +    }
   1.410 +
   1.411 +    try {
   1.412 +        let topPrincipal = domWin.top.document.nodePrincipal;
   1.413 +        let promptPrincipal = domWin.document.nodePrincipal;
   1.414 +        args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
   1.415 +        args.promptActive = true;
   1.416 +
   1.417 +        newPrompt = tabPrompt.appendPrompt(args, onPromptClose);
   1.418 +
   1.419 +        // TODO since we don't actually open a window, need to check if
   1.420 +        // there's other stuff in nsWindowWatcher::OpenWindowInternal
   1.421 +        // that we might need to do here as well.
   1.422 +
   1.423 +        let thread = Services.tm.currentThread;
   1.424 +        while (args.promptActive)
   1.425 +            thread.processNextEvent(true);
   1.426 +        delete args.promptActive;
   1.427 +
   1.428 +        if (args.promptAborted)
   1.429 +            throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE);
   1.430 +    } finally {
   1.431 +        // If the prompt unexpectedly failed to invoke the callback, do so here.
   1.432 +        if (!callbackInvoked)
   1.433 +            onPromptClose(true);
   1.434 +    }
   1.435 +}
   1.436 +
   1.437 +function openRemotePrompt(domWin, args, tabPrompt) {
   1.438 +    let messageManager = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
   1.439 +                         .getInterface(Ci.nsIWebNavigation)
   1.440 +                         .QueryInterface(Ci.nsIDocShell)
   1.441 +                         .QueryInterface(Ci.nsIInterfaceRequestor)
   1.442 +                         .getInterface(Ci.nsITabChild)
   1.443 +                         .messageManager;
   1.444 +
   1.445 +    PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog");
   1.446 +
   1.447 +    let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
   1.448 +                         .getInterface(Ci.nsIDOMWindowUtils);
   1.449 +    winUtils.enterModalState();
   1.450 +    let closed = false;
   1.451 +
   1.452 +    // It should be hard or impossible to cause a window to create multiple
   1.453 +    // prompts, but just in case, give our prompt an ID.
   1.454 +    let id = "id" + Cc["@mozilla.org/uuid-generator;1"]
   1.455 +                      .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   1.456 +
   1.457 +    messageManager.addMessageListener("Prompt:Close", function listener(message) {
   1.458 +        if (message.data._remoteId !== id) {
   1.459 +            return;
   1.460 +        }
   1.461 +
   1.462 +        messageManager.removeMessageListener("Prompt:Close", listener);
   1.463 +        domWin.removeEventListener("pagehide", pagehide);
   1.464 +
   1.465 +        winUtils.leaveModalState();
   1.466 +        PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
   1.467 +
   1.468 +        // Copy the response from the closed prompt into our args, it will be
   1.469 +        // read by our caller.
   1.470 +        if (message.data) {
   1.471 +            for (let key in message.data) {
   1.472 +                args[key] = message.data[key];
   1.473 +            }
   1.474 +        }
   1.475 +
   1.476 +        // Exit our nested event loop when we unwind.
   1.477 +        closed = true;
   1.478 +    });
   1.479 +
   1.480 +    domWin.addEventListener("pagehide", pagehide);
   1.481 +    function pagehide() {
   1.482 +        domWin.removeEventListener("pagehide", pagehide);
   1.483 +        messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id });
   1.484 +    }
   1.485 +
   1.486 +    let topPrincipal = domWin.top.document.nodePrincipal;
   1.487 +    let promptPrincipal = domWin.document.nodePrincipal;
   1.488 +    args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
   1.489 +
   1.490 +    args._remoteId = id;
   1.491 +
   1.492 +    messageManager.sendAsyncMessage("Prompt:Open", args, {});
   1.493 +
   1.494 +    let thread = Services.tm.currentThread;
   1.495 +    while (!closed) {
   1.496 +        thread.processNextEvent(true);
   1.497 +    }
   1.498 +}
   1.499 +
   1.500 +function ModalPrompter(domWin) {
   1.501 +    this.domWin = domWin;
   1.502 +}
   1.503 +ModalPrompter.prototype = {
   1.504 +    domWin : null,
   1.505 +    /*
   1.506 +     * Default to not using a tab-modal prompt, unless the caller opts in by
   1.507 +     * QIing to nsIWritablePropertyBag and setting the value of this property
   1.508 +     * to true.
   1.509 +     */
   1.510 +    allowTabModal : false,
   1.511 +
   1.512 +    QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt,
   1.513 +                                            Ci.nsIAuthPrompt2,
   1.514 +                                            Ci.nsIWritablePropertyBag2]),
   1.515 +
   1.516 +
   1.517 +    /* ---------- internal methods ---------- */
   1.518 +
   1.519 +
   1.520 +    openPrompt : function (args) {
   1.521 +        // Check pref, if false/missing do not ever allow tab-modal prompts.
   1.522 +        const prefName = "prompts.tab_modal.enabled";
   1.523 +        let prefValue = false;
   1.524 +        if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL)
   1.525 +            prefValue = Services.prefs.getBoolPref(prefName);
   1.526 +
   1.527 +        let allowTabModal = this.allowTabModal && prefValue;
   1.528 +
   1.529 +        if (allowTabModal && this.domWin) {
   1.530 +            if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
   1.531 +                openRemotePrompt(this.domWin, args, true);
   1.532 +                return;
   1.533 +            }
   1.534 +
   1.535 +            let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin);
   1.536 +            if (tabPrompt) {
   1.537 +                openTabPrompt(this.domWin, tabPrompt, args);
   1.538 +                return;
   1.539 +            }
   1.540 +        }
   1.541 +
   1.542 +        // If we can't do a tab modal prompt, fallback to using a window-modal dialog.
   1.543 +        const COMMON_DIALOG = "chrome://global/content/commonDialog.xul";
   1.544 +        const SELECT_DIALOG = "chrome://global/content/selectDialog.xul";
   1.545 +
   1.546 +        let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG;
   1.547 +
   1.548 +        if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
   1.549 +            args.uri = uri;
   1.550 +            openRemotePrompt(this.domWin, args);
   1.551 +            return;
   1.552 +        }
   1.553 +
   1.554 +        let propBag = PromptUtils.objectToPropBag(args);
   1.555 +        openModalWindow(this.domWin, uri, propBag);
   1.556 +        PromptUtils.propBagToObject(propBag, args);
   1.557 +    },
   1.558 +
   1.559 +
   1.560 +
   1.561 +    /*
   1.562 +     * ---------- interface disambiguation ----------
   1.563 +     *
   1.564 +     * nsIPrompt and nsIAuthPrompt share 3 method names with slightly
   1.565 +     * different arguments. All but prompt() have the same number of
   1.566 +     * arguments, so look at the arg types to figure out how we're being
   1.567 +     * called. :-(
   1.568 +     */
   1.569 +    prompt : function() {
   1.570 +        // also, the nsIPrompt flavor has 5 args instead of 6.
   1.571 +        if (typeof arguments[2] == "object")
   1.572 +            return this.nsIPrompt_prompt.apply(this, arguments);
   1.573 +        else
   1.574 +            return this.nsIAuthPrompt_prompt.apply(this, arguments);
   1.575 +    },
   1.576 +
   1.577 +    promptUsernameAndPassword : function() {
   1.578 +        // Both have 6 args, so use types.
   1.579 +        if (typeof arguments[2] == "object")
   1.580 +            return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
   1.581 +        else
   1.582 +            return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
   1.583 +    },
   1.584 +
   1.585 +    promptPassword : function() {
   1.586 +        // Both have 5 args, so use types.
   1.587 +        if (typeof arguments[2] == "object")
   1.588 +            return this.nsIPrompt_promptPassword.apply(this, arguments);
   1.589 +        else
   1.590 +            return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
   1.591 +    },
   1.592 +
   1.593 +
   1.594 +    /* ----------  nsIPrompt  ---------- */
   1.595 +
   1.596 +
   1.597 +    alert : function (title, text) {
   1.598 +        if (!title)
   1.599 +            title = PromptUtils.getLocalizedString("Alert");
   1.600 +
   1.601 +        let args = {
   1.602 +            promptType: "alert",
   1.603 +            title:      title,
   1.604 +            text:       text,
   1.605 +        };
   1.606 +
   1.607 +        this.openPrompt(args);
   1.608 +    },
   1.609 +
   1.610 +    alertCheck : function (title, text, checkLabel, checkValue) {
   1.611 +        if (!title)
   1.612 +            title = PromptUtils.getLocalizedString("Alert");
   1.613 +
   1.614 +        let args = {
   1.615 +            promptType: "alertCheck",
   1.616 +            title:      title,
   1.617 +            text:       text,
   1.618 +            checkLabel: checkLabel,
   1.619 +            checked:    checkValue.value,
   1.620 +        };
   1.621 +
   1.622 +        this.openPrompt(args);
   1.623 +
   1.624 +        // Checkbox state always returned, even if cancel clicked.
   1.625 +        checkValue.value = args.checked;
   1.626 +    },
   1.627 +
   1.628 +    confirm : function (title, text) {
   1.629 +        if (!title)
   1.630 +            title = PromptUtils.getLocalizedString("Confirm");
   1.631 +
   1.632 +        let args = {
   1.633 +            promptType: "confirm",
   1.634 +            title:      title,
   1.635 +            text:       text,
   1.636 +            ok:         false,
   1.637 +        };
   1.638 +
   1.639 +        this.openPrompt(args);
   1.640 +
   1.641 +        // Did user click Ok or Cancel?
   1.642 +        return args.ok;
   1.643 +    },
   1.644 +
   1.645 +    confirmCheck : function (title, text, checkLabel, checkValue) {
   1.646 +        if (!title)
   1.647 +            title = PromptUtils.getLocalizedString("ConfirmCheck");
   1.648 +
   1.649 +        let args = {
   1.650 +            promptType: "confirmCheck",
   1.651 +            title:      title,
   1.652 +            text:       text,
   1.653 +            checkLabel: checkLabel,
   1.654 +            checked:    checkValue.value,
   1.655 +            ok:         false,
   1.656 +        };
   1.657 +
   1.658 +        this.openPrompt(args);
   1.659 +
   1.660 +        // Checkbox state always returned, even if cancel clicked.
   1.661 +        checkValue.value = args.checked;
   1.662 +
   1.663 +        // Did user click Ok or Cancel?
   1.664 +        return args.ok;
   1.665 +    },
   1.666 +
   1.667 +    confirmEx : function (title, text, flags, button0, button1, button2,
   1.668 +                          checkLabel, checkValue) {
   1.669 +
   1.670 +        if (!title)
   1.671 +            title = PromptUtils.getLocalizedString("Confirm");
   1.672 +
   1.673 +        let args = {
   1.674 +            promptType:  "confirmEx",
   1.675 +            title:       title,
   1.676 +            text:        text,
   1.677 +            checkLabel:  checkLabel,
   1.678 +            checked:     checkValue.value,
   1.679 +            ok:          false,
   1.680 +            buttonNumClicked: 1,
   1.681 +        };
   1.682 +
   1.683 +        let [label0, label1, label2, defaultButtonNum, isDelayEnabled] =
   1.684 +            PromptUtils.confirmExHelper(flags, button0, button1, button2);
   1.685 +
   1.686 +        args.defaultButtonNum = defaultButtonNum;
   1.687 +        args.enableDelay = isDelayEnabled;
   1.688 +
   1.689 +        if (label0) {
   1.690 +            args.button0Label = label0;
   1.691 +            if (label1) {
   1.692 +                args.button1Label = label1;
   1.693 +                if (label2) {
   1.694 +                    args.button2Label = label2;
   1.695 +                }
   1.696 +            }
   1.697 +        }
   1.698 +
   1.699 +        this.openPrompt(args);
   1.700 +
   1.701 +        // Checkbox state always returned, even if cancel clicked.
   1.702 +        checkValue.value = args.checked;
   1.703 +
   1.704 +        // Get the number of the button the user clicked.
   1.705 +        return args.buttonNumClicked;
   1.706 +    },
   1.707 +
   1.708 +    nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) {
   1.709 +        if (!title)
   1.710 +            title = PromptUtils.getLocalizedString("Prompt");
   1.711 +
   1.712 +        let args = {
   1.713 +            promptType: "prompt",
   1.714 +            title:      title,
   1.715 +            text:       text,
   1.716 +            value:      value.value,
   1.717 +            checkLabel: checkLabel,
   1.718 +            checked:    checkValue.value,
   1.719 +            ok:         false,
   1.720 +        };
   1.721 +
   1.722 +        this.openPrompt(args);
   1.723 +
   1.724 +        // Did user click Ok or Cancel?
   1.725 +        let ok  = args.ok;
   1.726 +        if (ok) {
   1.727 +            checkValue.value = args.checked;
   1.728 +            value.value      = args.value;
   1.729 +        }
   1.730 +
   1.731 +        return ok;
   1.732 +    },
   1.733 +
   1.734 +    nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) {
   1.735 +        if (!title)
   1.736 +            title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2");
   1.737 +
   1.738 +        let args = {
   1.739 +            promptType: "promptUserAndPass",
   1.740 +            title:      title,
   1.741 +            text:       text,
   1.742 +            user:       user.value,
   1.743 +            pass:       pass.value,
   1.744 +            checkLabel: checkLabel,
   1.745 +            checked:    checkValue.value,
   1.746 +            ok:         false,
   1.747 +        };
   1.748 +
   1.749 +        this.openPrompt(args);
   1.750 +
   1.751 +        // Did user click Ok or Cancel?
   1.752 +        let ok  = args.ok;
   1.753 +        if (ok) {
   1.754 +            checkValue.value = args.checked;
   1.755 +            user.value       = args.user;
   1.756 +            pass.value       = args.pass;
   1.757 +        }
   1.758 +
   1.759 +        return ok;
   1.760 +    },
   1.761 +
   1.762 +    nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) {
   1.763 +        if (!title)
   1.764 +            title = PromptUtils.getLocalizedString("PromptPassword2");
   1.765 +
   1.766 +        let args = {
   1.767 +            promptType: "promptPassword",
   1.768 +            title:      title,
   1.769 +            text:       text,
   1.770 +            pass:       pass.value,
   1.771 +            checkLabel: checkLabel,
   1.772 +            checked:    checkValue.value,
   1.773 +            ok:         false,
   1.774 +        }
   1.775 +
   1.776 +        this.openPrompt(args);
   1.777 +
   1.778 +        // Did user click Ok or Cancel?
   1.779 +        let ok  = args.ok;
   1.780 +        if (ok) {
   1.781 +            checkValue.value = args.checked;
   1.782 +            pass.value       = args.pass;
   1.783 +        }
   1.784 +
   1.785 +        return ok;
   1.786 +    },
   1.787 +
   1.788 +    select : function (title, text, count, list, selected) {
   1.789 +        if (!title)
   1.790 +            title = PromptUtils.getLocalizedString("Select");
   1.791 +
   1.792 +        let args = {
   1.793 +            promptType: "select",
   1.794 +            title:      title,
   1.795 +            text:       text,
   1.796 +            list:       list,
   1.797 +            selected:   -1,
   1.798 +            ok:         false,
   1.799 +        };
   1.800 +
   1.801 +        this.openPrompt(args);
   1.802 +
   1.803 +        // Did user click Ok or Cancel?
   1.804 +        let ok  = args.ok;
   1.805 +        if (ok)
   1.806 +            selected.value = args.selected;
   1.807 +
   1.808 +        return ok;
   1.809 +    },
   1.810 +
   1.811 +
   1.812 +    /* ----------  nsIAuthPrompt  ---------- */
   1.813 +
   1.814 +
   1.815 +    nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
   1.816 +        // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
   1.817 +        if (defaultText)
   1.818 +            result.value = defaultText;
   1.819 +        return this.nsIPrompt_prompt(title, text, result, null, {});
   1.820 +    },
   1.821 +
   1.822 +    nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) {
   1.823 +        // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
   1.824 +        return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {});
   1.825 +    },
   1.826 +
   1.827 +    nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) {
   1.828 +        // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
   1.829 +        return this.nsIPrompt_promptPassword(title, text, pass, null, {});
   1.830 +    },
   1.831 +
   1.832 +
   1.833 +    /* ----------  nsIAuthPrompt2  ---------- */
   1.834 +
   1.835 +
   1.836 +    promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
   1.837 +        let message = PromptUtils.makeAuthMessage(channel, authInfo);
   1.838 +
   1.839 +        let [username, password] = PromptUtils.getAuthInfo(authInfo);
   1.840 +
   1.841 +        let userParam = { value: username };
   1.842 +        let passParam = { value: password };
   1.843 +
   1.844 +        let ok;
   1.845 +        if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
   1.846 +            ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue);
   1.847 +        else
   1.848 +            ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue);
   1.849 +
   1.850 +        if (ok)
   1.851 +            PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
   1.852 +        return ok;
   1.853 +    },
   1.854 +
   1.855 +    asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) {
   1.856 +        // Nothing calls this directly; netwerk ends up going through
   1.857 +        // nsIPromptService::GetPrompt, which delegates to login manager.
   1.858 +        // Login manger handles the async bits itself, and only calls out
   1.859 +        // promptAuth, never asyncPromptAuth.
   1.860 +        //
   1.861 +        // Bug 565582 will change this.
   1.862 +        throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.863 +    },
   1.864 +
   1.865 +    /* ----------  nsIWritablePropertyBag2 ---------- */
   1.866 +
   1.867 +    // Only a partial implementation, for one specific use case...
   1.868 +
   1.869 +    setPropertyAsBool : function(name, value) {
   1.870 +        if (name == "allowTabModal")
   1.871 +            this.allowTabModal = value;
   1.872 +        else
   1.873 +            throw Cr.NS_ERROR_ILLEGAL_VALUE;
   1.874 +    },
   1.875 +};
   1.876 +
   1.877 +
   1.878 +function AuthPromptAdapterFactory() {
   1.879 +}
   1.880 +AuthPromptAdapterFactory.prototype = {
   1.881 +    classID          : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"),
   1.882 +    QueryInterface   : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]),
   1.883 +
   1.884 +    /* ----------  nsIAuthPromptAdapterFactory ---------- */
   1.885 +
   1.886 +    createAdapter : function (oldPrompter) {
   1.887 +        return new AuthPromptAdapter(oldPrompter);
   1.888 +    }
   1.889 +};
   1.890 +
   1.891 +
   1.892 +// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell.
   1.893 +function AuthPromptAdapter(oldPrompter) {
   1.894 +    this.oldPrompter = oldPrompter;
   1.895 +}
   1.896 +AuthPromptAdapter.prototype = {
   1.897 +    QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
   1.898 +    oldPrompter    : null,
   1.899 +
   1.900 +    /* ----------  nsIAuthPrompt2 ---------- */
   1.901 +
   1.902 +    promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
   1.903 +        let message = PromptUtils.makeAuthMessage(channel, authInfo);
   1.904 +
   1.905 +        let [username, password] = PromptUtils.getAuthInfo(authInfo);
   1.906 +        let userParam = { value: username };
   1.907 +        let passParam = { value: password };
   1.908 +
   1.909 +        let [host, realm]  = PromptUtils.getAuthTarget(channel, authInfo);
   1.910 +        let authTarget = host + " (" + realm + ")";
   1.911 +
   1.912 +        let ok;
   1.913 +        if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
   1.914 +            ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam);
   1.915 +        else
   1.916 +            ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam);
   1.917 +
   1.918 +        if (ok)
   1.919 +            PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
   1.920 +        return ok;
   1.921 +    },
   1.922 +
   1.923 +    asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) {
   1.924 +        throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.925 +    }
   1.926 +};
   1.927 +
   1.928 +
   1.929 +// Wrapper using the old embedding contractID, since it's already common in
   1.930 +// the addon ecosystem.
   1.931 +function EmbedPrompter() {
   1.932 +}
   1.933 +EmbedPrompter.prototype = new Prompter();
   1.934 +EmbedPrompter.prototype.classID          = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}");
   1.935 +
   1.936 +var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory];
   1.937 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);

mercurial