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(®istered_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