- add sources.
[platform/framework/web/crosswalk.git] / src / sync / internal_api / sync_manager_impl.cc
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/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       change_delegate_(NULL),
172       initialized_(false),
173       observing_network_connectivity_changes_(false),
174       invalidator_state_(DEFAULT_INVALIDATION_ERROR),
175       traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord),
176       encryptor_(NULL),
177       report_unrecoverable_error_function_(NULL),
178       weak_ptr_factory_(this) {
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     CancelationSignal* cancelation_signal) {
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   DCHECK(cancelation_signal);
365   DVLOG(1) << "SyncManager starting Init...";
366
367   weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
368
369   change_delegate_ = change_delegate;
370
371   AddObserver(&js_sync_manager_observer_);
372   SetJsEventHandler(event_handler);
373
374   AddObserver(&debug_info_event_listener_);
375
376   database_path_ = database_location.Append(
377       syncable::Directory::kSyncDatabaseFilename);
378   encryptor_ = encryptor;
379   unrecoverable_error_handler_ = unrecoverable_error_handler.Pass();
380   report_unrecoverable_error_function_ = report_unrecoverable_error_function;
381
382   allstatus_.SetHasKeystoreKey(
383       !restored_keystore_key_for_bootstrapping.empty());
384   sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl(
385       &share_,
386       encryptor,
387       restored_key_for_bootstrapping,
388       restored_keystore_key_for_bootstrapping));
389   sync_encryption_handler_->AddObserver(this);
390   sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
391   sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
392
393   base::FilePath absolute_db_path = database_path_;
394   DCHECK(absolute_db_path.IsAbsolute());
395
396   scoped_ptr<syncable::DirectoryBackingStore> backing_store =
397       internal_components_factory->BuildDirectoryBackingStore(
398           credentials.email, absolute_db_path).Pass();
399
400   DCHECK(backing_store.get());
401   const std::string& username = credentials.email;
402   share_.directory.reset(
403       new syncable::Directory(
404           backing_store.release(),
405           unrecoverable_error_handler_.get(),
406           report_unrecoverable_error_function_,
407           sync_encryption_handler_.get(),
408           sync_encryption_handler_->GetCryptographerUnsafe()));
409
410   DVLOG(1) << "Username: " << username;
411   if (!OpenDirectory(username)) {
412     NotifyInitializationFailure();
413     LOG(ERROR) << "Sync manager initialization failed!";
414     return;
415   }
416
417   connection_manager_.reset(new SyncAPIServerConnectionManager(
418       sync_server_and_path, port, use_ssl,
419       post_factory.release(), cancelation_signal));
420   connection_manager_->set_client_id(directory()->cache_guid());
421   connection_manager_->AddListener(this);
422
423   std::string sync_id = directory()->cache_guid();
424
425   allstatus_.SetSyncId(sync_id);
426   allstatus_.SetInvalidatorClientId(invalidator_client_id);
427
428   DVLOG(1) << "Setting sync client ID: " << sync_id;
429   DVLOG(1) << "Setting invalidator client ID: " << invalidator_client_id;
430
431   // Build a SyncSessionContext and store the worker in it.
432   DVLOG(1) << "Sync is bringing up SyncSessionContext.";
433   std::vector<SyncEngineEventListener*> listeners;
434   listeners.push_back(&allstatus_);
435   listeners.push_back(this);
436   session_context_ = internal_components_factory->BuildContext(
437       connection_manager_.get(),
438       directory(),
439       workers,
440       extensions_activity,
441       listeners,
442       &debug_info_event_listener_,
443       &traffic_recorder_,
444       invalidator_client_id).Pass();
445   session_context_->set_account_name(credentials.email);
446   scheduler_ = internal_components_factory->BuildScheduler(
447       name_, session_context_.get(), cancelation_signal).Pass();
448
449   scheduler_->Start(SyncScheduler::CONFIGURATION_MODE);
450
451   initialized_ = true;
452
453   net::NetworkChangeNotifier::AddIPAddressObserver(this);
454   net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
455   observing_network_connectivity_changes_ = true;
456
457   UpdateCredentials(credentials);
458
459   NotifyInitializationSuccess();
460 }
461
462 void SyncManagerImpl::NotifyInitializationSuccess() {
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::NotifyInitializationFailure() {
471   FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
472                     OnInitializationComplete(
473                         MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()),
474                         MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()),
475                         false, ModelTypeSet()));
476 }
477
478 void SyncManagerImpl::OnPassphraseRequired(
479     PassphraseRequiredReason reason,
480     const sync_pb::EncryptedData& pending_keys) {
481   // Does nothing.
482 }
483
484 void SyncManagerImpl::OnPassphraseAccepted() {
485   // Does nothing.
486 }
487
488 void SyncManagerImpl::OnBootstrapTokenUpdated(
489     const std::string& bootstrap_token,
490     BootstrapTokenType type) {
491   if (type == KEYSTORE_BOOTSTRAP_TOKEN)
492     allstatus_.SetHasKeystoreKey(true);
493 }
494
495 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
496                                               bool encrypt_everything) {
497   allstatus_.SetEncryptedTypes(encrypted_types);
498 }
499
500 void SyncManagerImpl::OnEncryptionComplete() {
501   // Does nothing.
502 }
503
504 void SyncManagerImpl::OnCryptographerStateChanged(
505     Cryptographer* cryptographer) {
506   allstatus_.SetCryptographerReady(cryptographer->is_ready());
507   allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
508   allstatus_.SetKeystoreMigrationTime(
509       sync_encryption_handler_->migration_time());
510 }
511
512 void SyncManagerImpl::OnPassphraseTypeChanged(
513     PassphraseType type,
514     base::Time explicit_passphrase_time) {
515   allstatus_.SetPassphraseType(type);
516   allstatus_.SetKeystoreMigrationTime(
517       sync_encryption_handler_->migration_time());
518 }
519
520 void SyncManagerImpl::StartSyncingNormally(
521     const ModelSafeRoutingInfo& routing_info) {
522   // Start the sync scheduler.
523   // TODO(sync): We always want the newest set of routes when we switch back
524   // to normal mode. Figure out how to enforce set_routing_info is always
525   // appropriately set and that it's only modified when switching to normal
526   // mode.
527   DCHECK(thread_checker_.CalledOnValidThread());
528   session_context_->set_routing_info(routing_info);
529   scheduler_->Start(SyncScheduler::NORMAL_MODE);
530 }
531
532 syncable::Directory* SyncManagerImpl::directory() {
533   return share_.directory.get();
534 }
535
536 const SyncScheduler* SyncManagerImpl::scheduler() const {
537   return scheduler_.get();
538 }
539
540 bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const {
541   return connection_manager_->HasInvalidAuthToken();
542 }
543
544 bool SyncManagerImpl::OpenDirectory(const std::string& username) {
545   DCHECK(!initialized_) << "Should only happen once";
546
547   // Set before Open().
548   change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr());
549   WeakHandle<syncable::TransactionObserver> transaction_observer(
550       MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()));
551
552   syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED;
553   open_result = directory()->Open(username, this, transaction_observer);
554   if (open_result != syncable::OPENED) {
555     LOG(ERROR) << "Could not open share for:" << username;
556     return false;
557   }
558
559   // Unapplied datatypes (those that do not have initial sync ended set) get
560   // re-downloaded during any configuration. But, it's possible for a datatype
561   // to have a progress marker but not have initial sync ended yet, making
562   // it a candidate for migration. This is a problem, as the DataTypeManager
563   // does not support a migration while it's already in the middle of a
564   // configuration. As a result, any partially synced datatype can stall the
565   // DTM, waiting for the configuration to complete, which it never will due
566   // to the migration error. In addition, a partially synced nigori will
567   // trigger the migration logic before the backend is initialized, resulting
568   // in crashes. We therefore detect and purge any partially synced types as
569   // part of initialization.
570   if (!PurgePartiallySyncedTypes())
571     return false;
572
573   return true;
574 }
575
576 bool SyncManagerImpl::PurgePartiallySyncedTypes() {
577   ModelTypeSet partially_synced_types = ModelTypeSet::All();
578   partially_synced_types.RemoveAll(InitialSyncEndedTypes());
579   partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken(
580       ModelTypeSet::All()));
581
582   DVLOG(1) << "Purging partially synced types "
583            << ModelTypeSetToString(partially_synced_types);
584   UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes",
585                        partially_synced_types.Size());
586   if (partially_synced_types.Empty())
587     return true;
588   return directory()->PurgeEntriesWithTypeIn(partially_synced_types,
589                                              ModelTypeSet(),
590                                              ModelTypeSet());
591 }
592
593 bool SyncManagerImpl::PurgeDisabledTypes(
594     ModelTypeSet to_purge,
595     ModelTypeSet to_journal,
596     ModelTypeSet to_unapply) {
597   if (to_purge.Empty())
598     return true;
599   DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge);
600   DCHECK(to_purge.HasAll(to_journal));
601   DCHECK(to_purge.HasAll(to_unapply));
602   return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply);
603 }
604
605 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) {
606   DCHECK(thread_checker_.CalledOnValidThread());
607   DCHECK(initialized_);
608   DCHECK(!credentials.email.empty());
609   DCHECK(!credentials.sync_token.empty());
610
611   observing_network_connectivity_changes_ = true;
612   if (!connection_manager_->SetAuthToken(credentials.sync_token))
613     return;  // Auth token is known to be invalid, so exit early.
614
615   scheduler_->OnCredentialsUpdated();
616
617   // TODO(zea): pass the credential age to the debug info event listener.
618 }
619
620 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) {
621   DCHECK(thread_checker_.CalledOnValidThread());
622   observers_.AddObserver(observer);
623 }
624
625 void SyncManagerImpl::RemoveObserver(SyncManager::Observer* observer) {
626   DCHECK(thread_checker_.CalledOnValidThread());
627   observers_.RemoveObserver(observer);
628 }
629
630 void SyncManagerImpl::ShutdownOnSyncThread() {
631   DCHECK(thread_checker_.CalledOnValidThread());
632
633   // Prevent any in-flight method calls from running.  Also
634   // invalidates |weak_handle_this_| and |change_observer_|.
635   weak_ptr_factory_.InvalidateWeakPtrs();
636   js_mutation_event_observer_.InvalidateWeakPtrs();
637
638   scheduler_.reset();
639   session_context_.reset();
640
641   if (sync_encryption_handler_) {
642     sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_);
643     sync_encryption_handler_->RemoveObserver(this);
644   }
645
646   SetJsEventHandler(WeakHandle<JsEventHandler>());
647   RemoveObserver(&js_sync_manager_observer_);
648
649   RemoveObserver(&debug_info_event_listener_);
650
651   // |connection_manager_| may end up being NULL here in tests (in synchronous
652   // initialization mode).
653   //
654   // TODO(akalin): Fix this behavior.
655   if (connection_manager_)
656     connection_manager_->RemoveListener(this);
657   connection_manager_.reset();
658
659   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
660   net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
661   observing_network_connectivity_changes_ = false;
662
663   if (initialized_ && directory()) {
664     directory()->SaveChanges();
665   }
666
667   share_.directory.reset();
668
669   change_delegate_ = NULL;
670
671   initialized_ = false;
672
673   // We reset these here, since only now we know they will not be
674   // accessed from other threads (since we shut down everything).
675   change_observer_.Reset();
676   weak_handle_this_.Reset();
677 }
678
679 void SyncManagerImpl::OnIPAddressChanged() {
680   if (!observing_network_connectivity_changes_) {
681     DVLOG(1) << "IP address change dropped.";
682     return;
683   }
684   DVLOG(1) << "IP address change detected.";
685   OnNetworkConnectivityChangedImpl();
686 }
687
688 void SyncManagerImpl::OnConnectionTypeChanged(
689   net::NetworkChangeNotifier::ConnectionType) {
690   if (!observing_network_connectivity_changes_) {
691     DVLOG(1) << "Connection type change dropped.";
692     return;
693   }
694   DVLOG(1) << "Connection type change detected.";
695   OnNetworkConnectivityChangedImpl();
696 }
697
698 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() {
699   DCHECK(thread_checker_.CalledOnValidThread());
700   scheduler_->OnConnectionStatusChange();
701 }
702
703 void SyncManagerImpl::OnServerConnectionEvent(
704     const ServerConnectionEvent& event) {
705   DCHECK(thread_checker_.CalledOnValidThread());
706   if (event.connection_code ==
707       HttpResponse::SERVER_CONNECTION_OK) {
708     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
709                       OnConnectionStatusChange(CONNECTION_OK));
710   }
711
712   if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) {
713     observing_network_connectivity_changes_ = false;
714     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
715                       OnConnectionStatusChange(CONNECTION_AUTH_ERROR));
716   }
717
718   if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) {
719     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
720                       OnConnectionStatusChange(CONNECTION_SERVER_ERROR));
721   }
722 }
723
724 void SyncManagerImpl::HandleTransactionCompleteChangeEvent(
725     ModelTypeSet models_with_changes) {
726   // This notification happens immediately after the transaction mutex is
727   // released. This allows work to be performed without blocking other threads
728   // from acquiring a transaction.
729   if (!change_delegate_)
730     return;
731
732   // Call commit.
733   for (ModelTypeSet::Iterator it = models_with_changes.First();
734        it.Good(); it.Inc()) {
735     change_delegate_->OnChangesComplete(it.Get());
736     change_observer_.Call(
737         FROM_HERE,
738         &SyncManager::ChangeObserver::OnChangesComplete,
739         it.Get());
740   }
741 }
742
743 ModelTypeSet
744 SyncManagerImpl::HandleTransactionEndingChangeEvent(
745     const ImmutableWriteTransactionInfo& write_transaction_info,
746     syncable::BaseTransaction* trans) {
747   // This notification happens immediately before a syncable WriteTransaction
748   // falls out of scope. It happens while the channel mutex is still held,
749   // and while the transaction mutex is held, so it cannot be re-entrant.
750   if (!change_delegate_ || change_records_.empty())
751     return ModelTypeSet();
752
753   // This will continue the WriteTransaction using a read only wrapper.
754   // This is the last chance for read to occur in the WriteTransaction
755   // that's closing. This special ReadTransaction will not close the
756   // underlying transaction.
757   ReadTransaction read_trans(GetUserShare(), trans);
758
759   ModelTypeSet models_with_changes;
760   for (ChangeRecordMap::const_iterator it = change_records_.begin();
761       it != change_records_.end(); ++it) {
762     DCHECK(!it->second.Get().empty());
763     ModelType type = ModelTypeFromInt(it->first);
764     change_delegate_->
765         OnChangesApplied(type, trans->directory()->GetTransactionVersion(type),
766                          &read_trans, it->second);
767     change_observer_.Call(FROM_HERE,
768         &SyncManager::ChangeObserver::OnChangesApplied,
769         type, write_transaction_info.Get().id, it->second);
770     models_with_changes.Put(type);
771   }
772   change_records_.clear();
773   return models_with_changes;
774 }
775
776 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi(
777     const ImmutableWriteTransactionInfo& write_transaction_info,
778     syncable::BaseTransaction* trans,
779     std::vector<int64>* entries_changed) {
780   // We have been notified about a user action changing a sync model.
781   LOG_IF(WARNING, !change_records_.empty()) <<
782       "CALCULATE_CHANGES called with unapplied old changes.";
783
784   // The mutated model type, or UNSPECIFIED if nothing was mutated.
785   ModelTypeSet mutated_model_types;
786
787   const syncable::ImmutableEntryKernelMutationMap& mutations =
788       write_transaction_info.Get().mutations;
789   for (syncable::EntryKernelMutationMap::const_iterator it =
790            mutations.Get().begin(); it != mutations.Get().end(); ++it) {
791     if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) {
792       continue;
793     }
794
795     ModelType model_type =
796         GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
797     if (model_type < FIRST_REAL_MODEL_TYPE) {
798       NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
799       continue;
800     }
801
802     // Found real mutation.
803     if (model_type != UNSPECIFIED) {
804       mutated_model_types.Put(model_type);
805       entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE));
806     }
807   }
808
809   // Nudge if necessary.
810   if (!mutated_model_types.Empty()) {
811     if (weak_handle_this_.IsInitialized()) {
812       weak_handle_this_.Call(FROM_HERE,
813                              &SyncManagerImpl::RequestNudgeForDataTypes,
814                              FROM_HERE,
815                              mutated_model_types);
816     } else {
817       NOTREACHED();
818     }
819   }
820 }
821
822 void SyncManagerImpl::SetExtraChangeRecordData(int64 id,
823     ModelType type, ChangeReorderBuffer* buffer,
824     Cryptographer* cryptographer, const syncable::EntryKernel& original,
825     bool existed_before, bool exists_now) {
826   // If this is a deletion and the datatype was encrypted, we need to decrypt it
827   // and attach it to the buffer.
828   if (!exists_now && existed_before) {
829     sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
830     if (type == PASSWORDS) {
831       // Passwords must use their own legacy ExtraPasswordChangeRecordData.
832       scoped_ptr<sync_pb::PasswordSpecificsData> data(
833           DecryptPasswordSpecifics(original_specifics, cryptographer));
834       if (!data) {
835         NOTREACHED();
836         return;
837       }
838       buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
839     } else if (original_specifics.has_encrypted()) {
840       // All other datatypes can just create a new unencrypted specifics and
841       // attach it.
842       const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
843       if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
844         NOTREACHED();
845         return;
846       }
847     }
848     buffer->SetSpecificsForId(id, original_specifics);
849   }
850 }
851
852 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer(
853     const ImmutableWriteTransactionInfo& write_transaction_info,
854     syncable::BaseTransaction* trans,
855     std::vector<int64>* entries_changed) {
856   // We only expect one notification per sync step, so change_buffers_ should
857   // contain no pending entries.
858   LOG_IF(WARNING, !change_records_.empty()) <<
859       "CALCULATE_CHANGES called with unapplied old changes.";
860
861   ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT];
862
863   Cryptographer* crypto = directory()->GetCryptographer(trans);
864   const syncable::ImmutableEntryKernelMutationMap& mutations =
865       write_transaction_info.Get().mutations;
866   for (syncable::EntryKernelMutationMap::const_iterator it =
867            mutations.Get().begin(); it != mutations.Get().end(); ++it) {
868     bool existed_before = !it->second.original.ref(syncable::IS_DEL);
869     bool exists_now = !it->second.mutated.ref(syncable::IS_DEL);
870
871     // Omit items that aren't associated with a model.
872     ModelType type =
873         GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
874     if (type < FIRST_REAL_MODEL_TYPE)
875       continue;
876
877     int64 handle = it->first;
878     if (exists_now && !existed_before)
879       change_buffers[type].PushAddedItem(handle);
880     else if (!exists_now && existed_before)
881       change_buffers[type].PushDeletedItem(handle);
882     else if (exists_now && existed_before &&
883              VisiblePropertiesDiffer(it->second, crypto)) {
884       change_buffers[type].PushUpdatedItem(handle);
885     }
886
887     SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto,
888                              it->second.original, existed_before, exists_now);
889   }
890
891   ReadTransaction read_trans(GetUserShare(), trans);
892   for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
893     if (!change_buffers[i].IsEmpty()) {
894       if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans,
895                                                      &(change_records_[i]))) {
896         for (size_t j = 0; j < change_records_[i].Get().size(); ++j)
897           entries_changed->push_back((change_records_[i].Get())[j].id);
898       }
899       if (change_records_[i].Get().empty())
900         change_records_.erase(i);
901     }
902   }
903 }
904
905 TimeDelta SyncManagerImpl::GetNudgeDelayTimeDelta(
906     const ModelType& model_type) {
907   return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this);
908 }
909
910 void SyncManagerImpl::RequestNudgeForDataTypes(
911     const tracked_objects::Location& nudge_location,
912     ModelTypeSet types) {
913   debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get());
914
915   // TODO(lipalani) : Calculate the nudge delay based on all types.
916   base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta(
917       types.First().Get(),
918       this);
919   allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL);
920   scheduler_->ScheduleLocalNudge(nudge_delay,
921                                  types,
922                                  nudge_location);
923 }
924
925 void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) {
926   DCHECK(thread_checker_.CalledOnValidThread());
927   // Only send an event if this is due to a cycle ending and this cycle
928   // concludes a canonical "sync" process; that is, based on what is known
929   // locally we are "all happy" and up-to-date.  There may be new changes on
930   // the server, but we'll get them on a subsequent sync.
931   //
932   // Notifications are sent at the end of every sync cycle, regardless of
933   // whether we should sync again.
934   if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
935     if (!initialized_) {
936       LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not "
937                 << "initialized";
938       return;
939     }
940
941     DVLOG(1) << "Sending OnSyncCycleCompleted";
942     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
943                       OnSyncCycleCompleted(event.snapshot));
944   }
945
946   if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
947     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
948                       OnStopSyncingPermanently());
949     return;
950   }
951
952   if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) {
953     FOR_EACH_OBSERVER(
954         SyncManager::Observer, observers_,
955         OnActionableError(
956             event.snapshot.model_neutral_state().sync_protocol_error));
957     return;
958   }
959 }
960
961 void SyncManagerImpl::SetJsEventHandler(
962     const WeakHandle<JsEventHandler>& event_handler) {
963   js_event_handler_ = event_handler;
964   js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
965   js_mutation_event_observer_.SetJsEventHandler(js_event_handler_);
966   js_sync_encryption_handler_observer_.SetJsEventHandler(js_event_handler_);
967 }
968
969 void SyncManagerImpl::ProcessJsMessage(
970     const std::string& name, const JsArgList& args,
971     const WeakHandle<JsReplyHandler>& reply_handler) {
972   if (!initialized_) {
973     NOTREACHED();
974     return;
975   }
976
977   if (!reply_handler.IsInitialized()) {
978     DVLOG(1) << "Uninitialized reply handler; dropping unknown message "
979             << name << " with args " << args.ToString();
980     return;
981   }
982
983   JsMessageHandler js_message_handler = js_message_handlers_[name];
984   if (js_message_handler.is_null()) {
985     DVLOG(1) << "Dropping unknown message " << name
986              << " with args " << args.ToString();
987     return;
988   }
989
990   reply_handler.Call(FROM_HERE,
991                      &JsReplyHandler::HandleJsReply,
992                      name, js_message_handler.Run(args));
993 }
994
995 void SyncManagerImpl::BindJsMessageHandler(
996     const std::string& name,
997     UnboundJsMessageHandler unbound_message_handler) {
998   js_message_handlers_[name] =
999       base::Bind(unbound_message_handler, base::Unretained(this));
1000 }
1001
1002 base::DictionaryValue* SyncManagerImpl::NotificationInfoToValue(
1003     const NotificationInfoMap& notification_info) {
1004   base::DictionaryValue* value = new base::DictionaryValue();
1005
1006   for (NotificationInfoMap::const_iterator it = notification_info.begin();
1007       it != notification_info.end(); ++it) {
1008     const std::string model_type_str = ModelTypeToString(it->first);
1009     value->Set(model_type_str, it->second.ToValue());
1010   }
1011
1012   return value;
1013 }
1014
1015 std::string SyncManagerImpl::NotificationInfoToString(
1016     const NotificationInfoMap& notification_info) {
1017   scoped_ptr<base::DictionaryValue> value(
1018       NotificationInfoToValue(notification_info));
1019   std::string str;
1020   base::JSONWriter::Write(value.get(), &str);
1021   return str;
1022 }
1023
1024 JsArgList SyncManagerImpl::GetNotificationState(
1025     const JsArgList& args) {
1026   const std::string& notification_state =
1027       InvalidatorStateToString(invalidator_state_);
1028   DVLOG(1) << "GetNotificationState: " << notification_state;
1029   base::ListValue return_args;
1030   return_args.Append(new base::StringValue(notification_state));
1031   return JsArgList(&return_args);
1032 }
1033
1034 JsArgList SyncManagerImpl::GetNotificationInfo(
1035     const JsArgList& args) {
1036   DVLOG(1) << "GetNotificationInfo: "
1037            << NotificationInfoToString(notification_info_map_);
1038   base::ListValue return_args;
1039   return_args.Append(NotificationInfoToValue(notification_info_map_));
1040   return JsArgList(&return_args);
1041 }
1042
1043 JsArgList SyncManagerImpl::GetRootNodeDetails(
1044     const JsArgList& args) {
1045   ReadTransaction trans(FROM_HERE, GetUserShare());
1046   ReadNode root(&trans);
1047   root.InitByRootLookup();
1048   base::ListValue return_args;
1049   return_args.Append(root.GetDetailsAsValue());
1050   return JsArgList(&return_args);
1051 }
1052
1053 JsArgList SyncManagerImpl::GetClientServerTraffic(
1054     const JsArgList& args) {
1055   base::ListValue return_args;
1056   base::ListValue* value = traffic_recorder_.ToValue();
1057   if (value != NULL)
1058     return_args.Append(value);
1059   return JsArgList(&return_args);
1060 }
1061
1062 namespace {
1063
1064 int64 GetId(const base::ListValue& ids, int i) {
1065   std::string id_str;
1066   if (!ids.GetString(i, &id_str)) {
1067     return kInvalidId;
1068   }
1069   int64 id = kInvalidId;
1070   if (!base::StringToInt64(id_str, &id)) {
1071     return kInvalidId;
1072   }
1073   return id;
1074 }
1075
1076 JsArgList GetNodeInfoById(
1077     const JsArgList& args,
1078     UserShare* user_share,
1079     base::DictionaryValue* (BaseNode::*info_getter)() const) {
1080   CHECK(info_getter);
1081   base::ListValue return_args;
1082   base::ListValue* node_summaries = new base::ListValue();
1083   return_args.Append(node_summaries);
1084   const base::ListValue* id_list = NULL;
1085   ReadTransaction trans(FROM_HERE, user_share);
1086   if (args.Get().GetList(0, &id_list)) {
1087     CHECK(id_list);
1088     for (size_t i = 0; i < id_list->GetSize(); ++i) {
1089       int64 id = GetId(*id_list, i);
1090       if (id == kInvalidId) {
1091         continue;
1092       }
1093       ReadNode node(&trans);
1094       if (node.InitByIdLookup(id) != BaseNode::INIT_OK) {
1095         continue;
1096       }
1097       node_summaries->Append((node.*info_getter)());
1098     }
1099   }
1100   return JsArgList(&return_args);
1101 }
1102
1103 }  // namespace
1104
1105 JsArgList SyncManagerImpl::GetNodeSummariesById(const JsArgList& args) {
1106   return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue);
1107 }
1108
1109 JsArgList SyncManagerImpl::GetNodeDetailsById(const JsArgList& args) {
1110   return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue);
1111 }
1112
1113 JsArgList SyncManagerImpl::GetAllNodes(const JsArgList& args) {
1114   base::ListValue return_args;
1115   base::ListValue* result = new base::ListValue();
1116   return_args.Append(result);
1117
1118   ReadTransaction trans(FROM_HERE, GetUserShare());
1119   std::vector<const syncable::EntryKernel*> entry_kernels;
1120   trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(),
1121                                            &entry_kernels);
1122
1123   for (std::vector<const syncable::EntryKernel*>::const_iterator it =
1124            entry_kernels.begin(); it != entry_kernels.end(); ++it) {
1125     result->Append((*it)->ToValue(trans.GetCryptographer()));
1126   }
1127
1128   return JsArgList(&return_args);
1129 }
1130
1131 JsArgList SyncManagerImpl::GetChildNodeIds(const JsArgList& args) {
1132   base::ListValue return_args;
1133   base::ListValue* child_ids = new base::ListValue();
1134   return_args.Append(child_ids);
1135   int64 id = GetId(args.Get(), 0);
1136   if (id != kInvalidId) {
1137     ReadTransaction trans(FROM_HERE, GetUserShare());
1138     syncable::Directory::Metahandles child_handles;
1139     trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(),
1140                                                   id, &child_handles);
1141     for (syncable::Directory::Metahandles::const_iterator it =
1142              child_handles.begin(); it != child_handles.end(); ++it) {
1143       child_ids->Append(new base::StringValue(base::Int64ToString(*it)));
1144     }
1145   }
1146   return JsArgList(&return_args);
1147 }
1148
1149 void SyncManagerImpl::UpdateNotificationInfo(
1150     const ObjectIdInvalidationMap& invalidation_map) {
1151   ObjectIdSet ids = invalidation_map.GetObjectIds();
1152   for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1153     ModelType type = UNSPECIFIED;
1154     if (!ObjectIdToRealModelType(*it, &type)) {
1155       continue;
1156     }
1157     const SingleObjectInvalidationSet& type_invalidations =
1158         invalidation_map.ForObject(*it);
1159     for (SingleObjectInvalidationSet::const_iterator inv_it =
1160          type_invalidations.begin(); inv_it != type_invalidations.end();
1161          ++inv_it) {
1162       NotificationInfo* info = &notification_info_map_[type];
1163       info->total_count++;
1164       std::string payload =
1165           inv_it->is_unknown_version() ? "UNKNOWN" : inv_it->payload();
1166       info->payload = payload;
1167     }
1168   }
1169 }
1170
1171 void SyncManagerImpl::OnInvalidatorStateChange(InvalidatorState state) {
1172   DCHECK(thread_checker_.CalledOnValidThread());
1173
1174   const std::string& state_str = InvalidatorStateToString(state);
1175   invalidator_state_ = state;
1176   DVLOG(1) << "Invalidator state changed to: " << state_str;
1177   const bool notifications_enabled =
1178       (invalidator_state_ == INVALIDATIONS_ENABLED);
1179   allstatus_.SetNotificationsEnabled(notifications_enabled);
1180   scheduler_->SetNotificationsEnabled(notifications_enabled);
1181
1182   if (js_event_handler_.IsInitialized()) {
1183     base::DictionaryValue details;
1184     details.SetString("state", state_str);
1185     js_event_handler_.Call(FROM_HERE,
1186                            &JsEventHandler::HandleJsEvent,
1187                            "onNotificationStateChange",
1188                            JsEventDetails(&details));
1189   }
1190 }
1191
1192 void SyncManagerImpl::OnIncomingInvalidation(
1193     const ObjectIdInvalidationMap& invalidation_map) {
1194   DCHECK(thread_checker_.CalledOnValidThread());
1195
1196   // We should never receive IDs from non-sync objects.
1197   ObjectIdSet ids = invalidation_map.GetObjectIds();
1198   for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1199     ModelType type;
1200     if (!ObjectIdToRealModelType(*it, &type)) {
1201       DLOG(WARNING) << "Notification has invalid id: " << ObjectIdToString(*it);
1202     }
1203   }
1204
1205   if (invalidation_map.Empty()) {
1206     LOG(WARNING) << "Sync received invalidation without any type information.";
1207   } else {
1208     allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_NOTIFICATION);
1209     scheduler_->ScheduleInvalidationNudge(
1210         TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec),
1211         invalidation_map, FROM_HERE);
1212     allstatus_.IncrementNotificationsReceived();
1213     UpdateNotificationInfo(invalidation_map);
1214     debug_info_event_listener_.OnIncomingNotification(invalidation_map);
1215   }
1216
1217   if (js_event_handler_.IsInitialized()) {
1218     base::DictionaryValue details;
1219     base::ListValue* changed_types = new base::ListValue();
1220     details.Set("changedTypes", changed_types);
1221
1222     ObjectIdSet id_set = invalidation_map.GetObjectIds();
1223     ModelTypeSet nudged_types = ObjectIdSetToModelTypeSet(id_set);
1224     DCHECK(!nudged_types.Empty());
1225     for (ModelTypeSet::Iterator it = nudged_types.First();
1226          it.Good(); it.Inc()) {
1227       const std::string model_type_str = ModelTypeToString(it.Get());
1228       changed_types->Append(new base::StringValue(model_type_str));
1229     }
1230     details.SetString("source", "REMOTE_INVALIDATION");
1231     js_event_handler_.Call(FROM_HERE,
1232                            &JsEventHandler::HandleJsEvent,
1233                            "onIncomingNotification",
1234                            JsEventDetails(&details));
1235   }
1236 }
1237
1238 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) {
1239   DCHECK(thread_checker_.CalledOnValidThread());
1240   if (types.Empty()) {
1241     LOG(WARNING) << "Sync received refresh request with no types specified.";
1242   } else {
1243     allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL_REFRESH);
1244     scheduler_->ScheduleLocalRefreshRequest(
1245         TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec),
1246         types, FROM_HERE);
1247   }
1248
1249   if (js_event_handler_.IsInitialized()) {
1250     base::DictionaryValue details;
1251     base::ListValue* changed_types = new base::ListValue();
1252     details.Set("changedTypes", changed_types);
1253     for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
1254       const std::string& model_type_str =
1255           ModelTypeToString(it.Get());
1256       changed_types->Append(new base::StringValue(model_type_str));
1257     }
1258     details.SetString("source", "LOCAL_INVALIDATION");
1259     js_event_handler_.Call(FROM_HERE,
1260                            &JsEventHandler::HandleJsEvent,
1261                            "onIncomingNotification",
1262                            JsEventDetails(&details));
1263   }
1264 }
1265
1266 SyncStatus SyncManagerImpl::GetDetailedStatus() const {
1267   return allstatus_.status();
1268 }
1269
1270 void SyncManagerImpl::SaveChanges() {
1271   directory()->SaveChanges();
1272 }
1273
1274 UserShare* SyncManagerImpl::GetUserShare() {
1275   DCHECK(initialized_);
1276   return &share_;
1277 }
1278
1279 const std::string SyncManagerImpl::cache_guid() {
1280   DCHECK(initialized_);
1281   return directory()->cache_guid();
1282 }
1283
1284 bool SyncManagerImpl::ReceivedExperiment(Experiments* experiments) {
1285   ReadTransaction trans(FROM_HERE, GetUserShare());
1286   ReadNode nigori_node(&trans);
1287   if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
1288     DVLOG(1) << "Couldn't find Nigori node.";
1289     return false;
1290   }
1291   bool found_experiment = false;
1292
1293   ReadNode autofill_culling_node(&trans);
1294   if (autofill_culling_node.InitByClientTagLookup(
1295           syncer::EXPERIMENTS,
1296           syncer::kAutofillCullingTag) == BaseNode::INIT_OK &&
1297       autofill_culling_node.GetExperimentsSpecifics().
1298           autofill_culling().enabled()) {
1299     experiments->autofill_culling = true;
1300     found_experiment = true;
1301   }
1302
1303   ReadNode favicon_sync_node(&trans);
1304   if (favicon_sync_node.InitByClientTagLookup(
1305           syncer::EXPERIMENTS,
1306           syncer::kFaviconSyncTag) == BaseNode::INIT_OK) {
1307     experiments->favicon_sync_limit =
1308         favicon_sync_node.GetExperimentsSpecifics().favicon_sync().
1309             favicon_sync_limit();
1310     found_experiment = true;
1311   }
1312
1313   ReadNode pre_commit_update_avoidance_node(&trans);
1314   if (pre_commit_update_avoidance_node.InitByClientTagLookup(
1315           syncer::EXPERIMENTS,
1316           syncer::kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) {
1317     session_context_->set_server_enabled_pre_commit_update_avoidance(
1318         pre_commit_update_avoidance_node.GetExperimentsSpecifics().
1319             pre_commit_update_avoidance().enabled());
1320     // We don't bother setting found_experiment.  The frontend doesn't need to
1321     // know about this.
1322   }
1323
1324   return found_experiment;
1325 }
1326
1327 bool SyncManagerImpl::HasUnsyncedItems() {
1328   ReadTransaction trans(FROM_HERE, GetUserShare());
1329   return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
1330 }
1331
1332 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() {
1333   return sync_encryption_handler_.get();
1334 }
1335
1336 // static.
1337 int SyncManagerImpl::GetDefaultNudgeDelay() {
1338   return kDefaultNudgeDelayMilliseconds;
1339 }
1340
1341 // static.
1342 int SyncManagerImpl::GetPreferencesNudgeDelay() {
1343   return kPreferencesNudgeDelayMilliseconds;
1344 }
1345
1346 }  // namespace syncer