Home | History | Annotate | Download | only in sync
      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 
      5 #include "chrome/browser/sync/sync_setup_flow.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/json/json_reader.h"
      9 #include "base/json/json_writer.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/string_util.h"
     12 #include "base/utf_string_conversions.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/platform_util.h"
     15 #include "chrome/browser/prefs/pref_service.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/sync/profile_sync_service.h"
     18 #include "chrome/browser/sync/sync_setup_flow_handler.h"
     19 #include "chrome/browser/ui/browser.h"
     20 #include "chrome/browser/ui/browser_dialogs.h"
     21 #include "chrome/browser/ui/browser_list.h"
     22 #include "chrome/common/net/gaia/google_service_auth_error.h"
     23 #include "chrome/common/pref_names.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "content/browser/renderer_host/render_view_host.h"
     26 #include "content/browser/tab_contents/tab_contents.h"
     27 #include "grit/generated_resources.h"
     28 #include "grit/locale_settings.h"
     29 #include "ui/base/l10n/l10n_font_util.h"
     30 #include "ui/gfx/font.h"
     31 
     32 namespace {
     33 
     34 // Helper function to disable password sync.
     35 void DisablePasswordSync(ProfileSyncService* service) {
     36   syncable::ModelTypeSet types;
     37   service->GetPreferredDataTypes(&types);
     38   types.erase(syncable::PASSWORDS);
     39   service->OnUserChoseDatatypes(false, types);
     40 }
     41 
     42 }  // namespace
     43 
     44 SyncConfiguration::SyncConfiguration()
     45     : sync_everything(false),
     46       use_secondary_passphrase(false) {
     47 }
     48 
     49 SyncConfiguration::~SyncConfiguration() {}
     50 
     51 SyncSetupFlow::~SyncSetupFlow() {
     52   flow_handler_->SetFlow(NULL);
     53 }
     54 
     55 // static
     56 SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
     57                                   SyncSetupFlowContainer* container,
     58                                   SyncSetupWizard::State start,
     59                                   SyncSetupWizard::State end) {
     60   DictionaryValue args;
     61   if (start == SyncSetupWizard::GAIA_LOGIN)
     62     SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
     63   else if (start == SyncSetupWizard::CONFIGURE)
     64     SyncSetupFlow::GetArgsForConfigure(service, &args);
     65   else if (start == SyncSetupWizard::ENTER_PASSPHRASE)
     66     SyncSetupFlow::GetArgsForEnterPassphrase(false, false, &args);
     67   else if (start == SyncSetupWizard::PASSPHRASE_MIGRATION)
     68     args.SetString("iframeToShow", "firstpassphrase");
     69 
     70   std::string json_args;
     71   base::JSONWriter::Write(&args, false, &json_args);
     72 
     73   SyncSetupFlow* flow = new SyncSetupFlow(start, end, json_args,
     74       container, service);
     75 
     76   Browser* b = BrowserList::GetLastActive();
     77   b->ShowOptionsTab(chrome::kSyncSetupSubPage);
     78   return flow;
     79 }
     80 
     81 // static
     82 void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
     83                                         DictionaryValue* args) {
     84   args->SetString("iframeToShow", "login");
     85   const GoogleServiceAuthError& error = service->GetAuthError();
     86   if (!service->last_attempted_user_email().empty()) {
     87     args->SetString("user", service->last_attempted_user_email());
     88     args->SetInteger("error", error.state());
     89     args->SetBoolean("editable_user", true);
     90   } else {
     91     string16 user;
     92     if (!service->cros_user().empty())
     93       user = UTF8ToUTF16(service->cros_user());
     94     else
     95       user = service->GetAuthenticatedUsername();
     96     args->SetString("user", user);
     97     args->SetInteger("error", 0);
     98     args->SetBoolean("editable_user", user.empty());
     99   }
    100 
    101   args->SetString("captchaUrl", error.captcha().image_url.spec());
    102 }
    103 
    104 // static
    105 void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
    106                                         DictionaryValue* args) {
    107   args->SetString("iframeToShow", "configure");
    108 
    109   // The SYNC_EVERYTHING case will set this to true.
    110   args->SetBoolean("syncEverything", false);
    111 
    112   args->SetBoolean("keepEverythingSynced",
    113       service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced));
    114 
    115   // Bookmarks, Preferences, and Themes are launched for good, there's no
    116   // going back now.  Check if the other data types are registered though.
    117   syncable::ModelTypeSet registered_types;
    118   service->GetRegisteredDataTypes(&registered_types);
    119   args->SetBoolean("passwordsRegistered",
    120       registered_types.count(syncable::PASSWORDS) > 0);
    121   args->SetBoolean("autofillRegistered",
    122       registered_types.count(syncable::AUTOFILL) > 0);
    123   args->SetBoolean("extensionsRegistered",
    124       registered_types.count(syncable::EXTENSIONS) > 0);
    125   args->SetBoolean("typedUrlsRegistered",
    126       registered_types.count(syncable::TYPED_URLS) > 0);
    127   args->SetBoolean("appsRegistered",
    128       registered_types.count(syncable::APPS) > 0);
    129   args->SetBoolean("sessionsRegistered",
    130       registered_types.count(syncable::SESSIONS) > 0);
    131   args->SetBoolean("syncBookmarks",
    132       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
    133   args->SetBoolean("syncPreferences",
    134       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences));
    135   args->SetBoolean("syncThemes",
    136       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes));
    137   args->SetBoolean("syncPasswords",
    138       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords));
    139   args->SetBoolean("syncAutofill",
    140       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill));
    141   args->SetBoolean("syncExtensions",
    142       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions));
    143   args->SetBoolean("syncSessions",
    144       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions));
    145   args->SetBoolean("syncTypedUrls",
    146       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
    147   args->SetBoolean("syncApps",
    148       service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
    149 
    150   // Load the parameters for the encryption tab.
    151   args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase());
    152 }
    153 
    154 // static
    155 void SyncSetupFlow::GetArgsForEnterPassphrase(
    156     bool tried_creating_explicit_passphrase,
    157     bool tried_setting_explicit_passphrase,
    158     DictionaryValue* args) {
    159   args->SetString("iframeToShow", "passphrase");
    160   args->SetBoolean("passphrase_creation_rejected",
    161                    tried_creating_explicit_passphrase);
    162   args->SetBoolean("passphrase_setting_rejected",
    163                    tried_setting_explicit_passphrase);
    164 }
    165 
    166 void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) {
    167   flow_handler_ = handler;
    168   ActivateState(current_state_);
    169 }
    170 
    171 void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
    172   if (!ShouldAdvance(advance_state)) {
    173     LOG(WARNING) << "Invalid state change from "
    174                  << current_state_ << " to " << advance_state;
    175     return;
    176   }
    177 
    178   ActivateState(advance_state);
    179 }
    180 
    181 void SyncSetupFlow::Focus() {
    182   // TODO(jhawkins): Implement this.
    183 }
    184 
    185 // A callback to notify the delegate that the dialog closed.
    186 void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
    187   DCHECK(json_retval.empty());
    188   container_->set_flow(NULL);  // Sever ties from the wizard.
    189   if (current_state_ == SyncSetupWizard::DONE ||
    190       current_state_ == SyncSetupWizard::DONE_FIRST_TIME) {
    191     service_->SetSyncSetupCompleted();
    192   }
    193 
    194   // Record the state at which the user cancelled the signon dialog.
    195   switch (current_state_) {
    196     case SyncSetupWizard::GAIA_LOGIN:
    197       ProfileSyncService::SyncEvent(
    198           ProfileSyncService::CANCEL_FROM_SIGNON_WITHOUT_AUTH);
    199       break;
    200     case SyncSetupWizard::GAIA_SUCCESS:
    201       ProfileSyncService::SyncEvent(
    202           ProfileSyncService::CANCEL_DURING_SIGNON);
    203       break;
    204     case SyncSetupWizard::CONFIGURE:
    205     case SyncSetupWizard::ENTER_PASSPHRASE:
    206     case SyncSetupWizard::SETTING_UP:
    207       // TODO(atwilson): Treat a close during ENTER_PASSPHRASE like a
    208       // Cancel + Skip (i.e. call OnPassphraseCancel()). http://crbug.com/74645
    209       ProfileSyncService::SyncEvent(
    210           ProfileSyncService::CANCEL_DURING_CONFIGURE);
    211       break;
    212     case SyncSetupWizard::DONE_FIRST_TIME:
    213     case SyncSetupWizard::DONE:
    214       // TODO(sync): rename this histogram; it's tracking authorization AND
    215       // initial sync download time.
    216       UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedAuthorizationTime",
    217                                  base::TimeTicks::Now() - login_start_time_);
    218       break;
    219     default:
    220       break;
    221   }
    222 
    223   service_->OnUserCancelledDialog();
    224   delete this;
    225 }
    226 
    227 void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
    228                                         const std::string& password,
    229                                         const std::string& captcha,
    230                                         const std::string& access_code) {
    231   service_->OnUserSubmittedAuth(username, password, captcha, access_code);
    232 }
    233 
    234 void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
    235   // Go to the "loading..." screen.
    236   Advance(SyncSetupWizard::SETTING_UP);
    237 
    238   // If we are activating the passphrase, we need to have one supplied.
    239   DCHECK(service_->IsUsingSecondaryPassphrase() ||
    240          !configuration.use_secondary_passphrase ||
    241          configuration.secondary_passphrase.length() > 0);
    242 
    243   if (configuration.use_secondary_passphrase &&
    244       !service_->IsUsingSecondaryPassphrase()) {
    245     service_->SetPassphrase(configuration.secondary_passphrase, true, true);
    246     tried_creating_explicit_passphrase_ = true;
    247   }
    248 
    249   service_->OnUserChoseDatatypes(configuration.sync_everything,
    250                                  configuration.data_types);
    251 }
    252 
    253 void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
    254   Advance(SyncSetupWizard::SETTING_UP);
    255   service_->SetPassphrase(passphrase, true, false);
    256   tried_setting_explicit_passphrase_ = true;
    257 }
    258 
    259 void SyncSetupFlow::OnPassphraseCancel() {
    260   // If the user cancels when being asked for the passphrase,
    261   // just disable encrypted sync and continue setting up.
    262   if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE)
    263     DisablePasswordSync(service_);
    264 
    265   Advance(SyncSetupWizard::SETTING_UP);
    266 }
    267 
    268 // TODO(jhawkins): Remove this method.
    269 void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option,
    270                                            const std::string& passphrase) {
    271   NOTREACHED();
    272 }
    273 
    274 // TODO(jhawkins): Use this method instead of a direct link in the html.
    275 void SyncSetupFlow::OnGoToDashboard() {
    276   BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
    277 }
    278 
    279 // Use static Run method to get an instance.
    280 SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
    281                              SyncSetupWizard::State end_state,
    282                              const std::string& args,
    283                              SyncSetupFlowContainer* container,
    284                              ProfileSyncService* service)
    285     : container_(container),
    286       dialog_start_args_(args),
    287       current_state_(start_state),
    288       end_state_(end_state),
    289       login_start_time_(base::TimeTicks::Now()),
    290       flow_handler_(NULL),
    291       service_(service),
    292       tried_creating_explicit_passphrase_(false),
    293       tried_setting_explicit_passphrase_(false) {
    294 }
    295 
    296 // Returns true if the flow should advance to |state| based on |current_state_|.
    297 bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
    298   switch (state) {
    299     case SyncSetupWizard::GAIA_LOGIN:
    300       return current_state_ == SyncSetupWizard::FATAL_ERROR ||
    301              current_state_ == SyncSetupWizard::GAIA_LOGIN ||
    302              current_state_ == SyncSetupWizard::SETTING_UP;
    303     case SyncSetupWizard::GAIA_SUCCESS:
    304       return current_state_ == SyncSetupWizard::GAIA_LOGIN;
    305     case SyncSetupWizard::SYNC_EVERYTHING:
    306     case SyncSetupWizard::CONFIGURE:
    307       return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
    308     case SyncSetupWizard::ENTER_PASSPHRASE:
    309       return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
    310              current_state_ == SyncSetupWizard::CONFIGURE ||
    311              current_state_ == SyncSetupWizard::SETTING_UP;
    312     case SyncSetupWizard::PASSPHRASE_MIGRATION:
    313       return current_state_ == SyncSetupWizard::GAIA_LOGIN;
    314     case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR:
    315       DCHECK(current_state_ != SyncSetupWizard::GAIA_LOGIN &&
    316              current_state_ != SyncSetupWizard::GAIA_SUCCESS);
    317       return true;
    318     case SyncSetupWizard::SETTING_UP:
    319       return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
    320              current_state_ == SyncSetupWizard::CONFIGURE ||
    321              current_state_ == SyncSetupWizard::ENTER_PASSPHRASE ||
    322              current_state_ == SyncSetupWizard::PASSPHRASE_MIGRATION;
    323     case SyncSetupWizard::FATAL_ERROR:
    324       return true;  // You can always hit the panic button.
    325     case SyncSetupWizard::DONE_FIRST_TIME:
    326     case SyncSetupWizard::DONE:
    327       return current_state_ == SyncSetupWizard::SETTING_UP ||
    328              current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
    329     default:
    330       NOTREACHED() << "Unhandled State: " << state;
    331       return false;
    332   }
    333 }
    334 
    335 void SyncSetupFlow::ActivateState(SyncSetupWizard::State state) {
    336   switch (state) {
    337     case SyncSetupWizard::GAIA_LOGIN: {
    338       DictionaryValue args;
    339       SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
    340       flow_handler_->ShowGaiaLogin(args);
    341       break;
    342     }
    343     case SyncSetupWizard::GAIA_SUCCESS:
    344       if (end_state_ == SyncSetupWizard::GAIA_SUCCESS) {
    345         flow_handler_->ShowGaiaSuccessAndClose();
    346         break;
    347       }
    348       state = SyncSetupWizard::SYNC_EVERYTHING;
    349       //  Fall through.
    350     case SyncSetupWizard::SYNC_EVERYTHING: {
    351       DictionaryValue args;
    352       SyncSetupFlow::GetArgsForConfigure(service_, &args);
    353       args.SetBoolean("syncEverything", true);
    354       flow_handler_->ShowConfigure(args);
    355       break;
    356     }
    357     case SyncSetupWizard::CONFIGURE: {
    358       DictionaryValue args;
    359       SyncSetupFlow::GetArgsForConfigure(service_, &args);
    360       flow_handler_->ShowConfigure(args);
    361       break;
    362     }
    363     case SyncSetupWizard::ENTER_PASSPHRASE: {
    364       DictionaryValue args;
    365       SyncSetupFlow::GetArgsForEnterPassphrase(
    366           tried_creating_explicit_passphrase_,
    367           tried_setting_explicit_passphrase_,
    368           &args);
    369       flow_handler_->ShowPassphraseEntry(args);
    370       break;
    371     }
    372     case SyncSetupWizard::PASSPHRASE_MIGRATION: {
    373       DictionaryValue args;
    374       args.SetString("iframeToShow", "firstpassphrase");
    375       flow_handler_->ShowFirstPassphrase(args);
    376       break;
    377     }
    378     case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: {
    379       DictionaryValue args;
    380       SyncSetupFlow::GetArgsForConfigure(service_, &args);
    381       args.SetBoolean("was_aborted", true);
    382       flow_handler_->ShowConfigure(args);
    383       break;
    384     }
    385     case SyncSetupWizard::SETTING_UP: {
    386       flow_handler_->ShowSettingUp();
    387       break;
    388     }
    389     case SyncSetupWizard::FATAL_ERROR: {
    390       // This shows the user the "Could not connect to server" error.
    391       // TODO(sync): Update this error messaging.
    392       DictionaryValue args;
    393       SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
    394       args.SetInteger("error", GoogleServiceAuthError::CONNECTION_FAILED);
    395       flow_handler_->ShowGaiaLogin(args);
    396       break;
    397     }
    398     case SyncSetupWizard::DONE_FIRST_TIME:
    399       flow_handler_->ShowFirstTimeDone(
    400           UTF16ToWide(service_->GetAuthenticatedUsername()));
    401       break;
    402     case SyncSetupWizard::DONE:
    403       flow_handler_->ShowSetupDone(
    404           UTF16ToWide(service_->GetAuthenticatedUsername()));
    405       break;
    406     default:
    407       NOTREACHED() << "Invalid advance state: " << state;
    408   }
    409   current_state_ = state;
    410 }
    411