Home | History | Annotate | Download | only in internal_api
      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 = &notification_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