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