OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sync/internal_api/sync_manager_impl.h" | 5 #include "components/sync/core_impl/sync_manager_impl.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 |
9 #include <string> | 10 #include <string> |
10 #include <utility> | 11 #include <utility> |
11 | 12 |
12 #include "base/base64.h" | 13 #include "base/base64.h" |
13 #include "base/bind.h" | 14 #include "base/bind.h" |
14 #include "base/callback.h" | 15 #include "base/callback.h" |
15 #include "base/compiler_specific.h" | 16 #include "base/compiler_specific.h" |
16 #include "base/json/json_writer.h" | 17 #include "base/json/json_writer.h" |
17 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
18 #include "base/memory/ref_counted.h" | 19 #include "base/memory/ref_counted.h" |
19 #include "base/metrics/histogram.h" | 20 #include "base/metrics/histogram.h" |
20 #include "base/observer_list.h" | 21 #include "base/observer_list.h" |
21 #include "base/strings/string_number_conversions.h" | 22 #include "base/strings/string_number_conversions.h" |
22 #include "base/threading/thread_task_runner_handle.h" | 23 #include "base/threading/thread_task_runner_handle.h" |
23 #include "base/values.h" | 24 #include "base/values.h" |
24 #include "sync/engine/sync_scheduler.h" | 25 #include "components/sync/base/cancelation_signal.h" |
25 #include "sync/engine/syncer_types.h" | 26 #include "components/sync/base/experiments.h" |
26 #include "sync/internal_api/change_reorder_buffer.h" | 27 #include "components/sync/base/invalidation_interface.h" |
27 #include "sync/internal_api/model_type_connector_proxy.h" | 28 #include "components/sync/base/model_type.h" |
28 #include "sync/internal_api/public/base/cancelation_signal.h" | 29 #include "components/sync/core/base_node.h" |
29 #include "sync/internal_api/public/base/invalidation_interface.h" | 30 #include "components/sync/core/configure_reason.h" |
30 #include "sync/internal_api/public/base/model_type.h" | 31 #include "components/sync/core/http_post_provider_factory.h" |
31 #include "sync/internal_api/public/base_node.h" | 32 #include "components/sync/core/internal_components_factory.h" |
32 #include "sync/internal_api/public/configure_reason.h" | 33 #include "components/sync/core/read_node.h" |
33 #include "sync/internal_api/public/engine/polling_constants.h" | 34 #include "components/sync/core/read_transaction.h" |
34 #include "sync/internal_api/public/http_post_provider_factory.h" | 35 #include "components/sync/core/user_share.h" |
35 #include "sync/internal_api/public/internal_components_factory.h" | 36 #include "components/sync/core/write_node.h" |
36 #include "sync/internal_api/public/read_node.h" | 37 #include "components/sync/core/write_transaction.h" |
37 #include "sync/internal_api/public/read_transaction.h" | 38 #include "components/sync/core_impl/change_reorder_buffer.h" |
38 #include "sync/internal_api/public/user_share.h" | 39 #include "components/sync/core_impl/model_type_connector_proxy.h" |
39 #include "sync/internal_api/public/util/experiments.h" | 40 #include "components/sync/core_impl/syncapi_internal.h" |
40 #include "sync/internal_api/public/write_node.h" | 41 #include "components/sync/core_impl/syncapi_server_connection_manager.h" |
41 #include "sync/internal_api/public/write_transaction.h" | 42 #include "components/sync/engine/polling_constants.h" |
42 #include "sync/internal_api/syncapi_internal.h" | 43 #include "components/sync/engine_impl/sync_scheduler.h" |
43 #include "sync/internal_api/syncapi_server_connection_manager.h" | 44 #include "components/sync/engine_impl/syncer_types.h" |
44 #include "sync/protocol/proto_value_conversions.h" | 45 #include "components/sync/protocol/proto_value_conversions.h" |
45 #include "sync/protocol/sync.pb.h" | 46 #include "components/sync/protocol/sync.pb.h" |
46 #include "sync/sessions/directory_type_debug_info_emitter.h" | 47 #include "components/sync/sessions_impl/directory_type_debug_info_emitter.h" |
47 #include "sync/syncable/directory.h" | 48 #include "components/sync/syncable/directory.h" |
48 #include "sync/syncable/entry.h" | 49 #include "components/sync/syncable/entry.h" |
49 #include "sync/syncable/in_memory_directory_backing_store.h" | 50 #include "components/sync/syncable/in_memory_directory_backing_store.h" |
50 #include "sync/syncable/on_disk_directory_backing_store.h" | 51 #include "components/sync/syncable/on_disk_directory_backing_store.h" |
51 | 52 |
52 using base::TimeDelta; | 53 using base::TimeDelta; |
53 using sync_pb::GetUpdatesCallerInfo; | 54 using sync_pb::GetUpdatesCallerInfo; |
54 | 55 |
55 class GURL; | 56 class GURL; |
56 | 57 |
57 namespace syncer { | 58 namespace syncer { |
58 | 59 |
59 using sessions::SyncSessionContext; | 60 using sessions::SyncSessionContext; |
60 using syncable::ImmutableWriteTransactionInfo; | 61 using syncable::ImmutableWriteTransactionInfo; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics), | 138 DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics), |
138 GetModelTypeFromSpecifics(b_specifics)); | 139 GetModelTypeFromSpecifics(b_specifics)); |
139 ModelType model_type = GetModelTypeFromSpecifics(b_specifics); | 140 ModelType model_type = GetModelTypeFromSpecifics(b_specifics); |
140 // Suppress updates to items that aren't tracked by any browser model. | 141 // Suppress updates to items that aren't tracked by any browser model. |
141 if (model_type < FIRST_REAL_MODEL_TYPE || | 142 if (model_type < FIRST_REAL_MODEL_TYPE || |
142 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { | 143 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { |
143 return false; | 144 return false; |
144 } | 145 } |
145 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) | 146 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) |
146 return true; | 147 return true; |
147 if (!AreSpecificsEqual(cryptographer, | 148 if (!AreSpecificsEqual(cryptographer, a.ref(syncable::SPECIFICS), |
148 a.ref(syncable::SPECIFICS), | |
149 b.ref(syncable::SPECIFICS))) { | 149 b.ref(syncable::SPECIFICS))) { |
150 return true; | 150 return true; |
151 } | 151 } |
152 if (!AreAttachmentMetadataEqual(a.ref(syncable::ATTACHMENT_METADATA), | 152 if (!AreAttachmentMetadataEqual(a.ref(syncable::ATTACHMENT_METADATA), |
153 b.ref(syncable::ATTACHMENT_METADATA))) { | 153 b.ref(syncable::ATTACHMENT_METADATA))) { |
154 return true; | 154 return true; |
155 } | 155 } |
156 // We only care if the name has changed if neither specifics is encrypted | 156 // We only care if the name has changed if neither specifics is encrypted |
157 // (encrypted nodes blow away the NON_UNIQUE_NAME). | 157 // (encrypted nodes blow away the NON_UNIQUE_NAME). |
158 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && | 158 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && |
(...skipping 29 matching lines...) Expand all Loading... |
188 ModelTypeSet to_journal, | 188 ModelTypeSet to_journal, |
189 ModelTypeSet to_unapply, | 189 ModelTypeSet to_unapply, |
190 const ModelSafeRoutingInfo& new_routing_info, | 190 const ModelSafeRoutingInfo& new_routing_info, |
191 const base::Closure& ready_task, | 191 const base::Closure& ready_task, |
192 const base::Closure& retry_task) { | 192 const base::Closure& retry_task) { |
193 DCHECK(thread_checker_.CalledOnValidThread()); | 193 DCHECK(thread_checker_.CalledOnValidThread()); |
194 DCHECK(!ready_task.is_null()); | 194 DCHECK(!ready_task.is_null()); |
195 DCHECK(initialized_); | 195 DCHECK(initialized_); |
196 | 196 |
197 DVLOG(1) << "Configuring -" | 197 DVLOG(1) << "Configuring -" |
198 << "\n\t" << "current types: " | 198 << "\n\t" |
| 199 << "current types: " |
199 << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info)) | 200 << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info)) |
200 << "\n\t" << "types to download: " | 201 << "\n\t" |
201 << ModelTypeSetToString(to_download) | 202 << "types to download: " << ModelTypeSetToString(to_download) |
202 << "\n\t" << "types to purge: " | 203 << "\n\t" |
203 << ModelTypeSetToString(to_purge) | 204 << "types to purge: " << ModelTypeSetToString(to_purge) << "\n\t" |
204 << "\n\t" << "types to journal: " | 205 << "types to journal: " << ModelTypeSetToString(to_journal) << "\n\t" |
205 << ModelTypeSetToString(to_journal) | 206 << "types to unapply: " << ModelTypeSetToString(to_unapply); |
206 << "\n\t" << "types to unapply: " | 207 if (!PurgeDisabledTypes(to_purge, to_journal, to_unapply)) { |
207 << ModelTypeSetToString(to_unapply); | |
208 if (!PurgeDisabledTypes(to_purge, | |
209 to_journal, | |
210 to_unapply)) { | |
211 // We failed to cleanup the types. Invoke the ready task without actually | 208 // We failed to cleanup the types. Invoke the ready task without actually |
212 // configuring any types. The caller should detect this as a configuration | 209 // configuring any types. The caller should detect this as a configuration |
213 // failure and act appropriately. | 210 // failure and act appropriately. |
214 ready_task.Run(); | 211 ready_task.Run(); |
215 return; | 212 return; |
216 } | 213 } |
217 | 214 |
218 ConfigurationParams params(GetSourceFromReason(reason), | 215 ConfigurationParams params(GetSourceFromReason(reason), to_download, |
219 to_download, | 216 new_routing_info, ready_task, retry_task); |
220 new_routing_info, | |
221 ready_task, | |
222 retry_task); | |
223 | 217 |
224 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE, base::Time()); | 218 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE, base::Time()); |
225 scheduler_->ScheduleConfiguration(params); | 219 scheduler_->ScheduleConfiguration(params); |
226 } | 220 } |
227 | 221 |
228 void SyncManagerImpl::Init(InitArgs* args) { | 222 void SyncManagerImpl::Init(InitArgs* args) { |
229 CHECK(!initialized_); | 223 CHECK(!initialized_); |
230 DCHECK(thread_checker_.CalledOnValidThread()); | 224 DCHECK(thread_checker_.CalledOnValidThread()); |
231 DCHECK(args->post_factory.get()); | 225 DCHECK(args->post_factory.get()); |
232 DCHECK(!args->credentials.account_id.empty()); | 226 DCHECK(!args->credentials.account_id.empty()); |
(...skipping 27 matching lines...) Expand all Loading... |
260 | 254 |
261 base::FilePath absolute_db_path = database_path_; | 255 base::FilePath absolute_db_path = database_path_; |
262 DCHECK(absolute_db_path.IsAbsolute()); | 256 DCHECK(absolute_db_path.IsAbsolute()); |
263 | 257 |
264 std::unique_ptr<syncable::DirectoryBackingStore> backing_store = | 258 std::unique_ptr<syncable::DirectoryBackingStore> backing_store = |
265 args->internal_components_factory->BuildDirectoryBackingStore( | 259 args->internal_components_factory->BuildDirectoryBackingStore( |
266 InternalComponentsFactory::STORAGE_ON_DISK, | 260 InternalComponentsFactory::STORAGE_ON_DISK, |
267 args->credentials.account_id, absolute_db_path); | 261 args->credentials.account_id, absolute_db_path); |
268 | 262 |
269 DCHECK(backing_store.get()); | 263 DCHECK(backing_store.get()); |
270 share_.directory.reset( | 264 share_.directory.reset(new syncable::Directory( |
271 new syncable::Directory( | 265 backing_store.release(), args->unrecoverable_error_handler, |
272 backing_store.release(), | 266 report_unrecoverable_error_function_, sync_encryption_handler_.get(), |
273 args->unrecoverable_error_handler, | 267 sync_encryption_handler_->GetCryptographerUnsafe())); |
274 report_unrecoverable_error_function_, | |
275 sync_encryption_handler_.get(), | |
276 sync_encryption_handler_->GetCryptographerUnsafe())); | |
277 share_.sync_credentials = args->credentials; | 268 share_.sync_credentials = args->credentials; |
278 | 269 |
279 // UserShare is accessible to a lot of code that doesn't need access to the | 270 // UserShare is accessible to a lot of code that doesn't need access to the |
280 // sync token so clear sync_token from the UserShare. | 271 // sync token so clear sync_token from the UserShare. |
281 share_.sync_credentials.sync_token = ""; | 272 share_.sync_credentials.sync_token = ""; |
282 | 273 |
283 DVLOG(1) << "Username: " << args->credentials.email; | 274 DVLOG(1) << "Username: " << args->credentials.email; |
284 DVLOG(1) << "AccountId: " << args->credentials.account_id; | 275 DVLOG(1) << "AccountId: " << args->credentials.account_id; |
285 if (!OpenDirectory(args->credentials.account_id)) { | 276 if (!OpenDirectory(args->credentials.account_id)) { |
286 NotifyInitializationFailure(); | 277 NotifyInitializationFailure(); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 | 382 |
392 void SyncManagerImpl::OnPassphraseTypeChanged( | 383 void SyncManagerImpl::OnPassphraseTypeChanged( |
393 PassphraseType type, | 384 PassphraseType type, |
394 base::Time explicit_passphrase_time) { | 385 base::Time explicit_passphrase_time) { |
395 allstatus_.SetPassphraseType(type); | 386 allstatus_.SetPassphraseType(type); |
396 allstatus_.SetKeystoreMigrationTime( | 387 allstatus_.SetKeystoreMigrationTime( |
397 sync_encryption_handler_->migration_time()); | 388 sync_encryption_handler_->migration_time()); |
398 } | 389 } |
399 | 390 |
400 void SyncManagerImpl::OnLocalSetPassphraseEncryption( | 391 void SyncManagerImpl::OnLocalSetPassphraseEncryption( |
401 const SyncEncryptionHandler::NigoriState& nigori_state) { | 392 const SyncEncryptionHandler::NigoriState& nigori_state) {} |
402 } | |
403 | 393 |
404 void SyncManagerImpl::StartSyncingNormally( | 394 void SyncManagerImpl::StartSyncingNormally( |
405 const ModelSafeRoutingInfo& routing_info, | 395 const ModelSafeRoutingInfo& routing_info, |
406 base::Time last_poll_time) { | 396 base::Time last_poll_time) { |
407 // Start the sync scheduler. | 397 // Start the sync scheduler. |
408 // TODO(sync): We always want the newest set of routes when we switch back | 398 // TODO(sync): We always want the newest set of routes when we switch back |
409 // to normal mode. Figure out how to enforce set_routing_info is always | 399 // to normal mode. Figure out how to enforce set_routing_info is always |
410 // appropriately set and that it's only modified when switching to normal | 400 // appropriately set and that it's only modified when switching to normal |
411 // mode. | 401 // mode. |
412 DCHECK(thread_checker_.CalledOnValidThread()); | 402 DCHECK(thread_checker_.CalledOnValidThread()); |
413 session_context_->SetRoutingInfo(routing_info); | 403 session_context_->SetRoutingInfo(routing_info); |
414 scheduler_->Start(SyncScheduler::NORMAL_MODE, | 404 scheduler_->Start(SyncScheduler::NORMAL_MODE, last_poll_time); |
415 last_poll_time); | |
416 } | 405 } |
417 | 406 |
418 syncable::Directory* SyncManagerImpl::directory() { | 407 syncable::Directory* SyncManagerImpl::directory() { |
419 return share_.directory.get(); | 408 return share_.directory.get(); |
420 } | 409 } |
421 | 410 |
422 const SyncScheduler* SyncManagerImpl::scheduler() const { | 411 const SyncScheduler* SyncManagerImpl::scheduler() const { |
423 return scheduler_.get(); | 412 return scheduler_.get(); |
424 } | 413 } |
425 | 414 |
(...skipping 29 matching lines...) Expand all Loading... |
455 // part of initialization. | 444 // part of initialization. |
456 if (!PurgePartiallySyncedTypes()) | 445 if (!PurgePartiallySyncedTypes()) |
457 return false; | 446 return false; |
458 | 447 |
459 return true; | 448 return true; |
460 } | 449 } |
461 | 450 |
462 bool SyncManagerImpl::PurgePartiallySyncedTypes() { | 451 bool SyncManagerImpl::PurgePartiallySyncedTypes() { |
463 ModelTypeSet partially_synced_types = ModelTypeSet::All(); | 452 ModelTypeSet partially_synced_types = ModelTypeSet::All(); |
464 partially_synced_types.RemoveAll(directory()->InitialSyncEndedTypes()); | 453 partially_synced_types.RemoveAll(directory()->InitialSyncEndedTypes()); |
465 partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken( | 454 partially_synced_types.RemoveAll( |
466 ModelTypeSet::All())); | 455 GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All())); |
467 | 456 |
468 DVLOG(1) << "Purging partially synced types " | 457 DVLOG(1) << "Purging partially synced types " |
469 << ModelTypeSetToString(partially_synced_types); | 458 << ModelTypeSetToString(partially_synced_types); |
470 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes", | 459 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes", |
471 partially_synced_types.Size()); | 460 partially_synced_types.Size()); |
472 if (partially_synced_types.Empty()) | 461 if (partially_synced_types.Empty()) |
473 return true; | 462 return true; |
474 return directory()->PurgeEntriesWithTypeIn(partially_synced_types, | 463 return directory()->PurgeEntriesWithTypeIn(partially_synced_types, |
475 ModelTypeSet(), | 464 ModelTypeSet(), ModelTypeSet()); |
476 ModelTypeSet()); | |
477 } | 465 } |
478 | 466 |
479 bool SyncManagerImpl::PurgeDisabledTypes( | 467 bool SyncManagerImpl::PurgeDisabledTypes(ModelTypeSet to_purge, |
480 ModelTypeSet to_purge, | 468 ModelTypeSet to_journal, |
481 ModelTypeSet to_journal, | 469 ModelTypeSet to_unapply) { |
482 ModelTypeSet to_unapply) { | |
483 if (to_purge.Empty()) | 470 if (to_purge.Empty()) |
484 return true; | 471 return true; |
485 DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge); | 472 DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge); |
486 DCHECK(to_purge.HasAll(to_journal)); | 473 DCHECK(to_purge.HasAll(to_journal)); |
487 DCHECK(to_purge.HasAll(to_unapply)); | 474 DCHECK(to_purge.HasAll(to_unapply)); |
488 return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply); | 475 return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply); |
489 } | 476 } |
490 | 477 |
491 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) { | 478 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) { |
492 DCHECK(thread_checker_.CalledOnValidThread()); | 479 DCHECK(thread_checker_.CalledOnValidThread()); |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 void SyncManagerImpl::OnIPAddressChanged() { | 559 void SyncManagerImpl::OnIPAddressChanged() { |
573 if (!observing_network_connectivity_changes_) { | 560 if (!observing_network_connectivity_changes_) { |
574 DVLOG(1) << "IP address change dropped."; | 561 DVLOG(1) << "IP address change dropped."; |
575 return; | 562 return; |
576 } | 563 } |
577 DVLOG(1) << "IP address change detected."; | 564 DVLOG(1) << "IP address change detected."; |
578 OnNetworkConnectivityChangedImpl(); | 565 OnNetworkConnectivityChangedImpl(); |
579 } | 566 } |
580 | 567 |
581 void SyncManagerImpl::OnConnectionTypeChanged( | 568 void SyncManagerImpl::OnConnectionTypeChanged( |
582 net::NetworkChangeNotifier::ConnectionType) { | 569 net::NetworkChangeNotifier::ConnectionType) { |
583 if (!observing_network_connectivity_changes_) { | 570 if (!observing_network_connectivity_changes_) { |
584 DVLOG(1) << "Connection type change dropped."; | 571 DVLOG(1) << "Connection type change dropped."; |
585 return; | 572 return; |
586 } | 573 } |
587 DVLOG(1) << "Connection type change detected."; | 574 DVLOG(1) << "Connection type change detected."; |
588 OnNetworkConnectivityChangedImpl(); | 575 OnNetworkConnectivityChangedImpl(); |
589 } | 576 } |
590 | 577 |
591 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() { | 578 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() { |
592 DCHECK(thread_checker_.CalledOnValidThread()); | 579 DCHECK(thread_checker_.CalledOnValidThread()); |
593 scheduler_->OnConnectionStatusChange(); | 580 scheduler_->OnConnectionStatusChange(); |
594 } | 581 } |
595 | 582 |
596 void SyncManagerImpl::OnServerConnectionEvent( | 583 void SyncManagerImpl::OnServerConnectionEvent( |
597 const ServerConnectionEvent& event) { | 584 const ServerConnectionEvent& event) { |
598 DCHECK(thread_checker_.CalledOnValidThread()); | 585 DCHECK(thread_checker_.CalledOnValidThread()); |
599 if (event.connection_code == | 586 if (event.connection_code == HttpResponse::SERVER_CONNECTION_OK) { |
600 HttpResponse::SERVER_CONNECTION_OK) { | |
601 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | 587 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
602 OnConnectionStatusChange(CONNECTION_OK)); | 588 OnConnectionStatusChange(CONNECTION_OK)); |
603 } | 589 } |
604 | 590 |
605 if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) { | 591 if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) { |
606 observing_network_connectivity_changes_ = false; | 592 observing_network_connectivity_changes_ = false; |
607 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | 593 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
608 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); | 594 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); |
609 } | 595 } |
610 | 596 |
611 if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) { | 597 if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) { |
612 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | 598 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
613 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); | 599 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); |
614 } | 600 } |
615 } | 601 } |
616 | 602 |
617 void SyncManagerImpl::HandleTransactionCompleteChangeEvent( | 603 void SyncManagerImpl::HandleTransactionCompleteChangeEvent( |
618 ModelTypeSet models_with_changes) { | 604 ModelTypeSet models_with_changes) { |
619 // This notification happens immediately after the transaction mutex is | 605 // This notification happens immediately after the transaction mutex is |
620 // released. This allows work to be performed without blocking other threads | 606 // released. This allows work to be performed without blocking other threads |
621 // from acquiring a transaction. | 607 // from acquiring a transaction. |
622 if (!change_delegate_) | 608 if (!change_delegate_) |
623 return; | 609 return; |
624 | 610 |
625 // Call commit. | 611 // Call commit. |
626 for (ModelTypeSet::Iterator it = models_with_changes.First(); | 612 for (ModelTypeSet::Iterator it = models_with_changes.First(); it.Good(); |
627 it.Good(); it.Inc()) { | 613 it.Inc()) { |
628 change_delegate_->OnChangesComplete(it.Get()); | 614 change_delegate_->OnChangesComplete(it.Get()); |
629 change_observer_.Call( | 615 change_observer_.Call( |
630 FROM_HERE, | 616 FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, it.Get()); |
631 &SyncManager::ChangeObserver::OnChangesComplete, | |
632 it.Get()); | |
633 } | 617 } |
634 } | 618 } |
635 | 619 |
636 ModelTypeSet | 620 ModelTypeSet SyncManagerImpl::HandleTransactionEndingChangeEvent( |
637 SyncManagerImpl::HandleTransactionEndingChangeEvent( | |
638 const ImmutableWriteTransactionInfo& write_transaction_info, | 621 const ImmutableWriteTransactionInfo& write_transaction_info, |
639 syncable::BaseTransaction* trans) { | 622 syncable::BaseTransaction* trans) { |
640 // This notification happens immediately before a syncable WriteTransaction | 623 // This notification happens immediately before a syncable WriteTransaction |
641 // falls out of scope. It happens while the channel mutex is still held, | 624 // falls out of scope. It happens while the channel mutex is still held, |
642 // and while the transaction mutex is held, so it cannot be re-entrant. | 625 // and while the transaction mutex is held, so it cannot be re-entrant. |
643 if (!change_delegate_ || change_records_.empty()) | 626 if (!change_delegate_ || change_records_.empty()) |
644 return ModelTypeSet(); | 627 return ModelTypeSet(); |
645 | 628 |
646 // This will continue the WriteTransaction using a read only wrapper. | 629 // This will continue the WriteTransaction using a read only wrapper. |
647 // This is the last chance for read to occur in the WriteTransaction | 630 // This is the last chance for read to occur in the WriteTransaction |
648 // that's closing. This special ReadTransaction will not close the | 631 // that's closing. This special ReadTransaction will not close the |
649 // underlying transaction. | 632 // underlying transaction. |
650 ReadTransaction read_trans(GetUserShare(), trans); | 633 ReadTransaction read_trans(GetUserShare(), trans); |
651 | 634 |
652 ModelTypeSet models_with_changes; | 635 ModelTypeSet models_with_changes; |
653 for (ChangeRecordMap::const_iterator it = change_records_.begin(); | 636 for (ChangeRecordMap::const_iterator it = change_records_.begin(); |
654 it != change_records_.end(); ++it) { | 637 it != change_records_.end(); ++it) { |
655 DCHECK(!it->second.Get().empty()); | 638 DCHECK(!it->second.Get().empty()); |
656 ModelType type = ModelTypeFromInt(it->first); | 639 ModelType type = ModelTypeFromInt(it->first); |
657 change_delegate_-> | 640 change_delegate_->OnChangesApplied( |
658 OnChangesApplied(type, trans->directory()->GetTransactionVersion(type), | 641 type, trans->directory()->GetTransactionVersion(type), &read_trans, |
659 &read_trans, it->second); | 642 it->second); |
660 change_observer_.Call(FROM_HERE, | 643 change_observer_.Call(FROM_HERE, |
661 &SyncManager::ChangeObserver::OnChangesApplied, | 644 &SyncManager::ChangeObserver::OnChangesApplied, type, |
662 type, write_transaction_info.Get().id, it->second); | 645 write_transaction_info.Get().id, it->second); |
663 models_with_changes.Put(type); | 646 models_with_changes.Put(type); |
664 } | 647 } |
665 change_records_.clear(); | 648 change_records_.clear(); |
666 return models_with_changes; | 649 return models_with_changes; |
667 } | 650 } |
668 | 651 |
669 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi( | 652 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi( |
670 const ImmutableWriteTransactionInfo& write_transaction_info, | 653 const ImmutableWriteTransactionInfo& write_transaction_info, |
671 syncable::BaseTransaction* trans, | 654 syncable::BaseTransaction* trans, |
672 std::vector<int64_t>* entries_changed) { | 655 std::vector<int64_t>* entries_changed) { |
673 // We have been notified about a user action changing a sync model. | 656 // We have been notified about a user action changing a sync model. |
674 LOG_IF(WARNING, !change_records_.empty()) << | 657 LOG_IF(WARNING, !change_records_.empty()) |
675 "CALCULATE_CHANGES called with unapplied old changes."; | 658 << "CALCULATE_CHANGES called with unapplied old changes."; |
676 | 659 |
677 // The mutated model type, or UNSPECIFIED if nothing was mutated. | 660 // The mutated model type, or UNSPECIFIED if nothing was mutated. |
678 ModelTypeSet mutated_model_types; | 661 ModelTypeSet mutated_model_types; |
679 | 662 |
680 const syncable::ImmutableEntryKernelMutationMap& mutations = | 663 const syncable::ImmutableEntryKernelMutationMap& mutations = |
681 write_transaction_info.Get().mutations; | 664 write_transaction_info.Get().mutations; |
682 for (syncable::EntryKernelMutationMap::const_iterator it = | 665 for (syncable::EntryKernelMutationMap::const_iterator it = |
683 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | 666 mutations.Get().begin(); |
| 667 it != mutations.Get().end(); ++it) { |
684 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { | 668 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { |
685 continue; | 669 continue; |
686 } | 670 } |
687 | 671 |
688 ModelType model_type = | 672 ModelType model_type = |
689 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); | 673 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); |
690 if (model_type < FIRST_REAL_MODEL_TYPE) { | 674 if (model_type < FIRST_REAL_MODEL_TYPE) { |
691 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; | 675 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; |
692 continue; | 676 continue; |
693 } | 677 } |
694 | 678 |
695 // Found real mutation. | 679 // Found real mutation. |
696 if (model_type != UNSPECIFIED) { | 680 if (model_type != UNSPECIFIED) { |
697 mutated_model_types.Put(model_type); | 681 mutated_model_types.Put(model_type); |
698 entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE)); | 682 entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE)); |
699 } | 683 } |
700 } | 684 } |
701 | 685 |
702 // Nudge if necessary. | 686 // Nudge if necessary. |
703 if (!mutated_model_types.Empty()) { | 687 if (!mutated_model_types.Empty()) { |
704 if (weak_handle_this_.IsInitialized()) { | 688 if (weak_handle_this_.IsInitialized()) { |
705 weak_handle_this_.Call(FROM_HERE, | 689 weak_handle_this_.Call(FROM_HERE, |
706 &SyncManagerImpl::RequestNudgeForDataTypes, | 690 &SyncManagerImpl::RequestNudgeForDataTypes, |
707 FROM_HERE, | 691 FROM_HERE, mutated_model_types); |
708 mutated_model_types); | |
709 } else { | 692 } else { |
710 NOTREACHED(); | 693 NOTREACHED(); |
711 } | 694 } |
712 } | 695 } |
713 } | 696 } |
714 | 697 |
715 void SyncManagerImpl::SetExtraChangeRecordData( | 698 void SyncManagerImpl::SetExtraChangeRecordData( |
716 int64_t id, | 699 int64_t id, |
717 ModelType type, | 700 ModelType type, |
718 ChangeReorderBuffer* buffer, | 701 ChangeReorderBuffer* buffer, |
(...skipping 26 matching lines...) Expand all Loading... |
745 buffer->SetSpecificsForId(id, original_specifics); | 728 buffer->SetSpecificsForId(id, original_specifics); |
746 } | 729 } |
747 } | 730 } |
748 | 731 |
749 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer( | 732 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer( |
750 const ImmutableWriteTransactionInfo& write_transaction_info, | 733 const ImmutableWriteTransactionInfo& write_transaction_info, |
751 syncable::BaseTransaction* trans, | 734 syncable::BaseTransaction* trans, |
752 std::vector<int64_t>* entries_changed) { | 735 std::vector<int64_t>* entries_changed) { |
753 // We only expect one notification per sync step, so change_buffers_ should | 736 // We only expect one notification per sync step, so change_buffers_ should |
754 // contain no pending entries. | 737 // contain no pending entries. |
755 LOG_IF(WARNING, !change_records_.empty()) << | 738 LOG_IF(WARNING, !change_records_.empty()) |
756 "CALCULATE_CHANGES called with unapplied old changes."; | 739 << "CALCULATE_CHANGES called with unapplied old changes."; |
757 | 740 |
758 ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT]; | 741 ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT]; |
759 | 742 |
760 Cryptographer* crypto = directory()->GetCryptographer(trans); | 743 Cryptographer* crypto = directory()->GetCryptographer(trans); |
761 const syncable::ImmutableEntryKernelMutationMap& mutations = | 744 const syncable::ImmutableEntryKernelMutationMap& mutations = |
762 write_transaction_info.Get().mutations; | 745 write_transaction_info.Get().mutations; |
763 for (syncable::EntryKernelMutationMap::const_iterator it = | 746 for (syncable::EntryKernelMutationMap::const_iterator it = |
764 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | 747 mutations.Get().begin(); |
| 748 it != mutations.Get().end(); ++it) { |
765 bool existed_before = !it->second.original.ref(syncable::IS_DEL); | 749 bool existed_before = !it->second.original.ref(syncable::IS_DEL); |
766 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); | 750 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); |
767 | 751 |
768 // Omit items that aren't associated with a model. | 752 // Omit items that aren't associated with a model. |
769 ModelType type = | 753 ModelType type = |
770 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); | 754 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); |
771 if (type < FIRST_REAL_MODEL_TYPE) | 755 if (type < FIRST_REAL_MODEL_TYPE) |
772 continue; | 756 continue; |
773 | 757 |
774 int64_t handle = it->first; | 758 int64_t handle = it->first; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
837 return; | 821 return; |
838 } | 822 } |
839 | 823 |
840 DVLOG(1) << "Sending OnSyncCycleCompleted"; | 824 DVLOG(1) << "Sending OnSyncCycleCompleted"; |
841 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | 825 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
842 OnSyncCycleCompleted(event.snapshot)); | 826 OnSyncCycleCompleted(event.snapshot)); |
843 } | 827 } |
844 } | 828 } |
845 | 829 |
846 void SyncManagerImpl::OnActionableError(const SyncProtocolError& error) { | 830 void SyncManagerImpl::OnActionableError(const SyncProtocolError& error) { |
847 FOR_EACH_OBSERVER( | 831 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
848 SyncManager::Observer, observers_, | 832 OnActionableError(error)); |
849 OnActionableError(error)); | |
850 } | 833 } |
851 | 834 |
852 void SyncManagerImpl::OnRetryTimeChanged(base::Time) {} | 835 void SyncManagerImpl::OnRetryTimeChanged(base::Time) {} |
853 | 836 |
854 void SyncManagerImpl::OnThrottledTypesChanged(ModelTypeSet) {} | 837 void SyncManagerImpl::OnThrottledTypesChanged(ModelTypeSet) {} |
855 | 838 |
856 void SyncManagerImpl::OnMigrationRequested(ModelTypeSet types) { | 839 void SyncManagerImpl::OnMigrationRequested(ModelTypeSet types) { |
857 FOR_EACH_OBSERVER( | 840 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
858 SyncManager::Observer, observers_, | 841 OnMigrationRequested(types)); |
859 OnMigrationRequested(types)); | |
860 } | 842 } |
861 | 843 |
862 void SyncManagerImpl::OnProtocolEvent(const ProtocolEvent& event) { | 844 void SyncManagerImpl::OnProtocolEvent(const ProtocolEvent& event) { |
863 protocol_event_buffer_.RecordProtocolEvent(event); | 845 protocol_event_buffer_.RecordProtocolEvent(event); |
864 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | 846 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnProtocolEvent(event)); |
865 OnProtocolEvent(event)); | |
866 } | 847 } |
867 | 848 |
868 void SyncManagerImpl::SetJsEventHandler( | 849 void SyncManagerImpl::SetJsEventHandler( |
869 const WeakHandle<JsEventHandler>& event_handler) { | 850 const WeakHandle<JsEventHandler>& event_handler) { |
870 js_sync_manager_observer_.SetJsEventHandler(event_handler); | 851 js_sync_manager_observer_.SetJsEventHandler(event_handler); |
871 js_mutation_event_observer_.SetJsEventHandler(event_handler); | 852 js_mutation_event_observer_.SetJsEventHandler(event_handler); |
872 js_sync_encryption_handler_observer_.SetJsEventHandler(event_handler); | 853 js_sync_encryption_handler_observer_.SetJsEventHandler(event_handler); |
873 } | 854 } |
874 | 855 |
875 std::unique_ptr<base::ListValue> SyncManagerImpl::GetAllNodesForType( | 856 std::unique_ptr<base::ListValue> SyncManagerImpl::GetAllNodesForType( |
(...skipping 29 matching lines...) Expand all Loading... |
905 allstatus_.IncrementNotificationsReceived(); | 886 allstatus_.IncrementNotificationsReceived(); |
906 scheduler_->ScheduleInvalidationNudge(type, std::move(invalidation), | 887 scheduler_->ScheduleInvalidationNudge(type, std::move(invalidation), |
907 FROM_HERE); | 888 FROM_HERE); |
908 } | 889 } |
909 | 890 |
910 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) { | 891 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) { |
911 DCHECK(thread_checker_.CalledOnValidThread()); | 892 DCHECK(thread_checker_.CalledOnValidThread()); |
912 if (types.Empty()) { | 893 if (types.Empty()) { |
913 LOG(WARNING) << "Sync received refresh request with no types specified."; | 894 LOG(WARNING) << "Sync received refresh request with no types specified."; |
914 } else { | 895 } else { |
915 scheduler_->ScheduleLocalRefreshRequest( | 896 scheduler_->ScheduleLocalRefreshRequest(types, FROM_HERE); |
916 types, FROM_HERE); | |
917 } | 897 } |
918 } | 898 } |
919 | 899 |
920 SyncStatus SyncManagerImpl::GetDetailedStatus() const { | 900 SyncStatus SyncManagerImpl::GetDetailedStatus() const { |
921 return allstatus_.status(); | 901 return allstatus_.status(); |
922 } | 902 } |
923 | 903 |
924 void SyncManagerImpl::SaveChanges() { | 904 void SyncManagerImpl::SaveChanges() { |
925 directory()->SaveChanges(); | 905 directory()->SaveChanges(); |
926 } | 906 } |
(...skipping 19 matching lines...) Expand all Loading... |
946 ReadTransaction trans(FROM_HERE, GetUserShare()); | 926 ReadTransaction trans(FROM_HERE, GetUserShare()); |
947 ReadNode nigori_node(&trans); | 927 ReadNode nigori_node(&trans); |
948 if (nigori_node.InitTypeRoot(NIGORI) != BaseNode::INIT_OK) { | 928 if (nigori_node.InitTypeRoot(NIGORI) != BaseNode::INIT_OK) { |
949 DVLOG(1) << "Couldn't find Nigori node."; | 929 DVLOG(1) << "Couldn't find Nigori node."; |
950 return false; | 930 return false; |
951 } | 931 } |
952 bool found_experiment = false; | 932 bool found_experiment = false; |
953 | 933 |
954 ReadNode favicon_sync_node(&trans); | 934 ReadNode favicon_sync_node(&trans); |
955 if (favicon_sync_node.InitByClientTagLookup( | 935 if (favicon_sync_node.InitByClientTagLookup( |
956 syncer::EXPERIMENTS, | 936 syncer::EXPERIMENTS, syncer::kFaviconSyncTag) == BaseNode::INIT_OK) { |
957 syncer::kFaviconSyncTag) == BaseNode::INIT_OK) { | |
958 experiments->favicon_sync_limit = | 937 experiments->favicon_sync_limit = |
959 favicon_sync_node.GetExperimentsSpecifics().favicon_sync(). | 938 favicon_sync_node.GetExperimentsSpecifics() |
960 favicon_sync_limit(); | 939 .favicon_sync() |
| 940 .favicon_sync_limit(); |
961 found_experiment = true; | 941 found_experiment = true; |
962 } | 942 } |
963 | 943 |
964 ReadNode pre_commit_update_avoidance_node(&trans); | 944 ReadNode pre_commit_update_avoidance_node(&trans); |
965 if (pre_commit_update_avoidance_node.InitByClientTagLookup( | 945 if (pre_commit_update_avoidance_node.InitByClientTagLookup( |
966 syncer::EXPERIMENTS, | 946 syncer::EXPERIMENTS, syncer::kPreCommitUpdateAvoidanceTag) == |
967 syncer::kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) { | 947 BaseNode::INIT_OK) { |
968 session_context_->set_server_enabled_pre_commit_update_avoidance( | 948 session_context_->set_server_enabled_pre_commit_update_avoidance( |
969 pre_commit_update_avoidance_node.GetExperimentsSpecifics(). | 949 pre_commit_update_avoidance_node.GetExperimentsSpecifics() |
970 pre_commit_update_avoidance().enabled()); | 950 .pre_commit_update_avoidance() |
| 951 .enabled()); |
971 // We don't bother setting found_experiment. The frontend doesn't need to | 952 // We don't bother setting found_experiment. The frontend doesn't need to |
972 // know about this. | 953 // know about this. |
973 } | 954 } |
974 | 955 |
975 ReadNode gcm_invalidations_node(&trans); | 956 ReadNode gcm_invalidations_node(&trans); |
976 if (gcm_invalidations_node.InitByClientTagLookup( | 957 if (gcm_invalidations_node.InitByClientTagLookup( |
977 syncer::EXPERIMENTS, syncer::kGCMInvalidationsTag) == | 958 syncer::EXPERIMENTS, syncer::kGCMInvalidationsTag) == |
978 BaseNode::INIT_OK) { | 959 BaseNode::INIT_OK) { |
979 const sync_pb::GcmInvalidationsFlags& gcm_invalidations = | 960 const sync_pb::GcmInvalidationsFlags& gcm_invalidations = |
980 gcm_invalidations_node.GetExperimentsSpecifics().gcm_invalidations(); | 961 gcm_invalidations_node.GetExperimentsSpecifics().gcm_invalidations(); |
981 if (gcm_invalidations.has_enabled()) { | 962 if (gcm_invalidations.has_enabled()) { |
982 experiments->gcm_invalidations_enabled = gcm_invalidations.enabled(); | 963 experiments->gcm_invalidations_enabled = gcm_invalidations.enabled(); |
983 found_experiment = true; | 964 found_experiment = true; |
984 } | 965 } |
985 } | 966 } |
986 | 967 |
987 return found_experiment; | 968 return found_experiment; |
988 } | 969 } |
989 | 970 |
990 bool SyncManagerImpl::HasUnsyncedItems() { | 971 bool SyncManagerImpl::HasUnsyncedItems() { |
991 ReadTransaction trans(FROM_HERE, GetUserShare()); | 972 ReadTransaction trans(FROM_HERE, GetUserShare()); |
992 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); | 973 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); |
993 } | 974 } |
994 | 975 |
995 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() { | 976 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() { |
996 return sync_encryption_handler_.get(); | 977 return sync_encryption_handler_.get(); |
997 } | 978 } |
998 | 979 |
999 ScopedVector<syncer::ProtocolEvent> | 980 ScopedVector<syncer::ProtocolEvent> |
1000 SyncManagerImpl::GetBufferedProtocolEvents() { | 981 SyncManagerImpl::GetBufferedProtocolEvents() { |
1001 return protocol_event_buffer_.GetBufferedProtocolEvents(); | 982 return protocol_event_buffer_.GetBufferedProtocolEvents(); |
1002 } | 983 } |
1003 | 984 |
1004 void SyncManagerImpl::RegisterDirectoryTypeDebugInfoObserver( | 985 void SyncManagerImpl::RegisterDirectoryTypeDebugInfoObserver( |
1005 syncer::TypeDebugInfoObserver* observer) { | 986 syncer::TypeDebugInfoObserver* observer) { |
1006 model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(observer); | 987 model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(observer); |
1007 } | 988 } |
1008 | 989 |
1009 void SyncManagerImpl::UnregisterDirectoryTypeDebugInfoObserver( | 990 void SyncManagerImpl::UnregisterDirectoryTypeDebugInfoObserver( |
1010 syncer::TypeDebugInfoObserver* observer) { | 991 syncer::TypeDebugInfoObserver* observer) { |
(...skipping 17 matching lines...) Expand all Loading... |
1028 } | 1009 } |
1029 | 1010 |
1030 void SyncManagerImpl::OnCookieJarChanged(bool account_mismatch, | 1011 void SyncManagerImpl::OnCookieJarChanged(bool account_mismatch, |
1031 bool empty_jar) { | 1012 bool empty_jar) { |
1032 DCHECK(thread_checker_.CalledOnValidThread()); | 1013 DCHECK(thread_checker_.CalledOnValidThread()); |
1033 session_context_->set_cookie_jar_mismatch(account_mismatch); | 1014 session_context_->set_cookie_jar_mismatch(account_mismatch); |
1034 session_context_->set_cookie_jar_empty(empty_jar); | 1015 session_context_->set_cookie_jar_empty(empty_jar); |
1035 } | 1016 } |
1036 | 1017 |
1037 } // namespace syncer | 1018 } // namespace syncer |
OLD | NEW |