| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/sync/sync_setup_flow.h" |
| |
| #include "base/callback.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/metrics/histogram.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/sync_setup_flow_handler.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/common/net/gaia/google_service_auth_error.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "grit/generated_resources.h" |
| #include "grit/locale_settings.h" |
| #include "ui/base/l10n/l10n_font_util.h" |
| #include "ui/gfx/font.h" |
| |
| namespace { |
| |
| // Helper function to disable password sync. |
| void DisablePasswordSync(ProfileSyncService* service) { |
| syncable::ModelTypeSet types; |
| service->GetPreferredDataTypes(&types); |
| types.erase(syncable::PASSWORDS); |
| service->OnUserChoseDatatypes(false, types); |
| } |
| |
| } // namespace |
| |
| SyncConfiguration::SyncConfiguration() |
| : sync_everything(false), |
| use_secondary_passphrase(false) { |
| } |
| |
| SyncConfiguration::~SyncConfiguration() {} |
| |
| SyncSetupFlow::~SyncSetupFlow() { |
| flow_handler_->SetFlow(NULL); |
| } |
| |
| // static |
| SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service, |
| SyncSetupFlowContainer* container, |
| SyncSetupWizard::State start, |
| SyncSetupWizard::State end) { |
| DictionaryValue args; |
| if (start == SyncSetupWizard::GAIA_LOGIN) |
| SyncSetupFlow::GetArgsForGaiaLogin(service, &args); |
| else if (start == SyncSetupWizard::CONFIGURE) |
| SyncSetupFlow::GetArgsForConfigure(service, &args); |
| else if (start == SyncSetupWizard::ENTER_PASSPHRASE) |
| SyncSetupFlow::GetArgsForEnterPassphrase(false, false, &args); |
| else if (start == SyncSetupWizard::PASSPHRASE_MIGRATION) |
| args.SetString("iframeToShow", "firstpassphrase"); |
| |
| std::string json_args; |
| base::JSONWriter::Write(&args, false, &json_args); |
| |
| SyncSetupFlow* flow = new SyncSetupFlow(start, end, json_args, |
| container, service); |
| |
| Browser* b = BrowserList::GetLastActive(); |
| b->ShowOptionsTab(chrome::kSyncSetupSubPage); |
| return flow; |
| } |
| |
| // static |
| void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service, |
| DictionaryValue* args) { |
| args->SetString("iframeToShow", "login"); |
| const GoogleServiceAuthError& error = service->GetAuthError(); |
| if (!service->last_attempted_user_email().empty()) { |
| args->SetString("user", service->last_attempted_user_email()); |
| args->SetInteger("error", error.state()); |
| args->SetBoolean("editable_user", true); |
| } else { |
| string16 user; |
| if (!service->cros_user().empty()) |
| user = UTF8ToUTF16(service->cros_user()); |
| else |
| user = service->GetAuthenticatedUsername(); |
| args->SetString("user", user); |
| args->SetInteger("error", 0); |
| args->SetBoolean("editable_user", user.empty()); |
| } |
| |
| args->SetString("captchaUrl", error.captcha().image_url.spec()); |
| } |
| |
| // static |
| void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service, |
| DictionaryValue* args) { |
| args->SetString("iframeToShow", "configure"); |
| |
| // The SYNC_EVERYTHING case will set this to true. |
| args->SetBoolean("syncEverything", false); |
| |
| args->SetBoolean("keepEverythingSynced", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced)); |
| |
| // Bookmarks, Preferences, and Themes are launched for good, there's no |
| // going back now. Check if the other data types are registered though. |
| syncable::ModelTypeSet registered_types; |
| service->GetRegisteredDataTypes(®istered_types); |
| args->SetBoolean("passwordsRegistered", |
| registered_types.count(syncable::PASSWORDS) > 0); |
| args->SetBoolean("autofillRegistered", |
| registered_types.count(syncable::AUTOFILL) > 0); |
| args->SetBoolean("extensionsRegistered", |
| registered_types.count(syncable::EXTENSIONS) > 0); |
| args->SetBoolean("typedUrlsRegistered", |
| registered_types.count(syncable::TYPED_URLS) > 0); |
| args->SetBoolean("appsRegistered", |
| registered_types.count(syncable::APPS) > 0); |
| args->SetBoolean("sessionsRegistered", |
| registered_types.count(syncable::SESSIONS) > 0); |
| args->SetBoolean("syncBookmarks", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks)); |
| args->SetBoolean("syncPreferences", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences)); |
| args->SetBoolean("syncThemes", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes)); |
| args->SetBoolean("syncPasswords", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords)); |
| args->SetBoolean("syncAutofill", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill)); |
| args->SetBoolean("syncExtensions", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions)); |
| args->SetBoolean("syncSessions", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions)); |
| args->SetBoolean("syncTypedUrls", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls)); |
| args->SetBoolean("syncApps", |
| service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps)); |
| |
| // Load the parameters for the encryption tab. |
| args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase()); |
| } |
| |
| // static |
| void SyncSetupFlow::GetArgsForEnterPassphrase( |
| bool tried_creating_explicit_passphrase, |
| bool tried_setting_explicit_passphrase, |
| DictionaryValue* args) { |
| args->SetString("iframeToShow", "passphrase"); |
| args->SetBoolean("passphrase_creation_rejected", |
| tried_creating_explicit_passphrase); |
| args->SetBoolean("passphrase_setting_rejected", |
| tried_setting_explicit_passphrase); |
| } |
| |
| void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) { |
| flow_handler_ = handler; |
| ActivateState(current_state_); |
| } |
| |
| void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) { |
| if (!ShouldAdvance(advance_state)) { |
| LOG(WARNING) << "Invalid state change from " |
| << current_state_ << " to " << advance_state; |
| return; |
| } |
| |
| ActivateState(advance_state); |
| } |
| |
| void SyncSetupFlow::Focus() { |
| // TODO(jhawkins): Implement this. |
| } |
| |
| // A callback to notify the delegate that the dialog closed. |
| void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) { |
| DCHECK(json_retval.empty()); |
| container_->set_flow(NULL); // Sever ties from the wizard. |
| if (current_state_ == SyncSetupWizard::DONE || |
| current_state_ == SyncSetupWizard::DONE_FIRST_TIME) { |
| service_->SetSyncSetupCompleted(); |
| } |
| |
| // Record the state at which the user cancelled the signon dialog. |
| switch (current_state_) { |
| case SyncSetupWizard::GAIA_LOGIN: |
| ProfileSyncService::SyncEvent( |
| ProfileSyncService::CANCEL_FROM_SIGNON_WITHOUT_AUTH); |
| break; |
| case SyncSetupWizard::GAIA_SUCCESS: |
| ProfileSyncService::SyncEvent( |
| ProfileSyncService::CANCEL_DURING_SIGNON); |
| break; |
| case SyncSetupWizard::CONFIGURE: |
| case SyncSetupWizard::ENTER_PASSPHRASE: |
| case SyncSetupWizard::SETTING_UP: |
| // TODO(atwilson): Treat a close during ENTER_PASSPHRASE like a |
| // Cancel + Skip (i.e. call OnPassphraseCancel()). http://crbug.com/74645 |
| ProfileSyncService::SyncEvent( |
| ProfileSyncService::CANCEL_DURING_CONFIGURE); |
| break; |
| case SyncSetupWizard::DONE_FIRST_TIME: |
| case SyncSetupWizard::DONE: |
| // TODO(sync): rename this histogram; it's tracking authorization AND |
| // initial sync download time. |
| UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedAuthorizationTime", |
| base::TimeTicks::Now() - login_start_time_); |
| break; |
| default: |
| break; |
| } |
| |
| service_->OnUserCancelledDialog(); |
| delete this; |
| } |
| |
| void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username, |
| const std::string& password, |
| const std::string& captcha, |
| const std::string& access_code) { |
| service_->OnUserSubmittedAuth(username, password, captcha, access_code); |
| } |
| |
| void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) { |
| // Go to the "loading..." screen. |
| Advance(SyncSetupWizard::SETTING_UP); |
| |
| // If we are activating the passphrase, we need to have one supplied. |
| DCHECK(service_->IsUsingSecondaryPassphrase() || |
| !configuration.use_secondary_passphrase || |
| configuration.secondary_passphrase.length() > 0); |
| |
| if (configuration.use_secondary_passphrase && |
| !service_->IsUsingSecondaryPassphrase()) { |
| service_->SetPassphrase(configuration.secondary_passphrase, true, true); |
| tried_creating_explicit_passphrase_ = true; |
| } |
| |
| service_->OnUserChoseDatatypes(configuration.sync_everything, |
| configuration.data_types); |
| } |
| |
| void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) { |
| Advance(SyncSetupWizard::SETTING_UP); |
| service_->SetPassphrase(passphrase, true, false); |
| tried_setting_explicit_passphrase_ = true; |
| } |
| |
| void SyncSetupFlow::OnPassphraseCancel() { |
| // If the user cancels when being asked for the passphrase, |
| // just disable encrypted sync and continue setting up. |
| if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE) |
| DisablePasswordSync(service_); |
| |
| Advance(SyncSetupWizard::SETTING_UP); |
| } |
| |
| // TODO(jhawkins): Remove this method. |
| void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option, |
| const std::string& passphrase) { |
| NOTREACHED(); |
| } |
| |
| // TODO(jhawkins): Use this method instead of a direct link in the html. |
| void SyncSetupFlow::OnGoToDashboard() { |
| BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate(); |
| } |
| |
| // Use static Run method to get an instance. |
| SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state, |
| SyncSetupWizard::State end_state, |
| const std::string& args, |
| SyncSetupFlowContainer* container, |
| ProfileSyncService* service) |
| : container_(container), |
| dialog_start_args_(args), |
| current_state_(start_state), |
| end_state_(end_state), |
| login_start_time_(base::TimeTicks::Now()), |
| flow_handler_(NULL), |
| service_(service), |
| tried_creating_explicit_passphrase_(false), |
| tried_setting_explicit_passphrase_(false) { |
| } |
| |
| // Returns true if the flow should advance to |state| based on |current_state_|. |
| bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) { |
| switch (state) { |
| case SyncSetupWizard::GAIA_LOGIN: |
| return current_state_ == SyncSetupWizard::FATAL_ERROR || |
| current_state_ == SyncSetupWizard::GAIA_LOGIN || |
| current_state_ == SyncSetupWizard::SETTING_UP; |
| case SyncSetupWizard::GAIA_SUCCESS: |
| return current_state_ == SyncSetupWizard::GAIA_LOGIN; |
| case SyncSetupWizard::SYNC_EVERYTHING: |
| case SyncSetupWizard::CONFIGURE: |
| return current_state_ == SyncSetupWizard::GAIA_SUCCESS; |
| case SyncSetupWizard::ENTER_PASSPHRASE: |
| return current_state_ == SyncSetupWizard::SYNC_EVERYTHING || |
| current_state_ == SyncSetupWizard::CONFIGURE || |
| current_state_ == SyncSetupWizard::SETTING_UP; |
| case SyncSetupWizard::PASSPHRASE_MIGRATION: |
| return current_state_ == SyncSetupWizard::GAIA_LOGIN; |
| case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: |
| DCHECK(current_state_ != SyncSetupWizard::GAIA_LOGIN && |
| current_state_ != SyncSetupWizard::GAIA_SUCCESS); |
| return true; |
| case SyncSetupWizard::SETTING_UP: |
| return current_state_ == SyncSetupWizard::SYNC_EVERYTHING || |
| current_state_ == SyncSetupWizard::CONFIGURE || |
| current_state_ == SyncSetupWizard::ENTER_PASSPHRASE || |
| current_state_ == SyncSetupWizard::PASSPHRASE_MIGRATION; |
| case SyncSetupWizard::FATAL_ERROR: |
| return true; // You can always hit the panic button. |
| case SyncSetupWizard::DONE_FIRST_TIME: |
| case SyncSetupWizard::DONE: |
| return current_state_ == SyncSetupWizard::SETTING_UP || |
| current_state_ == SyncSetupWizard::ENTER_PASSPHRASE; |
| default: |
| NOTREACHED() << "Unhandled State: " << state; |
| return false; |
| } |
| } |
| |
| void SyncSetupFlow::ActivateState(SyncSetupWizard::State state) { |
| switch (state) { |
| case SyncSetupWizard::GAIA_LOGIN: { |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForGaiaLogin(service_, &args); |
| flow_handler_->ShowGaiaLogin(args); |
| break; |
| } |
| case SyncSetupWizard::GAIA_SUCCESS: |
| if (end_state_ == SyncSetupWizard::GAIA_SUCCESS) { |
| flow_handler_->ShowGaiaSuccessAndClose(); |
| break; |
| } |
| state = SyncSetupWizard::SYNC_EVERYTHING; |
| // Fall through. |
| case SyncSetupWizard::SYNC_EVERYTHING: { |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForConfigure(service_, &args); |
| args.SetBoolean("syncEverything", true); |
| flow_handler_->ShowConfigure(args); |
| break; |
| } |
| case SyncSetupWizard::CONFIGURE: { |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForConfigure(service_, &args); |
| flow_handler_->ShowConfigure(args); |
| break; |
| } |
| case SyncSetupWizard::ENTER_PASSPHRASE: { |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForEnterPassphrase( |
| tried_creating_explicit_passphrase_, |
| tried_setting_explicit_passphrase_, |
| &args); |
| flow_handler_->ShowPassphraseEntry(args); |
| break; |
| } |
| case SyncSetupWizard::PASSPHRASE_MIGRATION: { |
| DictionaryValue args; |
| args.SetString("iframeToShow", "firstpassphrase"); |
| flow_handler_->ShowFirstPassphrase(args); |
| break; |
| } |
| case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: { |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForConfigure(service_, &args); |
| args.SetBoolean("was_aborted", true); |
| flow_handler_->ShowConfigure(args); |
| break; |
| } |
| case SyncSetupWizard::SETTING_UP: { |
| flow_handler_->ShowSettingUp(); |
| break; |
| } |
| case SyncSetupWizard::FATAL_ERROR: { |
| // This shows the user the "Could not connect to server" error. |
| // TODO(sync): Update this error messaging. |
| DictionaryValue args; |
| SyncSetupFlow::GetArgsForGaiaLogin(service_, &args); |
| args.SetInteger("error", GoogleServiceAuthError::CONNECTION_FAILED); |
| flow_handler_->ShowGaiaLogin(args); |
| break; |
| } |
| case SyncSetupWizard::DONE_FIRST_TIME: |
| flow_handler_->ShowFirstTimeDone( |
| UTF16ToWide(service_->GetAuthenticatedUsername())); |
| break; |
| case SyncSetupWizard::DONE: |
| flow_handler_->ShowSetupDone( |
| UTF16ToWide(service_->GetAuthenticatedUsername())); |
| break; |
| default: |
| NOTREACHED() << "Invalid advance state: " << state; |
| } |
| current_state_ = state; |
| } |