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/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 = &notification_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