Index: components/sync/driver/data_type_manager_impl.cc |
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc |
index afd0f877550bf97e19f72174e858c809b01ea9ae..cb4730b01706b664ae0abd681d6f66c25d43b135 100644 |
--- a/components/sync/driver/data_type_manager_impl.cc |
+++ b/components/sync/driver/data_type_manager_impl.cc |
@@ -6,6 +6,7 @@ |
#include <algorithm> |
#include <functional> |
+#include <utility> |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
@@ -50,7 +51,8 @@ DataTypeManagerImpl::DataTypeManagerImpl( |
const DataTypeEncryptionHandler* encryption_handler, |
ModelTypeConfigurer* configurer, |
DataTypeManagerObserver* observer) |
- : configurer_(configurer), |
+ : engine_types_(initial_types), |
+ configurer_(configurer), |
controllers_(controllers), |
state_(DataTypeManager::STOPPED), |
needs_reconfigure_(false), |
@@ -165,7 +167,28 @@ void DataTypeManagerImpl::RegisterTypesWithBackend() { |
} |
} |
-ModelTypeConfigurer::DataTypeConfigStateMap |
+// static |
+ModelTypeSet DataTypeManagerImpl::GetDataTypesInState( |
+ DataTypeConfigState state, |
+ const DataTypeConfigStateMap& state_map) { |
+ ModelTypeSet types; |
+ for (const auto& kv : state_map) { |
+ if (kv.second == state) |
+ types.Put(kv.first); |
+ } |
+ return types; |
+} |
+ |
+// static |
+void DataTypeManagerImpl::SetDataTypesState(DataTypeConfigState state, |
+ ModelTypeSet types, |
+ DataTypeConfigStateMap* state_map) { |
+ for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { |
+ (*state_map)[it.Get()] = state; |
+ } |
+} |
+ |
+DataTypeManagerImpl::DataTypeConfigStateMap |
DataTypeManagerImpl::BuildDataTypeConfigStateMap( |
const ModelTypeSet& types_being_configured) const { |
// 1. Get the failed types (due to fatal, crypto, and unready errors). |
@@ -202,22 +225,14 @@ DataTypeManagerImpl::BuildDataTypeConfigStateMap( |
DVLOG(1) << "Configuring: " << ModelTypeSetToString(to_configure); |
DVLOG(1) << "Disabling: " << ModelTypeSetToString(disabled_types); |
- ModelTypeConfigurer::DataTypeConfigStateMap config_state_map; |
- ModelTypeConfigurer::SetDataTypesState( |
- ModelTypeConfigurer::CONFIGURE_INACTIVE, enabled_types, |
- &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::CONFIGURE_ACTIVE, |
- to_configure, &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::CONFIGURE_CLEAN, |
- clean_types, &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::DISABLED, |
- disabled_types, &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::FATAL, |
- fatal_types, &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::CRYPTO, |
- crypto_types, &config_state_map); |
- ModelTypeConfigurer::SetDataTypesState(ModelTypeConfigurer::UNREADY, |
- unready_types, &config_state_map); |
+ DataTypeConfigStateMap config_state_map; |
+ SetDataTypesState(CONFIGURE_INACTIVE, enabled_types, &config_state_map); |
+ SetDataTypesState(CONFIGURE_ACTIVE, to_configure, &config_state_map); |
+ SetDataTypesState(CONFIGURE_CLEAN, clean_types, &config_state_map); |
+ SetDataTypesState(DISABLED, disabled_types, &config_state_map); |
+ SetDataTypesState(FATAL, fatal_types, &config_state_map); |
+ SetDataTypesState(CRYPTO, crypto_types, &config_state_map); |
+ SetDataTypesState(UNREADY, unready_types, &config_state_map); |
return config_state_map; |
} |
@@ -444,13 +459,7 @@ void DataTypeManagerImpl::StartNextDownload( |
// Tell the backend about the new set of data types we wish to sync. |
// The task will be invoked when updates are downloaded. |
- ModelTypeSet ready_types = configurer_->ConfigureDataTypes( |
- last_configure_reason_, |
- BuildDataTypeConfigStateMap(download_types_queue_.front()), |
- base::Bind(&DataTypeManagerImpl::DownloadReady, |
- weak_ptr_factory_.GetWeakPtr(), download_types_queue_.front()), |
- base::Bind(&DataTypeManagerImpl::OnDownloadRetry, |
- weak_ptr_factory_.GetWeakPtr())); |
+ ModelTypeSet ready_types = ConfigureDataTypes(); |
AssociationTypesInfo association_info; |
association_info.types = download_types_queue_.front(); |
@@ -464,6 +473,132 @@ void DataTypeManagerImpl::StartNextDownload( |
StartNextAssociation(READY_AT_CONFIG); |
} |
+ModelTypeSet DataTypeManagerImpl::ConfigureDataTypes() { |
+ // Divide up the types into their corresponding actions: |
+ // - Types which are newly enabled are downloaded. |
+ // - Types which have encountered a fatal error (fatal_types) are deleted |
+ // from the directory and journaled in the delete journal. |
+ // - Types which have encountered a cryptographer error (crypto_types) are |
+ // unapplied (local state is purged but sync state is not). |
+ // - All other types not in the routing info (types just disabled) are deleted |
+ // from the directory. |
+ // - Everything else (enabled types and already disabled types) is not |
+ // touched. |
+ const DataTypeConfigStateMap config_state_map = |
+ BuildDataTypeConfigStateMap(download_types_queue_.front()); |
+ const ModelTypeSet fatal_types = GetDataTypesInState(FATAL, config_state_map); |
+ const ModelTypeSet crypto_types = |
+ GetDataTypesInState(CRYPTO, config_state_map); |
+ const ModelTypeSet unready_types = |
+ GetDataTypesInState(UNREADY, config_state_map); |
+ const ModelTypeSet active_types = |
+ GetDataTypesInState(CONFIGURE_ACTIVE, config_state_map); |
+ const ModelTypeSet clean_types = |
+ GetDataTypesInState(CONFIGURE_CLEAN, config_state_map); |
+ const ModelTypeSet inactive_types = |
+ GetDataTypesInState(CONFIGURE_INACTIVE, config_state_map); |
+ |
+ ModelTypeSet enabled_types = Union(active_types, clean_types); |
+ ModelTypeSet disabled_types = GetDataTypesInState(DISABLED, config_state_map); |
+ disabled_types.PutAll(fatal_types); |
+ disabled_types.PutAll(crypto_types); |
+ disabled_types.PutAll(unready_types); |
+ |
+ DCHECK(Intersection(enabled_types, disabled_types).Empty()); |
+ |
+ // The sync engine's enabled types will be updated by adding |enabled_types| |
+ // to the list then removing |disabled_types|. Any types which are not in |
+ // either of those sets will remain untouched. Types which were not in |
+ // |engine_types_| previously are not fully downloaded, so we must ask the |
+ // engine to download them. Any newly supported datatypes won't have been in |
+ // |engine_types_|, so they will also be downloaded if they are enabled. |
+ ModelTypeSet types_to_download = Difference(enabled_types, engine_types_); |
+ engine_types_.PutAll(enabled_types); |
+ engine_types_.RemoveAll(disabled_types); |
+ |
+ types_to_download.PutAll(clean_types); |
+ types_to_download.RemoveAll(ProxyTypes()); |
+ if (!types_to_download.Empty()) |
+ types_to_download.Put(NIGORI); |
+ |
+ // TODO(sync): crbug.com/137550. |
+ // It's dangerous to configure types that have progress markers. Types with |
+ // progress markers can trigger a MIGRATION_DONE response. We are not |
+ // prepared to handle a migration during a configure, so we must ensure that |
+ // all our types_to_download actually contain no data before we sync them. |
+ // |
+ // One common way to end up in this situation used to be types which |
+ // downloaded some or all of their data but have not applied it yet. We avoid |
+ // problems with those types by purging the data of any such partially synced |
+ // types soon after we load the directory. |
+ // |
+ // Another possible scenario is that we have newly supported or newly enabled |
+ // data types being downloaded here but the nigori type, which is always |
+ // included in any GetUpdates request, requires migration. The server has |
+ // code to detect this scenario based on the configure reason, the fact that |
+ // the nigori type is the only requested type which requires migration, and |
+ // that the requested types list includes at least one non-nigori type. It |
+ // will not send a MIGRATION_DONE response in that case. We still need to be |
+ // careful to not send progress markers for non-nigori types, though. If a |
+ // non-nigori type in the request requires migration, a MIGRATION_DONE |
+ // response will be sent. |
+ |
+ ModelTypeSet types_to_purge = Difference(ModelTypeSet::All(), engine_types_); |
+ // Include clean_types in types_to_purge, they are part of |engine_types_|, |
+ // but still need to be cleared. |
+ DCHECK(engine_types_.HasAll(clean_types)); |
+ types_to_purge.PutAll(clean_types); |
+ types_to_purge.RemoveAll(inactive_types); |
+ types_to_purge.RemoveAll(unready_types); |
+ |
+ // If a type has already been disabled and unapplied or journaled, it will |
+ // not be part of the |types_to_purge| set, and therefore does not need |
+ // to be acted on again. |
+ ModelTypeSet types_to_journal = Intersection(fatal_types, types_to_purge); |
+ ModelTypeSet unapply_types = Union(crypto_types, clean_types); |
+ unapply_types.RetainAll(types_to_purge); |
+ |
+ DCHECK(Intersection(engine_types_, types_to_journal).Empty()); |
+ DCHECK(Intersection(engine_types_, crypto_types).Empty()); |
+ DCHECK(engine_types_.HasAll(types_to_download)); |
+ |
+ DVLOG(1) << "Types " << ModelTypeSetToString(types_to_download) |
+ << " added; calling ConfigureDataTypes"; |
+ |
+ ModelTypeConfigurer::ConfigureParams params; |
+ params.reason = last_configure_reason_; |
+ params.enabled_types = enabled_types; |
+ params.disabled_types = disabled_types; |
+ params.to_download = types_to_download; |
+ params.to_purge = types_to_purge; |
+ params.to_journal = types_to_journal; |
+ params.to_unapply = unapply_types; |
+ params.ready_task = |
+ base::Bind(&DataTypeManagerImpl::DownloadReady, |
+ weak_ptr_factory_.GetWeakPtr(), download_types_queue_.front()); |
+ params.retry_callback = base::Bind(&DataTypeManagerImpl::OnDownloadRetry, |
+ weak_ptr_factory_.GetWeakPtr()); |
+ |
+ // The engine's state was initially derived from the types detected to have |
+ // been downloaded in the database. Afterwards it is modified only by this |
+ // function. We expect |engine_types_| to remain consistent because |
+ // configuration requests are never aborted; they are retried until they |
+ // succeed or the engine is shut down. |
+ // |
+ // Only one configure is allowed at a time. This is guaranteed by our callers. |
+ // The sync engine requests one configure as it is initializing and waits for |
+ // it to complete. After engine initialization, all configurations pass |
+ // through the DataTypeManager, and we are careful to never send a new |
+ // configure request until the current request succeeds. |
+ configurer_->ConfigureDataTypes(std::move(params)); |
+ |
+ DCHECK(Intersection(active_types, types_to_purge).Empty()); |
+ DCHECK(Intersection(active_types, fatal_types).Empty()); |
+ DCHECK(Intersection(active_types, unapply_types).Empty()); |
+ DCHECK(Intersection(active_types, inactive_types).Empty()); |
+ return Difference(active_types, types_to_download); |
+} |
+ |
void DataTypeManagerImpl::StartNextAssociation(AssociationGroup group) { |
CHECK(!association_types_queue_.empty()); |