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.
DisablePasswordSync(ProfileSyncService * service)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
SyncConfiguration()44 SyncConfiguration::SyncConfiguration()
45 : sync_everything(false),
46 use_secondary_passphrase(false) {
47 }
48
~SyncConfiguration()49 SyncConfiguration::~SyncConfiguration() {}
50
~SyncSetupFlow()51 SyncSetupFlow::~SyncSetupFlow() {
52 flow_handler_->SetFlow(NULL);
53 }
54
55 // static
Run(ProfileSyncService * service,SyncSetupFlowContainer * container,SyncSetupWizard::State start,SyncSetupWizard::State end)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
GetArgsForGaiaLogin(const ProfileSyncService * service,DictionaryValue * args)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
GetArgsForConfigure(ProfileSyncService * service,DictionaryValue * args)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
GetArgsForEnterPassphrase(bool tried_creating_explicit_passphrase,bool tried_setting_explicit_passphrase,DictionaryValue * args)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
AttachSyncSetupHandler(SyncSetupFlowHandler * handler)166 void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) {
167 flow_handler_ = handler;
168 ActivateState(current_state_);
169 }
170
Advance(SyncSetupWizard::State advance_state)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
Focus()181 void SyncSetupFlow::Focus() {
182 // TODO(jhawkins): Implement this.
183 }
184
185 // A callback to notify the delegate that the dialog closed.
OnDialogClosed(const std::string & json_retval)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
OnUserSubmittedAuth(const std::string & username,const std::string & password,const std::string & captcha,const std::string & access_code)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
OnUserConfigured(const SyncConfiguration & configuration)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
OnPassphraseEntry(const std::string & passphrase)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
OnPassphraseCancel()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.
OnFirstPassphraseEntry(const std::string & option,const std::string & passphrase)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.
OnGoToDashboard()275 void SyncSetupFlow::OnGoToDashboard() {
276 BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
277 }
278
279 // Use static Run method to get an instance.
SyncSetupFlow(SyncSetupWizard::State start_state,SyncSetupWizard::State end_state,const std::string & args,SyncSetupFlowContainer * container,ProfileSyncService * service)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_|.
ShouldAdvance(SyncSetupWizard::State 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
ActivateState(SyncSetupWizard::State state)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