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. 4 5 #include "sync/internal_api/sync_manager_impl.h" 6 7 #include <string> 8 9 #include "base/base64.h" 10 #include "base/bind.h" 11 #include "base/callback.h" 12 #include "base/compiler_specific.h" 13 #include "base/json/json_writer.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/metrics/histogram.h" 16 #include "base/observer_list.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/values.h" 19 #include "sync/engine/sync_scheduler.h" 20 #include "sync/engine/syncer_types.h" 21 #include "sync/internal_api/change_reorder_buffer.h" 22 #include "sync/internal_api/public/base/cancelation_signal.h" 23 #include "sync/internal_api/public/base/model_type.h" 24 #include "sync/internal_api/public/base_node.h" 25 #include "sync/internal_api/public/configure_reason.h" 26 #include "sync/internal_api/public/engine/polling_constants.h" 27 #include "sync/internal_api/public/http_post_provider_factory.h" 28 #include "sync/internal_api/public/internal_components_factory.h" 29 #include "sync/internal_api/public/read_node.h" 30 #include "sync/internal_api/public/read_transaction.h" 31 #include "sync/internal_api/public/user_share.h" 32 #include "sync/internal_api/public/util/experiments.h" 33 #include "sync/internal_api/public/write_node.h" 34 #include "sync/internal_api/public/write_transaction.h" 35 #include "sync/internal_api/syncapi_internal.h" 36 #include "sync/internal_api/syncapi_server_connection_manager.h" 37 #include "sync/js/js_arg_list.h" 38 #include "sync/js/js_event_details.h" 39 #include "sync/js/js_event_handler.h" 40 #include "sync/js/js_reply_handler.h" 41 #include "sync/notifier/invalidation_util.h" 42 #include "sync/notifier/invalidator.h" 43 #include "sync/notifier/object_id_invalidation_map.h" 44 #include "sync/protocol/proto_value_conversions.h" 45 #include "sync/protocol/sync.pb.h" 46 #include "sync/syncable/directory.h" 47 #include "sync/syncable/entry.h" 48 #include "sync/syncable/in_memory_directory_backing_store.h" 49 #include "sync/syncable/on_disk_directory_backing_store.h" 50 51 using base::TimeDelta; 52 using sync_pb::GetUpdatesCallerInfo; 53 54 namespace syncer { 55 56 using sessions::SyncSessionContext; 57 using syncable::ImmutableWriteTransactionInfo; 58 using syncable::SPECIFICS; 59 using syncable::UNIQUE_POSITION; 60 61 namespace { 62 63 // Delays for syncer nudges. 64 static const int kDefaultNudgeDelayMilliseconds = 200; 65 static const int kPreferencesNudgeDelayMilliseconds = 2000; 66 static const int kSyncRefreshDelayMsec = 500; 67 static const int kSyncSchedulerDelayMsec = 250; 68 69 // Maximum count and size for traffic recorder. 70 static const unsigned int kMaxMessagesToRecord = 10; 71 static const unsigned int kMaxMessageSizeToRecord = 5 * 1024; 72 73 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( 74 ConfigureReason reason) { 75 switch (reason) { 76 case CONFIGURE_REASON_RECONFIGURATION: 77 return GetUpdatesCallerInfo::RECONFIGURATION; 78 case CONFIGURE_REASON_MIGRATION: 79 return GetUpdatesCallerInfo::MIGRATION; 80 case CONFIGURE_REASON_NEW_CLIENT: 81 return GetUpdatesCallerInfo::NEW_CLIENT; 82 case CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE: 83 case CONFIGURE_REASON_CRYPTO: 84 return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE; 85 default: 86 NOTREACHED(); 87 } 88 return GetUpdatesCallerInfo::UNKNOWN; 89 } 90 91 } // namespace 92 93 // A class to calculate nudge delays for types. 94 class NudgeStrategy { 95 public: 96 static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type, 97 SyncManagerImpl* core) { 98 NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type); 99 return GetNudgeDelayTimeDeltaFromType(delay_type, 100 model_type, 101 core); 102 } 103 104 private: 105 // Possible types of nudge delay for datatypes. 106 // Note: These are just hints. If a sync happens then all dirty entries 107 // would be committed as part of the sync. 108 enum NudgeDelayStrategy { 109 // Sync right away. 110 IMMEDIATE, 111 112 // Sync this change while syncing another change. 113 ACCOMPANY_ONLY, 114 115 // The datatype does not use one of the predefined wait times but defines 116 // its own wait time logic for nudge. 117 CUSTOM, 118 }; 119 120 static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) { 121 switch (type) { 122 case AUTOFILL: 123 return ACCOMPANY_ONLY; 124 case PREFERENCES: 125 case SESSIONS: 126 case FAVICON_IMAGES: 127 case FAVICON_TRACKING: 128 return CUSTOM; 129 default: 130 return IMMEDIATE; 131 } 132 } 133 134 static TimeDelta GetNudgeDelayTimeDeltaFromType( 135 const NudgeDelayStrategy& delay_type, const ModelType& model_type, 136 const SyncManagerImpl* core) { 137 CHECK(core); 138 TimeDelta delay = TimeDelta::FromMilliseconds( 139 kDefaultNudgeDelayMilliseconds); 140 switch (delay_type) { 141 case IMMEDIATE: 142 delay = TimeDelta::FromMilliseconds( 143 kDefaultNudgeDelayMilliseconds); 144 break; 145 case ACCOMPANY_ONLY: 146 delay = TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds); 147 break; 148 case CUSTOM: 149 switch (model_type) { 150 case PREFERENCES: 151 delay = TimeDelta::FromMilliseconds( 152 kPreferencesNudgeDelayMilliseconds); 153 break; 154 case SESSIONS: 155 case FAVICON_IMAGES: 156 case FAVICON_TRACKING: 157 delay = core->scheduler()->GetSessionsCommitDelay(); 158 break; 159 default: 160 NOTREACHED(); 161 } 162 break; 163 default: 164 NOTREACHED(); 165 } 166 return delay; 167 } 168 }; 169 170 SyncManagerImpl::SyncManagerImpl(const std::string& name) 171 : name_(name), 172 change_delegate_(NULL), 173 initialized_(false), 174 observing_network_connectivity_changes_(false), 175 invalidator_state_(DEFAULT_INVALIDATION_ERROR), 176 traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord), 177 encryptor_(NULL), 178 report_unrecoverable_error_function_(NULL), 179 weak_ptr_factory_(this) { 180 // Pre-fill |notification_info_map_|. 181 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { 182 notification_info_map_.insert( 183 std::make_pair(ModelTypeFromInt(i), NotificationInfo())); 184 } 185 186 // Bind message handlers. 187 BindJsMessageHandler( 188 "getNotificationState", 189 &SyncManagerImpl::GetNotificationState); 190 BindJsMessageHandler( 191 "getNotificationInfo", 192 &SyncManagerImpl::GetNotificationInfo); 193 BindJsMessageHandler( 194 "getRootNodeDetails", 195 &SyncManagerImpl::GetRootNodeDetails); 196 BindJsMessageHandler( 197 "getNodeSummariesById", 198 &SyncManagerImpl::GetNodeSummariesById); 199 BindJsMessageHandler( 200 "getNodeDetailsById", 201 &SyncManagerImpl::GetNodeDetailsById); 202 BindJsMessageHandler( 203 "getAllNodes", 204 &SyncManagerImpl::GetAllNodes); 205 BindJsMessageHandler( 206 "getChildNodeIds", 207 &SyncManagerImpl::GetChildNodeIds); 208 BindJsMessageHandler( 209 "getClientServerTraffic", 210 &SyncManagerImpl::GetClientServerTraffic); 211 } 212 213 SyncManagerImpl::~SyncManagerImpl() { 214 DCHECK(thread_checker_.CalledOnValidThread()); 215 CHECK(!initialized_); 216 } 217 218 SyncManagerImpl::NotificationInfo::NotificationInfo() : total_count(0) {} 219 SyncManagerImpl::NotificationInfo::~NotificationInfo() {} 220 221 base::DictionaryValue* SyncManagerImpl::NotificationInfo::ToValue() const { 222 base::DictionaryValue* value = new base::DictionaryValue(); 223 value->SetInteger("totalCount", total_count); 224 value->SetString("payload", payload); 225 return value; 226 } 227 228 bool SyncManagerImpl::VisiblePositionsDiffer( 229 const syncable::EntryKernelMutation& mutation) const { 230 const syncable::EntryKernel& a = mutation.original; 231 const syncable::EntryKernel& b = mutation.mutated; 232 if (!b.ShouldMaintainPosition()) 233 return false; 234 if (!a.ref(UNIQUE_POSITION).Equals(b.ref(UNIQUE_POSITION))) 235 return true; 236 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) 237 return true; 238 return false; 239 } 240 241 bool SyncManagerImpl::VisiblePropertiesDiffer( 242 const syncable::EntryKernelMutation& mutation, 243 Cryptographer* cryptographer) const { 244 const syncable::EntryKernel& a = mutation.original; 245 const syncable::EntryKernel& b = mutation.mutated; 246 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); 247 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); 248 DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics), 249 GetModelTypeFromSpecifics(b_specifics)); 250 ModelType model_type = GetModelTypeFromSpecifics(b_specifics); 251 // Suppress updates to items that aren't tracked by any browser model. 252 if (model_type < FIRST_REAL_MODEL_TYPE || 253 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { 254 return false; 255 } 256 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) 257 return true; 258 if (!AreSpecificsEqual(cryptographer, 259 a.ref(syncable::SPECIFICS), 260 b.ref(syncable::SPECIFICS))) { 261 return true; 262 } 263 // We only care if the name has changed if neither specifics is encrypted 264 // (encrypted nodes blow away the NON_UNIQUE_NAME). 265 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && 266 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) 267 return true; 268 if (VisiblePositionsDiffer(mutation)) 269 return true; 270 return false; 271 } 272 273 void SyncManagerImpl::ThrowUnrecoverableError() { 274 DCHECK(thread_checker_.CalledOnValidThread()); 275 ReadTransaction trans(FROM_HERE, GetUserShare()); 276 trans.GetWrappedTrans()->OnUnrecoverableError( 277 FROM_HERE, "Simulating unrecoverable error for testing purposes."); 278 } 279 280 ModelTypeSet SyncManagerImpl::InitialSyncEndedTypes() { 281 return directory()->InitialSyncEndedTypes(); 282 } 283 284 ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken( 285 ModelTypeSet types) { 286 ModelTypeSet result; 287 for (ModelTypeSet::Iterator i = types.First(); i.Good(); i.Inc()) { 288 sync_pb::DataTypeProgressMarker marker; 289 directory()->GetDownloadProgress(i.Get(), &marker); 290 291 if (marker.token().empty()) 292 result.Put(i.Get()); 293 } 294 return result; 295 } 296 297 void SyncManagerImpl::ConfigureSyncer( 298 ConfigureReason reason, 299 ModelTypeSet to_download, 300 ModelTypeSet to_purge, 301 ModelTypeSet to_journal, 302 ModelTypeSet to_unapply, 303 const ModelSafeRoutingInfo& new_routing_info, 304 const base::Closure& ready_task, 305 const base::Closure& retry_task) { 306 DCHECK(thread_checker_.CalledOnValidThread()); 307 DCHECK(!ready_task.is_null()); 308 DCHECK(!retry_task.is_null()); 309 310 DVLOG(1) << "Configuring -" 311 << "\n\t" << "current types: " 312 << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info)) 313 << "\n\t" << "types to download: " 314 << ModelTypeSetToString(to_download) 315 << "\n\t" << "types to purge: " 316 << ModelTypeSetToString(to_purge) 317 << "\n\t" << "types to journal: " 318 << ModelTypeSetToString(to_journal) 319 << "\n\t" << "types to unapply: " 320 << ModelTypeSetToString(to_unapply); 321 if (!PurgeDisabledTypes(to_purge, 322 to_journal, 323 to_unapply)) { 324 // We failed to cleanup the types. Invoke the ready task without actually 325 // configuring any types. The caller should detect this as a configuration 326 // failure and act appropriately. 327 ready_task.Run(); 328 return; 329 } 330 331 ConfigurationParams params(GetSourceFromReason(reason), 332 to_download, 333 new_routing_info, 334 ready_task, 335 retry_task); 336 337 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE); 338 scheduler_->ScheduleConfiguration(params); 339 } 340 341 void SyncManagerImpl::Init( 342 const base::FilePath& database_location, 343 const WeakHandle<JsEventHandler>& event_handler, 344 const std::string& sync_server_and_path, 345 int port, 346 bool use_ssl, 347 scoped_ptr<HttpPostProviderFactory> post_factory, 348 const std::vector<ModelSafeWorker*>& workers, 349 ExtensionsActivity* extensions_activity, 350 SyncManager::ChangeDelegate* change_delegate, 351 const SyncCredentials& credentials, 352 const std::string& invalidator_client_id, 353 const std::string& restored_key_for_bootstrapping, 354 const std::string& restored_keystore_key_for_bootstrapping, 355 InternalComponentsFactory* internal_components_factory, 356 Encryptor* encryptor, 357 scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, 358 ReportUnrecoverableErrorFunction report_unrecoverable_error_function, 359 CancelationSignal* cancelation_signal) { 360 CHECK(!initialized_); 361 DCHECK(thread_checker_.CalledOnValidThread()); 362 DCHECK(post_factory.get()); 363 DCHECK(!credentials.email.empty()); 364 DCHECK(!credentials.sync_token.empty()); 365 DCHECK(cancelation_signal); 366 DVLOG(1) << "SyncManager starting Init..."; 367 368 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); 369 370 change_delegate_ = change_delegate; 371 372 AddObserver(&js_sync_manager_observer_); 373 SetJsEventHandler(event_handler); 374 375 AddObserver(&debug_info_event_listener_); 376 377 database_path_ = database_location.Append( 378 syncable::Directory::kSyncDatabaseFilename); 379 encryptor_ = encryptor; 380 unrecoverable_error_handler_ = unrecoverable_error_handler.Pass(); 381 report_unrecoverable_error_function_ = report_unrecoverable_error_function; 382 383 allstatus_.SetHasKeystoreKey( 384 !restored_keystore_key_for_bootstrapping.empty()); 385 sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl( 386 &share_, 387 encryptor, 388 restored_key_for_bootstrapping, 389 restored_keystore_key_for_bootstrapping)); 390 sync_encryption_handler_->AddObserver(this); 391 sync_encryption_handler_->AddObserver(&debug_info_event_listener_); 392 sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_); 393 394 base::FilePath absolute_db_path = database_path_; 395 DCHECK(absolute_db_path.IsAbsolute()); 396 397 scoped_ptr<syncable::DirectoryBackingStore> backing_store = 398 internal_components_factory->BuildDirectoryBackingStore( 399 credentials.email, absolute_db_path).Pass(); 400 401 DCHECK(backing_store.get()); 402 const std::string& username = credentials.email; 403 share_.directory.reset( 404 new syncable::Directory( 405 backing_store.release(), 406 unrecoverable_error_handler_.get(), 407 report_unrecoverable_error_function_, 408 sync_encryption_handler_.get(), 409 sync_encryption_handler_->GetCryptographerUnsafe())); 410 411 DVLOG(1) << "Username: " << username; 412 if (!OpenDirectory(username)) { 413 NotifyInitializationFailure(); 414 LOG(ERROR) << "Sync manager initialization failed!"; 415 return; 416 } 417 418 connection_manager_.reset(new SyncAPIServerConnectionManager( 419 sync_server_and_path, port, use_ssl, 420 post_factory.release(), cancelation_signal)); 421 connection_manager_->set_client_id(directory()->cache_guid()); 422 connection_manager_->AddListener(this); 423 424 std::string sync_id = directory()->cache_guid(); 425 426 allstatus_.SetSyncId(sync_id); 427 allstatus_.SetInvalidatorClientId(invalidator_client_id); 428 429 DVLOG(1) << "Setting sync client ID: " << sync_id; 430 DVLOG(1) << "Setting invalidator client ID: " << invalidator_client_id; 431 432 // Build a SyncSessionContext and store the worker in it. 433 DVLOG(1) << "Sync is bringing up SyncSessionContext."; 434 std::vector<SyncEngineEventListener*> listeners; 435 listeners.push_back(&allstatus_); 436 listeners.push_back(this); 437 session_context_ = internal_components_factory->BuildContext( 438 connection_manager_.get(), 439 directory(), 440 workers, 441 extensions_activity, 442 listeners, 443 &debug_info_event_listener_, 444 &traffic_recorder_, 445 invalidator_client_id).Pass(); 446 session_context_->set_account_name(credentials.email); 447 scheduler_ = internal_components_factory->BuildScheduler( 448 name_, session_context_.get(), cancelation_signal).Pass(); 449 450 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE); 451 452 initialized_ = true; 453 454 net::NetworkChangeNotifier::AddIPAddressObserver(this); 455 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); 456 observing_network_connectivity_changes_ = true; 457 458 UpdateCredentials(credentials); 459 460 NotifyInitializationSuccess(); 461 } 462 463 void SyncManagerImpl::NotifyInitializationSuccess() { 464 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 465 OnInitializationComplete( 466 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), 467 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()), 468 true, InitialSyncEndedTypes())); 469 } 470 471 void SyncManagerImpl::NotifyInitializationFailure() { 472 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 473 OnInitializationComplete( 474 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), 475 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()), 476 false, ModelTypeSet())); 477 } 478 479 void SyncManagerImpl::OnPassphraseRequired( 480 PassphraseRequiredReason reason, 481 const sync_pb::EncryptedData& pending_keys) { 482 // Does nothing. 483 } 484 485 void SyncManagerImpl::OnPassphraseAccepted() { 486 // Does nothing. 487 } 488 489 void SyncManagerImpl::OnBootstrapTokenUpdated( 490 const std::string& bootstrap_token, 491 BootstrapTokenType type) { 492 if (type == KEYSTORE_BOOTSTRAP_TOKEN) 493 allstatus_.SetHasKeystoreKey(true); 494 } 495 496 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types, 497 bool encrypt_everything) { 498 allstatus_.SetEncryptedTypes(encrypted_types); 499 } 500 501 void SyncManagerImpl::OnEncryptionComplete() { 502 // Does nothing. 503 } 504 505 void SyncManagerImpl::OnCryptographerStateChanged( 506 Cryptographer* cryptographer) { 507 allstatus_.SetCryptographerReady(cryptographer->is_ready()); 508 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); 509 allstatus_.SetKeystoreMigrationTime( 510 sync_encryption_handler_->migration_time()); 511 } 512 513 void SyncManagerImpl::OnPassphraseTypeChanged( 514 PassphraseType type, 515 base::Time explicit_passphrase_time) { 516 allstatus_.SetPassphraseType(type); 517 allstatus_.SetKeystoreMigrationTime( 518 sync_encryption_handler_->migration_time()); 519 } 520 521 void SyncManagerImpl::StartSyncingNormally( 522 const ModelSafeRoutingInfo& routing_info) { 523 // Start the sync scheduler. 524 // TODO(sync): We always want the newest set of routes when we switch back 525 // to normal mode. Figure out how to enforce set_routing_info is always 526 // appropriately set and that it's only modified when switching to normal 527 // mode. 528 DCHECK(thread_checker_.CalledOnValidThread()); 529 session_context_->set_routing_info(routing_info); 530 scheduler_->Start(SyncScheduler::NORMAL_MODE); 531 } 532 533 syncable::Directory* SyncManagerImpl::directory() { 534 return share_.directory.get(); 535 } 536 537 const SyncScheduler* SyncManagerImpl::scheduler() const { 538 return scheduler_.get(); 539 } 540 541 bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const { 542 return connection_manager_->HasInvalidAuthToken(); 543 } 544 545 bool SyncManagerImpl::OpenDirectory(const std::string& username) { 546 DCHECK(!initialized_) << "Should only happen once"; 547 548 // Set before Open(). 549 change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()); 550 WeakHandle<syncable::TransactionObserver> transaction_observer( 551 MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr())); 552 553 syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED; 554 open_result = directory()->Open(username, this, transaction_observer); 555 if (open_result != syncable::OPENED) { 556 LOG(ERROR) << "Could not open share for:" << username; 557 return false; 558 } 559 560 // Unapplied datatypes (those that do not have initial sync ended set) get 561 // re-downloaded during any configuration. But, it's possible for a datatype 562 // to have a progress marker but not have initial sync ended yet, making 563 // it a candidate for migration. This is a problem, as the DataTypeManager 564 // does not support a migration while it's already in the middle of a 565 // configuration. As a result, any partially synced datatype can stall the 566 // DTM, waiting for the configuration to complete, which it never will due 567 // to the migration error. In addition, a partially synced nigori will 568 // trigger the migration logic before the backend is initialized, resulting 569 // in crashes. We therefore detect and purge any partially synced types as 570 // part of initialization. 571 if (!PurgePartiallySyncedTypes()) 572 return false; 573 574 return true; 575 } 576 577 bool SyncManagerImpl::PurgePartiallySyncedTypes() { 578 ModelTypeSet partially_synced_types = ModelTypeSet::All(); 579 partially_synced_types.RemoveAll(InitialSyncEndedTypes()); 580 partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken( 581 ModelTypeSet::All())); 582 583 DVLOG(1) << "Purging partially synced types " 584 << ModelTypeSetToString(partially_synced_types); 585 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes", 586 partially_synced_types.Size()); 587 if (partially_synced_types.Empty()) 588 return true; 589 return directory()->PurgeEntriesWithTypeIn(partially_synced_types, 590 ModelTypeSet(), 591 ModelTypeSet()); 592 } 593 594 bool SyncManagerImpl::PurgeDisabledTypes( 595 ModelTypeSet to_purge, 596 ModelTypeSet to_journal, 597 ModelTypeSet to_unapply) { 598 if (to_purge.Empty()) 599 return true; 600 DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge); 601 DCHECK(to_purge.HasAll(to_journal)); 602 DCHECK(to_purge.HasAll(to_unapply)); 603 return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply); 604 } 605 606 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) { 607 DCHECK(thread_checker_.CalledOnValidThread()); 608 DCHECK(initialized_); 609 DCHECK(!credentials.email.empty()); 610 DCHECK(!credentials.sync_token.empty()); 611 612 observing_network_connectivity_changes_ = true; 613 if (!connection_manager_->SetAuthToken(credentials.sync_token)) 614 return; // Auth token is known to be invalid, so exit early. 615 616 scheduler_->OnCredentialsUpdated(); 617 618 // TODO(zea): pass the credential age to the debug info event listener. 619 } 620 621 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) { 622 DCHECK(thread_checker_.CalledOnValidThread()); 623 observers_.AddObserver(observer); 624 } 625 626 void SyncManagerImpl::RemoveObserver(SyncManager::Observer* observer) { 627 DCHECK(thread_checker_.CalledOnValidThread()); 628 observers_.RemoveObserver(observer); 629 } 630 631 void SyncManagerImpl::ShutdownOnSyncThread() { 632 DCHECK(thread_checker_.CalledOnValidThread()); 633 634 // Prevent any in-flight method calls from running. Also 635 // invalidates |weak_handle_this_| and |change_observer_|. 636 weak_ptr_factory_.InvalidateWeakPtrs(); 637 js_mutation_event_observer_.InvalidateWeakPtrs(); 638 639 scheduler_.reset(); 640 session_context_.reset(); 641 642 if (sync_encryption_handler_) { 643 sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_); 644 sync_encryption_handler_->RemoveObserver(this); 645 } 646 647 SetJsEventHandler(WeakHandle<JsEventHandler>()); 648 RemoveObserver(&js_sync_manager_observer_); 649 650 RemoveObserver(&debug_info_event_listener_); 651 652 // |connection_manager_| may end up being NULL here in tests (in synchronous 653 // initialization mode). 654 // 655 // TODO(akalin): Fix this behavior. 656 if (connection_manager_) 657 connection_manager_->RemoveListener(this); 658 connection_manager_.reset(); 659 660 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 661 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 662 observing_network_connectivity_changes_ = false; 663 664 if (initialized_ && directory()) { 665 directory()->SaveChanges(); 666 } 667 668 share_.directory.reset(); 669 670 change_delegate_ = NULL; 671 672 initialized_ = false; 673 674 // We reset these here, since only now we know they will not be 675 // accessed from other threads (since we shut down everything). 676 change_observer_.Reset(); 677 weak_handle_this_.Reset(); 678 } 679 680 void SyncManagerImpl::OnIPAddressChanged() { 681 if (!observing_network_connectivity_changes_) { 682 DVLOG(1) << "IP address change dropped."; 683 return; 684 } 685 DVLOG(1) << "IP address change detected."; 686 OnNetworkConnectivityChangedImpl(); 687 } 688 689 void SyncManagerImpl::OnConnectionTypeChanged( 690 net::NetworkChangeNotifier::ConnectionType) { 691 if (!observing_network_connectivity_changes_) { 692 DVLOG(1) << "Connection type change dropped."; 693 return; 694 } 695 DVLOG(1) << "Connection type change detected."; 696 OnNetworkConnectivityChangedImpl(); 697 } 698 699 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() { 700 DCHECK(thread_checker_.CalledOnValidThread()); 701 scheduler_->OnConnectionStatusChange(); 702 } 703 704 void SyncManagerImpl::OnServerConnectionEvent( 705 const ServerConnectionEvent& event) { 706 DCHECK(thread_checker_.CalledOnValidThread()); 707 if (event.connection_code == 708 HttpResponse::SERVER_CONNECTION_OK) { 709 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 710 OnConnectionStatusChange(CONNECTION_OK)); 711 } 712 713 if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) { 714 observing_network_connectivity_changes_ = false; 715 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 716 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); 717 } 718 719 if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) { 720 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 721 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); 722 } 723 } 724 725 void SyncManagerImpl::HandleTransactionCompleteChangeEvent( 726 ModelTypeSet models_with_changes) { 727 // This notification happens immediately after the transaction mutex is 728 // released. This allows work to be performed without blocking other threads 729 // from acquiring a transaction. 730 if (!change_delegate_) 731 return; 732 733 // Call commit. 734 for (ModelTypeSet::Iterator it = models_with_changes.First(); 735 it.Good(); it.Inc()) { 736 change_delegate_->OnChangesComplete(it.Get()); 737 change_observer_.Call( 738 FROM_HERE, 739 &SyncManager::ChangeObserver::OnChangesComplete, 740 it.Get()); 741 } 742 } 743 744 ModelTypeSet 745 SyncManagerImpl::HandleTransactionEndingChangeEvent( 746 const ImmutableWriteTransactionInfo& write_transaction_info, 747 syncable::BaseTransaction* trans) { 748 // This notification happens immediately before a syncable WriteTransaction 749 // falls out of scope. It happens while the channel mutex is still held, 750 // and while the transaction mutex is held, so it cannot be re-entrant. 751 if (!change_delegate_ || change_records_.empty()) 752 return ModelTypeSet(); 753 754 // This will continue the WriteTransaction using a read only wrapper. 755 // This is the last chance for read to occur in the WriteTransaction 756 // that's closing. This special ReadTransaction will not close the 757 // underlying transaction. 758 ReadTransaction read_trans(GetUserShare(), trans); 759 760 ModelTypeSet models_with_changes; 761 for (ChangeRecordMap::const_iterator it = change_records_.begin(); 762 it != change_records_.end(); ++it) { 763 DCHECK(!it->second.Get().empty()); 764 ModelType type = ModelTypeFromInt(it->first); 765 change_delegate_-> 766 OnChangesApplied(type, trans->directory()->GetTransactionVersion(type), 767 &read_trans, it->second); 768 change_observer_.Call(FROM_HERE, 769 &SyncManager::ChangeObserver::OnChangesApplied, 770 type, write_transaction_info.Get().id, it->second); 771 models_with_changes.Put(type); 772 } 773 change_records_.clear(); 774 return models_with_changes; 775 } 776 777 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi( 778 const ImmutableWriteTransactionInfo& write_transaction_info, 779 syncable::BaseTransaction* trans, 780 std::vector<int64>* entries_changed) { 781 // We have been notified about a user action changing a sync model. 782 LOG_IF(WARNING, !change_records_.empty()) << 783 "CALCULATE_CHANGES called with unapplied old changes."; 784 785 // The mutated model type, or UNSPECIFIED if nothing was mutated. 786 ModelTypeSet mutated_model_types; 787 788 const syncable::ImmutableEntryKernelMutationMap& mutations = 789 write_transaction_info.Get().mutations; 790 for (syncable::EntryKernelMutationMap::const_iterator it = 791 mutations.Get().begin(); it != mutations.Get().end(); ++it) { 792 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { 793 continue; 794 } 795 796 ModelType model_type = 797 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); 798 if (model_type < FIRST_REAL_MODEL_TYPE) { 799 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; 800 continue; 801 } 802 803 // Found real mutation. 804 if (model_type != UNSPECIFIED) { 805 mutated_model_types.Put(model_type); 806 entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE)); 807 } 808 } 809 810 // Nudge if necessary. 811 if (!mutated_model_types.Empty()) { 812 if (weak_handle_this_.IsInitialized()) { 813 weak_handle_this_.Call(FROM_HERE, 814 &SyncManagerImpl::RequestNudgeForDataTypes, 815 FROM_HERE, 816 mutated_model_types); 817 } else { 818 NOTREACHED(); 819 } 820 } 821 } 822 823 void SyncManagerImpl::SetExtraChangeRecordData(int64 id, 824 ModelType type, ChangeReorderBuffer* buffer, 825 Cryptographer* cryptographer, const syncable::EntryKernel& original, 826 bool existed_before, bool exists_now) { 827 // If this is a deletion and the datatype was encrypted, we need to decrypt it 828 // and attach it to the buffer. 829 if (!exists_now && existed_before) { 830 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); 831 if (type == PASSWORDS) { 832 // Passwords must use their own legacy ExtraPasswordChangeRecordData. 833 scoped_ptr<sync_pb::PasswordSpecificsData> data( 834 DecryptPasswordSpecifics(original_specifics, cryptographer)); 835 if (!data) { 836 NOTREACHED(); 837 return; 838 } 839 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); 840 } else if (original_specifics.has_encrypted()) { 841 // All other datatypes can just create a new unencrypted specifics and 842 // attach it. 843 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); 844 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { 845 NOTREACHED(); 846 return; 847 } 848 } 849 buffer->SetSpecificsForId(id, original_specifics); 850 } 851 } 852 853 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer( 854 const ImmutableWriteTransactionInfo& write_transaction_info, 855 syncable::BaseTransaction* trans, 856 std::vector<int64>* entries_changed) { 857 // We only expect one notification per sync step, so change_buffers_ should 858 // contain no pending entries. 859 LOG_IF(WARNING, !change_records_.empty()) << 860 "CALCULATE_CHANGES called with unapplied old changes."; 861 862 ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT]; 863 864 Cryptographer* crypto = directory()->GetCryptographer(trans); 865 const syncable::ImmutableEntryKernelMutationMap& mutations = 866 write_transaction_info.Get().mutations; 867 for (syncable::EntryKernelMutationMap::const_iterator it = 868 mutations.Get().begin(); it != mutations.Get().end(); ++it) { 869 bool existed_before = !it->second.original.ref(syncable::IS_DEL); 870 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); 871 872 // Omit items that aren't associated with a model. 873 ModelType type = 874 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); 875 if (type < FIRST_REAL_MODEL_TYPE) 876 continue; 877 878 int64 handle = it->first; 879 if (exists_now && !existed_before) 880 change_buffers[type].PushAddedItem(handle); 881 else if (!exists_now && existed_before) 882 change_buffers[type].PushDeletedItem(handle); 883 else if (exists_now && existed_before && 884 VisiblePropertiesDiffer(it->second, crypto)) { 885 change_buffers[type].PushUpdatedItem(handle); 886 } 887 888 SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto, 889 it->second.original, existed_before, exists_now); 890 } 891 892 ReadTransaction read_trans(GetUserShare(), trans); 893 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { 894 if (!change_buffers[i].IsEmpty()) { 895 if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans, 896 &(change_records_[i]))) { 897 for (size_t j = 0; j < change_records_[i].Get().size(); ++j) 898 entries_changed->push_back((change_records_[i].Get())[j].id); 899 } 900 if (change_records_[i].Get().empty()) 901 change_records_.erase(i); 902 } 903 } 904 } 905 906 TimeDelta SyncManagerImpl::GetNudgeDelayTimeDelta( 907 const ModelType& model_type) { 908 return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this); 909 } 910 911 void SyncManagerImpl::RequestNudgeForDataTypes( 912 const tracked_objects::Location& nudge_location, 913 ModelTypeSet types) { 914 debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get()); 915 916 // TODO(lipalani) : Calculate the nudge delay based on all types. 917 base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta( 918 types.First().Get(), 919 this); 920 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL); 921 scheduler_->ScheduleLocalNudge(nudge_delay, 922 types, 923 nudge_location); 924 } 925 926 void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) { 927 DCHECK(thread_checker_.CalledOnValidThread()); 928 // Only send an event if this is due to a cycle ending and this cycle 929 // concludes a canonical "sync" process; that is, based on what is known 930 // locally we are "all happy" and up-to-date. There may be new changes on 931 // the server, but we'll get them on a subsequent sync. 932 // 933 // Notifications are sent at the end of every sync cycle, regardless of 934 // whether we should sync again. 935 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { 936 if (!initialized_) { 937 DVLOG(1) << "OnSyncCycleCompleted not sent because sync api is not " 938 << "initialized"; 939 return; 940 } 941 942 DVLOG(1) << "Sending OnSyncCycleCompleted"; 943 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 944 OnSyncCycleCompleted(event.snapshot)); 945 } 946 947 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { 948 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 949 OnStopSyncingPermanently()); 950 return; 951 } 952 953 if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) { 954 FOR_EACH_OBSERVER( 955 SyncManager::Observer, observers_, 956 OnActionableError( 957 event.snapshot.model_neutral_state().sync_protocol_error)); 958 return; 959 } 960 } 961 962 void SyncManagerImpl::SetJsEventHandler( 963 const WeakHandle<JsEventHandler>& event_handler) { 964 js_event_handler_ = event_handler; 965 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_); 966 js_mutation_event_observer_.SetJsEventHandler(js_event_handler_); 967 js_sync_encryption_handler_observer_.SetJsEventHandler(js_event_handler_); 968 } 969 970 void SyncManagerImpl::ProcessJsMessage( 971 const std::string& name, const JsArgList& args, 972 const WeakHandle<JsReplyHandler>& reply_handler) { 973 if (!initialized_) { 974 NOTREACHED(); 975 return; 976 } 977 978 if (!reply_handler.IsInitialized()) { 979 DVLOG(1) << "Uninitialized reply handler; dropping unknown message " 980 << name << " with args " << args.ToString(); 981 return; 982 } 983 984 JsMessageHandler js_message_handler = js_message_handlers_[name]; 985 if (js_message_handler.is_null()) { 986 DVLOG(1) << "Dropping unknown message " << name 987 << " with args " << args.ToString(); 988 return; 989 } 990 991 reply_handler.Call(FROM_HERE, 992 &JsReplyHandler::HandleJsReply, 993 name, js_message_handler.Run(args)); 994 } 995 996 void SyncManagerImpl::BindJsMessageHandler( 997 const std::string& name, 998 UnboundJsMessageHandler unbound_message_handler) { 999 js_message_handlers_[name] = 1000 base::Bind(unbound_message_handler, base::Unretained(this)); 1001 } 1002 1003 base::DictionaryValue* SyncManagerImpl::NotificationInfoToValue( 1004 const NotificationInfoMap& notification_info) { 1005 base::DictionaryValue* value = new base::DictionaryValue(); 1006 1007 for (NotificationInfoMap::const_iterator it = notification_info.begin(); 1008 it != notification_info.end(); ++it) { 1009 const std::string model_type_str = ModelTypeToString(it->first); 1010 value->Set(model_type_str, it->second.ToValue()); 1011 } 1012 1013 return value; 1014 } 1015 1016 std::string SyncManagerImpl::NotificationInfoToString( 1017 const NotificationInfoMap& notification_info) { 1018 scoped_ptr<base::DictionaryValue> value( 1019 NotificationInfoToValue(notification_info)); 1020 std::string str; 1021 base::JSONWriter::Write(value.get(), &str); 1022 return str; 1023 } 1024 1025 JsArgList SyncManagerImpl::GetNotificationState( 1026 const JsArgList& args) { 1027 const std::string& notification_state = 1028 InvalidatorStateToString(invalidator_state_); 1029 DVLOG(1) << "GetNotificationState: " << notification_state; 1030 base::ListValue return_args; 1031 return_args.Append(new base::StringValue(notification_state)); 1032 return JsArgList(&return_args); 1033 } 1034 1035 JsArgList SyncManagerImpl::GetNotificationInfo( 1036 const JsArgList& args) { 1037 DVLOG(1) << "GetNotificationInfo: " 1038 << NotificationInfoToString(notification_info_map_); 1039 base::ListValue return_args; 1040 return_args.Append(NotificationInfoToValue(notification_info_map_)); 1041 return JsArgList(&return_args); 1042 } 1043 1044 JsArgList SyncManagerImpl::GetRootNodeDetails( 1045 const JsArgList& args) { 1046 ReadTransaction trans(FROM_HERE, GetUserShare()); 1047 ReadNode root(&trans); 1048 root.InitByRootLookup(); 1049 base::ListValue return_args; 1050 return_args.Append(root.GetDetailsAsValue()); 1051 return JsArgList(&return_args); 1052 } 1053 1054 JsArgList SyncManagerImpl::GetClientServerTraffic( 1055 const JsArgList& args) { 1056 base::ListValue return_args; 1057 base::ListValue* value = traffic_recorder_.ToValue(); 1058 if (value != NULL) 1059 return_args.Append(value); 1060 return JsArgList(&return_args); 1061 } 1062 1063 namespace { 1064 1065 int64 GetId(const base::ListValue& ids, int i) { 1066 std::string id_str; 1067 if (!ids.GetString(i, &id_str)) { 1068 return kInvalidId; 1069 } 1070 int64 id = kInvalidId; 1071 if (!base::StringToInt64(id_str, &id)) { 1072 return kInvalidId; 1073 } 1074 return id; 1075 } 1076 1077 JsArgList GetNodeInfoById( 1078 const JsArgList& args, 1079 UserShare* user_share, 1080 base::DictionaryValue* (BaseNode::*info_getter)() const) { 1081 CHECK(info_getter); 1082 base::ListValue return_args; 1083 base::ListValue* node_summaries = new base::ListValue(); 1084 return_args.Append(node_summaries); 1085 const base::ListValue* id_list = NULL; 1086 ReadTransaction trans(FROM_HERE, user_share); 1087 if (args.Get().GetList(0, &id_list)) { 1088 CHECK(id_list); 1089 for (size_t i = 0; i < id_list->GetSize(); ++i) { 1090 int64 id = GetId(*id_list, i); 1091 if (id == kInvalidId) { 1092 continue; 1093 } 1094 ReadNode node(&trans); 1095 if (node.InitByIdLookup(id) != BaseNode::INIT_OK) { 1096 continue; 1097 } 1098 node_summaries->Append((node.*info_getter)()); 1099 } 1100 } 1101 return JsArgList(&return_args); 1102 } 1103 1104 } // namespace 1105 1106 JsArgList SyncManagerImpl::GetNodeSummariesById(const JsArgList& args) { 1107 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue); 1108 } 1109 1110 JsArgList SyncManagerImpl::GetNodeDetailsById(const JsArgList& args) { 1111 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue); 1112 } 1113 1114 JsArgList SyncManagerImpl::GetAllNodes(const JsArgList& args) { 1115 base::ListValue return_args; 1116 base::ListValue* result = new base::ListValue(); 1117 return_args.Append(result); 1118 1119 ReadTransaction trans(FROM_HERE, GetUserShare()); 1120 std::vector<const syncable::EntryKernel*> entry_kernels; 1121 trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(), 1122 &entry_kernels); 1123 1124 for (std::vector<const syncable::EntryKernel*>::const_iterator it = 1125 entry_kernels.begin(); it != entry_kernels.end(); ++it) { 1126 result->Append((*it)->ToValue(trans.GetCryptographer())); 1127 } 1128 1129 return JsArgList(&return_args); 1130 } 1131 1132 JsArgList SyncManagerImpl::GetChildNodeIds(const JsArgList& args) { 1133 base::ListValue return_args; 1134 base::ListValue* child_ids = new base::ListValue(); 1135 return_args.Append(child_ids); 1136 int64 id = GetId(args.Get(), 0); 1137 if (id != kInvalidId) { 1138 ReadTransaction trans(FROM_HERE, GetUserShare()); 1139 syncable::Directory::Metahandles child_handles; 1140 trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(), 1141 id, &child_handles); 1142 for (syncable::Directory::Metahandles::const_iterator it = 1143 child_handles.begin(); it != child_handles.end(); ++it) { 1144 child_ids->Append(new base::StringValue(base::Int64ToString(*it))); 1145 } 1146 } 1147 return JsArgList(&return_args); 1148 } 1149 1150 void SyncManagerImpl::UpdateNotificationInfo( 1151 const ObjectIdInvalidationMap& invalidation_map) { 1152 ObjectIdSet ids = invalidation_map.GetObjectIds(); 1153 for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) { 1154 ModelType type = UNSPECIFIED; 1155 if (!ObjectIdToRealModelType(*it, &type)) { 1156 continue; 1157 } 1158 const SingleObjectInvalidationSet& type_invalidations = 1159 invalidation_map.ForObject(*it); 1160 for (SingleObjectInvalidationSet::const_iterator inv_it = 1161 type_invalidations.begin(); inv_it != type_invalidations.end(); 1162 ++inv_it) { 1163 NotificationInfo* info = ¬ification_info_map_[type]; 1164 info->total_count++; 1165 std::string payload = 1166 inv_it->is_unknown_version() ? "UNKNOWN" : inv_it->payload(); 1167 info->payload = payload; 1168 } 1169 } 1170 } 1171 1172 void SyncManagerImpl::OnInvalidatorStateChange(InvalidatorState state) { 1173 DCHECK(thread_checker_.CalledOnValidThread()); 1174 1175 const std::string& state_str = InvalidatorStateToString(state); 1176 invalidator_state_ = state; 1177 DVLOG(1) << "Invalidator state changed to: " << state_str; 1178 const bool notifications_enabled = 1179 (invalidator_state_ == INVALIDATIONS_ENABLED); 1180 allstatus_.SetNotificationsEnabled(notifications_enabled); 1181 scheduler_->SetNotificationsEnabled(notifications_enabled); 1182 1183 if (js_event_handler_.IsInitialized()) { 1184 base::DictionaryValue details; 1185 details.SetString("state", state_str); 1186 js_event_handler_.Call(FROM_HERE, 1187 &JsEventHandler::HandleJsEvent, 1188 "onNotificationStateChange", 1189 JsEventDetails(&details)); 1190 } 1191 } 1192 1193 void SyncManagerImpl::OnIncomingInvalidation( 1194 const ObjectIdInvalidationMap& invalidation_map) { 1195 DCHECK(thread_checker_.CalledOnValidThread()); 1196 1197 // We should never receive IDs from non-sync objects. 1198 ObjectIdSet ids = invalidation_map.GetObjectIds(); 1199 for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) { 1200 ModelType type; 1201 if (!ObjectIdToRealModelType(*it, &type)) { 1202 DLOG(WARNING) << "Notification has invalid id: " << ObjectIdToString(*it); 1203 } 1204 } 1205 1206 if (invalidation_map.Empty()) { 1207 LOG(WARNING) << "Sync received invalidation without any type information."; 1208 } else { 1209 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_NOTIFICATION); 1210 scheduler_->ScheduleInvalidationNudge( 1211 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), 1212 invalidation_map, FROM_HERE); 1213 allstatus_.IncrementNotificationsReceived(); 1214 UpdateNotificationInfo(invalidation_map); 1215 debug_info_event_listener_.OnIncomingNotification(invalidation_map); 1216 } 1217 1218 if (js_event_handler_.IsInitialized()) { 1219 base::DictionaryValue details; 1220 base::ListValue* changed_types = new base::ListValue(); 1221 details.Set("changedTypes", changed_types); 1222 1223 ObjectIdSet id_set = invalidation_map.GetObjectIds(); 1224 ModelTypeSet nudged_types = ObjectIdSetToModelTypeSet(id_set); 1225 DCHECK(!nudged_types.Empty()); 1226 for (ModelTypeSet::Iterator it = nudged_types.First(); 1227 it.Good(); it.Inc()) { 1228 const std::string model_type_str = ModelTypeToString(it.Get()); 1229 changed_types->Append(new base::StringValue(model_type_str)); 1230 } 1231 details.SetString("source", "REMOTE_INVALIDATION"); 1232 js_event_handler_.Call(FROM_HERE, 1233 &JsEventHandler::HandleJsEvent, 1234 "onIncomingNotification", 1235 JsEventDetails(&details)); 1236 } 1237 } 1238 1239 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) { 1240 DCHECK(thread_checker_.CalledOnValidThread()); 1241 if (types.Empty()) { 1242 LOG(WARNING) << "Sync received refresh request with no types specified."; 1243 } else { 1244 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL_REFRESH); 1245 scheduler_->ScheduleLocalRefreshRequest( 1246 TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec), 1247 types, FROM_HERE); 1248 } 1249 1250 if (js_event_handler_.IsInitialized()) { 1251 base::DictionaryValue details; 1252 base::ListValue* changed_types = new base::ListValue(); 1253 details.Set("changedTypes", changed_types); 1254 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { 1255 const std::string& model_type_str = 1256 ModelTypeToString(it.Get()); 1257 changed_types->Append(new base::StringValue(model_type_str)); 1258 } 1259 details.SetString("source", "LOCAL_INVALIDATION"); 1260 js_event_handler_.Call(FROM_HERE, 1261 &JsEventHandler::HandleJsEvent, 1262 "onIncomingNotification", 1263 JsEventDetails(&details)); 1264 } 1265 } 1266 1267 SyncStatus SyncManagerImpl::GetDetailedStatus() const { 1268 return allstatus_.status(); 1269 } 1270 1271 void SyncManagerImpl::SaveChanges() { 1272 directory()->SaveChanges(); 1273 } 1274 1275 UserShare* SyncManagerImpl::GetUserShare() { 1276 DCHECK(initialized_); 1277 return &share_; 1278 } 1279 1280 const std::string SyncManagerImpl::cache_guid() { 1281 DCHECK(initialized_); 1282 return directory()->cache_guid(); 1283 } 1284 1285 bool SyncManagerImpl::ReceivedExperiment(Experiments* experiments) { 1286 ReadTransaction trans(FROM_HERE, GetUserShare()); 1287 ReadNode nigori_node(&trans); 1288 if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) { 1289 DVLOG(1) << "Couldn't find Nigori node."; 1290 return false; 1291 } 1292 bool found_experiment = false; 1293 1294 ReadNode autofill_culling_node(&trans); 1295 if (autofill_culling_node.InitByClientTagLookup( 1296 syncer::EXPERIMENTS, 1297 syncer::kAutofillCullingTag) == BaseNode::INIT_OK && 1298 autofill_culling_node.GetExperimentsSpecifics(). 1299 autofill_culling().enabled()) { 1300 experiments->autofill_culling = true; 1301 found_experiment = true; 1302 } 1303 1304 ReadNode favicon_sync_node(&trans); 1305 if (favicon_sync_node.InitByClientTagLookup( 1306 syncer::EXPERIMENTS, 1307 syncer::kFaviconSyncTag) == BaseNode::INIT_OK) { 1308 experiments->favicon_sync_limit = 1309 favicon_sync_node.GetExperimentsSpecifics().favicon_sync(). 1310 favicon_sync_limit(); 1311 found_experiment = true; 1312 } 1313 1314 ReadNode pre_commit_update_avoidance_node(&trans); 1315 if (pre_commit_update_avoidance_node.InitByClientTagLookup( 1316 syncer::EXPERIMENTS, 1317 syncer::kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) { 1318 session_context_->set_server_enabled_pre_commit_update_avoidance( 1319 pre_commit_update_avoidance_node.GetExperimentsSpecifics(). 1320 pre_commit_update_avoidance().enabled()); 1321 // We don't bother setting found_experiment. The frontend doesn't need to 1322 // know about this. 1323 } 1324 1325 return found_experiment; 1326 } 1327 1328 bool SyncManagerImpl::HasUnsyncedItems() { 1329 ReadTransaction trans(FROM_HERE, GetUserShare()); 1330 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); 1331 } 1332 1333 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() { 1334 return sync_encryption_handler_.get(); 1335 } 1336 1337 // static. 1338 int SyncManagerImpl::GetDefaultNudgeDelay() { 1339 return kDefaultNudgeDelayMilliseconds; 1340 } 1341 1342 // static. 1343 int SyncManagerImpl::GetPreferencesNudgeDelay() { 1344 return kPreferencesNudgeDelayMilliseconds; 1345 } 1346 1347 } // namespace syncer 1348