1 // Copyright (c) 2012 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.
5 #include "chrome/browser/sync/profile_sync_service.h"
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/compiler_specific.h"
17 #include "base/logging.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/message_loop.h"
20 #include "base/metrics/histogram.h"
21 #include "base/string16.h"
22 #include "base/stringprintf.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "build/build_config.h"
25 #include "chrome/browser/about_flags.h"
26 #include "chrome/browser/browser_process.h"
27 #include "chrome/browser/defaults.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/extensions/extension_system.h"
30 #include "chrome/browser/net/chrome_cookie_notification_details.h"
31 #include "chrome/browser/prefs/pref_service.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/signin/signin_manager.h"
34 #include "chrome/browser/signin/signin_manager_factory.h"
35 #include "chrome/browser/signin/token_service.h"
36 #include "chrome/browser/signin/token_service_factory.h"
37 #include "chrome/browser/sync/backend_migrator.h"
38 #include "chrome/browser/sync/glue/change_processor.h"
39 #include "chrome/browser/sync/glue/chrome_encryptor.h"
40 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
41 #include "chrome/browser/sync/glue/data_type_controller.h"
42 #include "chrome/browser/sync/glue/session_data_type_controller.h"
43 #include "chrome/browser/sync/glue/session_model_associator.h"
44 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
45 #include "chrome/browser/sync/profile_sync_components_factory_impl.h"
46 #include "chrome/browser/sync/sync_global_error.h"
47 #include "chrome/browser/sync/user_selectable_sync_type.h"
48 #include "chrome/browser/ui/browser.h"
49 #include "chrome/browser/ui/browser_list.h"
50 #include "chrome/browser/ui/browser_window.h"
51 #include "chrome/browser/ui/global_error/global_error_service.h"
52 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
53 #include "chrome/common/chrome_notification_types.h"
54 #include "chrome/common/chrome_switches.h"
55 #include "chrome/common/chrome_version_info.h"
56 #include "chrome/common/pref_names.h"
57 #include "chrome/common/time_format.h"
58 #include "chrome/common/url_constants.h"
59 #include "content/public/browser/notification_details.h"
60 #include "content/public/browser/notification_service.h"
61 #include "content/public/browser/notification_source.h"
62 #include "google_apis/gaia/gaia_constants.h"
63 #include "grit/generated_resources.h"
64 #include "net/cookies/cookie_monster.h"
65 #include "sync/api/sync_error.h"
66 #include "sync/internal_api/public/configure_reason.h"
67 #include "sync/internal_api/public/sync_encryption_handler.h"
68 #include "sync/internal_api/public/util/experiments.h"
69 #include "sync/internal_api/public/util/sync_string_conversions.h"
70 #include "sync/js/js_arg_list.h"
71 #include "sync/js/js_event_details.h"
72 #include "sync/notifier/invalidator_registrar.h"
73 #include "sync/util/cryptographer.h"
74 #include "ui/base/l10n/l10n_util.h"
76 using browser_sync::ChangeProcessor
;
77 using browser_sync::DataTypeController
;
78 using browser_sync::DataTypeManager
;
79 using browser_sync::SyncBackendHost
;
80 using syncer::ModelType
;
81 using syncer::ModelTypeSet
;
82 using syncer::JsBackend
;
83 using syncer::JsController
;
84 using syncer::JsEventDetails
;
85 using syncer::JsEventHandler
;
86 using syncer::ModelSafeRoutingInfo
;
87 using syncer::SyncCredentials
;
88 using syncer::SyncProtocolError
;
89 using syncer::WeakHandle
;
91 typedef GoogleServiceAuthError AuthError
;
93 const char* ProfileSyncService::kSyncServerUrl
=
94 "https://clients4.google.com/chrome-sync";
96 const char* ProfileSyncService::kDevServerUrl
=
97 "https://clients4.google.com/chrome-sync/dev";
99 static const int kSyncClearDataTimeoutInSeconds
= 60; // 1 minute.
101 static const char* kRelevantTokenServices
[] = {
102 GaiaConstants::kSyncService
104 static const int kRelevantTokenServicesCount
=
105 arraysize(kRelevantTokenServices
);
107 static const char* kSyncUnrecoverableErrorHistogram
=
108 "Sync.UnrecoverableErrors";
110 // Helper to check if the given token service is relevant for sync.
111 static bool IsTokenServiceRelevant(const std::string
& service
) {
112 for (int i
= 0; i
< kRelevantTokenServicesCount
; ++i
) {
113 if (service
== kRelevantTokenServices
[i
])
119 bool ShouldShowActionOnUI(
120 const syncer::SyncProtocolError
& error
) {
121 return (error
.action
!= syncer::UNKNOWN_ACTION
&&
122 error
.action
!= syncer::DISABLE_SYNC_ON_CLIENT
);
125 ProfileSyncService::ProfileSyncService(ProfileSyncComponentsFactory
* factory
,
127 SigninManager
* signin_manager
,
128 StartBehavior start_behavior
)
129 : last_auth_error_(AuthError::None()),
130 passphrase_required_reason_(syncer::REASON_PASSPHRASE_NOT_REQUIRED
),
133 // |profile| may be NULL in unit tests.
134 sync_prefs_(profile_
? profile_
->GetPrefs() : NULL
),
135 invalidator_storage_(profile_
? profile_
->GetPrefs(): NULL
),
136 sync_service_url_(kDevServerUrl
),
137 is_first_time_sync_configure_(false),
138 backend_initialized_(false),
139 is_auth_in_progress_(false),
140 signin_(signin_manager
),
141 unrecoverable_error_reason_(ERROR_REASON_UNSET
),
142 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
143 expect_sync_configuration_aborted_(false),
144 encrypted_types_(syncer::SyncEncryptionHandler::SensitiveTypes()),
145 encrypt_everything_(false),
146 encryption_pending_(false),
147 auto_start_enabled_(start_behavior
== AUTO_START
),
148 failed_datatypes_handler_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
149 configure_status_(DataTypeManager::UNKNOWN
),
150 setup_in_progress_(false),
151 invalidator_state_(syncer::DEFAULT_INVALIDATION_ERROR
) {
152 #if defined(OS_ANDROID)
153 chrome::VersionInfo version_info
;
154 if (version_info
.IsOfficialBuild()) {
155 sync_service_url_
= GURL(kSyncServerUrl
);
158 // By default, dev, canary, and unbranded Chromium users will go to the
159 // development servers. Development servers have more features than standard
160 // sync servers. Users with officially-branded Chrome stable and beta builds
161 // will go to the standard sync servers.
163 // GetChannel hits the registry on Windows. See http://crbug.com/70380.
164 base::ThreadRestrictions::ScopedAllowIO allow_io
;
165 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
166 if (channel
== chrome::VersionInfo::CHANNEL_STABLE
||
167 channel
== chrome::VersionInfo::CHANNEL_BETA
) {
168 sync_service_url_
= GURL(kSyncServerUrl
);
173 ProfileSyncService::~ProfileSyncService() {
174 sync_prefs_
.RemoveSyncPrefObserver(this);
175 // Shutdown() should have been called before destruction.
176 CHECK(!backend_initialized_
);
179 bool ProfileSyncService::IsSyncEnabledAndLoggedIn() {
180 // Exit if sync is disabled.
181 if (IsManaged() || sync_prefs_
.IsStartSuppressed())
184 // Sync is logged in if there is a non-empty authenticated username.
185 return !signin_
->GetAuthenticatedUsername().empty();
188 bool ProfileSyncService::IsSyncTokenAvailable() {
189 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
192 return token_service
->HasTokenForService(GaiaConstants::kSyncService
);
195 void ProfileSyncService::Initialize() {
196 DCHECK(!invalidator_registrar_
.get());
197 invalidator_registrar_
.reset(new syncer::InvalidatorRegistrar());
201 // We clear this here (vs Shutdown) because we want to remember that an error
202 // happened on shutdown so we can display details (message, location) about it
206 sync_prefs_
.AddSyncPrefObserver(this);
208 // For now, the only thing we can do through policy is to turn sync off.
214 RegisterAuthNotifications();
216 if (!HasSyncSetupCompleted() || signin_
->GetAuthenticatedUsername().empty()) {
217 // Clean up in case of previous crash / setup abort / signout.
221 TrySyncDatatypePrefRecovery();
226 void ProfileSyncService::TrySyncDatatypePrefRecovery() {
227 DCHECK(!sync_initialized());
228 if (!HasSyncSetupCompleted())
231 // There was a bug where OnUserChoseDatatypes was not properly called on
232 // configuration (see crbug.com/154940). We detect this by checking whether
233 // kSyncKeepEverythingSynced has a default value. If so, and sync setup has
234 // completed, it means sync was not properly configured, so we manually
235 // set kSyncKeepEverythingSynced.
236 PrefService
* const pref_service
= profile_
->GetPrefs();
239 if (sync_prefs_
.HasKeepEverythingSynced())
241 const syncer::ModelTypeSet registered_types
= GetRegisteredDataTypes();
242 if (sync_prefs_
.GetPreferredDataTypes(registered_types
).Size() > 1)
245 const PrefService::Preference
* keep_everything_synced
=
246 pref_service
->FindPreference(prefs::kSyncKeepEverythingSynced
);
247 // This will be false if the preference was properly set or if it's controlled
249 if (!keep_everything_synced
->IsDefaultValue())
252 // kSyncKeepEverythingSynced was not properly set. Set it and the preferred
253 // types now, before we configure.
254 UMA_HISTOGRAM_COUNTS("Sync.DatatypePrefRecovery", 1);
255 sync_prefs_
.SetKeepEverythingSynced(true);
256 sync_prefs_
.SetPreferredDataTypes(registered_types
,
260 void ProfileSyncService::TryStart() {
261 if (!IsSyncEnabledAndLoggedIn())
263 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
266 // Don't start the backend if the token service hasn't finished loading tokens
267 // yet (if the backend is started before the sync token has been loaded,
268 // GetCredentials() will return bogus credentials). On auto_start platforms
269 // (like ChromeOS) we don't start sync until tokens are loaded, because the
270 // user can be "signed in" on those platforms long before the tokens get
271 // loaded, and we don't want to generate spurious auth errors.
272 if (IsSyncTokenAvailable() ||
273 (!auto_start_enabled_
&& token_service
->TokensLoadedFromDB())) {
274 if (HasSyncSetupCompleted() || auto_start_enabled_
) {
275 // If sync setup has completed we always start the backend.
276 // If autostart is enabled, but we haven't completed sync setup, we try to
277 // start sync anyway, since it's possible we crashed/shutdown after
278 // logging in but before the backend finished initializing the last time.
279 // Note that if we haven't finished setting up sync, backend bring up will
280 // be done by the wizard.
286 void ProfileSyncService::StartSyncingWithServer() {
288 backend_
->StartSyncingWithServer();
291 void ProfileSyncService::RegisterAuthNotifications() {
292 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
294 chrome::NOTIFICATION_TOKEN_AVAILABLE
,
295 content::Source
<TokenService
>(token_service
));
297 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED
,
298 content::Source
<TokenService
>(token_service
));
300 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED
,
301 content::Source
<TokenService
>(token_service
));
303 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
,
304 content::Source
<Profile
>(profile_
));
307 void ProfileSyncService::RegisterDataTypeController(
308 DataTypeController
* data_type_controller
) {
309 DCHECK_EQ(data_type_controllers_
.count(data_type_controller
->type()), 0U);
310 data_type_controllers_
[data_type_controller
->type()] =
311 data_type_controller
;
314 browser_sync::SessionModelAssociator
*
315 ProfileSyncService::GetSessionModelAssociator() {
316 if (data_type_controllers_
.find(syncer::SESSIONS
) ==
317 data_type_controllers_
.end() ||
318 data_type_controllers_
.find(syncer::SESSIONS
)->second
->state() !=
319 DataTypeController::RUNNING
) {
322 return static_cast<browser_sync::SessionDataTypeController
*>(
323 data_type_controllers_
.find(
324 syncer::SESSIONS
)->second
.get())->GetModelAssociator();
327 void ProfileSyncService::GetDataTypeControllerStates(
328 browser_sync::DataTypeController::StateMap
* state_map
) const {
329 for (browser_sync::DataTypeController::TypeMap::const_iterator iter
=
330 data_type_controllers_
.begin(); iter
!= data_type_controllers_
.end();
332 (*state_map
)[iter
->first
] = iter
->second
.get()->state();
335 void ProfileSyncService::InitSettings() {
336 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
338 // Override the sync server URL from the command-line, if sync server
339 // command-line argument exists.
340 if (command_line
.HasSwitch(switches::kSyncServiceURL
)) {
341 std::string
value(command_line
.GetSwitchValueASCII(
342 switches::kSyncServiceURL
));
343 if (!value
.empty()) {
344 GURL
custom_sync_url(value
);
345 if (custom_sync_url
.is_valid()) {
346 sync_service_url_
= custom_sync_url
;
348 LOG(WARNING
) << "The following sync URL specified at the command-line "
349 << "is invalid: " << value
;
355 SyncCredentials
ProfileSyncService::GetCredentials() {
356 SyncCredentials credentials
;
357 credentials
.email
= signin_
->GetAuthenticatedUsername();
358 DCHECK(!credentials
.email
.empty());
359 TokenService
* service
= TokenServiceFactory::GetForProfile(profile_
);
360 if (service
->HasTokenForService(GaiaConstants::kSyncService
)) {
361 credentials
.sync_token
= service
->GetTokenForService(
362 GaiaConstants::kSyncService
);
363 UMA_HISTOGRAM_BOOLEAN("Sync.CredentialsLost", false);
365 // We've lost our sync credentials (crbug.com/121755), so just make up some
366 // invalid credentials so the backend will generate an auth error.
367 UMA_HISTOGRAM_BOOLEAN("Sync.CredentialsLost", true);
368 credentials
.sync_token
= "credentials_lost";
373 void ProfileSyncService::InitializeBackend(bool delete_stale_data
) {
374 if (!backend_
.get()) {
379 SyncCredentials credentials
= GetCredentials();
381 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter(
382 profile_
->GetRequestContext());
384 if (delete_stale_data
)
387 backend_unrecoverable_error_handler_
.reset(
388 new browser_sync::BackendUnrecoverableErrorHandler(
389 MakeWeakHandle(weak_factory_
.GetWeakPtr())));
391 backend_
->Initialize(
393 MakeWeakHandle(sync_js_controller_
.AsWeakPtr()),
397 &sync_manager_factory_
,
398 backend_unrecoverable_error_handler_
.get(),
399 &browser_sync::ChromeReportUnrecoverableError
);
402 void ProfileSyncService::CreateBackend() {
404 new SyncBackendHost(profile_
->GetDebugName(),
405 profile_
, sync_prefs_
.AsWeakPtr(),
406 invalidator_storage_
.AsWeakPtr()));
409 bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
410 if (encryption_pending())
412 const syncer::ModelTypeSet preferred_types
= GetPreferredDataTypes();
413 const syncer::ModelTypeSet encrypted_types
= GetEncryptedDataTypes();
414 DCHECK(encrypted_types
.Has(syncer::PASSWORDS
));
415 return !Intersection(preferred_types
, encrypted_types
).Empty();
418 void ProfileSyncService::OnSyncConfigureDone(
419 DataTypeManager::ConfigureResult result
) {
420 if (failed_datatypes_handler_
.UpdateFailedDatatypes(result
.failed_data_types
,
421 FailedDatatypesHandler::STARTUP
)) {
422 ReconfigureDatatypeManager();
426 void ProfileSyncService::OnSyncConfigureRetry() {
427 // In platforms with auto start we would just wait for the
428 // configure to finish. In other platforms we would throw
429 // an unrecoverable error. The reason we do this is so that
430 // the login dialog would show an error and the user would have
432 // Also if backend has been initialized(the user is authenticated
433 // and nigori is downloaded) we would simply wait rather than going into
434 // unrecoverable error, even if the platform has auto start disabled.
435 // Note: In those scenarios the UI does not wait for the configuration
437 if (!auto_start_enabled_
&& !backend_initialized_
) {
438 OnInternalUnrecoverableError(FROM_HERE
,
439 "Configure failed to download.",
441 ERROR_REASON_CONFIGURATION_RETRY
);
448 void ProfileSyncService::StartUp() {
449 // Don't start up multiple times.
450 if (backend_
.get()) {
451 DVLOG(1) << "Skipping bringing up backend host.";
455 DCHECK(IsSyncEnabledAndLoggedIn());
457 last_synced_time_
= sync_prefs_
.GetLastSyncedTime();
458 start_up_time_
= base::Time::Now();
460 #if defined(OS_CHROMEOS)
461 std::string bootstrap_token
= sync_prefs_
.GetEncryptionBootstrapToken();
462 if (bootstrap_token
.empty()) {
463 sync_prefs_
.SetEncryptionBootstrapToken(
464 sync_prefs_
.GetSpareBootstrapToken());
469 // Initialize the backend. Every time we start up a new SyncBackendHost,
470 // we'll want to start from a fresh SyncDB, so delete any old one that might
472 InitializeBackend(!HasSyncSetupCompleted());
474 // |backend_| may end up being NULL here in tests (in synchronous
475 // initialization mode).
477 // TODO(akalin): Fix this horribly non-intuitive behavior (see
478 // http://crbug.com/140354).
479 if (backend_
.get()) {
480 backend_
->UpdateRegisteredInvalidationIds(
481 invalidator_registrar_
->GetAllRegisteredIds());
484 if (!sync_global_error_
.get()) {
485 #if !defined(OS_ANDROID)
486 sync_global_error_
.reset(new SyncGlobalError(this, signin()));
488 GlobalErrorServiceFactory::GetForProfile(profile_
)->AddGlobalError(
489 sync_global_error_
.get());
490 AddObserver(sync_global_error_
.get());
494 void ProfileSyncService::RegisterInvalidationHandler(
495 syncer::InvalidationHandler
* handler
) {
496 invalidator_registrar_
->RegisterHandler(handler
);
499 void ProfileSyncService::UpdateRegisteredInvalidationIds(
500 syncer::InvalidationHandler
* handler
,
501 const syncer::ObjectIdSet
& ids
) {
502 invalidator_registrar_
->UpdateRegisteredIds(handler
, ids
);
504 // If |backend_| is NULL, its registered IDs will be updated when
505 // it's created and initialized.
506 if (backend_
.get()) {
507 backend_
->UpdateRegisteredInvalidationIds(
508 invalidator_registrar_
->GetAllRegisteredIds());
512 void ProfileSyncService::UnregisterInvalidationHandler(
513 syncer::InvalidationHandler
* handler
) {
514 invalidator_registrar_
->UnregisterHandler(handler
);
517 syncer::InvalidatorState
ProfileSyncService::GetInvalidatorState() const {
518 return invalidator_registrar_
->GetInvalidatorState();
521 void ProfileSyncService::EmitInvalidationForTest(
522 const invalidation::ObjectId
& id
,
523 const std::string
& payload
) {
524 syncer::ObjectIdSet notify_ids
;
525 notify_ids
.insert(id
);
527 const syncer::ObjectIdInvalidationMap
& invalidation_map
=
528 ObjectIdSetToInvalidationMap(notify_ids
, payload
);
529 OnIncomingInvalidation(invalidation_map
, syncer::REMOTE_INVALIDATION
);
532 void ProfileSyncService::Shutdown() {
533 DCHECK(invalidator_registrar_
.get());
534 // TODO(akalin): Remove this once http://crbug.com/153827 is fixed.
535 ExtensionService
* const extension_service
=
536 extensions::ExtensionSystem::Get(profile_
)->extension_service();
537 // |extension_service| may be NULL if it was never initialized
538 // (e.g., extension sync wasn't enabled in tests).
539 if (extension_service
)
540 extension_service
->OnProfileSyncServiceShutdown();
542 // Reset |invalidator_registrar_| first so that ShutdownImpl cannot
544 invalidator_registrar_
.reset();
549 void ProfileSyncService::ShutdownImpl(bool sync_disabled
) {
550 // First, we spin down the backend and wait for it to stop syncing completely
551 // before we Stop the data type manager. This is to avoid a late sync cycle
552 // applying changes to the sync db that wouldn't get applied via
553 // ChangeProcessors, leading to back-from-the-dead bugs.
554 base::Time shutdown_start_time
= base::Time::Now();
555 if (backend_
.get()) {
556 backend_
->StopSyncingForShutdown();
559 // Stop all data type controllers, if needed. Note that until Stop
560 // completes, it is possible in theory to have a ChangeProcessor apply a
561 // change from a native model. In that case, it will get applied to the sync
562 // database (which doesn't get destroyed until we destroy the backend below)
563 // as an unsynced change. That will be persisted, and committed on restart.
564 if (data_type_manager_
.get()) {
565 if (data_type_manager_
->state() != DataTypeManager::STOPPED
) {
566 // When aborting as part of shutdown, we should expect an aborted sync
567 // configure result, else we'll dcheck when we try to read the sync error.
568 expect_sync_configuration_aborted_
= true;
569 data_type_manager_
->Stop();
571 data_type_manager_
.reset();
574 // Shutdown the migrator before the backend to ensure it doesn't pull a null
577 sync_js_controller_
.AttachJsBackend(WeakHandle
<syncer::JsBackend
>());
579 // Move aside the backend so nobody else tries to use it while we are
581 scoped_ptr
<SyncBackendHost
> doomed_backend(backend_
.release());
582 if (doomed_backend
.get()) {
583 doomed_backend
->Shutdown(sync_disabled
);
585 doomed_backend
.reset();
587 base::TimeDelta shutdown_time
= base::Time::Now() - shutdown_start_time
;
588 UMA_HISTOGRAM_TIMES("Sync.Shutdown.BackendDestroyedTime", shutdown_time
);
590 weak_factory_
.InvalidateWeakPtrs();
592 // Clear various flags.
593 expect_sync_configuration_aborted_
= false;
594 is_auth_in_progress_
= false;
595 backend_initialized_
= false;
596 // NULL if we're called from Shutdown().
597 if (invalidator_registrar_
.get())
598 UpdateInvalidatorRegistrarState();
599 cached_passphrase_
.clear();
600 encryption_pending_
= false;
601 encrypt_everything_
= false;
602 encrypted_types_
= syncer::SyncEncryptionHandler::SensitiveTypes();
603 passphrase_required_reason_
= syncer::REASON_PASSPHRASE_NOT_REQUIRED
;
604 last_auth_error_
= AuthError::None();
606 if (sync_global_error_
.get()) {
607 GlobalErrorServiceFactory::GetForProfile(profile_
)->RemoveGlobalError(
608 sync_global_error_
.get());
609 RemoveObserver(sync_global_error_
.get());
610 sync_global_error_
.reset(NULL
);
614 void ProfileSyncService::DisableForUser() {
615 // Clear prefs (including SyncSetupHasCompleted) before shutting down so
616 // PSS clients don't think we're set up while we're shutting down.
617 sync_prefs_
.ClearPreferences();
618 invalidator_storage_
.Clear();
619 ClearUnrecoverableError();
622 // TODO(atwilson): Don't call SignOut() on *any* platform - move this into
623 // the UI layer if needed (sync activity should never result in the user
624 // being logged out of all chrome services).
625 if (!auto_start_enabled_
&& !signin_
->GetAuthenticatedUsername().empty())
631 bool ProfileSyncService::HasSyncSetupCompleted() const {
632 return sync_prefs_
.HasSyncSetupCompleted();
635 void ProfileSyncService::SetSyncSetupCompleted() {
636 sync_prefs_
.SetSyncSetupCompleted();
639 void ProfileSyncService::UpdateLastSyncedTime() {
640 last_synced_time_
= base::Time::Now();
641 sync_prefs_
.SetLastSyncedTime(last_synced_time_
);
644 void ProfileSyncService::NotifyObservers() {
645 FOR_EACH_OBSERVER(Observer
, observers_
, OnStateChanged());
646 // TODO(akalin): Make an Observer subclass that listens and does the
648 sync_js_controller_
.HandleJsEvent(
649 "onServiceStateChanged", JsEventDetails());
652 void ProfileSyncService::ClearStaleErrors() {
653 ClearUnrecoverableError();
654 last_actionable_error_
= SyncProtocolError();
657 void ProfileSyncService::ClearUnrecoverableError() {
658 unrecoverable_error_reason_
= ERROR_REASON_UNSET
;
659 unrecoverable_error_message_
.clear();
660 unrecoverable_error_location_
= tracked_objects::Location();
664 // TODO(sync): Consider having syncer::Experiments provide this.
665 std::string
ProfileSyncService::GetExperimentNameForDataType(
666 syncer::ModelType data_type
) {
671 void ProfileSyncService::RegisterNewDataType(syncer::ModelType data_type
) {
672 if (data_type_controllers_
.count(data_type
) > 0)
677 // An invariant has been violated. Transition to an error state where we try
678 // to do as little work as possible, to avoid further corruption or crashes.
679 void ProfileSyncService::OnUnrecoverableError(
680 const tracked_objects::Location
& from_here
,
681 const std::string
& message
) {
682 // Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler
683 // interface are assumed to originate within the syncer.
684 unrecoverable_error_reason_
= ERROR_REASON_SYNCER
;
685 OnUnrecoverableErrorImpl(from_here
, message
, true);
688 void ProfileSyncService::OnUnrecoverableErrorImpl(
689 const tracked_objects::Location
& from_here
,
690 const std::string
& message
,
691 bool delete_sync_database
) {
692 DCHECK(HasUnrecoverableError());
693 unrecoverable_error_message_
= message
;
694 unrecoverable_error_location_
= from_here
;
696 UMA_HISTOGRAM_ENUMERATION(kSyncUnrecoverableErrorHistogram
,
697 unrecoverable_error_reason_
,
700 std::string location
;
701 from_here
.Write(true, true, &location
);
703 << "Unrecoverable error detected at " << location
704 << " -- ProfileSyncService unusable: " << message
;
706 // Shut all data types down.
707 MessageLoop::current()->PostTask(FROM_HERE
,
708 base::Bind(&ProfileSyncService::ShutdownImpl
, weak_factory_
.GetWeakPtr(),
709 delete_sync_database
));
712 void ProfileSyncService::DisableBrokenDatatype(
713 syncer::ModelType type
,
714 const tracked_objects::Location
& from_here
,
715 std::string message
) {
716 // First deactivate the type so that no further server changes are
717 // passed onto the change processor.
718 DeactivateDataType(type
);
720 syncer::SyncError
error(from_here
, message
, type
);
722 std::list
<syncer::SyncError
> errors
;
723 errors
.push_back(error
);
725 // Update this before posting a task. So if a configure happens before
726 // the task that we are going to post, this type would still be disabled.
727 failed_datatypes_handler_
.UpdateFailedDatatypes(errors
,
728 FailedDatatypesHandler::RUNTIME
);
730 MessageLoop::current()->PostTask(FROM_HERE
,
731 base::Bind(&ProfileSyncService::ReconfigureDatatypeManager
,
732 weak_factory_
.GetWeakPtr()));
735 void ProfileSyncService::OnInvalidatorStateChange(
736 syncer::InvalidatorState state
) {
737 invalidator_state_
= state
;
738 UpdateInvalidatorRegistrarState();
741 void ProfileSyncService::OnIncomingInvalidation(
742 const syncer::ObjectIdInvalidationMap
& invalidation_map
,
743 syncer::IncomingInvalidationSource source
) {
744 invalidator_registrar_
->DispatchInvalidationsToHandlers(invalidation_map
,
748 void ProfileSyncService::OnBackendInitialized(
749 const syncer::WeakHandle
<syncer::JsBackend
>& js_backend
,
750 const syncer::WeakHandle
<syncer::DataTypeDebugInfoListener
>&
753 is_first_time_sync_configure_
= !HasSyncSetupCompleted();
755 if (is_first_time_sync_configure_
) {
756 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeFirstTimeSuccess", success
);
758 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeRestoreSuccess", success
);
761 if (!start_up_time_
.is_null()) {
762 base::Time on_backend_initialized_time
= base::Time::Now();
763 base::TimeDelta delta
= on_backend_initialized_time
- start_up_time_
;
764 if (is_first_time_sync_configure_
) {
765 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeFirstTime", delta
);
767 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeRestoreTime", delta
);
769 start_up_time_
= base::Time();
773 // Something went unexpectedly wrong. Play it safe: stop syncing at once
774 // and surface error UI to alert the user sync has stopped.
775 // Keep the directory around for now so that on restart we will retry
776 // again and potentially succeed in presence of transient file IO failures
777 // or permissions issues, etc.
779 // TODO(rlarocque): Consider making this UnrecoverableError less special.
780 // Unlike every other UnrecoverableError, it does not delete our sync data.
781 // This exception made sense at the time it was implemented, but our new
782 // directory corruption recovery mechanism makes it obsolete. By the time
783 // we get here, we will have already tried and failed to delete the
784 // directory. It would be no big deal if we tried to delete it again.
785 OnInternalUnrecoverableError(FROM_HERE
,
786 "BackendInitialize failure",
788 ERROR_REASON_BACKEND_INIT_FAILURE
);
792 backend_initialized_
= true;
793 UpdateInvalidatorRegistrarState();
795 sync_js_controller_
.AttachJsBackend(js_backend
);
796 debug_info_listener_
= debug_info_listener
;
798 // If we have a cached passphrase use it to decrypt/encrypt data now that the
799 // backend is initialized. We want to call this before notifying observers in
800 // case this operation affects the "passphrase required" status.
801 ConsumeCachedPassphraseIfPossible();
803 // The very first time the backend initializes is effectively the first time
804 // we can say we successfully "synced". last_synced_time_ will only be null
805 // in this case, because the pref wasn't restored on StartUp.
806 if (last_synced_time_
.is_null()) {
807 UpdateLastSyncedTime();
811 if (auto_start_enabled_
&& !FirstSetupInProgress()) {
812 // Backend is initialized but we're not in sync setup, so this must be an
813 // autostart - mark our sync setup as completed and we'll start syncing
815 SetSyncSetupCompleted();
819 if (HasSyncSetupCompleted()) {
820 ConfigureDataTypeManager();
822 DCHECK(FirstSetupInProgress());
826 void ProfileSyncService::OnSyncCycleCompleted() {
827 UpdateLastSyncedTime();
828 if (GetSessionModelAssociator()) {
829 // Trigger garbage collection of old sessions now that we've downloaded
830 // any new session data. TODO(zea): Have this be a notification the session
831 // model associator listens too. Also consider somehow plumbing the current
832 // server time as last reported by CheckServerReachable, so we don't have to
833 // rely on the local clock, which may be off significantly.
834 MessageLoop::current()->PostTask(FROM_HERE
,
835 base::Bind(&browser_sync::SessionModelAssociator::DeleteStaleSessions
,
836 GetSessionModelAssociator()->AsWeakPtr()));
838 DVLOG(2) << "Notifying observers sync cycle completed";
842 void ProfileSyncService::OnExperimentsChanged(
843 const syncer::Experiments
& experiments
) {
844 if (current_experiments
.Matches(experiments
))
847 // If this is a first time sync for a client, this will be called before
848 // OnBackendInitialized() to ensure the new datatypes are available at sync
849 // setup. As a result, the migrator won't exist yet. This is fine because for
850 // first time sync cases we're only concerned with making the datatype
852 if (migrator_
.get() &&
853 migrator_
->state() != browser_sync::BackendMigrator::IDLE
) {
854 DVLOG(1) << "Dropping OnExperimentsChanged due to migrator busy.";
858 const syncer::ModelTypeSet registered_types
= GetRegisteredDataTypes();
859 syncer::ModelTypeSet to_add
;
860 const syncer::ModelTypeSet to_register
=
861 Difference(to_add
, registered_types
);
862 DVLOG(2) << "OnExperimentsChanged called with types: "
863 << syncer::ModelTypeSetToString(to_add
);
864 DVLOG(2) << "Enabling types: " << syncer::ModelTypeSetToString(to_register
);
866 for (syncer::ModelTypeSet::Iterator it
= to_register
.First();
867 it
.Good(); it
.Inc()) {
868 // Received notice to enable experimental type. Check if the type is
869 // registered, and if not register a new datatype controller.
870 RegisterNewDataType(it
.Get());
871 // Enable the about:flags switch for the experimental type so we don't have
872 // to always perform this reconfiguration. Once we set this, the type will
873 // remain registered on restart, so we will no longer go down this code
875 std::string experiment_name
= GetExperimentNameForDataType(it
.Get());
876 if (experiment_name
.empty())
878 about_flags::SetExperimentEnabled(g_browser_process
->local_state(),
883 // Check if the user has "Keep Everything Synced" enabled. If so, we want
884 // to turn on all experimental types if they're not already on. Otherwise we
886 // Note: if any types are already registered, we don't turn them on. This
887 // covers the case where we're already in the process of reconfiguring
888 // to turn an experimental type on.
889 if (sync_prefs_
.HasKeepEverythingSynced()) {
890 // Mark all data types as preferred.
891 sync_prefs_
.SetPreferredDataTypes(registered_types
, registered_types
);
893 // Only automatically turn on types if we have already finished set up.
894 // Otherwise, just leave the experimental types on by default.
895 if (!to_register
.Empty() && HasSyncSetupCompleted() && migrator_
.get()) {
896 DVLOG(1) << "Dynamically enabling new datatypes: "
897 << syncer::ModelTypeSetToString(to_register
);
898 OnMigrationNeededForTypes(to_register
);
902 // Now enable any non-datatype features.
903 if (experiments
.sync_tab_favicons
) {
904 DVLOG(1) << "Enabling syncing of tab favicons.";
905 about_flags::SetExperimentEnabled(g_browser_process
->local_state(),
908 #if defined(OS_ANDROID)
909 // Android does not support about:flags and experiments, so we need to force
910 // setting the experiments as command line switches.
911 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kSyncTabFavicons
);
915 if (experiments
.keystore_encryption
) {
916 about_flags::SetExperimentEnabled(g_browser_process
->local_state(),
917 syncer::kKeystoreEncryptionFlag
,
921 current_experiments
= experiments
;
924 void ProfileSyncService::UpdateAuthErrorState(const AuthError
& error
) {
925 is_auth_in_progress_
= false;
926 last_auth_error_
= error
;
928 // Fan the notification out to interested UI-thread components.
934 AuthError
ConnectionStatusToAuthError(
935 syncer::ConnectionStatus status
) {
937 case syncer::CONNECTION_OK
:
938 return AuthError::None();
940 case syncer::CONNECTION_AUTH_ERROR
:
941 return AuthError(AuthError::INVALID_GAIA_CREDENTIALS
);
943 case syncer::CONNECTION_SERVER_ERROR
:
944 return AuthError(AuthError::CONNECTION_FAILED
);
948 return AuthError(AuthError::CONNECTION_FAILED
);
954 void ProfileSyncService::OnConnectionStatusChange(
955 syncer::ConnectionStatus status
) {
956 UpdateAuthErrorState(ConnectionStatusToAuthError(status
));
959 void ProfileSyncService::OnStopSyncingPermanently() {
960 UpdateAuthErrorState(AuthError(AuthError::SERVICE_UNAVAILABLE
));
961 sync_prefs_
.SetStartSuppressed(true);
965 void ProfileSyncService::OnPassphraseRequired(
966 syncer::PassphraseRequiredReason reason
,
967 const sync_pb::EncryptedData
& pending_keys
) {
968 DCHECK(backend_
.get());
969 DCHECK(backend_
->IsNigoriEnabled());
971 // TODO(lipalani) : add this check to other locations as well.
972 if (HasUnrecoverableError()) {
973 // When unrecoverable error is detected we post a task to shutdown the
974 // backend. The task might not have executed yet.
978 DVLOG(1) << "Passphrase required with reason: "
979 << syncer::PassphraseRequiredReasonToString(reason
);
980 passphrase_required_reason_
= reason
;
982 // Notify observers that the passphrase status may have changed.
986 void ProfileSyncService::OnPassphraseAccepted() {
987 DVLOG(1) << "Received OnPassphraseAccepted.";
988 // If we are not using an explicit passphrase, and we have a cache of the gaia
989 // password, use it for encryption at this point.
990 DCHECK(cached_passphrase_
.empty()) <<
991 "Passphrase no longer required but there is still a cached passphrase";
993 // Reset passphrase_required_reason_ since we know we no longer require the
994 // passphrase. We do this here rather than down in ResolvePassphraseRequired()
995 // because that can be called by OnPassphraseRequired() if no encrypted data
996 // types are enabled, and we don't want to clobber the true passphrase error.
997 passphrase_required_reason_
= syncer::REASON_PASSPHRASE_NOT_REQUIRED
;
999 // Make sure the data types that depend on the passphrase are started at
1001 const syncer::ModelTypeSet types
= GetPreferredDataTypes();
1003 if (data_type_manager_
.get()) {
1004 // Unblock the data type manager if necessary.
1005 data_type_manager_
->Configure(types
,
1006 syncer::CONFIGURE_REASON_RECONFIGURATION
);
1012 void ProfileSyncService::OnEncryptedTypesChanged(
1013 syncer::ModelTypeSet encrypted_types
,
1014 bool encrypt_everything
) {
1015 encrypted_types_
= encrypted_types
;
1016 encrypt_everything_
= encrypt_everything
;
1017 DVLOG(1) << "Encrypted types changed to "
1018 << syncer::ModelTypeSetToString(encrypted_types_
)
1019 << " (encrypt everything is set to "
1020 << (encrypt_everything_
? "true" : "false") << ")";
1021 DCHECK(encrypted_types_
.Has(syncer::PASSWORDS
));
1024 void ProfileSyncService::OnEncryptionComplete() {
1025 DVLOG(1) << "Encryption complete";
1026 if (encryption_pending_
&& encrypt_everything_
) {
1027 encryption_pending_
= false;
1028 // This is to nudge the integration tests when encryption is
1034 void ProfileSyncService::OnMigrationNeededForTypes(
1035 syncer::ModelTypeSet types
) {
1036 DCHECK(backend_initialized_
);
1037 DCHECK(data_type_manager_
.get());
1039 // Migrator must be valid, because we don't sync until it is created and this
1040 // callback originates from a sync cycle.
1041 migrator_
->MigrateTypes(types
);
1044 void ProfileSyncService::OnActionableError(const SyncProtocolError
& error
) {
1045 last_actionable_error_
= error
;
1046 DCHECK_NE(last_actionable_error_
.action
,
1047 syncer::UNKNOWN_ACTION
);
1048 switch (error
.action
) {
1049 case syncer::UPGRADE_CLIENT
:
1050 case syncer::CLEAR_USER_DATA_AND_RESYNC
:
1051 case syncer::ENABLE_SYNC_ON_ACCOUNT
:
1052 case syncer::STOP_AND_RESTART_SYNC
:
1053 // TODO(lipalani) : if setup in progress we want to display these
1054 // actions in the popup. The current experience might not be optimal for
1055 // the user. We just dismiss the dialog.
1056 if (setup_in_progress_
) {
1057 OnStopSyncingPermanently();
1058 expect_sync_configuration_aborted_
= true;
1060 // Trigger an unrecoverable error to stop syncing.
1061 OnInternalUnrecoverableError(FROM_HERE
,
1062 last_actionable_error_
.error_description
,
1064 ERROR_REASON_ACTIONABLE_ERROR
);
1066 case syncer::DISABLE_SYNC_ON_CLIENT
:
1067 OnStopSyncingPermanently();
1075 void ProfileSyncService::OnConfigureBlocked() {
1079 void ProfileSyncService::OnConfigureDone(
1080 const browser_sync::DataTypeManager::ConfigureResult
& result
) {
1081 // We should have cleared our cached passphrase before we get here (in
1082 // OnBackendInitialized()).
1083 DCHECK(cached_passphrase_
.empty());
1085 if (!sync_configure_start_time_
.is_null()) {
1086 if (result
.status
== DataTypeManager::OK
||
1087 result
.status
== DataTypeManager::PARTIAL_SUCCESS
) {
1088 base::Time sync_configure_stop_time
= base::Time::Now();
1089 base::TimeDelta delta
= sync_configure_stop_time
-
1090 sync_configure_start_time_
;
1091 if (is_first_time_sync_configure_
) {
1092 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceInitialConfigureTime", delta
);
1094 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceSubsequentConfigureTime",
1098 sync_configure_start_time_
= base::Time();
1101 // Notify listeners that configuration is done.
1102 content::NotificationService::current()->Notify(
1103 chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
,
1104 content::Source
<ProfileSyncService
>(this),
1105 content::NotificationService::NoDetails());
1107 configure_status_
= result
.status
;
1108 DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_
;
1109 // The possible status values:
1110 // ABORT - Configuration was aborted. This is not an error, if
1111 // initiated by user.
1112 // OK - Everything succeeded.
1113 // PARTIAL_SUCCESS - Some datatypes failed to start.
1114 // Everything else is an UnrecoverableError. So treat it as such.
1116 // First handle the abort case.
1117 if (configure_status_
== DataTypeManager::ABORTED
&&
1118 expect_sync_configuration_aborted_
) {
1119 DVLOG(0) << "ProfileSyncService::Observe Sync Configure aborted";
1120 expect_sync_configuration_aborted_
= false;
1124 // Handle unrecoverable error.
1125 if (configure_status_
!= DataTypeManager::OK
&&
1126 configure_status_
!= DataTypeManager::PARTIAL_SUCCESS
) {
1127 // Something catastrophic had happened. We should only have one
1128 // error representing it.
1129 DCHECK_EQ(result
.failed_data_types
.size(),
1130 static_cast<unsigned int>(1));
1131 syncer::SyncError error
= result
.failed_data_types
.front();
1132 DCHECK(error
.IsSet());
1133 std::string message
=
1134 "Sync configuration failed with status " +
1135 DataTypeManager::ConfigureStatusToString(configure_status_
) +
1136 " during " + syncer::ModelTypeToString(error
.type()) +
1137 ": " + error
.message();
1138 LOG(ERROR
) << "ProfileSyncService error: "
1140 OnInternalUnrecoverableError(error
.location(),
1143 ERROR_REASON_CONFIGURATION_FAILURE
);
1147 // Now handle partial success and full success.
1148 MessageLoop::current()->PostTask(FROM_HERE
,
1149 base::Bind(&ProfileSyncService::OnSyncConfigureDone
,
1150 weak_factory_
.GetWeakPtr(), result
));
1152 // We should never get in a state where we have no encrypted datatypes
1153 // enabled, and yet we still think we require a passphrase for decryption.
1154 DCHECK(!(IsPassphraseRequiredForDecryption() &&
1155 !IsEncryptedDatatypeEnabled()));
1157 // This must be done before we start syncing with the server to avoid
1158 // sending unencrypted data up on a first time sync.
1159 if (encryption_pending_
)
1160 backend_
->EnableEncryptEverything();
1163 if (migrator_
.get() &&
1164 migrator_
->state() != browser_sync::BackendMigrator::IDLE
) {
1165 // Migration in progress. Let the migrator know we just finished
1166 // configuring something. It will be up to the migrator to call
1167 // StartSyncingWithServer() if migration is now finished.
1168 migrator_
->OnConfigureDone(result
);
1170 StartSyncingWithServer();
1174 void ProfileSyncService::OnConfigureRetry() {
1175 // We should have cleared our cached passphrase before we get here (in
1176 // OnBackendInitialized()).
1177 DCHECK(cached_passphrase_
.empty());
1179 OnSyncConfigureRetry();
1182 void ProfileSyncService::OnConfigureStart() {
1183 sync_configure_start_time_
= base::Time::Now();
1184 content::NotificationService::current()->Notify(
1185 chrome::NOTIFICATION_SYNC_CONFIGURE_START
,
1186 content::Source
<ProfileSyncService
>(this),
1187 content::NotificationService::NoDetails());
1191 std::string
ProfileSyncService::QuerySyncStatusSummary() {
1192 if (HasUnrecoverableError()) {
1193 return "Unrecoverable error detected";
1194 } else if (!backend_
.get()) {
1195 return "Syncing not enabled";
1196 } else if (backend_
.get() && !HasSyncSetupCompleted()) {
1197 return "First time sync setup incomplete";
1198 } else if (backend_
.get() && HasSyncSetupCompleted() &&
1199 data_type_manager_
.get() &&
1200 data_type_manager_
->state() != DataTypeManager::CONFIGURED
) {
1201 return "Datatypes not fully initialized";
1202 } else if (ShouldPushChanges()) {
1203 return "Sync service initialized";
1205 return "Status unknown: Internal error?";
1209 bool ProfileSyncService::QueryDetailedSyncStatus(
1210 SyncBackendHost::Status
* result
) {
1211 if (backend_
.get() && backend_initialized_
) {
1212 *result
= backend_
->GetDetailedStatus();
1215 SyncBackendHost::Status status
;
1216 status
.sync_protocol_error
= last_actionable_error_
;
1222 const AuthError
& ProfileSyncService::GetAuthError() const {
1223 return last_auth_error_
;
1226 bool ProfileSyncService::FirstSetupInProgress() const {
1227 return !HasSyncSetupCompleted() && setup_in_progress_
;
1230 void ProfileSyncService::SetSetupInProgress(bool setup_in_progress
) {
1231 bool was_in_progress
= setup_in_progress_
;
1232 setup_in_progress_
= setup_in_progress
;
1233 if (!setup_in_progress
&& was_in_progress
) {
1234 if (sync_initialized()) {
1235 ReconfigureDatatypeManager();
1240 bool ProfileSyncService::sync_initialized() const {
1241 return backend_initialized_
;
1244 bool ProfileSyncService::waiting_for_auth() const {
1245 return is_auth_in_progress_
;
1248 bool ProfileSyncService::HasUnrecoverableError() const {
1249 return unrecoverable_error_reason_
!= ERROR_REASON_UNSET
;
1252 bool ProfileSyncService::IsPassphraseRequired() const {
1253 return passphrase_required_reason_
!=
1254 syncer::REASON_PASSPHRASE_NOT_REQUIRED
;
1257 // TODO(zea): Rename this IsPassphraseNeededFromUI and ensure it's used
1258 // appropriately (see http://crbug.com/91379).
1259 bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
1260 // If there is an encrypted datatype enabled and we don't have the proper
1261 // passphrase, we must prompt the user for a passphrase. The only way for the
1262 // user to avoid entering their passphrase is to disable the encrypted types.
1263 return IsEncryptedDatatypeEnabled() && IsPassphraseRequired();
1266 string16
ProfileSyncService::GetLastSyncedTimeString() const {
1267 if (last_synced_time_
.is_null())
1268 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER
);
1270 base::TimeDelta last_synced
= base::Time::Now() - last_synced_time_
;
1272 if (last_synced
< base::TimeDelta::FromMinutes(1))
1273 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW
);
1275 return TimeFormat::TimeElapsed(last_synced
);
1278 void ProfileSyncService::UpdateSelectedTypesHistogram(
1279 bool sync_everything
, const syncer::ModelTypeSet chosen_types
) const {
1280 if (!HasSyncSetupCompleted() ||
1281 sync_everything
!= sync_prefs_
.HasKeepEverythingSynced()) {
1282 UMA_HISTOGRAM_BOOLEAN("Sync.SyncEverything", sync_everything
);
1285 // Only log the data types that are shown in the sync settings ui.
1286 const syncer::ModelType model_types
[] = {
1292 syncer::PREFERENCES
,
1298 const browser_sync::user_selectable_type::UserSelectableSyncType
1299 user_selectable_types
[] = {
1300 browser_sync::user_selectable_type::APPS
,
1301 browser_sync::user_selectable_type::AUTOFILL
,
1302 browser_sync::user_selectable_type::BOOKMARKS
,
1303 browser_sync::user_selectable_type::EXTENSIONS
,
1304 browser_sync::user_selectable_type::PASSWORDS
,
1305 browser_sync::user_selectable_type::PREFERENCES
,
1306 browser_sync::user_selectable_type::SESSIONS
,
1307 browser_sync::user_selectable_type::THEMES
,
1308 browser_sync::user_selectable_type::TYPED_URLS
1311 COMPILE_ASSERT(20 == syncer::MODEL_TYPE_COUNT
, UpdateCustomConfigHistogram
);
1312 COMPILE_ASSERT(arraysize(model_types
) ==
1313 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT
,
1314 UpdateCustomConfigHistogram
);
1315 COMPILE_ASSERT(arraysize(model_types
) == arraysize(user_selectable_types
),
1316 UpdateCustomConfigHistogram
);
1318 if (!sync_everything
) {
1319 const syncer::ModelTypeSet current_types
= GetPreferredDataTypes();
1320 for (size_t i
= 0; i
< arraysize(model_types
); ++i
) {
1321 const syncer::ModelType type
= model_types
[i
];
1322 if (chosen_types
.Has(type
) &&
1323 (!HasSyncSetupCompleted() || !current_types
.Has(type
))) {
1324 // Selected type has changed - log it.
1325 UMA_HISTOGRAM_ENUMERATION(
1327 user_selectable_types
[i
],
1328 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT
+ 1);
1334 #if defined(OS_CHROMEOS)
1335 void ProfileSyncService::RefreshSpareBootstrapToken(
1336 const std::string
& passphrase
) {
1337 browser_sync::ChromeEncryptor encryptor
;
1338 syncer::Cryptographer
temp_cryptographer(&encryptor
);
1339 // The first 2 params (hostname and username) doesn't have any effect here.
1340 syncer::KeyParams key_params
= {"localhost", "dummy", passphrase
};
1342 std::string bootstrap_token
;
1343 if (!temp_cryptographer
.AddKey(key_params
)) {
1344 NOTREACHED() << "Failed to add key to cryptographer.";
1346 temp_cryptographer
.GetBootstrapToken(&bootstrap_token
);
1347 sync_prefs_
.SetSpareBootstrapToken(bootstrap_token
);
1351 void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything
,
1352 syncer::ModelTypeSet chosen_types
) {
1353 if (!backend_
.get() && !HasUnrecoverableError()) {
1358 UpdateSelectedTypesHistogram(sync_everything
, chosen_types
);
1359 sync_prefs_
.SetKeepEverythingSynced(sync_everything
);
1361 failed_datatypes_handler_
.OnUserChoseDatatypes();
1362 ChangePreferredDataTypes(chosen_types
);
1363 AcknowledgeSyncedTypes();
1367 void ProfileSyncService::ChangePreferredDataTypes(
1368 syncer::ModelTypeSet preferred_types
) {
1370 DVLOG(1) << "ChangePreferredDataTypes invoked";
1371 const syncer::ModelTypeSet registered_types
= GetRegisteredDataTypes();
1372 const syncer::ModelTypeSet registered_preferred_types
=
1373 Intersection(registered_types
, preferred_types
);
1374 sync_prefs_
.SetPreferredDataTypes(registered_types
,
1375 registered_preferred_types
);
1377 // Now reconfigure the DTM.
1378 ReconfigureDatatypeManager();
1381 syncer::ModelTypeSet
ProfileSyncService::GetPreferredDataTypes() const {
1382 const syncer::ModelTypeSet registered_types
= GetRegisteredDataTypes();
1383 const syncer::ModelTypeSet preferred_types
=
1384 sync_prefs_
.GetPreferredDataTypes(registered_types
);
1385 const syncer::ModelTypeSet failed_types
=
1386 failed_datatypes_handler_
.GetFailedTypes();
1387 return Difference(preferred_types
, failed_types
);
1390 syncer::ModelTypeSet
ProfileSyncService::GetRegisteredDataTypes() const {
1391 syncer::ModelTypeSet registered_types
;
1392 // The data_type_controllers_ are determined by command-line flags; that's
1393 // effectively what controls the values returned here.
1394 for (DataTypeController::TypeMap::const_iterator it
=
1395 data_type_controllers_
.begin();
1396 it
!= data_type_controllers_
.end(); ++it
) {
1397 registered_types
.Put(it
->first
);
1399 return registered_types
;
1402 bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
1403 syncer::PassphraseType passphrase_type
= GetPassphraseType();
1404 return passphrase_type
== syncer::FROZEN_IMPLICIT_PASSPHRASE
||
1405 passphrase_type
== syncer::CUSTOM_PASSPHRASE
;
1408 syncer::PassphraseType
ProfileSyncService::GetPassphraseType() const {
1409 return backend_
->GetPassphraseType();
1412 base::Time
ProfileSyncService::GetExplicitPassphraseTime() const {
1413 return backend_
->GetExplicitPassphraseTime();
1416 bool ProfileSyncService::IsCryptographerReady(
1417 const syncer::BaseTransaction
* trans
) const {
1418 return backend_
.get() && backend_
->IsCryptographerReady(trans
);
1421 SyncBackendHost
* ProfileSyncService::GetBackendForTest() {
1422 // We don't check |backend_initialized_|; we assume the test class
1423 // knows what it's doing.
1424 return backend_
.get();
1427 void ProfileSyncService::ConfigureDataTypeManager() {
1428 // Don't configure datatypes if the setup UI is still on the screen - this
1429 // is to help multi-screen setting UIs (like iOS) where they don't want to
1430 // start syncing data until the user is done configuring encryption options,
1431 // etc. ReconfigureDatatypeManager() will get called again once the UI calls
1432 // SetSetupInProgress(false).
1433 if (setup_in_progress_
)
1436 bool restart
= false;
1437 if (!data_type_manager_
.get()) {
1439 data_type_manager_
.reset(
1440 factory_
->CreateDataTypeManager(debug_info_listener_
,
1442 &data_type_controllers_
,
1445 // We create the migrator at the same time.
1447 new browser_sync::BackendMigrator(
1448 profile_
->GetDebugName(), GetUserShare(),
1449 this, data_type_manager_
.get(),
1450 base::Bind(&ProfileSyncService::StartSyncingWithServer
,
1451 base::Unretained(this))));
1454 const syncer::ModelTypeSet types
= GetPreferredDataTypes();
1455 if (IsPassphraseRequiredForDecryption()) {
1456 // We need a passphrase still. We don't bother to attempt to configure
1457 // until we receive an OnPassphraseAccepted (which triggers a configure).
1458 DVLOG(1) << "ProfileSyncService::ConfigureDataTypeManager bailing out "
1459 << "because a passphrase required";
1463 syncer::ConfigureReason reason
= syncer::CONFIGURE_REASON_UNKNOWN
;
1464 if (!HasSyncSetupCompleted()) {
1465 reason
= syncer::CONFIGURE_REASON_NEW_CLIENT
;
1466 } else if (restart
) {
1467 // Datatype downloads on restart are generally due to newly supported
1468 // datatypes (although it's also possible we're picking up where a failed
1469 // previous configuration left off).
1470 // TODO(sync): consider detecting configuration recovery and setting
1471 // the reason here appropriately.
1472 reason
= syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE
;
1474 // The user initiated a reconfiguration (either to add or remove types).
1475 reason
= syncer::CONFIGURE_REASON_RECONFIGURATION
;
1478 data_type_manager_
->Configure(types
, reason
);
1481 syncer::UserShare
* ProfileSyncService::GetUserShare() const {
1482 if (backend_
.get() && backend_initialized_
) {
1483 return backend_
->GetUserShare();
1489 syncer::sessions::SyncSessionSnapshot
1490 ProfileSyncService::GetLastSessionSnapshot() const {
1491 if (backend_
.get() && backend_initialized_
) {
1492 return backend_
->GetLastSessionSnapshot();
1495 return syncer::sessions::SyncSessionSnapshot();
1498 bool ProfileSyncService::HasUnsyncedItems() const {
1499 if (backend_
.get() && backend_initialized_
) {
1500 return backend_
->HasUnsyncedItems();
1506 browser_sync::BackendMigrator
*
1507 ProfileSyncService::GetBackendMigratorForTest() {
1508 return migrator_
.get();
1511 void ProfileSyncService::GetModelSafeRoutingInfo(
1512 syncer::ModelSafeRoutingInfo
* out
) const {
1513 if (backend_
.get() && backend_initialized_
) {
1514 backend_
->GetModelSafeRoutingInfo(out
);
1520 Value
* ProfileSyncService::GetTypeStatusMap() const {
1521 scoped_ptr
<ListValue
> result(new ListValue());
1523 if (!backend_
.get() || !backend_initialized_
) {
1524 return result
.release();
1527 std::vector
<syncer::SyncError
> errors
=
1528 failed_datatypes_handler_
.GetAllErrors();
1529 std::map
<ModelType
, syncer::SyncError
> error_map
;
1530 for (std::vector
<syncer::SyncError
>::iterator it
= errors
.begin();
1531 it
!= errors
.end(); ++it
) {
1532 error_map
[it
->type()] = *it
;
1535 ModelTypeSet active_types
;
1536 ModelTypeSet passive_types
;
1537 ModelSafeRoutingInfo routing_info
;
1538 backend_
->GetModelSafeRoutingInfo(&routing_info
);
1539 for (ModelSafeRoutingInfo::const_iterator it
= routing_info
.begin();
1540 it
!= routing_info
.end(); ++it
) {
1541 if (it
->second
== syncer::GROUP_PASSIVE
) {
1542 passive_types
.Put(it
->first
);
1544 active_types
.Put(it
->first
);
1548 SyncBackendHost::Status detailed_status
= backend_
->GetDetailedStatus();
1549 ModelTypeSet
&throttled_types(detailed_status
.throttled_types
);
1550 ModelTypeSet registered
= GetRegisteredDataTypes();
1551 scoped_ptr
<DictionaryValue
> type_status_header(new DictionaryValue());
1553 type_status_header
->SetString("name", "Model Type");
1554 type_status_header
->SetString("status", "header");
1555 type_status_header
->SetString("value", "Group Type");
1556 type_status_header
->SetString("num_entries", "Total Entries");
1557 type_status_header
->SetString("num_live", "Live Entries");
1558 result
->Append(type_status_header
.release());
1560 scoped_ptr
<DictionaryValue
> type_status
;
1561 for (ModelTypeSet::Iterator it
= registered
.First(); it
.Good(); it
.Inc()) {
1562 ModelType type
= it
.Get();
1564 type_status
.reset(new DictionaryValue());
1565 type_status
->SetString("name", ModelTypeToString(type
));
1567 if (error_map
.find(type
) != error_map
.end()) {
1568 const syncer::SyncError
&error
= error_map
.find(type
)->second
;
1569 DCHECK(error
.IsSet());
1570 std::string error_text
= "Error: " + error
.location().ToString() +
1571 ", " + error
.message();
1572 type_status
->SetString("status", "error");
1573 type_status
->SetString("value", error_text
);
1574 } else if (throttled_types
.Has(type
) && passive_types
.Has(type
)) {
1575 type_status
->SetString("status", "warning");
1576 type_status
->SetString("value", "Passive, Throttled");
1577 } else if (passive_types
.Has(type
)) {
1578 type_status
->SetString("status", "warning");
1579 type_status
->SetString("value", "Passive");
1580 } else if (throttled_types
.Has(type
)) {
1581 type_status
->SetString("status", "warning");
1582 type_status
->SetString("value", "Throttled");
1583 } else if (active_types
.Has(type
)) {
1584 type_status
->SetString("status", "ok");
1585 type_status
->SetString("value", "Active: " +
1586 ModelSafeGroupToString(routing_info
[type
]));
1588 type_status
->SetString("status", "warning");
1589 type_status
->SetString("value", "Disabled by User");
1592 int live_count
= detailed_status
.num_entries_by_type
[type
] -
1593 detailed_status
.num_to_delete_entries_by_type
[type
];
1594 type_status
->SetInteger("num_entries",
1595 detailed_status
.num_entries_by_type
[type
]);
1596 type_status
->SetInteger("num_live", live_count
);
1598 result
->Append(type_status
.release());
1600 return result
.release();
1603 void ProfileSyncService::ActivateDataType(
1604 syncer::ModelType type
, syncer::ModelSafeGroup group
,
1605 ChangeProcessor
* change_processor
) {
1606 if (!backend_
.get()) {
1610 DCHECK(backend_initialized_
);
1611 backend_
->ActivateDataType(type
, group
, change_processor
);
1614 void ProfileSyncService::DeactivateDataType(syncer::ModelType type
) {
1615 if (!backend_
.get())
1617 backend_
->DeactivateDataType(type
);
1620 void ProfileSyncService::ConsumeCachedPassphraseIfPossible() {
1621 // If no cached passphrase, or sync backend hasn't started up yet, just exit.
1622 // If the backend isn't running yet, OnBackendInitialized() will call this
1623 // method again after the backend starts up.
1624 if (cached_passphrase_
.empty() || !sync_initialized())
1627 // Backend is up and running, so we can consume the cached passphrase.
1628 std::string passphrase
= cached_passphrase_
;
1629 cached_passphrase_
.clear();
1631 // If we need a passphrase to decrypt data, try the cached passphrase.
1632 if (passphrase_required_reason() == syncer::REASON_DECRYPTION
) {
1633 if (SetDecryptionPassphrase(passphrase
)) {
1634 DVLOG(1) << "Cached passphrase successfully decrypted pending keys";
1639 // If we get here, we don't have pending keys (or at least, the passphrase
1640 // doesn't decrypt them) - just try to re-encrypt using the encryption
1642 if (!IsUsingSecondaryPassphrase())
1643 SetEncryptionPassphrase(passphrase
, IMPLICIT
);
1646 void ProfileSyncService::SetEncryptionPassphrase(const std::string
& passphrase
,
1647 PassphraseType type
) {
1648 // This should only be called when the backend has been initialized.
1649 DCHECK(sync_initialized());
1650 DCHECK(!(type
== IMPLICIT
&& IsUsingSecondaryPassphrase())) <<
1651 "Data is already encrypted using an explicit passphrase";
1652 DCHECK(!(type
== EXPLICIT
&& IsPassphraseRequired())) <<
1653 "Cannot switch to an explicit passphrase if a passphrase is required";
1655 if (type
== EXPLICIT
)
1656 UMA_HISTOGRAM_BOOLEAN("Sync.CustomPassphrase", true);
1658 DVLOG(1) << "Setting " << (type
== EXPLICIT
? "explicit" : "implicit")
1659 << " passphrase for encryption.";
1660 if (passphrase_required_reason_
== syncer::REASON_ENCRYPTION
) {
1661 // REASON_ENCRYPTION implies that the cryptographer does not have pending
1662 // keys. Hence, as long as we're not trying to do an invalid passphrase
1663 // change (e.g. explicit -> explicit or explicit -> implicit), we know this
1664 // will succeed. If for some reason a new encryption key arrives via
1665 // sync later, the SBH will trigger another OnPassphraseRequired().
1666 passphrase_required_reason_
= syncer::REASON_PASSPHRASE_NOT_REQUIRED
;
1669 backend_
->SetEncryptionPassphrase(passphrase
, type
== EXPLICIT
);
1672 bool ProfileSyncService::SetDecryptionPassphrase(
1673 const std::string
& passphrase
) {
1674 if (IsPassphraseRequired()) {
1675 DVLOG(1) << "Setting passphrase for decryption.";
1676 return backend_
->SetDecryptionPassphrase(passphrase
);
1678 NOTREACHED() << "SetDecryptionPassphrase must not be called when "
1679 "IsPassphraseRequired() is false.";
1684 void ProfileSyncService::EnableEncryptEverything() {
1685 // Tests override sync_initialized() to always return true, so we
1686 // must check that instead of |backend_initialized_|.
1687 // TODO(akalin): Fix the above. :/
1688 DCHECK(sync_initialized());
1689 // TODO(atwilson): Persist the encryption_pending_ flag to address the various
1690 // problems around cancelling encryption in the background (crbug.com/119649).
1691 if (!encrypt_everything_
)
1692 encryption_pending_
= true;
1693 UMA_HISTOGRAM_BOOLEAN("Sync.EncryptAllData", true);
1696 bool ProfileSyncService::encryption_pending() const {
1697 // We may be called during the setup process before we're
1698 // initialized (via IsEncryptedDatatypeEnabled and
1699 // IsPassphraseRequiredForDecryption).
1700 return encryption_pending_
;
1703 bool ProfileSyncService::EncryptEverythingEnabled() const {
1704 DCHECK(backend_initialized_
);
1705 return encrypt_everything_
|| encryption_pending_
;
1708 syncer::ModelTypeSet
ProfileSyncService::GetEncryptedDataTypes() const {
1709 DCHECK(encrypted_types_
.Has(syncer::PASSWORDS
));
1710 // We may be called during the setup process before we're
1711 // initialized. In this case, we default to the sensitive types.
1712 return encrypted_types_
;
1715 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed
) {
1717 if (is_sync_managed
) {
1719 } else if (HasSyncSetupCompleted() &&
1720 IsSyncEnabledAndLoggedIn() &&
1721 IsSyncTokenAvailable()) {
1722 // Previously-configured sync has been re-enabled, so start sync now.
1727 void ProfileSyncService::Observe(int type
,
1728 const content::NotificationSource
& source
,
1729 const content::NotificationDetails
& details
) {
1731 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
: {
1732 const GoogleServiceSigninSuccessDetails
* successful
=
1733 content::Details
<const GoogleServiceSigninSuccessDetails
>(
1735 DCHECK(!successful
->password
.empty());
1736 if (!sync_prefs_
.IsStartSuppressed()) {
1737 cached_passphrase_
= successful
->password
;
1738 // Try to consume the passphrase we just cached. If the sync backend
1739 // is not running yet, the passphrase will remain cached until the
1740 // backend starts up.
1741 ConsumeCachedPassphraseIfPossible();
1743 #if defined(OS_CHROMEOS)
1744 RefreshSpareBootstrapToken(successful
->password
);
1746 if (!sync_initialized() ||
1747 GetAuthError().state() != AuthError::NONE
) {
1748 // Track the fact that we're still waiting for auth to complete.
1749 is_auth_in_progress_
= true;
1753 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED
: {
1754 const TokenService::TokenRequestFailedDetails
& token_details
=
1755 *(content::Details
<const TokenService::TokenRequestFailedDetails
>(
1757 if (IsTokenServiceRelevant(token_details
.service()) &&
1758 !IsSyncTokenAvailable()) {
1759 // The additional check around IsSyncTokenAvailable() above prevents us
1760 // sounding the alarm if we actually have a valid token but a refresh
1761 // attempt by TokenService failed for any variety of reasons (e.g. flaky
1762 // network). It's possible the token we do have is also invalid, but in
1763 // that case we should already have (or can expect) an auth error sent
1764 // from the sync backend.
1765 AuthError
error(AuthError::INVALID_GAIA_CREDENTIALS
);
1766 UpdateAuthErrorState(error
);
1770 case chrome::NOTIFICATION_TOKEN_AVAILABLE
: {
1771 const TokenService::TokenAvailableDetails
& token_details
=
1772 *(content::Details
<const TokenService::TokenAvailableDetails
>(
1774 if (IsTokenServiceRelevant(token_details
.service()) &&
1775 IsSyncEnabledAndLoggedIn() &&
1776 IsSyncTokenAvailable()) {
1777 if (backend_initialized_
)
1778 backend_
->UpdateCredentials(GetCredentials());
1784 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED
: {
1785 // This notification gets fired when TokenService loads the tokens
1787 if (IsSyncEnabledAndLoggedIn()) {
1788 // Don't start up sync and generate an auth error on auto_start
1789 // platforms as they have their own way to resolve TokenService errors.
1790 // (crbug.com/128592).
1791 if (auto_start_enabled_
&& !IsSyncTokenAvailable())
1794 // Initialize the backend if sync is enabled. If the sync token was
1795 // not loaded, GetCredentials() will generate invalid credentials to
1796 // cause the backend to generate an auth error (crbug.com/121755).
1797 if (backend_initialized_
)
1798 backend_
->UpdateCredentials(GetCredentials());
1810 void ProfileSyncService::AddObserver(Observer
* observer
) {
1811 observers_
.AddObserver(observer
);
1814 void ProfileSyncService::RemoveObserver(Observer
* observer
) {
1815 observers_
.RemoveObserver(observer
);
1818 bool ProfileSyncService::HasObserver(Observer
* observer
) const {
1819 return observers_
.HasObserver(observer
);
1822 base::WeakPtr
<syncer::JsController
> ProfileSyncService::GetJsController() {
1823 return sync_js_controller_
.AsWeakPtr();
1826 void ProfileSyncService::SyncEvent(SyncEventCodes code
) {
1827 UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code
, MAX_SYNC_EVENT_CODE
);
1831 bool ProfileSyncService::IsSyncEnabled() {
1832 // We have switches::kEnableSync just in case we need to change back to
1833 // sync-disabled-by-default on a platform.
1834 return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync
);
1837 bool ProfileSyncService::IsManaged() const {
1838 return sync_prefs_
.IsManaged();
1841 bool ProfileSyncService::ShouldPushChanges() {
1842 // True only after all bootstrapping has succeeded: the sync backend
1843 // is initialized, all enabled data types are consistent with one
1844 // another, and no unrecoverable error has transpired.
1845 if (HasUnrecoverableError())
1848 if (!data_type_manager_
.get())
1851 return data_type_manager_
->state() == DataTypeManager::CONFIGURED
;
1854 void ProfileSyncService::StopAndSuppress() {
1855 sync_prefs_
.SetStartSuppressed(true);
1856 ShutdownImpl(false);
1859 void ProfileSyncService::UnsuppressAndStart() {
1861 sync_prefs_
.SetStartSuppressed(false);
1862 // Set username in SigninManager, as SigninManager::OnGetUserInfoSuccess
1863 // is never called for some clients.
1864 if (signin_
&& signin_
->GetAuthenticatedUsername().empty()) {
1865 signin_
->SetAuthenticatedUsername(sync_prefs_
.GetGoogleServicesUsername());
1870 void ProfileSyncService::AcknowledgeSyncedTypes() {
1871 sync_prefs_
.AcknowledgeSyncedTypes(GetRegisteredDataTypes());
1874 void ProfileSyncService::ReconfigureDatatypeManager() {
1875 // If we haven't initialized yet, don't configure the DTM as it could cause
1876 // association to start before a Directory has even been created.
1877 if (backend_initialized_
) {
1878 DCHECK(backend_
.get());
1879 ConfigureDataTypeManager();
1880 } else if (HasUnrecoverableError()) {
1881 // There is nothing more to configure. So inform the listeners,
1884 DVLOG(1) << "ConfigureDataTypeManager not invoked because of an "
1885 << "Unrecoverable error.";
1887 DVLOG(0) << "ConfigureDataTypeManager not invoked because backend is not "
1892 const FailedDatatypesHandler
& ProfileSyncService::failed_datatypes_handler()
1894 return failed_datatypes_handler_
;
1897 void ProfileSyncService::OnInternalUnrecoverableError(
1898 const tracked_objects::Location
& from_here
,
1899 const std::string
& message
,
1900 bool delete_sync_database
,
1901 UnrecoverableErrorReason reason
) {
1902 DCHECK(!HasUnrecoverableError());
1903 unrecoverable_error_reason_
= reason
;
1904 OnUnrecoverableErrorImpl(from_here
, message
, delete_sync_database
);
1907 void ProfileSyncService::UpdateInvalidatorRegistrarState() {
1908 const syncer::InvalidatorState effective_state
=
1909 backend_initialized_
?
1910 invalidator_state_
: syncer::TRANSIENT_INVALIDATION_ERROR
;
1911 invalidator_registrar_
->UpdateInvalidatorState(effective_state
);
1914 void ProfileSyncService::ResetForTest() {
1915 Profile
* profile
= profile_
;
1916 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile
);
1917 ProfileSyncService::StartBehavior behavior
=
1918 browser_defaults::kSyncAutoStarts
? ProfileSyncService::AUTO_START
1919 : ProfileSyncService::MANUAL_START
;
1921 // We call the destructor and placement new here because we want to explicitly
1922 // recreate a new ProfileSyncService instance at the same memory location as
1923 // the old one. Doing so is fine because this code is run only from within
1924 // integration tests, and the message loop is not running at this point.
1925 // See http://stackoverflow.com/questions/6224121/is-new-this-myclass-undefined-behaviour-after-directly-calling-the-destru.
1926 ProfileSyncService
* old_this
= this;
1927 this->~ProfileSyncService();
1928 new(old_this
) ProfileSyncService(
1929 new ProfileSyncComponentsFactoryImpl(profile
,
1930 CommandLine::ForCurrentProcess()),