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