• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5cr.define('options', function() {
6  const OptionsPage = options.OptionsPage;
7
8  // Variable to track if a captcha challenge was issued. If this gets set to
9  // true, it stays that way until we are told about successful login from
10  // the browser.  This means subsequent errors (like invalid password) are
11  // rendered in the captcha state, which is basically identical except we
12  // don't show the top error blurb "Error Signing in" or the "Create
13  // account" link.
14  var captchaChallengeActive_ = false;
15
16  // True if the synced account uses a custom passphrase.
17  var usePassphrase_ = false;
18
19  /**
20   * SyncSetupOverlay class
21   * Encapsulated handling of the 'Sync Setup' overlay page.
22   * @class
23   */
24  function SyncSetupOverlay() {
25    OptionsPage.call(this, 'syncSetup',
26                     templateData.syncSetupOverlayTitle,
27                     'sync-setup-overlay');
28  }
29
30  cr.addSingletonGetter(SyncSetupOverlay);
31
32  SyncSetupOverlay.prototype = {
33    __proto__: OptionsPage.prototype,
34
35    /**
36     * Initializes the page.
37     */
38    initializePage: function() {
39      OptionsPage.prototype.initializePage.call(this);
40
41      var acct_text = $('gaia-account-text');
42      var translated_text = acct_text.textContent;
43      var posGoogle = translated_text.indexOf('Google');
44      if (posGoogle != -1) {
45        var ltr = templateData['textdirection'] == 'ltr';
46        var googleIsAtEndOfSentence = posGoogle != 0;
47        if (googleIsAtEndOfSentence == ltr) {
48          // We're in ltr and in the translation the word 'Google' is AFTER the
49          // word 'Account' OR we're in rtl and 'Google' is BEFORE 'Account'.
50          var logo_td = $('gaia-logo');
51          logo_td.parentNode.appendChild(logo_td);
52        }
53        acct_text.textContent = translated_text.replace('Google','');
54      }
55
56      var self = this;
57      $('gaia-login-form').onsubmit = function() {
58        self.sendCredentialsAndClose_();
59        return false;
60      };
61      $('chooseDataTypesForm').onsubmit = function() {
62        self.sendConfiguration_();
63        return false;
64      };
65      $('google-option').onchange = $('explicit-option').onchange = function() {
66        self.onRadioChange_();
67      };
68      $('choose-datatypes-cancel').onclick = $('sync-setup-cancel').onclick =
69          $('confirm-everything-cancel').onclick = function() {
70        self.closeOverlay_();
71      };
72      $('customize-link').onclick = function() {
73        self.showCustomizePage_(false);
74      };
75      $('confirm-everything-ok').onclick = function() {
76        self.sendConfiguration_();
77      };
78      $('use-default-link').onclick = function() {
79        self.showSyncEverythingPage_();
80      };
81      $('cancel-no-button').onclick = function() {
82        self.hideCancelWarning_();
83        return false;
84      };
85      $('cancel-yes-button').onclick = function() {
86        chrome.send('PassphraseCancel', ['']);
87        return false;
88      };
89      $('passphraseForm').onsubmit = $('passphrase-ok').onclick = function() {
90        self.sendPassphraseAndClose_();
91        return false;
92      };
93      $('passphrase-cancel').onclick = function() {
94        self.showCancelWarning_();
95        return false;
96      };
97    },
98
99    closeOverlay_: function() {
100      OptionsPage.closeOverlay();
101    },
102
103    /** @inheritDoc */
104    didShowPage: function() {
105      chrome.send('didShowPage');
106    },
107
108    /** @inheritDoc */
109    didClosePage: function() {
110      chrome.send('didClosePage');
111    },
112
113    showCancelWarning_: function() {
114      $('cancel-warning-box').hidden = false;
115      $('passphrase-ok').disabled = true;
116      $('passphrase-cancel').disabled = true;
117      $('cancel-no-button').focus();
118    },
119
120    sendPassphraseAndClose_: function() {
121      var f = $('passphraseForm');
122      var result = JSON.stringify({"passphrase": f.passphrase.value});
123      chrome.send("Passphrase", [result]);
124    },
125
126    getRadioCheckedValue_: function() {
127      var f = $('chooseDataTypesForm');
128      for (var i = 0; i < f.option.length; ++i) {
129        if (f.option[i].checked) {
130          return f.option[i].value;
131        }
132      }
133
134      return undefined;
135    },
136
137    // TODO(jhawkins): Remove this method.
138    switchToMode_: function(mode) {
139      if (mode == "google")
140        $('sync-custom-passphrase').hidden = true;
141      else if (mode =="explicit")
142        $('sync-custom-passphrase').hidden = false;
143    },
144
145    onRadioChange_: function() {
146      this.switchToMode_(this.getRadioCheckedValue_());
147    },
148
149    checkAllDataTypeCheckboxes_: function() {
150      var checkboxes = document.getElementsByName("dataTypeCheckbox");
151      for (var i = 0; i < checkboxes.length; i++) {
152        // Only check the visible ones (since there's no way to uncheck
153        // the invisible ones).
154        if (checkboxes[i].parentElement.className == "sync-item-show") {
155          checkboxes[i].checked = true;
156        }
157      }
158    },
159
160    setDataTypeCheckboxesEnabled_: function(enabled) {
161      var checkboxes = document.getElementsByName("dataTypeCheckbox");
162      var labels = document.getElementsByName("dataTypeLabel");
163      for (var i = 0; i < checkboxes.length; i++) {
164        checkboxes[i].disabled = !enabled;
165        if (checkboxes[i].disabled) {
166          labels[i].className = "sync-label-inactive";
167        } else {
168          labels[i].className = "sync-label-active";
169        }
170      }
171    },
172
173    setCheckboxesToKeepEverythingSynced_: function(value) {
174      this.setDataTypeCheckboxesEnabled_(!value);
175      if (value)
176        this.checkAllDataTypeCheckboxes_();
177    },
178
179    // Returns true if at least one data type is enabled and no data types are
180    // checked. (If all data type checkboxes are disabled, it's because "keep
181    // everything synced" is checked.)
182    noDataTypesChecked_: function() {
183      var checkboxes = document.getElementsByName("dataTypeCheckbox");
184      var atLeastOneChecked = false;
185      var atLeastOneEnabled = false;
186      for (var i = 0; i < checkboxes.length; i++) {
187        if (!checkboxes[i].disabled &&
188            checkboxes[i].parentElement.className == "sync-item-show") {
189          atLeastOneEnabled = true;
190          if (checkboxes[i].checked) {
191            atLeastOneChecked = true;
192          }
193        }
194      }
195
196      return atLeastOneEnabled && !atLeastOneChecked;
197    },
198
199    checkPassphraseMatch_: function() {
200      var emptyError = $('emptyerror');
201      var mismatchError = $('mismatcherror');
202      emptyError.style.display = "none";
203      mismatchError.style.display = "none";
204
205      var f = $('chooseDataTypesForm');
206      if (this.getRadioCheckedValue_() != "explicit" || f.option[0].disabled)
207        return true;
208
209      var customPassphrase = $('custom-passphrase');
210      if (customPassphrase.value.length == 0) {
211        emptyError.style.display = "block";
212        return false;
213      }
214
215      var confirmPassphrase = $('confirm-passphrase');
216      if (confirmPassphrase.value != customPassphrase.value) {
217        mismatchError.style.display = "block";
218        return false;
219      }
220
221      return true;
222    },
223
224    hideCancelWarning_: function() {
225      $('cancel-warning-box').hidden = true;
226      $('passphrase-ok').disabled = false;
227      $('passphrase-cancel').disabled = false;
228    },
229
230    sendConfiguration_: function() {
231      // Trying to submit, so hide previous errors.
232      $('aborted-text').className = "sync-error-hide";
233      $('error-text').className = "sync-error-hide";
234
235      if (this.noDataTypesChecked_()) {
236        $('error-text').className = "sync-error-show";
237        return;
238      }
239
240      var f = $('chooseDataTypesForm');
241      if (!this.checkPassphraseMatch_())
242        return;
243
244      // Don't allow the user to tweak the settings once we send the
245      // configuration to the backend.
246      this.disableConfigureElements_();
247
248      var syncAll =
249        document.getElementById('sync-select-datatypes').selectedIndex == 0;
250      var customPassphrase = $('custom-passphrase');
251
252      // These values need to be kept in sync with where they are read in
253      // SyncSetupFlow::GetDataTypeChoiceData().
254      var result = JSON.stringify({
255          "keepEverythingSynced": syncAll,
256          "syncBookmarks": syncAll || f.bookmarksCheckbox.checked,
257          "syncPreferences": syncAll || f.preferencesCheckbox.checked,
258          "syncThemes": syncAll || f.themesCheckbox.checked,
259          "syncPasswords": syncAll || f.passwordsCheckbox.checked,
260          "syncAutofill": syncAll || f.autofillCheckbox.checked,
261          "syncExtensions": syncAll || f.extensionsCheckbox.checked,
262          "syncTypedUrls": syncAll || f.typedUrlsCheckbox.checked,
263          "syncApps": syncAll || f.appsCheckbox.checked,
264          "syncSessions": syncAll || f.sessionsCheckbox.checked,
265          "usePassphrase": (this.getRadioCheckedValue_() == 'explicit'),
266          "passphrase": customPassphrase.value
267      });
268      chrome.send("Configure", [result]);
269    },
270
271    /**
272     * Disables all input elements within the 'Customize Sync Preferences'
273     * screen. This is used to prohibit the user from changing the inputs after
274     * confirming the customized sync preferences.
275     * @private
276     */
277    disableConfigureElements_: function() {
278      var configureElements =
279          $('customize-sync-preferences').querySelectorAll('input');
280      for (var i = 0; i < configureElements.length; i++)
281        configureElements[i].disabled = true;
282    },
283
284    setChooseDataTypesCheckboxes_: function(args) {
285      // If this frame is on top, the focus should be on it, so pressing enter
286      // submits this form.
287      if (args.iframeToShow == 'configure') {
288        $('choose-datatypes-ok').focus();
289      }
290
291      var datatypeSelect = document.getElementById('sync-select-datatypes');
292      datatypeSelect.selectedIndex = args.keepEverythingSynced ? 0 : 1;
293
294      $('bookmarksCheckbox').checked = args.syncBookmarks;
295      $('preferencesCheckbox').checked = args.syncPreferences;
296      $('themesCheckbox').checked = args.syncThemes;
297
298      if (args.passwordsRegistered) {
299        $('passwordsCheckbox').checked = args.syncPasswords;
300        $('passwordsItem').className = "sync-item-show";
301      } else {
302        $('passwordsItem').className = "sync-item-hide";
303      }
304      if (args.autofillRegistered) {
305        $('autofillCheckbox').checked = args.syncAutofill;
306        $('autofillItem').className = "sync-item-show";
307      } else {
308        $('autofillItem').className = "sync-item-hide";
309      }
310      if (args.extensionsRegistered) {
311        $('extensionsCheckbox').checked = args.syncExtensions;
312        $('extensionsItem').className = "sync-item-show";
313      } else {
314        $('extensionsItem').className = "sync-item-hide";
315      }
316      if (args.typedUrlsRegistered) {
317        $('typedUrlsCheckbox').checked = args.syncTypedUrls;
318        $('omniboxItem').className = "sync-item-show";
319      } else {
320        $('omniboxItem').className = "sync-item-hide";
321      }
322      if (args.appsRegistered) {
323        $('appsCheckbox').checked = args.syncApps;
324        $('appsItem').className = "sync-item-show";
325      } else {
326        $('appsItem').className = "sync-item-hide";
327      }
328
329      this.setCheckboxesToKeepEverythingSynced_(args.keepEverythingSynced);
330      if (args.sessionsRegistered) {
331        $('sessionsCheckbox').checked = args.syncSessions;
332        $('sessionsItem').className = "sync-item-show";
333      } else {
334        $('sessionsItem').className = "sync-item-hide";
335      }
336    },
337
338    setEncryptionCheckboxes_: function(args) {
339      if (args["usePassphrase"]) {
340        $('explicit-option').checked = true;
341
342        // The passphrase, once set, cannot be unset, but we show a reset link.
343        $('explicit-option').disabled = true;
344        $('google-option').disabled = true;
345        $('sync-custom-passphrase').hidden = true;
346      } else {
347        $('google-option').checked = true;
348      }
349
350      this.switchToMode_("");
351    },
352
353    setErrorState_: function(args) {
354      if (!args.was_aborted)
355        return;
356
357      $('aborted-text').className = "sync-error-show";
358      $('choose-datatypes-ok').disabled = true;
359      $('keepEverythingSyncedRadio').disabled = true;
360      $('chooseDataTypesRadio').disabled = true;
361    },
362
363    setCheckboxesAndErrors_: function(args) {
364      this.setChooseDataTypesCheckboxes_(args);
365      this.setEncryptionCheckboxes_(args);
366      this.setErrorState_(args);
367    },
368
369    // Called once, when this html/js is loaded.
370    showConfigure_: function(args) {
371      var datatypeSelect = document.getElementById('sync-select-datatypes');
372      var self = this;
373      datatypeSelect.onchange = function() {
374        var syncAll = this.selectedIndex == 0;
375        self.setCheckboxesToKeepEverythingSynced_(syncAll);
376      };
377
378      $('sync-setup-configure').classList.remove('hidden');
379
380      if (args) {
381        this.setCheckboxesAndErrors_(args);
382
383        // Whether to display the 'Sync everything' confirmation screen or the
384        // customize data types screen.
385        // TODO(jhawkins): Rename |keepEverythingSynced| to |syncAllDataTypes|.
386        var syncEverything = args['syncEverything'];
387        var syncAllDataTypes = args['keepEverythingSynced'];
388        this.usePassphrase_ = args['usePassphrase'];
389        if (syncEverything == false || syncAllDataTypes == false ||
390            this.usePassphrase_) {
391          this.showCustomizePage_(syncAllDataTypes);
392        } else {
393          this.showSyncEverythingPage_();
394        }
395      }
396    },
397
398    showSyncEverythingPage_: function() {
399      $('confirm-sync-preferences').hidden = false;
400      $('customize-sync-preferences').hidden = true;
401
402      // Reset the selection to 'Sync everything'.
403      $('sync-select-datatypes').selectedIndex = 0;
404
405      // The default state is to sync everything.
406      this.setCheckboxesToKeepEverythingSynced_(true);
407
408      // If the account is not synced with a custom passphrase, reset the
409      // passphrase radio when switching to the 'Sync everything' page.
410      if (!this.usePassphrase_) {
411        $('google-option').checked = true;
412        this.switchToMode_("google");
413      }
414
415      $('confirm-everything-ok').focus();
416    },
417
418    showCustomizePage_: function(syncEverything) {
419      document.getElementById('confirm-sync-preferences').hidden = true;
420      document.getElementById('customize-sync-preferences').hidden = false;
421
422      // If the user has selected the 'Customize' page on initial set up, it's
423      // likely he intends to change the data types.  Select the
424      // 'Choose data types' option in this case.
425      var index = syncEverything ? 0 : 1;
426      document.getElementById('sync-select-datatypes').selectedIndex = index;
427      this.setDataTypeCheckboxesEnabled_(!syncEverything);
428      $('choose-datatypes-ok').focus();
429    },
430
431    showSyncSetupPage_: function(page, args) {
432      if (page == 'settingUp') {
433        this.setThrobbersVisible_(true);
434        return;
435      } else {
436        this.setThrobbersVisible_(false);
437      }
438
439      // Hide an existing visible overlay.
440      var overlay = $('sync-setup-overlay');
441      for (var i = 0; i < overlay.children.length; i++)
442        overlay.children[i].classList.add('hidden');
443
444      if (page == 'login')
445        this.showGaiaLogin_(args);
446      else if (page == 'configure')
447        this.showConfigure_(args);
448      else if (page == 'passphrase')
449        this.showPassphrase_(args);
450      else if (page == 'done')
451        this.closeOverlay_();
452    },
453
454    setThrobbersVisible_: function(visible) {
455      var throbbers = document.getElementsByClassName("throbber");
456        for (var i = 0; i < throbbers.length; i++)
457          throbbers[i].style.visibility = visible ? "visible" : "hidden";
458    },
459
460    showPassphrase_: function(args) {
461      $('sync-setup-passphrase').classList.remove('hidden');
462
463      $('passphraseRejectedBody').style.display = "none";
464      $('normalBody').style.display = "none";
465      $('incorrectPassphrase').style.display = "none";
466
467      if (args["passphrase_creation_rejected"]) {
468        $('passphraseRejectedBody').style.display = "block";
469      } else {
470        $('normalBody').style.display = "block";
471      }
472
473      if (args["passphrase_setting_rejected"]) {
474        $('incorrectPassphrase').style.display = "block";
475      }
476
477      $('passphrase').focus();
478    },
479
480    setElementDisplay_: function(id, display) {
481      var d = document.getElementById(id);
482      if (d)
483        d.style.display = display;
484    },
485
486    loginSetFocus_: function() {
487      var email = $('gaia-email');
488      var passwd = $('gaia-passwd');
489      if (email && (email.value == null || email.value == "")) {
490        email.focus();
491      } else if (passwd) {
492        passwd.focus();
493      }
494    },
495
496    showAccessCodeRequired_: function() {
497      this.setElementDisplay_("password-row", "none");
498      this.setElementDisplay_("email-row", "none");
499      $('create-account-cell').style.visibility = "hidden";
500
501      this.setElementDisplay_("access-code-label-row", "table-row");
502      this.setElementDisplay_("access-code-input-row", "table-row");
503      this.setElementDisplay_("access-code-help-row", "table-row");
504      document.getElementById('access-code').disabled = false;
505    },
506
507    showCaptcha_: function(args) {
508      this.captchaChallengeActive_ = true;
509
510      // The captcha takes up lots of space, so make room.
511      this.setElementDisplay_("top-blurb", "none");
512      this.setElementDisplay_("top-blurb-error", "none");
513      this.setElementDisplay_("create-account-div", "none");
514      document.getElementById('create-account-cell').height = 0;
515
516      // It's showtime for the captcha now.
517      this.setElementDisplay_("captcha-div", "block");
518      document.getElementById('gaia-email').disabled = true;
519      document.getElementById('gaia-passwd').disabled = false;
520      document.getElementById('captcha-value').disabled = false;
521      document.getElementById('captcha-wrapper').style.backgroundImage =
522          url(args.captchaUrl);
523    },
524
525    showGaiaLogin_: function(args) {
526      $('sync-setup-login').classList.remove('hidden');
527
528      document.getElementById('gaia-email').disabled = false;
529      document.getElementById('gaia-passwd').disabled = false;
530
531      var f = $('gaia-login-form');
532      var email = $('gaia-email');
533      var passwd = $('gaia-passwd');
534      if (f) {
535        if (args.user != undefined) {
536          if (email.value != args.user)
537            passwd.value = ""; // Reset the password field
538          email.value = args.user;
539        }
540
541        if (!args.editable_user) {
542          email.style.display = 'none';
543          var span = document.getElementById('email-readonly');
544          span.appendChild(document.createTextNode(email.value));
545          span.style.display = 'inline';
546          this.setElementDisplay_("create-account-div", "none");
547        }
548
549        f.accessCode.disabled = true;
550      }
551
552      if (1 == args.error) {
553        var access_code = document.getElementById('access-code');
554        if (access_code.value && access_code.value != "") {
555          this.setElementDisplay_("errormsg-0-access-code", 'block');
556          this.showAccessCodeRequired_();
557        } else {
558          this.setElementDisplay_("errormsg-1-password", 'table-row');
559        }
560        this.setBlurbError_(args.error_message);
561      } else if (3 == args.error) {
562        this.setElementDisplay_("errormsg-0-connection", 'table-row');
563        this.setBlurbError_(args.error_message);
564      } else if (4 == args.error) {
565        this.showCaptcha_(args);
566      } else if (8 == args.error) {
567        this.showAccessCodeRequired_();
568      } else if (args.error_message) {
569        this.setBlurbError_(args.error_message);
570      }
571
572      $('sign-in').disabled = false;
573      $('sign-in').value = templateData['signin'];
574      this.loginSetFocus_();
575    },
576
577    resetErrorVisibility_: function() {
578      this.setElementDisplay_("errormsg-0-email", 'none');
579      this.setElementDisplay_("errormsg-0-password", 'none');
580      this.setElementDisplay_("errormsg-1-password", 'none');
581      this.setElementDisplay_("errormsg-0-connection", 'none');
582      this.setElementDisplay_("errormsg-0-access-code", 'none');
583    },
584
585    setBlurbError_: function(error_message) {
586      if (this.captchaChallengeActive_)
587        return;  // No blurb in captcha challenge mode.
588
589      if (error_message) {
590        document.getElementById('error-signing-in').style.display = 'none';
591        document.getElementById('error-custom').style.display = 'inline';
592        document.getElementById('error-custom').textContent = error_message;
593      } else {
594        document.getElementById('error-signing-in').style.display = 'inline';
595        document.getElementById('error-custom').style.display = 'none';
596      }
597
598      $('top-blurb-error').style.visibility = "visible";
599      document.getElementById('gaia-email').disabled = false;
600      document.getElementById('gaia-passwd').disabled = false;
601    },
602
603    setErrorVisibility_: function() {
604      this.resetErrorVisibility_();
605      var f = $('gaia-login-form');
606      var email = $('gaia-email');
607      var passwd = $('gaia-passwd');
608      if (null == email.value || "" == email.value) {
609        this.setElementDisplay_("errormsg-0-email", 'table-row');
610        this.setBlurbError_();
611        return false;
612      }
613      if (null == passwd.value || "" == passwd.value) {
614        this.setElementDisplay_("errormsg-0-password", 'table-row');
615        this.setBlurbError_();
616        return false;
617      }
618      if (!f.accessCode.disabled && (null == f.accessCode.value ||
619          "" == f.accessCode.value)) {
620        this.setElementDisplay_("errormsg-0-password", 'table-row');
621        return false;
622      }
623      return true;
624    },
625
626    sendCredentialsAndClose_: function() {
627      if (!this.setErrorVisibility_()) {
628        return false;
629      }
630
631      $('gaia-email').disabled = true;
632      $('gaia-passwd').disabled = true;
633      $('captcha-value').disabled = true;
634      $('access-code').disabled = true;
635
636      $('logging-in-throbber').style.visibility = "visible";
637
638      var f = $('gaia-login-form');
639      var email = $('gaia-email');
640      var passwd = $('gaia-passwd');
641      var result = JSON.stringify({"user" : email.value,
642                                   "pass" : passwd.value,
643                                   "captcha" : f.captchaValue.value,
644                                   "access_code" : f.accessCode.value});
645      $('sign-in').disabled = true;
646      chrome.send("SubmitAuth", [result]);
647    },
648
649    showGaiaSuccessAndClose_: function() {
650      $('sign-in').value = localStrings.getString('loginSuccess');
651      setTimeout(this.closeOverlay_, 1600);
652    },
653
654    showSuccessAndSettingUp_: function() {
655      $('sign-in').value = localStrings.getString('settingup');
656    },
657
658    /** @inheritDoc */
659    shouldClose: function() {
660      if (!$('cancel-warning-box').hidden) {
661        chrome.send('PassphraseCancel', ['']);
662        return true;
663      } else if (!$('sync-setup-passphrase').classList.contains('hidden')) {
664        // The Passphrase page is showing, and the use has pressed escape.
665        // Activate the cancel logic in this case.
666        this.showCancelWarning_();
667        return false;
668      }
669
670      return true;
671    },
672  };
673
674  SyncSetupOverlay.showSyncSetupPage = function(page, args) {
675    SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
676  };
677
678  SyncSetupOverlay.showSuccessAndClose = function() {
679    SyncSetupOverlay.getInstance().showSuccessAndClose_();
680  };
681
682  SyncSetupOverlay.showSuccessAndSettingUp = function() {
683    SyncSetupOverlay.getInstance().showSuccessAndSettingUp_();
684  };
685
686  // Export
687  return {
688    SyncSetupOverlay: SyncSetupOverlay
689  };
690});
691