sync_backend_host.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 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 "build/build_config.h"
6
7#include <algorithm>
8
9#include "base/command_line.h"
10#include "base/file_util.h"
11#include "base/task.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/browser_thread.h"
14#include "chrome/browser/net/gaia/token_service.h"
15#include "chrome/browser/prefs/pref_service.h"
16#include "chrome/browser/profile.h"
17#include "chrome/browser/sync/engine/syncapi.h"
18#include "chrome/browser/sync/glue/change_processor.h"
19#include "chrome/browser/sync/glue/database_model_worker.h"
20#include "chrome/browser/sync/glue/history_model_worker.h"
21#include "chrome/browser/sync/glue/sync_backend_host.h"
22#include "chrome/browser/sync/glue/http_bridge.h"
23#include "chrome/browser/sync/glue/password_model_worker.h"
24#include "chrome/browser/sync/sessions/session_state.h"
25// TODO(tim): Remove this! We should have a syncapi pass-thru instead.
26#include "chrome/browser/sync/syncable/directory_manager.h"  // Cryptographer.
27#include "chrome/common/chrome_switches.h"
28#include "chrome/common/chrome_version_info.h"
29#include "chrome/common/net/gaia/gaia_constants.h"
30#include "chrome/common/notification_service.h"
31#include "chrome/common/notification_type.h"
32#include "chrome/common/pref_names.h"
33#include "webkit/glue/webkit_glue.h"
34
35static const int kSaveChangesIntervalSeconds = 10;
36static const FilePath::CharType kSyncDataFolderName[] =
37    FILE_PATH_LITERAL("Sync Data");
38
39using browser_sync::DataTypeController;
40typedef TokenService::TokenAvailableDetails TokenAvailableDetails;
41
42typedef GoogleServiceAuthError AuthError;
43
44namespace browser_sync {
45
46using sessions::SyncSessionSnapshot;
47using sync_api::SyncCredentials;
48
49SyncBackendHost::SyncBackendHost(
50    SyncFrontend* frontend,
51    Profile* profile,
52    const FilePath& profile_path,
53    const DataTypeController::TypeMap& data_type_controllers)
54    : core_thread_("Chrome_SyncCoreThread"),
55      frontend_loop_(MessageLoop::current()),
56      profile_(profile),
57      frontend_(frontend),
58      sync_data_folder_path_(profile_path.Append(kSyncDataFolderName)),
59      data_type_controllers_(data_type_controllers),
60      last_auth_error_(AuthError::None()),
61      syncapi_initialized_(false) {
62
63  core_ = new Core(this);
64}
65
66SyncBackendHost::SyncBackendHost()
67    : core_thread_("Chrome_SyncCoreThread"),
68      frontend_loop_(MessageLoop::current()),
69      profile_(NULL),
70      frontend_(NULL),
71      last_auth_error_(AuthError::None()),
72      syncapi_initialized_(false) {
73}
74
75SyncBackendHost::~SyncBackendHost() {
76  DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor.";
77  DCHECK(registrar_.workers.empty());
78}
79
80void SyncBackendHost::Initialize(
81    const GURL& sync_service_url,
82    const syncable::ModelTypeSet& types,
83    URLRequestContextGetter* baseline_context_getter,
84    const SyncCredentials& credentials,
85    bool delete_sync_data_folder,
86    const notifier::NotifierOptions& notifier_options) {
87  if (!core_thread_.Start())
88    return;
89
90  // Create a worker for the UI thread and route bookmark changes to it.
91  // TODO(tim): Pull this into a method to reuse.  For now we don't even
92  // need to lock because we init before the syncapi exists and we tear down
93  // after the syncapi is destroyed.  Make sure to NULL-check workers_ indices
94  // when a new type is synced as the worker may already exist and you just
95  // need to update routing_info_.
96  registrar_.workers[GROUP_DB] = new DatabaseModelWorker();
97  registrar_.workers[GROUP_HISTORY] =
98      new HistoryModelWorker(
99          profile_->GetHistoryService(Profile::IMPLICIT_ACCESS));
100  registrar_.workers[GROUP_UI] = new UIModelWorker(frontend_loop_);
101  registrar_.workers[GROUP_PASSIVE] = new ModelSafeWorker();
102
103  PasswordStore* password_store =
104      profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS);
105  if (password_store) {
106    registrar_.workers[GROUP_PASSWORD] =
107        new PasswordModelWorker(password_store);
108  } else {
109    LOG(WARNING) << "Password store not initialized, cannot sync passwords";
110  }
111
112  // Any datatypes that we want the syncer to pull down must
113  // be in the routing_info map.  We set them to group passive, meaning that
114  // updates will be applied, but not dispatched to the UI thread yet.
115  for (syncable::ModelTypeSet::const_iterator it = types.begin();
116      it != types.end(); ++it) {
117    registrar_.routing_info[(*it)] = GROUP_PASSIVE;
118  }
119
120  // TODO(tim): Remove this special case once NIGORI is populated by
121  // default.  We piggy back off of the passwords flag for now to not
122  // require both encryption and passwords flags.
123  bool enable_encryption = CommandLine::ForCurrentProcess()->HasSwitch(
124      switches::kEnableSyncPasswords) || types.count(syncable::PASSWORDS);
125  if (enable_encryption)
126    registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE;
127
128  InitCore(Core::DoInitializeOptions(
129      sync_service_url,
130      MakeHttpBridgeFactory(baseline_context_getter),
131      credentials,
132      delete_sync_data_folder,
133      notifier_options,
134      RestoreEncryptionBootstrapToken(),
135      false));
136}
137
138void SyncBackendHost::PersistEncryptionBootstrapToken(
139    const std::string& token) {
140  PrefService* prefs = profile_->GetPrefs();
141
142  prefs->SetString(prefs::kEncryptionBootstrapToken, token);
143  prefs->ScheduleSavePersistentPrefs();
144}
145
146std::string SyncBackendHost::RestoreEncryptionBootstrapToken() {
147  PrefService* prefs = profile_->GetPrefs();
148  std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken);
149  return token;
150}
151
152bool SyncBackendHost::IsNigoriEnabled() const {
153  AutoLock lock(registrar_lock_);
154  // Note that NIGORI is only ever added/removed from routing_info once,
155  // during initialization / first configuration, so there is no real 'race'
156  // possible here or possibility of stale return value.
157  return registrar_.routing_info.find(syncable::NIGORI) !=
158      registrar_.routing_info.end();
159}
160
161bool SyncBackendHost::IsUsingExplicitPassphrase() {
162  return IsNigoriEnabled() && syncapi_initialized_ &&
163      core_->syncapi()->InitialSyncEndedForAllEnabledTypes() &&
164      core_->syncapi()->IsUsingExplicitPassphrase();
165}
166
167bool SyncBackendHost::IsCryptographerReady() const {
168  return syncapi_initialized_ &&
169      GetUserShareHandle()->dir_manager->cryptographer()->is_ready();
170}
171
172sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory(
173    URLRequestContextGetter* getter) {
174  return new HttpBridgeFactory(getter);
175}
176
177void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) {
178  core_thread_.message_loop()->PostTask(FROM_HERE,
179      NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize,
180                        options));
181}
182
183void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) {
184  core_thread_.message_loop()->PostTask(FROM_HERE,
185      NewRunnableMethod(core_.get(),
186                        &SyncBackendHost::Core::DoUpdateCredentials,
187                        credentials));
188}
189
190void SyncBackendHost::StartSyncingWithServer() {
191  core_thread_.message_loop()->PostTask(FROM_HERE,
192      NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
193}
194
195void SyncBackendHost::SetPassphrase(const std::string& passphrase,
196                                    bool is_explicit) {
197  if (!IsNigoriEnabled()) {
198    LOG(WARNING) << "Silently dropping SetPassphrase request.";
199    return;
200  }
201
202  // If encryption is enabled and we've got a SetPassphrase
203  core_thread_.message_loop()->PostTask(FROM_HERE,
204      NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase,
205                        passphrase, is_explicit));
206}
207
208void SyncBackendHost::Shutdown(bool sync_disabled) {
209  // Thread shutdown should occur in the following order:
210  // - SyncerThread
211  // - CoreThread
212  // - UI Thread (stops some time after we return from this call).
213  if (core_thread_.IsRunning()) {  // Not running in tests.
214    core_thread_.message_loop()->PostTask(FROM_HERE,
215        NewRunnableMethod(core_.get(),
216                          &SyncBackendHost::Core::DoShutdown,
217                          sync_disabled));
218  }
219
220  // Before joining the core_thread_, we wait for the UIModelWorker to
221  // give us the green light that it is not depending on the frontend_loop_ to
222  // process any more tasks. Stop() blocks until this termination condition
223  // is true.
224  if (ui_worker())
225    ui_worker()->Stop();
226
227  // Stop will return once the thread exits, which will be after DoShutdown
228  // runs. DoShutdown needs to run from core_thread_ because the sync backend
229  // requires any thread that opened sqlite handles to relinquish them
230  // personally. We need to join threads, because otherwise the main Chrome
231  // thread (ui loop) can exit before DoShutdown finishes, at which point
232  // virtually anything the sync backend does (or the post-back to
233  // frontend_loop_ by our Core) will epically fail because the CRT won't be
234  // initialized. For now this only ever happens at sync-enabled-Chrome exit,
235  // meaning bug 1482548 applies to prolonged "waiting" that may occur in
236  // DoShutdown.
237  core_thread_.Stop();
238
239  registrar_.routing_info.clear();
240  registrar_.workers[GROUP_DB] = NULL;
241  registrar_.workers[GROUP_HISTORY] = NULL;
242  registrar_.workers[GROUP_UI] = NULL;
243  registrar_.workers[GROUP_PASSIVE] = NULL;
244  registrar_.workers[GROUP_PASSWORD] = NULL;
245  registrar_.workers.erase(GROUP_DB);
246  registrar_.workers.erase(GROUP_HISTORY);
247  registrar_.workers.erase(GROUP_UI);
248  registrar_.workers.erase(GROUP_PASSIVE);
249  registrar_.workers.erase(GROUP_PASSWORD);
250  frontend_ = NULL;
251  core_ = NULL;  // Releases reference to core_.
252}
253
254void SyncBackendHost::ConfigureDataTypes(const syncable::ModelTypeSet& types,
255                                         CancelableTask* ready_task) {
256  // Only one configure is allowed at a time.
257  DCHECK(!configure_ready_task_.get());
258  DCHECK(syncapi_initialized_);
259
260  bool deleted_type = false;
261
262  {
263    AutoLock lock(registrar_lock_);
264    for (DataTypeController::TypeMap::const_iterator it =
265             data_type_controllers_.begin();
266         it != data_type_controllers_.end(); ++it) {
267      syncable::ModelType type = (*it).first;
268
269      // If a type is not specified, remove it from the routing_info.
270      if (types.count(type) == 0) {
271        registrar_.routing_info.erase(type);
272        deleted_type = true;
273      } else {
274        // Add a newly specified data type as GROUP_PASSIVE into the
275        // routing_info, if it does not already exist.
276        if (registrar_.routing_info.count(type) == 0) {
277          registrar_.routing_info[type] = GROUP_PASSIVE;
278        }
279      }
280    }
281  }
282
283  // If no new data types were added to the passive group, no need to
284  // wait for the syncer.
285  if (core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) {
286    ready_task->Run();
287    delete ready_task;
288  } else {
289    // Save the task here so we can run it when the syncer finishes
290    // initializing the new data types.  It will be run only when the
291    // set of initially synced data types matches the types requested in
292    // this configure.
293    configure_ready_task_.reset(ready_task);
294    configure_initial_sync_types_ = types;
295  }
296
297  // Nudge the syncer. This is necessary for both datatype addition/deletion.
298  //
299  // Deletions need a nudge in order to ensure the deletion occurs in a timely
300  // manner (see issue 56416).
301  //
302  // In the case of additions, on the next sync cycle, the syncer should
303  // notice that the routing info has changed and start the process of
304  // downloading updates for newly added data types.  Once this is
305  // complete, the configure_ready_task_ is run via an
306  // OnInitializationComplete notification.
307  if (deleted_type || !core_->syncapi()->InitialSyncEndedForAllEnabledTypes())
308    // We can only nudge when we've either deleted a dataype or added one, else
309    // we break all the profile sync unit tests.
310    RequestNudge();
311}
312
313void SyncBackendHost::RequestNudge() {
314  core_thread_.message_loop()->PostTask(FROM_HERE,
315      NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge));
316}
317
318void SyncBackendHost::ActivateDataType(
319    DataTypeController* data_type_controller,
320    ChangeProcessor* change_processor) {
321  AutoLock lock(registrar_lock_);
322
323  // Ensure that the given data type is in the PASSIVE group.
324  browser_sync::ModelSafeRoutingInfo::iterator i =
325      registrar_.routing_info.find(data_type_controller->type());
326  DCHECK(i != registrar_.routing_info.end());
327  DCHECK((*i).second == GROUP_PASSIVE);
328  syncable::ModelType type = data_type_controller->type();
329  // Change the data type's routing info to its group.
330  registrar_.routing_info[type] = data_type_controller->model_safe_group();
331
332  // Add the data type's change processor to the list of change
333  // processors so it can receive updates.
334  DCHECK_EQ(processors_.count(type), 0U);
335  processors_[type] = change_processor;
336}
337
338void SyncBackendHost::DeactivateDataType(
339    DataTypeController* data_type_controller,
340    ChangeProcessor* change_processor) {
341  AutoLock lock(registrar_lock_);
342  registrar_.routing_info.erase(data_type_controller->type());
343
344  std::map<syncable::ModelType, ChangeProcessor*>::size_type erased =
345      processors_.erase(data_type_controller->type());
346  DCHECK_EQ(erased, 1U);
347
348  // TODO(sync): At this point we need to purge the data associated
349  // with this data type from the sync db.
350}
351
352bool SyncBackendHost::RequestPause() {
353  core_thread_.message_loop()->PostTask(FROM_HERE,
354     NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestPause));
355  return true;
356}
357
358bool SyncBackendHost::RequestResume() {
359  core_thread_.message_loop()->PostTask(FROM_HERE,
360     NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestResume));
361  return true;
362}
363
364bool SyncBackendHost::RequestClearServerData() {
365  core_thread_.message_loop()->PostTask(FROM_HERE,
366     NewRunnableMethod(core_.get(),
367     &SyncBackendHost::Core::DoRequestClearServerData));
368  return true;
369}
370
371SyncBackendHost::Core::~Core() {
372}
373
374void SyncBackendHost::Core::NotifyPaused() {
375  NotificationService::current()->Notify(NotificationType::SYNC_PAUSED,
376                                         NotificationService::AllSources(),
377                                         NotificationService::NoDetails());
378}
379
380void SyncBackendHost::Core::NotifyResumed() {
381  NotificationService::current()->Notify(NotificationType::SYNC_RESUMED,
382                                         NotificationService::AllSources(),
383                                         NotificationService::NoDetails());
384}
385
386void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) {
387  NotificationService::current()->Notify(
388      NotificationType::SYNC_PASSPHRASE_REQUIRED,
389      Source<SyncBackendHost>(host_),
390      Details<bool>(&for_decryption));
391}
392
393void SyncBackendHost::Core::NotifyPassphraseAccepted(
394    const std::string& bootstrap_token) {
395  if (!host_)
396    return;
397  host_->PersistEncryptionBootstrapToken(bootstrap_token);
398  NotificationService::current()->Notify(
399      NotificationType::SYNC_PASSPHRASE_ACCEPTED,
400      Source<SyncBackendHost>(host_),
401      NotificationService::NoDetails());
402}
403
404void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) {
405  if (!host_)
406    return;
407  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
408  TokenAvailableDetails details(GaiaConstants::kSyncService, token);
409  NotificationService::current()->Notify(
410      NotificationType::TOKEN_UPDATED,
411      NotificationService::AllSources(),
412      Details<const TokenAvailableDetails>(&details));
413}
414
415SyncBackendHost::UserShareHandle SyncBackendHost::GetUserShareHandle() const {
416  DCHECK(syncapi_initialized_);
417  return core_->syncapi()->GetUserShare();
418}
419
420SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() {
421  DCHECK(syncapi_initialized_);
422  return core_->syncapi()->GetDetailedStatus();
423}
424
425SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() {
426  DCHECK(syncapi_initialized_);
427  return core_->syncapi()->GetStatusSummary();
428}
429
430string16 SyncBackendHost::GetAuthenticatedUsername() const {
431  DCHECK(syncapi_initialized_);
432  return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername());
433}
434
435const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const {
436  return last_auth_error_;
437}
438
439const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const {
440  return last_snapshot_.get();
441}
442
443void SyncBackendHost::GetWorkers(std::vector<ModelSafeWorker*>* out) {
444  AutoLock lock(registrar_lock_);
445  out->clear();
446  for (WorkerMap::const_iterator it = registrar_.workers.begin();
447       it != registrar_.workers.end(); ++it) {
448    out->push_back((*it).second);
449  }
450}
451
452void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
453  AutoLock lock(registrar_lock_);
454  ModelSafeRoutingInfo copy(registrar_.routing_info);
455  out->swap(copy);
456}
457
458bool SyncBackendHost::HasUnsyncedItems() const {
459  DCHECK(syncapi_initialized_);
460  return core_->syncapi()->HasUnsyncedItems();
461}
462
463SyncBackendHost::Core::Core(SyncBackendHost* backend)
464    : host_(backend),
465      syncapi_(new sync_api::SyncManager()) {
466}
467
468// Helper to construct a user agent string (ASCII) suitable for use by
469// the syncapi for any HTTP communication. This string is used by the sync
470// backend for classifying client types when calculating statistics.
471std::string MakeUserAgentForSyncapi() {
472  std::string user_agent;
473  user_agent = "Chrome ";
474#if defined(OS_WIN)
475  user_agent += "WIN ";
476#elif defined(OS_LINUX)
477  user_agent += "LINUX ";
478#elif defined(OS_FREEBSD)
479  user_agent += "FREEBSD ";
480#elif defined(OS_OPENBSD)
481  user_agent += "OPENBSD ";
482#elif defined(OS_MACOSX)
483  user_agent += "MAC ";
484#endif
485  chrome::VersionInfo version_info;
486  if (!version_info.is_valid()) {
487    DLOG(ERROR) << "Unable to create chrome::VersionInfo object";
488    return user_agent;
489  }
490
491  user_agent += version_info.Version();
492  user_agent += " (" + version_info.LastChange() + ")";
493  if (!version_info.IsOfficialBuild())
494    user_agent += "-devel";
495  return user_agent;
496}
497
498void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) {
499  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
500
501  // Blow away the partial or corrupt sync data folder before doing any more
502  // initialization, if necessary.
503  if (options.delete_sync_data_folder) {
504    DeleteSyncDataFolder();
505  }
506
507  // Make sure that the directory exists before initializing the backend.
508  // If it already exists, this will do no harm.
509  bool success = file_util::CreateDirectory(host_->sync_data_folder_path());
510  DCHECK(success);
511
512  syncapi_->SetObserver(this);
513  const FilePath& path_str = host_->sync_data_folder_path();
514  success = syncapi_->Init(
515      path_str,
516      (options.service_url.host() + options.service_url.path()).c_str(),
517      options.service_url.EffectiveIntPort(),
518      options.service_url.SchemeIsSecure(),
519      options.http_bridge_factory,
520      host_,  // ModelSafeWorkerRegistrar.
521      MakeUserAgentForSyncapi().c_str(),
522      options.credentials,
523      options.notifier_options,
524      options.restored_key_for_bootstrapping,
525      options.setup_for_test_mode);
526  DCHECK(success) << "Syncapi initialization failed!";
527}
528
529void SyncBackendHost::Core::DoUpdateCredentials(
530    const SyncCredentials& credentials) {
531  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
532  syncapi_->UpdateCredentials(credentials);
533}
534
535void SyncBackendHost::Core::DoStartSyncing() {
536  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
537  syncapi_->StartSyncing();
538}
539
540void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase,
541                                            bool is_explicit) {
542  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
543  syncapi_->SetPassphrase(passphrase, is_explicit);
544}
545
546UIModelWorker* SyncBackendHost::ui_worker() {
547  ModelSafeWorker* w = registrar_.workers[GROUP_UI];
548  if (w == NULL)
549    return NULL;
550  if (w->GetModelSafeGroup() != GROUP_UI)
551    NOTREACHED();
552  return static_cast<UIModelWorker*>(w);
553}
554
555void SyncBackendHost::Core::DoShutdown(bool sync_disabled) {
556  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
557
558  save_changes_timer_.Stop();
559  syncapi_->Shutdown();  // Stops the SyncerThread.
560  syncapi_->RemoveObserver();
561  host_->ui_worker()->OnSyncerShutdownComplete();
562
563  if (sync_disabled)
564    DeleteSyncDataFolder();
565
566  host_ = NULL;
567}
568
569ChangeProcessor* SyncBackendHost::Core::GetProcessor(
570    syncable::ModelType model_type) {
571  std::map<syncable::ModelType, ChangeProcessor*>::const_iterator it =
572      host_->processors_.find(model_type);
573
574  // Until model association happens for a datatype, it will not appear in
575  // the processors list.  During this time, it is OK to drop changes on
576  // the floor (since model association has not happened yet).  When the
577  // data type is activated, model association takes place then the change
578  // processor is added to the processors_ list.  This all happens on
579  // the UI thread so we will never drop any changes after model
580  // association.
581  if (it == host_->processors_.end())
582    return NULL;
583
584  if (!IsCurrentThreadSafeForModel(model_type)) {
585    NOTREACHED() << "Changes applied on wrong thread.";
586    return NULL;
587  }
588
589  // Now that we're sure we're on the correct thread, we can access the
590  // ChangeProcessor.
591  ChangeProcessor* processor = it->second;
592
593  // Ensure the change processor is willing to accept changes.
594  if (!processor->IsRunning())
595    return NULL;
596
597  return processor;
598}
599
600void SyncBackendHost::Core::OnChangesApplied(
601    syncable::ModelType model_type,
602    const sync_api::BaseTransaction* trans,
603    const sync_api::SyncManager::ChangeRecord* changes,
604    int change_count) {
605  if (!host_ || !host_->frontend_) {
606    DCHECK(false) << "OnChangesApplied called after Shutdown?";
607    return;
608  }
609  ChangeProcessor* processor = GetProcessor(model_type);
610  if (!processor)
611    return;
612
613  processor->ApplyChangesFromSyncModel(trans, changes, change_count);
614}
615
616void SyncBackendHost::Core::OnChangesComplete(
617    syncable::ModelType model_type) {
618  if (!host_ || !host_->frontend_) {
619    DCHECK(false) << "OnChangesComplete called after Shutdown?";
620    return;
621  }
622
623  ChangeProcessor* processor = GetProcessor(model_type);
624  if (!processor)
625    return;
626
627  // This call just notifies the processor that it can commit, it already
628  // buffered any changes it plans to makes so needs no further information.
629  processor->CommitChangesFromSyncModel();
630}
631
632
633void SyncBackendHost::Core::OnSyncCycleCompleted(
634    const SyncSessionSnapshot* snapshot) {
635  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
636      &Core::HandleSyncCycleCompletedOnFrontendLoop,
637      new SyncSessionSnapshot(*snapshot)));
638}
639
640void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop(
641    SyncSessionSnapshot* snapshot) {
642  if (!host_ || !host_->frontend_)
643    return;
644  DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
645
646  host_->last_snapshot_.reset(snapshot);
647
648  // If we are waiting for a configuration change, check here to see
649  // if this sync cycle has initialized all of the types we've been
650  // waiting for.
651  if (host_->configure_ready_task_.get()) {
652    bool found_all = true;
653    for (syncable::ModelTypeSet::const_iterator it =
654             host_->configure_initial_sync_types_.begin();
655         it != host_->configure_initial_sync_types_.end(); ++it) {
656      found_all &= snapshot->initial_sync_ended.test(*it);
657    }
658
659    if (found_all) {
660      host_->configure_ready_task_->Run();
661      host_->configure_ready_task_.reset();
662      host_->configure_initial_sync_types_.clear();
663    }
664  }
665  host_->frontend_->OnSyncCycleCompleted();
666}
667
668void SyncBackendHost::Core::OnInitializationComplete() {
669  if (!host_ || !host_->frontend_)
670    return;  // We may have been told to Shutdown before initialization
671             // completed.
672
673  // We could be on some random sync backend thread, so MessageLoop::current()
674  // can definitely be null in here.
675  host_->frontend_loop_->PostTask(FROM_HERE,
676      NewRunnableMethod(this,
677                        &Core::HandleInitalizationCompletedOnFrontendLoop));
678
679  // Initialization is complete, so we can schedule recurring SaveChanges.
680  host_->core_thread_.message_loop()->PostTask(FROM_HERE,
681      NewRunnableMethod(this, &Core::StartSavingChanges));
682}
683
684void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() {
685  if (!host_)
686    return;
687  host_->HandleInitializationCompletedOnFrontendLoop();
688}
689
690void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() {
691  if (!frontend_)
692    return;
693  syncapi_initialized_ = true;
694  frontend_->OnBackendInitialized();
695}
696
697bool SyncBackendHost::Core::IsCurrentThreadSafeForModel(
698    syncable::ModelType model_type) {
699  AutoLock lock(host_->registrar_lock_);
700
701  browser_sync::ModelSafeRoutingInfo::const_iterator routing_it =
702      host_->registrar_.routing_info.find(model_type);
703  if (routing_it == host_->registrar_.routing_info.end())
704    return false;
705  browser_sync::ModelSafeGroup group = routing_it->second;
706  WorkerMap::const_iterator worker_it = host_->registrar_.workers.find(group);
707  if (worker_it == host_->registrar_.workers.end())
708    return false;
709  ModelSafeWorker* worker = worker_it->second;
710  return worker->CurrentThreadIsWorkThread();
711}
712
713
714void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) {
715  // Post to our core loop so we can modify state. Could be on another thread.
716  host_->frontend_loop_->PostTask(FROM_HERE,
717      NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop,
718      auth_error));
719}
720
721void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) {
722  host_->frontend_loop_->PostTask(FROM_HERE,
723      NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption));
724}
725
726void SyncBackendHost::Core::OnPassphraseAccepted(
727    const std::string& bootstrap_token) {
728  host_->frontend_loop_->PostTask(FROM_HERE,
729      NewRunnableMethod(this, &Core::NotifyPassphraseAccepted,
730          bootstrap_token));
731}
732
733void SyncBackendHost::Core::OnPaused() {
734  host_->frontend_loop_->PostTask(
735      FROM_HERE,
736      NewRunnableMethod(this, &Core::NotifyPaused));
737}
738
739void SyncBackendHost::Core::OnResumed() {
740  host_->frontend_loop_->PostTask(
741      FROM_HERE,
742      NewRunnableMethod(this, &Core::NotifyResumed));
743}
744
745void SyncBackendHost::Core::OnStopSyncingPermanently() {
746  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
747      &Core::HandleStopSyncingPermanentlyOnFrontendLoop));
748}
749
750void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) {
751  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
752      &Core::NotifyUpdatedToken, token));
753}
754
755void SyncBackendHost::Core::OnClearServerDataSucceeded() {
756  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
757      &Core::HandleClearServerDataSucceededOnFrontendLoop));
758}
759
760void SyncBackendHost::Core::OnClearServerDataFailed() {
761  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
762      &Core::HandleClearServerDataFailedOnFrontendLoop));
763}
764
765void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
766  if (!host_ || !host_->frontend_)
767    return;
768  host_->frontend_->OnStopSyncingPermanently();
769}
770
771void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() {
772  if (!host_ || !host_->frontend_)
773    return;
774  host_->frontend_->OnClearServerDataSucceeded();
775}
776
777void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() {
778  if (!host_ || !host_->frontend_)
779    return;
780  host_->frontend_->OnClearServerDataFailed();
781}
782
783void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop(
784    const GoogleServiceAuthError& new_auth_error) {
785  if (!host_ || !host_->frontend_)
786    return;
787
788  DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
789
790  host_->last_auth_error_ = new_auth_error;
791  host_->frontend_->OnAuthError();
792}
793
794void SyncBackendHost::Core::StartSavingChanges() {
795  save_changes_timer_.Start(
796      base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds),
797      this, &Core::SaveChanges);
798}
799
800void SyncBackendHost::Core::DoRequestNudge() {
801  syncapi_->RequestNudge();
802}
803
804void SyncBackendHost::Core::DoRequestClearServerData() {
805  syncapi_->RequestClearServerData();
806}
807
808void SyncBackendHost::Core::DoRequestResume() {
809  syncapi_->RequestResume();
810}
811
812void SyncBackendHost::Core::DoRequestPause() {
813  syncapi()->RequestPause();
814}
815
816void SyncBackendHost::Core::SaveChanges() {
817  syncapi_->SaveChanges();
818}
819
820void SyncBackendHost::Core::DeleteSyncDataFolder() {
821  if (file_util::DirectoryExists(host_->sync_data_folder_path())) {
822    if (!file_util::Delete(host_->sync_data_folder_path(), true))
823      LOG(DFATAL) << "Could not delete the Sync Data folder.";
824  }
825}
826
827}  // namespace browser_sync
828