browser/base/content/sync/setup.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/sync/setup.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1074 @@
     1.4 +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +const Ci = Components.interfaces;
    1.10 +const Cc = Components.classes;
    1.11 +const Cr = Components.results;
    1.12 +const Cu = Components.utils;
    1.13 +
    1.14 +// page consts
    1.15 +
    1.16 +const PAIR_PAGE                     = 0;
    1.17 +const INTRO_PAGE                    = 1;
    1.18 +const NEW_ACCOUNT_START_PAGE        = 2;
    1.19 +const EXISTING_ACCOUNT_CONNECT_PAGE = 3;
    1.20 +const EXISTING_ACCOUNT_LOGIN_PAGE   = 4;
    1.21 +const OPTIONS_PAGE                  = 5;
    1.22 +const OPTIONS_CONFIRM_PAGE          = 6;
    1.23 +
    1.24 +// Broader than we'd like, but after this changed from api-secure.recaptcha.net
    1.25 +// we had no choice. At least we only do this for the duration of setup.
    1.26 +// See discussion in Bugs 508112 and 653307.
    1.27 +const RECAPTCHA_DOMAIN = "https://www.google.com";
    1.28 +
    1.29 +const PIN_PART_LENGTH = 4;
    1.30 +
    1.31 +Cu.import("resource://services-sync/main.js");
    1.32 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.33 +Cu.import("resource://gre/modules/Services.jsm");
    1.34 +Cu.import("resource://gre/modules/PlacesUtils.jsm");
    1.35 +Cu.import("resource://gre/modules/PluralForm.jsm");
    1.36 +
    1.37 +
    1.38 +function setVisibility(element, visible) {
    1.39 +  element.style.visibility = visible ? "visible" : "hidden";
    1.40 +}
    1.41 +
    1.42 +var gSyncSetup = {
    1.43 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
    1.44 +                                         Ci.nsIWebProgressListener,
    1.45 +                                         Ci.nsISupportsWeakReference]),
    1.46 +
    1.47 +  captchaBrowser: null,
    1.48 +  wizard: null,
    1.49 +  _disabledSites: [],
    1.50 +
    1.51 +  status: {
    1.52 +    password: false,
    1.53 +    email: false,
    1.54 +    server: false
    1.55 +  },
    1.56 +
    1.57 +  get _remoteSites() [Weave.Service.serverURL, RECAPTCHA_DOMAIN],
    1.58 +
    1.59 +  get _usingMainServers() {
    1.60 +    if (this._settingUpNew)
    1.61 +      return document.getElementById("server").selectedIndex == 0;
    1.62 +    return document.getElementById("existingServer").selectedIndex == 0;
    1.63 +  },
    1.64 +
    1.65 +  init: function () {
    1.66 +    let obs = [
    1.67 +      ["weave:service:change-passphrase", "onResetPassphrase"],
    1.68 +      ["weave:service:login:start",       "onLoginStart"],
    1.69 +      ["weave:service:login:error",       "onLoginEnd"],
    1.70 +      ["weave:service:login:finish",      "onLoginEnd"]];
    1.71 +
    1.72 +    // Add the observers now and remove them on unload
    1.73 +    let self = this;
    1.74 +    let addRem = function(add) {
    1.75 +      obs.forEach(function([topic, func]) {
    1.76 +        //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
    1.77 +        //        of `this`. Fix in a followup. (bug 583347)
    1.78 +        if (add)
    1.79 +          Weave.Svc.Obs.add(topic, self[func], self);
    1.80 +        else
    1.81 +          Weave.Svc.Obs.remove(topic, self[func], self);
    1.82 +      });
    1.83 +    };
    1.84 +    addRem(true);
    1.85 +    window.addEventListener("unload", function() addRem(false), false);
    1.86 +
    1.87 +    window.setTimeout(function () {
    1.88 +      // Force Service to be loaded so that engines are registered.
    1.89 +      // See Bug 670082.
    1.90 +      Weave.Service;
    1.91 +    }, 0);
    1.92 +
    1.93 +    this.captchaBrowser = document.getElementById("captcha");
    1.94 +
    1.95 +    this.wizardType = null;
    1.96 +    if (window.arguments && window.arguments[0]) {
    1.97 +      this.wizardType = window.arguments[0];
    1.98 +    }
    1.99 +    switch (this.wizardType) {
   1.100 +      case null:
   1.101 +        this.wizard.pageIndex = INTRO_PAGE;
   1.102 +        // Fall through!
   1.103 +      case "pair":
   1.104 +        this.captchaBrowser.addProgressListener(this);
   1.105 +        Weave.Svc.Prefs.set("firstSync", "notReady");
   1.106 +        break;
   1.107 +      case "reset":
   1.108 +        this._resettingSync = true;
   1.109 +        this.wizard.pageIndex = OPTIONS_PAGE;
   1.110 +        break;
   1.111 +    }
   1.112 +
   1.113 +    this.wizard.getButton("extra1").label =
   1.114 +      this._stringBundle.GetStringFromName("button.syncOptions.label");
   1.115 +
   1.116 +    // Remember these values because the options pages change them temporarily.
   1.117 +    this._nextButtonLabel = this.wizard.getButton("next").label;
   1.118 +    this._nextButtonAccesskey = this.wizard.getButton("next")
   1.119 +                                           .getAttribute("accesskey");
   1.120 +    this._backButtonLabel = this.wizard.getButton("back").label;
   1.121 +    this._backButtonAccesskey = this.wizard.getButton("back")
   1.122 +                                           .getAttribute("accesskey");
   1.123 +  },
   1.124 +
   1.125 +  startNewAccountSetup: function () {
   1.126 +    if (!Weave.Utils.ensureMPUnlocked())
   1.127 +      return false;
   1.128 +    this._settingUpNew = true;
   1.129 +    this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
   1.130 +  },
   1.131 +
   1.132 +  useExistingAccount: function () {
   1.133 +    if (!Weave.Utils.ensureMPUnlocked())
   1.134 +      return false;
   1.135 +    this._settingUpNew = false;
   1.136 +    if (this.wizardType == "pair") {
   1.137 +      // We're already pairing, so there's no point in pairing again.
   1.138 +      // Go straight to the manual login page.
   1.139 +      this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
   1.140 +    } else {
   1.141 +      this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
   1.142 +    }
   1.143 +  },
   1.144 +
   1.145 +  resetPassphrase: function resetPassphrase() {
   1.146 +    // Apply the existing form fields so that
   1.147 +    // Weave.Service.changePassphrase() has the necessary credentials.
   1.148 +    Weave.Service.identity.account = document.getElementById("existingAccountName").value;
   1.149 +    Weave.Service.identity.basicPassword = document.getElementById("existingPassword").value;
   1.150 +
   1.151 +    // Generate a new passphrase so that Weave.Service.login() will
   1.152 +    // actually do something.
   1.153 +    let passphrase = Weave.Utils.generatePassphrase();
   1.154 +    Weave.Service.identity.syncKey = passphrase;
   1.155 +
   1.156 +    // Only open the dialog if username + password are actually correct.
   1.157 +    Weave.Service.login();
   1.158 +    if ([Weave.LOGIN_FAILED_INVALID_PASSPHRASE,
   1.159 +         Weave.LOGIN_FAILED_NO_PASSPHRASE,
   1.160 +         Weave.LOGIN_SUCCEEDED].indexOf(Weave.Status.login) == -1) {
   1.161 +      return;
   1.162 +    }
   1.163 +
   1.164 +    // Hide any errors about the passphrase, we know it's not right.
   1.165 +    let feedback = document.getElementById("existingPassphraseFeedbackRow");
   1.166 +    feedback.hidden = true;
   1.167 +    let el = document.getElementById("existingPassphrase");
   1.168 +    el.value = Weave.Utils.hyphenatePassphrase(passphrase);
   1.169 +
   1.170 +    // changePassphrase() will sync, make sure we set the "firstSync" pref
   1.171 +    // according to the user's pref.
   1.172 +    Weave.Svc.Prefs.reset("firstSync");
   1.173 +    this.setupInitialSync();
   1.174 +    gSyncUtils.resetPassphrase(true);
   1.175 +  },
   1.176 +
   1.177 +  onResetPassphrase: function () {
   1.178 +    document.getElementById("existingPassphrase").value =
   1.179 +      Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
   1.180 +    this.checkFields();
   1.181 +    this.wizard.advance();
   1.182 +  },
   1.183 +
   1.184 +  onLoginStart: function () {
   1.185 +    this.toggleLoginFeedback(false);
   1.186 +  },
   1.187 +
   1.188 +  onLoginEnd: function () {
   1.189 +    this.toggleLoginFeedback(true);
   1.190 +  },
   1.191 +
   1.192 +  sendCredentialsAfterSync: function () {
   1.193 +    let send = function() {
   1.194 +      Services.obs.removeObserver("weave:service:sync:finish", send);
   1.195 +      Services.obs.removeObserver("weave:service:sync:error", send);
   1.196 +      let credentials = {account:   Weave.Service.identity.account,
   1.197 +                         password:  Weave.Service.identity.basicPassword,
   1.198 +                         synckey:   Weave.Service.identity.syncKey,
   1.199 +                         serverURL: Weave.Service.serverURL};
   1.200 +      this._jpakeclient.sendAndComplete(credentials);
   1.201 +    }.bind(this);
   1.202 +    Services.obs.addObserver("weave:service:sync:finish", send, false);
   1.203 +    Services.obs.addObserver("weave:service:sync:error", send, false);
   1.204 +  },
   1.205 +
   1.206 +  toggleLoginFeedback: function (stop) {
   1.207 +    document.getElementById("login-throbber").hidden = stop;
   1.208 +    let password = document.getElementById("existingPasswordFeedbackRow");
   1.209 +    let server = document.getElementById("existingServerFeedbackRow");
   1.210 +    let passphrase = document.getElementById("existingPassphraseFeedbackRow");
   1.211 +
   1.212 +    if (!stop || (Weave.Status.login == Weave.LOGIN_SUCCEEDED)) {
   1.213 +      password.hidden = server.hidden = passphrase.hidden = true;
   1.214 +      return;
   1.215 +    }
   1.216 +
   1.217 +    let feedback;
   1.218 +    switch (Weave.Status.login) {
   1.219 +      case Weave.LOGIN_FAILED_NETWORK_ERROR:
   1.220 +      case Weave.LOGIN_FAILED_SERVER_ERROR:
   1.221 +        feedback = server;
   1.222 +        break;
   1.223 +      case Weave.LOGIN_FAILED_LOGIN_REJECTED:
   1.224 +      case Weave.LOGIN_FAILED_NO_USERNAME:
   1.225 +      case Weave.LOGIN_FAILED_NO_PASSWORD:
   1.226 +        feedback = password;
   1.227 +        break;
   1.228 +      case Weave.LOGIN_FAILED_INVALID_PASSPHRASE:
   1.229 +        feedback = passphrase;
   1.230 +        break;
   1.231 +    }
   1.232 +    this._setFeedbackMessage(feedback, false, Weave.Status.login);
   1.233 +  },
   1.234 +
   1.235 +  setupInitialSync: function () {
   1.236 +    let action = document.getElementById("mergeChoiceRadio").selectedItem.id;
   1.237 +    switch (action) {
   1.238 +      case "resetClient":
   1.239 +        // if we're not resetting sync, we don't need to explicitly
   1.240 +        // call resetClient
   1.241 +        if (!this._resettingSync)
   1.242 +          return;
   1.243 +        // otherwise, fall through
   1.244 +      case "wipeClient":
   1.245 +      case "wipeRemote":
   1.246 +        Weave.Svc.Prefs.set("firstSync", action);
   1.247 +        break;
   1.248 +    }
   1.249 +  },
   1.250 +
   1.251 +  // fun with validation!
   1.252 +  checkFields: function () {
   1.253 +    this.wizard.canAdvance = this.readyToAdvance();
   1.254 +  },
   1.255 +
   1.256 +  readyToAdvance: function () {
   1.257 +    switch (this.wizard.pageIndex) {
   1.258 +      case INTRO_PAGE:
   1.259 +        return false;
   1.260 +      case NEW_ACCOUNT_START_PAGE:
   1.261 +        for (let i in this.status) {
   1.262 +          if (!this.status[i])
   1.263 +            return false;
   1.264 +        }
   1.265 +        if (this._usingMainServers)
   1.266 +          return document.getElementById("tos").checked;
   1.267 +
   1.268 +        return true;
   1.269 +      case EXISTING_ACCOUNT_LOGIN_PAGE:
   1.270 +        let hasUser = document.getElementById("existingAccountName").value != "";
   1.271 +        let hasPass = document.getElementById("existingPassword").value != "";
   1.272 +        let hasKey = document.getElementById("existingPassphrase").value != "";
   1.273 +
   1.274 +        if (hasUser && hasPass && hasKey) {
   1.275 +          if (this._usingMainServers)
   1.276 +            return true;
   1.277 +
   1.278 +          if (this._validateServer(document.getElementById("existingServer"))) {
   1.279 +            return true;
   1.280 +          }
   1.281 +        }
   1.282 +        return false;
   1.283 +    }
   1.284 +    // Default, e.g. wizard's special page -1 etc.
   1.285 +    return true;
   1.286 +  },
   1.287 +
   1.288 +  onPINInput: function onPINInput(textbox) {
   1.289 +    if (textbox && textbox.value.length == PIN_PART_LENGTH) {
   1.290 +      this.nextFocusEl[textbox.id].focus();
   1.291 +    }
   1.292 +    this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH &&
   1.293 +                              this.pin2.value.length == PIN_PART_LENGTH &&
   1.294 +                              this.pin3.value.length == PIN_PART_LENGTH);
   1.295 +  },
   1.296 +
   1.297 +  onEmailInput: function () {
   1.298 +    // Check account validity when the user stops typing for 1 second.
   1.299 +    if (this._checkAccountTimer)
   1.300 +      window.clearTimeout(this._checkAccountTimer);
   1.301 +    this._checkAccountTimer = window.setTimeout(function () {
   1.302 +      gSyncSetup.checkAccount();
   1.303 +    }, 1000);
   1.304 +  },
   1.305 +
   1.306 +  checkAccount: function() {
   1.307 +    delete this._checkAccountTimer;
   1.308 +    let value = Weave.Utils.normalizeAccount(
   1.309 +      document.getElementById("weaveEmail").value);
   1.310 +    if (!value) {
   1.311 +      this.status.email = false;
   1.312 +      this.checkFields();
   1.313 +      return;
   1.314 +    }
   1.315 +
   1.316 +    let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
   1.317 +    let feedback = document.getElementById("emailFeedbackRow");
   1.318 +    let valid = re.test(value);
   1.319 +
   1.320 +    let str = "";
   1.321 +    if (!valid) {
   1.322 +      str = "invalidEmail.label";
   1.323 +    } else {
   1.324 +      let availCheck = Weave.Service.checkAccount(value);
   1.325 +      valid = availCheck == "available";
   1.326 +      if (!valid) {
   1.327 +        if (availCheck == "notAvailable")
   1.328 +          str = "usernameNotAvailable.label";
   1.329 +        else
   1.330 +          str = availCheck;
   1.331 +      }
   1.332 +    }
   1.333 +
   1.334 +    this._setFeedbackMessage(feedback, valid, str);
   1.335 +    this.status.email = valid;
   1.336 +    if (valid)
   1.337 +      Weave.Service.identity.account = value;
   1.338 +    this.checkFields();
   1.339 +  },
   1.340 +
   1.341 +  onPasswordChange: function () {
   1.342 +    let password = document.getElementById("weavePassword");
   1.343 +    let pwconfirm = document.getElementById("weavePasswordConfirm");
   1.344 +    let [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
   1.345 +
   1.346 +    let feedback = document.getElementById("passwordFeedbackRow");
   1.347 +    this._setFeedback(feedback, valid, errorString);
   1.348 +
   1.349 +    this.status.password = valid;
   1.350 +    this.checkFields();
   1.351 +  },
   1.352 +
   1.353 +  onPageShow: function() {
   1.354 +    switch (this.wizard.pageIndex) {
   1.355 +      case PAIR_PAGE:
   1.356 +        this.wizard.getButton("back").hidden = true;
   1.357 +        this.wizard.getButton("extra1").hidden = true;
   1.358 +        this.onPINInput();
   1.359 +        this.pin1.focus();
   1.360 +        break;
   1.361 +      case INTRO_PAGE:
   1.362 +        // We may not need the captcha in the Existing Account branch of the
   1.363 +        // wizard. However, we want to preload it to avoid any flickering while
   1.364 +        // the Create Account page is shown.
   1.365 +        this.loadCaptcha();
   1.366 +        this.wizard.getButton("next").hidden = true;
   1.367 +        this.wizard.getButton("back").hidden = true;
   1.368 +        this.wizard.getButton("extra1").hidden = true;
   1.369 +        this.checkFields();
   1.370 +        break;
   1.371 +      case NEW_ACCOUNT_START_PAGE:
   1.372 +        this.wizard.getButton("extra1").hidden = false;
   1.373 +        this.wizard.getButton("next").hidden = false;
   1.374 +        this.wizard.getButton("back").hidden = false;
   1.375 +        this.onServerCommand();
   1.376 +        this.wizard.canRewind = true;
   1.377 +        this.checkFields();
   1.378 +        break;
   1.379 +      case EXISTING_ACCOUNT_CONNECT_PAGE:
   1.380 +        Weave.Svc.Prefs.set("firstSync", "existingAccount");
   1.381 +        this.wizard.getButton("next").hidden = false;
   1.382 +        this.wizard.getButton("back").hidden = false;
   1.383 +        this.wizard.getButton("extra1").hidden = false;
   1.384 +        this.wizard.canAdvance = false;
   1.385 +        this.wizard.canRewind = true;
   1.386 +        this.startEasySetup();
   1.387 +        break;
   1.388 +      case EXISTING_ACCOUNT_LOGIN_PAGE:
   1.389 +        this.wizard.getButton("next").hidden = false;
   1.390 +        this.wizard.getButton("back").hidden = false;
   1.391 +        this.wizard.getButton("extra1").hidden = false;
   1.392 +        this.wizard.canRewind = true;
   1.393 +        this.checkFields();
   1.394 +        break;
   1.395 +      case OPTIONS_PAGE:
   1.396 +        this.wizard.canRewind = false;
   1.397 +        this.wizard.canAdvance = true;
   1.398 +        if (!this._resettingSync) {
   1.399 +          this.wizard.getButton("next").label =
   1.400 +            this._stringBundle.GetStringFromName("button.syncOptionsDone.label");
   1.401 +          this.wizard.getButton("next").removeAttribute("accesskey");
   1.402 +        }
   1.403 +        this.wizard.getButton("next").hidden = false;
   1.404 +        this.wizard.getButton("back").hidden = true;
   1.405 +        this.wizard.getButton("cancel").hidden = !this._resettingSync;
   1.406 +        this.wizard.getButton("extra1").hidden = true;
   1.407 +        document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
   1.408 +        document.getElementById("syncOptions").collapsed = this._resettingSync;
   1.409 +        document.getElementById("mergeOptions").collapsed = this._settingUpNew;
   1.410 +        break;
   1.411 +      case OPTIONS_CONFIRM_PAGE:
   1.412 +        this.wizard.canRewind = true;
   1.413 +        this.wizard.canAdvance = true;
   1.414 +        this.wizard.getButton("back").label =
   1.415 +          this._stringBundle.GetStringFromName("button.syncOptionsCancel.label");
   1.416 +        this.wizard.getButton("back").removeAttribute("accesskey");
   1.417 +        this.wizard.getButton("back").hidden = this._resettingSync;
   1.418 +        this.wizard.getButton("next").hidden = false;
   1.419 +        this.wizard.getButton("finish").hidden = true;
   1.420 +        break;
   1.421 +    }
   1.422 +  },
   1.423 +
   1.424 +  onWizardAdvance: function () {
   1.425 +    // Check pageIndex so we don't prompt before the Sync setup wizard appears.
   1.426 +    // This is a fallback in case the Master Password gets locked mid-wizard.
   1.427 +    if ((this.wizard.pageIndex >= 0) &&
   1.428 +        !Weave.Utils.ensureMPUnlocked()) {
   1.429 +      return false;
   1.430 +    }
   1.431 +
   1.432 +    switch (this.wizard.pageIndex) {
   1.433 +      case PAIR_PAGE:
   1.434 +        this.startPairing();
   1.435 +        return false;
   1.436 +      case NEW_ACCOUNT_START_PAGE:
   1.437 +        // If the user selects Next (e.g. by hitting enter) when we haven't
   1.438 +        // executed the delayed checks yet, execute them immediately.
   1.439 +        if (this._checkAccountTimer) {
   1.440 +          this.checkAccount();
   1.441 +        }
   1.442 +        if (this._checkServerTimer) {
   1.443 +          this.checkServer();
   1.444 +        }
   1.445 +        if (!this.wizard.canAdvance) {
   1.446 +          return false;
   1.447 +        }
   1.448 +
   1.449 +        let doc = this.captchaBrowser.contentDocument;
   1.450 +        let getField = function getField(field) {
   1.451 +          let node = doc.getElementById("recaptcha_" + field + "_field");
   1.452 +          return node && node.value;
   1.453 +        };
   1.454 +
   1.455 +        // Display throbber
   1.456 +        let feedback = document.getElementById("captchaFeedback");
   1.457 +        let image = feedback.firstChild;
   1.458 +        let label = image.nextSibling;
   1.459 +        image.setAttribute("status", "active");
   1.460 +        label.value = this._stringBundle.GetStringFromName("verifying.label");
   1.461 +        setVisibility(feedback, true);
   1.462 +
   1.463 +        let password = document.getElementById("weavePassword").value;
   1.464 +        let email = Weave.Utils.normalizeAccount(
   1.465 +          document.getElementById("weaveEmail").value);
   1.466 +        let challenge = getField("challenge");
   1.467 +        let response = getField("response");
   1.468 +
   1.469 +        let error = Weave.Service.createAccount(email, password,
   1.470 +                                                challenge, response);
   1.471 +
   1.472 +        if (error == null) {
   1.473 +          Weave.Service.identity.account = email;
   1.474 +          Weave.Service.identity.basicPassword = password;
   1.475 +          Weave.Service.identity.syncKey = Weave.Utils.generatePassphrase();
   1.476 +          this._handleNoScript(false);
   1.477 +          Weave.Svc.Prefs.set("firstSync", "newAccount");
   1.478 +#ifdef XP_WIN
   1.479 +#ifdef MOZ_METRO
   1.480 +          if (document.getElementById("metroSetupCheckbox").checked) {
   1.481 +            Services.metro.storeSyncInfo(email, password, Weave.Service.identity.syncKey);
   1.482 +          }
   1.483 +#endif
   1.484 +#endif
   1.485 +          this.wizardFinish();
   1.486 +          return false;
   1.487 +        }
   1.488 +
   1.489 +        image.setAttribute("status", "error");
   1.490 +        label.value = Weave.Utils.getErrorString(error);
   1.491 +        return false;
   1.492 +      case EXISTING_ACCOUNT_LOGIN_PAGE:
   1.493 +        Weave.Service.identity.account = Weave.Utils.normalizeAccount(
   1.494 +          document.getElementById("existingAccountName").value);
   1.495 +        Weave.Service.identity.basicPassword =
   1.496 +          document.getElementById("existingPassword").value;
   1.497 +        let pp = document.getElementById("existingPassphrase").value;
   1.498 +        Weave.Service.identity.syncKey = Weave.Utils.normalizePassphrase(pp);
   1.499 +        if (Weave.Service.login()) {
   1.500 +          this.wizardFinish();
   1.501 +        }
   1.502 +        return false;
   1.503 +      case OPTIONS_PAGE:
   1.504 +        let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
   1.505 +        // No confirmation needed on new account setup or merge option
   1.506 +        // with existing account.
   1.507 +        if (this._settingUpNew || (!this._resettingSync && desc == 0))
   1.508 +          return this.returnFromOptions();
   1.509 +        return this._handleChoice();
   1.510 +      case OPTIONS_CONFIRM_PAGE:
   1.511 +        if (this._resettingSync) {
   1.512 +          this.wizardFinish();
   1.513 +          return false;
   1.514 +        }
   1.515 +        return this.returnFromOptions();
   1.516 +    }
   1.517 +    return true;
   1.518 +  },
   1.519 +
   1.520 +  onWizardBack: function () {
   1.521 +    switch (this.wizard.pageIndex) {
   1.522 +      case NEW_ACCOUNT_START_PAGE:
   1.523 +      case EXISTING_ACCOUNT_LOGIN_PAGE:
   1.524 +        this.wizard.pageIndex = INTRO_PAGE;
   1.525 +        return false;
   1.526 +      case EXISTING_ACCOUNT_CONNECT_PAGE:
   1.527 +        this.abortEasySetup();
   1.528 +        this.wizard.pageIndex = INTRO_PAGE;
   1.529 +        return false;
   1.530 +      case EXISTING_ACCOUNT_LOGIN_PAGE:
   1.531 +        // If we were already pairing on entry, we went straight to the manual
   1.532 +        // login page. If subsequently we go back, return to the page that lets
   1.533 +        // us choose whether we already have an account.
   1.534 +        if (this.wizardType == "pair") {
   1.535 +          this.wizard.pageIndex = INTRO_PAGE;
   1.536 +          return false;
   1.537 +        }
   1.538 +        return true;
   1.539 +      case OPTIONS_CONFIRM_PAGE:
   1.540 +        // Backing up from the confirmation page = resetting first sync to merge.
   1.541 +        document.getElementById("mergeChoiceRadio").selectedIndex = 0;
   1.542 +        return this.returnFromOptions();
   1.543 +    }
   1.544 +    return true;
   1.545 +  },
   1.546 +
   1.547 +  wizardFinish: function () {
   1.548 +    this.setupInitialSync();
   1.549 +
   1.550 +    if (this.wizardType == "pair") {
   1.551 +      this.completePairing();
   1.552 +    }
   1.553 +
   1.554 +    if (!this._resettingSync) {
   1.555 +      function isChecked(element) {
   1.556 +        return document.getElementById(element).hasAttribute("checked");
   1.557 +      }
   1.558 +
   1.559 +      let prefs = ["engine.bookmarks", "engine.passwords", "engine.history",
   1.560 +                   "engine.tabs", "engine.prefs", "engine.addons"];
   1.561 +      for (let i = 0;i < prefs.length;i++) {
   1.562 +        Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i]));
   1.563 +      }
   1.564 +      this._handleNoScript(false);
   1.565 +      if (Weave.Svc.Prefs.get("firstSync", "") == "notReady")
   1.566 +        Weave.Svc.Prefs.reset("firstSync");
   1.567 +
   1.568 +      Weave.Service.persistLogin();
   1.569 +      Weave.Svc.Obs.notify("weave:service:setup-complete");
   1.570 +
   1.571 +      gSyncUtils.openFirstSyncProgressPage();
   1.572 +    }
   1.573 +    Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
   1.574 +    window.close();
   1.575 +  },
   1.576 +
   1.577 +  onWizardCancel: function () {
   1.578 +    if (this._resettingSync)
   1.579 +      return;
   1.580 +
   1.581 +    this.abortEasySetup();
   1.582 +    this._handleNoScript(false);
   1.583 +    Weave.Service.startOver();
   1.584 +  },
   1.585 +
   1.586 +  onSyncOptions: function () {
   1.587 +    this._beforeOptionsPage = this.wizard.pageIndex;
   1.588 +    this.wizard.pageIndex = OPTIONS_PAGE;
   1.589 +  },
   1.590 +
   1.591 +  returnFromOptions: function() {
   1.592 +    this.wizard.getButton("next").label = this._nextButtonLabel;
   1.593 +    this.wizard.getButton("next").setAttribute("accesskey",
   1.594 +                                               this._nextButtonAccesskey);
   1.595 +    this.wizard.getButton("back").label = this._backButtonLabel;
   1.596 +    this.wizard.getButton("back").setAttribute("accesskey",
   1.597 +                                               this._backButtonAccesskey);
   1.598 +    this.wizard.getButton("cancel").hidden = false;
   1.599 +    this.wizard.getButton("extra1").hidden = false;
   1.600 +    this.wizard.pageIndex = this._beforeOptionsPage;
   1.601 +    return false;
   1.602 +  },
   1.603 +
   1.604 +  startPairing: function startPairing() {
   1.605 +    this.pairDeviceErrorRow.hidden = true;
   1.606 +    // When onAbort is called, Weave may already be gone.
   1.607 +    const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
   1.608 +
   1.609 +    let self = this;
   1.610 +    let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
   1.611 +      onPaired: function onPaired() {
   1.612 +        self.wizard.pageIndex = INTRO_PAGE;
   1.613 +      },
   1.614 +      onComplete: function onComplete() {
   1.615 +        // This method will never be called since SendCredentialsController
   1.616 +        // will take over after the wizard completes.
   1.617 +      },
   1.618 +      onAbort: function onAbort(error) {
   1.619 +        delete self._jpakeclient;
   1.620 +
   1.621 +        // Aborted by user, ignore. The window is almost certainly going to close
   1.622 +        // or is already closed.
   1.623 +        if (error == JPAKE_ERROR_USERABORT) {
   1.624 +          return;
   1.625 +        }
   1.626 +
   1.627 +        self.pairDeviceErrorRow.hidden = false;
   1.628 +        self.pairDeviceThrobber.hidden = true;
   1.629 +        self.pin1.value = self.pin2.value = self.pin3.value = "";
   1.630 +        self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
   1.631 +        if (self.wizard.pageIndex == PAIR_PAGE) {
   1.632 +          self.pin1.focus();
   1.633 +        }
   1.634 +      }
   1.635 +    });
   1.636 +    this.pairDeviceThrobber.hidden = false;
   1.637 +    this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
   1.638 +    this.wizard.canAdvance = false;
   1.639 +
   1.640 +    let pin = this.pin1.value + this.pin2.value + this.pin3.value;
   1.641 +    let expectDelay = true;
   1.642 +    jpakeclient.pairWithPIN(pin, expectDelay);
   1.643 +  },
   1.644 +
   1.645 +  completePairing: function completePairing() {
   1.646 +    if (!this._jpakeclient) {
   1.647 +      // The channel was aborted while we were setting up the account
   1.648 +      // locally. XXX TODO should we do anything here, e.g. tell
   1.649 +      // the user on the last wizard page that it's ok, they just
   1.650 +      // have to pair again?
   1.651 +      return;
   1.652 +    }
   1.653 +    let controller = new Weave.SendCredentialsController(this._jpakeclient,
   1.654 +                                                         Weave.Service);
   1.655 +    this._jpakeclient.controller = controller;
   1.656 +  },
   1.657 +
   1.658 +  startEasySetup: function () {
   1.659 +    // Don't do anything if we have a client already (e.g. we went to
   1.660 +    // Sync Options and just came back).
   1.661 +    if (this._jpakeclient)
   1.662 +      return;
   1.663 +
   1.664 +    // When onAbort is called, Weave may already be gone
   1.665 +    const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
   1.666 +
   1.667 +    let self = this;
   1.668 +    this._jpakeclient = new Weave.JPAKEClient({
   1.669 +      displayPIN: function displayPIN(pin) {
   1.670 +        document.getElementById("easySetupPIN1").value = pin.slice(0, 4);
   1.671 +        document.getElementById("easySetupPIN2").value = pin.slice(4, 8);
   1.672 +        document.getElementById("easySetupPIN3").value = pin.slice(8);
   1.673 +      },
   1.674 +
   1.675 +      onPairingStart: function onPairingStart() {},
   1.676 +
   1.677 +      onComplete: function onComplete(credentials) {
   1.678 +        Weave.Service.identity.account = credentials.account;
   1.679 +        Weave.Service.identity.basicPassword = credentials.password;
   1.680 +        Weave.Service.identity.syncKey = credentials.synckey;
   1.681 +        Weave.Service.serverURL = credentials.serverURL;
   1.682 +        gSyncSetup.wizardFinish();
   1.683 +      },
   1.684 +
   1.685 +      onAbort: function onAbort(error) {
   1.686 +        delete self._jpakeclient;
   1.687 +
   1.688 +        // Ignore if wizard is aborted.
   1.689 +        if (error == JPAKE_ERROR_USERABORT)
   1.690 +          return;
   1.691 +
   1.692 +        // Automatically go to manual setup if we couldn't acquire a channel.
   1.693 +        if (error == Weave.JPAKE_ERROR_CHANNEL) {
   1.694 +          self.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
   1.695 +          return;
   1.696 +        }
   1.697 +
   1.698 +        // Restart on all other errors.
   1.699 +        self.startEasySetup();
   1.700 +      }
   1.701 +    });
   1.702 +    this._jpakeclient.receiveNoPIN();
   1.703 +  },
   1.704 +
   1.705 +  abortEasySetup: function () {
   1.706 +    document.getElementById("easySetupPIN1").value = "";
   1.707 +    document.getElementById("easySetupPIN2").value = "";
   1.708 +    document.getElementById("easySetupPIN3").value = "";
   1.709 +    if (!this._jpakeclient)
   1.710 +      return;
   1.711 +
   1.712 +    this._jpakeclient.abort();
   1.713 +    delete this._jpakeclient;
   1.714 +  },
   1.715 +
   1.716 +  manualSetup: function () {
   1.717 +    this.abortEasySetup();
   1.718 +    this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
   1.719 +  },
   1.720 +
   1.721 +  // _handleNoScript is needed because it blocks the captcha. So we temporarily
   1.722 +  // allow the necessary sites so that we can verify the user is in fact a human.
   1.723 +  // This was done with the help of Giorgio (NoScript author). See bug 508112.
   1.724 +  _handleNoScript: function (addExceptions) {
   1.725 +    // if NoScript isn't installed, or is disabled, bail out.
   1.726 +    let ns = Cc["@maone.net/noscript-service;1"];
   1.727 +    if (ns == null)
   1.728 +      return;
   1.729 +
   1.730 +    ns = ns.getService().wrappedJSObject;
   1.731 +    if (addExceptions) {
   1.732 +      this._remoteSites.forEach(function(site) {
   1.733 +        site = ns.getSite(site);
   1.734 +        if (!ns.isJSEnabled(site)) {
   1.735 +          this._disabledSites.push(site); // save status
   1.736 +          ns.setJSEnabled(site, true); // allow site
   1.737 +        }
   1.738 +      }, this);
   1.739 +    }
   1.740 +    else {
   1.741 +      this._disabledSites.forEach(function(site) {
   1.742 +        ns.setJSEnabled(site, false);
   1.743 +      });
   1.744 +      this._disabledSites = [];
   1.745 +    }
   1.746 +  },
   1.747 +
   1.748 +  onExistingServerCommand: function () {
   1.749 +    let control = document.getElementById("existingServer");
   1.750 +    if (control.selectedIndex == 0) {
   1.751 +      control.removeAttribute("editable");
   1.752 +      Weave.Svc.Prefs.reset("serverURL");
   1.753 +    } else {
   1.754 +      control.setAttribute("editable", "true");
   1.755 +      // Force a style flush to ensure that the binding is attached.
   1.756 +      control.clientTop;
   1.757 +      control.value = "";
   1.758 +      control.inputField.focus();
   1.759 +    }
   1.760 +    document.getElementById("existingServerFeedbackRow").hidden = true;
   1.761 +    this.checkFields();
   1.762 +  },
   1.763 +
   1.764 +  onExistingServerInput: function () {
   1.765 +    // Check custom server validity when the user stops typing for 1 second.
   1.766 +    if (this._existingServerTimer)
   1.767 +      window.clearTimeout(this._existingServerTimer);
   1.768 +    this._existingServerTimer = window.setTimeout(function () {
   1.769 +      gSyncSetup.checkFields();
   1.770 +    }, 1000);
   1.771 +  },
   1.772 +
   1.773 +  onServerCommand: function () {
   1.774 +    setVisibility(document.getElementById("TOSRow"), this._usingMainServers);
   1.775 +    let control = document.getElementById("server");
   1.776 +    if (!this._usingMainServers) {
   1.777 +      control.setAttribute("editable", "true");
   1.778 +      // Force a style flush to ensure that the binding is attached.
   1.779 +      control.clientTop;
   1.780 +      control.value = "";
   1.781 +      control.inputField.focus();
   1.782 +      // checkServer() will call checkAccount() and checkFields().
   1.783 +      this.checkServer();
   1.784 +      return;
   1.785 +    }
   1.786 +    control.removeAttribute("editable");
   1.787 +    Weave.Svc.Prefs.reset("serverURL");
   1.788 +    if (this._settingUpNew) {
   1.789 +      this.loadCaptcha();
   1.790 +    }
   1.791 +    this.checkAccount();
   1.792 +    this.status.server = true;
   1.793 +    document.getElementById("serverFeedbackRow").hidden = true;
   1.794 +    this.checkFields();
   1.795 +  },
   1.796 +
   1.797 +  onServerInput: function () {
   1.798 +    // Check custom server validity when the user stops typing for 1 second.
   1.799 +    if (this._checkServerTimer)
   1.800 +      window.clearTimeout(this._checkServerTimer);
   1.801 +    this._checkServerTimer = window.setTimeout(function () {
   1.802 +      gSyncSetup.checkServer();
   1.803 +    }, 1000);
   1.804 +  },
   1.805 +
   1.806 +  checkServer: function () {
   1.807 +    delete this._checkServerTimer;
   1.808 +    let el = document.getElementById("server");
   1.809 +    let valid = false;
   1.810 +    let feedback = document.getElementById("serverFeedbackRow");
   1.811 +    let str = "";
   1.812 +    if (el.value) {
   1.813 +      valid = this._validateServer(el);
   1.814 +      let str = valid ? "" : "serverInvalid.label";
   1.815 +      this._setFeedbackMessage(feedback, valid, str);
   1.816 +    }
   1.817 +    else
   1.818 +      this._setFeedbackMessage(feedback, true);
   1.819 +
   1.820 +    // Recheck account against the new server.
   1.821 +    if (valid)
   1.822 +      this.checkAccount();
   1.823 +
   1.824 +    this.status.server = valid;
   1.825 +    this.checkFields();
   1.826 +  },
   1.827 +
   1.828 +  _validateServer: function (element) {
   1.829 +    let valid = false;
   1.830 +    let val = element.value;
   1.831 +    if (!val)
   1.832 +      return false;
   1.833 +
   1.834 +    let uri = Weave.Utils.makeURI(val);
   1.835 +
   1.836 +    if (!uri)
   1.837 +      uri = Weave.Utils.makeURI("https://" + val);
   1.838 +
   1.839 +    if (uri && this._settingUpNew) {
   1.840 +      function isValid(uri) {
   1.841 +        Weave.Service.serverURL = uri.spec;
   1.842 +        let check = Weave.Service.checkAccount("a");
   1.843 +        return (check == "available" || check == "notAvailable");
   1.844 +      }
   1.845 +
   1.846 +      if (uri.schemeIs("http")) {
   1.847 +        uri.scheme = "https";
   1.848 +        if (isValid(uri))
   1.849 +          valid = true;
   1.850 +        else
   1.851 +          // setting the scheme back to http
   1.852 +          uri.scheme = "http";
   1.853 +      }
   1.854 +      if (!valid)
   1.855 +        valid = isValid(uri);
   1.856 +
   1.857 +      if (valid) {
   1.858 +        this.loadCaptcha();
   1.859 +      }
   1.860 +    }
   1.861 +    else if (uri) {
   1.862 +      valid = true;
   1.863 +      Weave.Service.serverURL = uri.spec;
   1.864 +    }
   1.865 +
   1.866 +    if (valid)
   1.867 +      element.value = Weave.Service.serverURL;
   1.868 +    else
   1.869 +      Weave.Svc.Prefs.reset("serverURL");
   1.870 +
   1.871 +    return valid;
   1.872 +  },
   1.873 +
   1.874 +  _handleChoice: function () {
   1.875 +    let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
   1.876 +    document.getElementById("chosenActionDeck").selectedIndex = desc;
   1.877 +    switch (desc) {
   1.878 +      case 1:
   1.879 +        if (this._case1Setup)
   1.880 +          break;
   1.881 +
   1.882 +        let places_db = PlacesUtils.history
   1.883 +                                   .QueryInterface(Ci.nsPIPlacesDatabase)
   1.884 +                                   .DBConnection;
   1.885 +        if (Weave.Service.engineManager.get("history").enabled) {
   1.886 +          let daysOfHistory = 0;
   1.887 +          let stm = places_db.createStatement(
   1.888 +            "SELECT ROUND(( " +
   1.889 +              "strftime('%s','now','localtime','utc') - " +
   1.890 +              "( " +
   1.891 +                "SELECT visit_date FROM moz_historyvisits " +
   1.892 +                "ORDER BY visit_date ASC LIMIT 1 " +
   1.893 +                ")/1000000 " +
   1.894 +              ")/86400) AS daysOfHistory ");
   1.895 +
   1.896 +          if (stm.step())
   1.897 +            daysOfHistory = stm.getInt32(0);
   1.898 +          // Support %S for historical reasons (see bug 600141)
   1.899 +          document.getElementById("historyCount").value =
   1.900 +            PluralForm.get(daysOfHistory,
   1.901 +                           this._stringBundle.GetStringFromName("historyDaysCount.label"))
   1.902 +                      .replace("%S", daysOfHistory)
   1.903 +                      .replace("#1", daysOfHistory);
   1.904 +        } else {
   1.905 +          document.getElementById("historyCount").hidden = true;
   1.906 +        }
   1.907 +
   1.908 +        if (Weave.Service.engineManager.get("bookmarks").enabled) {
   1.909 +          let bookmarks = 0;
   1.910 +          let stm = places_db.createStatement(
   1.911 +            "SELECT count(*) AS bookmarks " +
   1.912 +            "FROM moz_bookmarks b " +
   1.913 +            "LEFT JOIN moz_bookmarks t ON " +
   1.914 +            "b.parent = t.id WHERE b.type = 1 AND t.parent <> :tag");
   1.915 +          stm.params.tag = PlacesUtils.tagsFolderId;
   1.916 +          if (stm.executeStep())
   1.917 +            bookmarks = stm.row.bookmarks;
   1.918 +          // Support %S for historical reasons (see bug 600141)
   1.919 +          document.getElementById("bookmarkCount").value =
   1.920 +            PluralForm.get(bookmarks,
   1.921 +                           this._stringBundle.GetStringFromName("bookmarksCount.label"))
   1.922 +                      .replace("%S", bookmarks)
   1.923 +                      .replace("#1", bookmarks);
   1.924 +        } else {
   1.925 +          document.getElementById("bookmarkCount").hidden = true;
   1.926 +        }
   1.927 +
   1.928 +        if (Weave.Service.engineManager.get("passwords").enabled) {
   1.929 +          let logins = Services.logins.getAllLogins({});
   1.930 +          // Support %S for historical reasons (see bug 600141)
   1.931 +          document.getElementById("passwordCount").value =
   1.932 +            PluralForm.get(logins.length,
   1.933 +                           this._stringBundle.GetStringFromName("passwordsCount.label"))
   1.934 +                      .replace("%S", logins.length)
   1.935 +                      .replace("#1", logins.length);
   1.936 +        } else {
   1.937 +          document.getElementById("passwordCount").hidden = true;
   1.938 +        }
   1.939 +
   1.940 +        if (!Weave.Service.engineManager.get("prefs").enabled) {
   1.941 +          document.getElementById("prefsWipe").hidden = true;
   1.942 +        }
   1.943 +
   1.944 +        let addonsEngine = Weave.Service.engineManager.get("addons");
   1.945 +        if (addonsEngine.enabled) {
   1.946 +          let ids = addonsEngine._store.getAllIDs();
   1.947 +          let blessedcount = 0;
   1.948 +          for each (let i in ids) {
   1.949 +            if (i) {
   1.950 +              blessedcount++;
   1.951 +            }
   1.952 +          }
   1.953 +          // bug 600141 does not apply, as this does not have to support existing strings
   1.954 +          document.getElementById("addonCount").value =
   1.955 +            PluralForm.get(blessedcount,
   1.956 +                           this._stringBundle.GetStringFromName("addonsCount.label"))
   1.957 +                      .replace("#1", blessedcount);
   1.958 +        } else {
   1.959 +          document.getElementById("addonCount").hidden = true;
   1.960 +        }
   1.961 +
   1.962 +        this._case1Setup = true;
   1.963 +        break;
   1.964 +      case 2:
   1.965 +        if (this._case2Setup)
   1.966 +          break;
   1.967 +        let count = 0;
   1.968 +        function appendNode(label) {
   1.969 +          let box = document.getElementById("clientList");
   1.970 +          let node = document.createElement("label");
   1.971 +          node.setAttribute("value", label);
   1.972 +          node.setAttribute("class", "data indent");
   1.973 +          box.appendChild(node);
   1.974 +        }
   1.975 +
   1.976 +        for each (let name in Weave.Service.clientsEngine.stats.names) {
   1.977 +          // Don't list the current client
   1.978 +          if (name == Weave.Service.clientsEngine.localName)
   1.979 +            continue;
   1.980 +
   1.981 +          // Only show the first several client names
   1.982 +          if (++count <= 5)
   1.983 +            appendNode(name);
   1.984 +        }
   1.985 +        if (count > 5) {
   1.986 +          // Support %S for historical reasons (see bug 600141)
   1.987 +          let label =
   1.988 +            PluralForm.get(count - 5,
   1.989 +                           this._stringBundle.GetStringFromName("additionalClientCount.label"))
   1.990 +                      .replace("%S", count - 5)
   1.991 +                      .replace("#1", count - 5);
   1.992 +          appendNode(label);
   1.993 +        }
   1.994 +        this._case2Setup = true;
   1.995 +        break;
   1.996 +    }
   1.997 +
   1.998 +    return true;
   1.999 +  },
  1.1000 +
  1.1001 +  // sets class and string on a feedback element
  1.1002 +  // if no property string is passed in, we clear label/style
  1.1003 +  _setFeedback: function (element, success, string) {
  1.1004 +    element.hidden = success || !string;
  1.1005 +    let classname = success ? "success" : "error";
  1.1006 +    let image = element.getElementsByAttribute("class", "statusIcon")[0];
  1.1007 +    image.setAttribute("status", classname);
  1.1008 +    let label = element.getElementsByAttribute("class", "status")[0];
  1.1009 +    label.value = string;
  1.1010 +  },
  1.1011 +
  1.1012 +  // shim
  1.1013 +  _setFeedbackMessage: function (element, success, string) {
  1.1014 +    let str = "";
  1.1015 +    if (string) {
  1.1016 +      try {
  1.1017 +        str = this._stringBundle.GetStringFromName(string);
  1.1018 +      } catch(e) {}
  1.1019 +
  1.1020 +      if (!str)
  1.1021 +        str = Weave.Utils.getErrorString(string);
  1.1022 +    }
  1.1023 +    this._setFeedback(element, success, str);
  1.1024 +  },
  1.1025 +
  1.1026 +  loadCaptcha: function loadCaptcha() {
  1.1027 +    let captchaURI = Weave.Service.miscAPI + "captcha_html";
  1.1028 +    // First check for NoScript and whitelist the right sites.
  1.1029 +    this._handleNoScript(true);
  1.1030 +    if (this.captchaBrowser.currentURI.spec != captchaURI) {
  1.1031 +      this.captchaBrowser.loadURI(captchaURI);
  1.1032 +    }
  1.1033 +  },
  1.1034 +
  1.1035 +  onStateChange: function(webProgress, request, stateFlags, status) {
  1.1036 +    // We're only looking for the end of the frame load
  1.1037 +    if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) == 0)
  1.1038 +      return;
  1.1039 +    if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) == 0)
  1.1040 +      return;
  1.1041 +    if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) == 0)
  1.1042 +      return;
  1.1043 +
  1.1044 +    // If we didn't find a captcha, assume it's not needed and don't show it.
  1.1045 +    let responseStatus = request.QueryInterface(Ci.nsIHttpChannel).responseStatus;
  1.1046 +    setVisibility(this.captchaBrowser, responseStatus != 404);
  1.1047 +    //XXX TODO we should really log any responseStatus other than 200
  1.1048 +  },
  1.1049 +  onProgressChange: function() {},
  1.1050 +  onStatusChange: function() {},
  1.1051 +  onSecurityChange: function() {},
  1.1052 +  onLocationChange: function () {}
  1.1053 +};
  1.1054 +
  1.1055 +// Define lazy getters for various XUL elements.
  1.1056 +//
  1.1057 +// onWizardAdvance() and onPageShow() are run before init(), so we'll even
  1.1058 +// define things that will almost certainly be used (like 'wizard') as a lazy
  1.1059 +// getter here.
  1.1060 +["wizard",
  1.1061 + "pin1",
  1.1062 + "pin2",
  1.1063 + "pin3",
  1.1064 + "pairDeviceErrorRow",
  1.1065 + "pairDeviceThrobber"].forEach(function (id) {
  1.1066 +  XPCOMUtils.defineLazyGetter(gSyncSetup, id, function() {
  1.1067 +    return document.getElementById(id);
  1.1068 +  });
  1.1069 +});
  1.1070 +XPCOMUtils.defineLazyGetter(gSyncSetup, "nextFocusEl", function () {
  1.1071 +  return {pin1: this.pin2,
  1.1072 +          pin2: this.pin3,
  1.1073 +          pin3: this.wizard.getButton("next")};
  1.1074 +});
  1.1075 +XPCOMUtils.defineLazyGetter(gSyncSetup, "_stringBundle", function() {
  1.1076 +  return Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
  1.1077 +});

mercurial