Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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/compiler_specific.h"
     11 #include "base/file_util.h"
     12 #include "base/task.h"
     13 #include "base/threading/thread_restrictions.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "chrome/browser/net/gaia/token_service.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/sync/engine/syncapi.h"
     19 #include "chrome/browser/sync/glue/autofill_model_associator.h"
     20 #include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
     21 #include "chrome/browser/sync/glue/change_processor.h"
     22 #include "chrome/browser/sync/glue/database_model_worker.h"
     23 #include "chrome/browser/sync/glue/history_model_worker.h"
     24 #include "chrome/browser/sync/glue/http_bridge.h"
     25 #include "chrome/browser/sync/glue/password_model_worker.h"
     26 #include "chrome/browser/sync/glue/sync_backend_host.h"
     27 #include "chrome/browser/sync/js_arg_list.h"
     28 #include "chrome/browser/sync/notifier/sync_notifier.h"
     29 #include "chrome/browser/sync/notifier/sync_notifier_factory.h"
     30 #include "chrome/browser/sync/sessions/session_state.h"
     31 // TODO(tim): Remove this! We should have a syncapi pass-thru instead.
     32 #include "chrome/browser/sync/syncable/directory_manager.h"  // Cryptographer.
     33 #include "chrome/browser/sync/syncable/model_type.h"
     34 #include "chrome/browser/sync/syncable/nigori_util.h"
     35 #include "chrome/common/chrome_switches.h"
     36 #include "chrome/common/chrome_version_info.h"
     37 #include "chrome/common/net/gaia/gaia_constants.h"
     38 #include "chrome/common/pref_names.h"
     39 #include "content/browser/browser_thread.h"
     40 #include "content/common/notification_service.h"
     41 #include "content/common/notification_type.h"
     42 #include "googleurl/src/gurl.h"
     43 #include "webkit/glue/webkit_glue.h"
     44 
     45 static const int kSaveChangesIntervalSeconds = 10;
     46 static const FilePath::CharType kSyncDataFolderName[] =
     47     FILE_PATH_LITERAL("Sync Data");
     48 
     49 using browser_sync::DataTypeController;
     50 using sync_notifier::SyncNotifierFactory;
     51 typedef TokenService::TokenAvailableDetails TokenAvailableDetails;
     52 
     53 typedef GoogleServiceAuthError AuthError;
     54 
     55 namespace browser_sync {
     56 
     57 using sessions::SyncSessionSnapshot;
     58 using sync_api::SyncCredentials;
     59 
     60 SyncBackendHost::SyncBackendHost(Profile* profile)
     61     : core_(new Core(ALLOW_THIS_IN_INITIALIZER_LIST(this))),
     62       core_thread_("Chrome_SyncCoreThread"),
     63       frontend_loop_(MessageLoop::current()),
     64       profile_(profile),
     65       frontend_(NULL),
     66       sync_data_folder_path_(
     67           profile_->GetPath().Append(kSyncDataFolderName)),
     68       last_auth_error_(AuthError::None()),
     69       syncapi_initialized_(false) {
     70 }
     71 
     72 SyncBackendHost::SyncBackendHost()
     73     : core_thread_("Chrome_SyncCoreThread"),
     74       frontend_loop_(MessageLoop::current()),
     75       profile_(NULL),
     76       frontend_(NULL),
     77       last_auth_error_(AuthError::None()),
     78       syncapi_initialized_(false) {
     79 }
     80 
     81 SyncBackendHost::~SyncBackendHost() {
     82   DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor.";
     83   DCHECK(registrar_.workers.empty());
     84 }
     85 
     86 void SyncBackendHost::Initialize(
     87     SyncFrontend* frontend,
     88     const GURL& sync_service_url,
     89     const syncable::ModelTypeSet& types,
     90     net::URLRequestContextGetter* baseline_context_getter,
     91     const SyncCredentials& credentials,
     92     bool delete_sync_data_folder) {
     93   if (!core_thread_.Start())
     94     return;
     95 
     96   frontend_ = frontend;
     97   DCHECK(frontend);
     98 
     99   // Create a worker for the UI thread and route bookmark changes to it.
    100   // TODO(tim): Pull this into a method to reuse.  For now we don't even
    101   // need to lock because we init before the syncapi exists and we tear down
    102   // after the syncapi is destroyed.  Make sure to NULL-check workers_ indices
    103   // when a new type is synced as the worker may already exist and you just
    104   // need to update routing_info_.
    105   registrar_.workers[GROUP_DB] = new DatabaseModelWorker();
    106   registrar_.workers[GROUP_UI] = new UIModelWorker();
    107   registrar_.workers[GROUP_PASSIVE] = new ModelSafeWorker();
    108 
    109   if (CommandLine::ForCurrentProcess()->HasSwitch(
    110       switches::kEnableSyncTypedUrls) || types.count(syncable::TYPED_URLS)) {
    111     // TODO(tim): Bug 53916.  HistoryModelWorker crashes, so avoid adding it
    112     // unless specifically requested until bug is fixed.
    113     registrar_.workers[GROUP_HISTORY] =
    114         new HistoryModelWorker(
    115             profile_->GetHistoryService(Profile::IMPLICIT_ACCESS));
    116   }
    117 
    118   // Any datatypes that we want the syncer to pull down must
    119   // be in the routing_info map.  We set them to group passive, meaning that
    120   // updates will be applied, but not dispatched to the UI thread yet.
    121   for (syncable::ModelTypeSet::const_iterator it = types.begin();
    122       it != types.end(); ++it) {
    123     registrar_.routing_info[(*it)] = GROUP_PASSIVE;
    124   }
    125 
    126   PasswordStore* password_store =
    127       profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS);
    128   if (password_store) {
    129     registrar_.workers[GROUP_PASSWORD] =
    130         new PasswordModelWorker(password_store);
    131   } else {
    132     LOG_IF(WARNING, types.count(syncable::PASSWORDS) > 0) << "Password store "
    133         << "not initialized, cannot sync passwords";
    134     registrar_.routing_info.erase(syncable::PASSWORDS);
    135   }
    136 
    137   // Nigori is populated by default now.
    138   registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE;
    139 
    140   // TODO(akalin): Create SyncNotifier here and pass it in as part of
    141   // DoInitializeOptions.
    142   core_->CreateSyncNotifier(baseline_context_getter);
    143 
    144   InitCore(Core::DoInitializeOptions(
    145       sync_service_url,
    146       MakeHttpBridgeFactory(baseline_context_getter),
    147       credentials,
    148       delete_sync_data_folder,
    149       RestoreEncryptionBootstrapToken(),
    150       false));
    151 }
    152 
    153 void SyncBackendHost::PersistEncryptionBootstrapToken(
    154     const std::string& token) {
    155   PrefService* prefs = profile_->GetPrefs();
    156 
    157   prefs->SetString(prefs::kEncryptionBootstrapToken, token);
    158   prefs->ScheduleSavePersistentPrefs();
    159 }
    160 
    161 std::string SyncBackendHost::RestoreEncryptionBootstrapToken() {
    162   PrefService* prefs = profile_->GetPrefs();
    163   std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken);
    164   return token;
    165 }
    166 
    167 bool SyncBackendHost::IsNigoriEnabled() const {
    168   base::AutoLock lock(registrar_lock_);
    169   // Note that NIGORI is only ever added/removed from routing_info once,
    170   // during initialization / first configuration, so there is no real 'race'
    171   // possible here or possibility of stale return value.
    172   return registrar_.routing_info.find(syncable::NIGORI) !=
    173       registrar_.routing_info.end();
    174 }
    175 
    176 bool SyncBackendHost::IsUsingExplicitPassphrase() {
    177   return IsNigoriEnabled() && syncapi_initialized_ &&
    178       core_->syncapi()->InitialSyncEndedForAllEnabledTypes() &&
    179       core_->syncapi()->IsUsingExplicitPassphrase();
    180 }
    181 
    182 bool SyncBackendHost::IsCryptographerReady(
    183     const sync_api::BaseTransaction* trans) const {
    184   return syncapi_initialized_ && trans->GetCryptographer()->is_ready();
    185 }
    186 
    187 JsBackend* SyncBackendHost::GetJsBackend() {
    188   if (syncapi_initialized_) {
    189     return core_.get();
    190   } else {
    191     NOTREACHED();
    192     return NULL;
    193   }
    194 }
    195 
    196 sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory(
    197     net::URLRequestContextGetter* getter) {
    198   return new HttpBridgeFactory(getter);
    199 }
    200 
    201 void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) {
    202   core_thread_.message_loop()->PostTask(FROM_HERE,
    203       NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize,
    204                         options));
    205 }
    206 
    207 void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) {
    208   core_thread_.message_loop()->PostTask(FROM_HERE,
    209       NewRunnableMethod(core_.get(),
    210                         &SyncBackendHost::Core::DoUpdateCredentials,
    211                         credentials));
    212 }
    213 
    214 void SyncBackendHost::StartSyncingWithServer() {
    215   core_thread_.message_loop()->PostTask(FROM_HERE,
    216       NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
    217 }
    218 
    219 void SyncBackendHost::SetPassphrase(const std::string& passphrase,
    220                                     bool is_explicit) {
    221   if (!IsNigoriEnabled()) {
    222     LOG(WARNING) << "Silently dropping SetPassphrase request.";
    223     return;
    224   }
    225 
    226   // This should only be called by the frontend.
    227   DCHECK_EQ(MessageLoop::current(), frontend_loop_);
    228   if (core_->processing_passphrase()) {
    229     VLOG(1) << "Attempted to call SetPassphrase while already waiting for "
    230             << " result from previous SetPassphrase call. Silently dropping.";
    231     return;
    232   }
    233   core_->set_processing_passphrase();
    234 
    235   // If encryption is enabled and we've got a SetPassphrase
    236   core_thread_.message_loop()->PostTask(FROM_HERE,
    237       NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase,
    238                         passphrase, is_explicit));
    239 }
    240 
    241 void SyncBackendHost::Shutdown(bool sync_disabled) {
    242   // Thread shutdown should occur in the following order:
    243   // - SyncerThread
    244   // - CoreThread
    245   // - UI Thread (stops some time after we return from this call).
    246   if (core_thread_.IsRunning()) {  // Not running in tests.
    247     core_thread_.message_loop()->PostTask(FROM_HERE,
    248         NewRunnableMethod(core_.get(),
    249                           &SyncBackendHost::Core::DoShutdown,
    250                           sync_disabled));
    251   }
    252 
    253   // Before joining the core_thread_, we wait for the UIModelWorker to
    254   // give us the green light that it is not depending on the frontend_loop_ to
    255   // process any more tasks. Stop() blocks until this termination condition
    256   // is true.
    257   if (ui_worker())
    258     ui_worker()->Stop();
    259 
    260   // Stop will return once the thread exits, which will be after DoShutdown
    261   // runs. DoShutdown needs to run from core_thread_ because the sync backend
    262   // requires any thread that opened sqlite handles to relinquish them
    263   // personally. We need to join threads, because otherwise the main Chrome
    264   // thread (ui loop) can exit before DoShutdown finishes, at which point
    265   // virtually anything the sync backend does (or the post-back to
    266   // frontend_loop_ by our Core) will epically fail because the CRT won't be
    267   // initialized.
    268   // Since we are blocking the UI thread here, we need to turn ourselves in
    269   // with the ThreadRestriction police.  For sentencing and how we plan to fix
    270   // this, see bug 19757.
    271   {
    272     base::ThreadRestrictions::ScopedAllowIO allow_io;
    273     core_thread_.Stop();
    274   }
    275 
    276   registrar_.routing_info.clear();
    277   registrar_.workers[GROUP_DB] = NULL;
    278   registrar_.workers[GROUP_HISTORY] = NULL;
    279   registrar_.workers[GROUP_UI] = NULL;
    280   registrar_.workers[GROUP_PASSIVE] = NULL;
    281   registrar_.workers[GROUP_PASSWORD] = NULL;
    282   registrar_.workers.erase(GROUP_DB);
    283   registrar_.workers.erase(GROUP_HISTORY);
    284   registrar_.workers.erase(GROUP_UI);
    285   registrar_.workers.erase(GROUP_PASSIVE);
    286   registrar_.workers.erase(GROUP_PASSWORD);
    287   frontend_ = NULL;
    288   core_ = NULL;  // Releases reference to core_.
    289 }
    290 
    291 syncable::AutofillMigrationState
    292     SyncBackendHost::GetAutofillMigrationState() {
    293   return core_->syncapi()->GetAutofillMigrationState();
    294 }
    295 
    296 void SyncBackendHost::SetAutofillMigrationState(
    297     syncable::AutofillMigrationState state) {
    298   return core_->syncapi()->SetAutofillMigrationState(state);
    299 }
    300 
    301 syncable::AutofillMigrationDebugInfo
    302     SyncBackendHost::GetAutofillMigrationDebugInfo() {
    303   return core_->syncapi()->GetAutofillMigrationDebugInfo();
    304 }
    305 
    306 void SyncBackendHost::SetAutofillMigrationDebugInfo(
    307     syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set,
    308     const syncable::AutofillMigrationDebugInfo& info) {
    309   return core_->syncapi()->SetAutofillMigrationDebugInfo(property_to_set, info);
    310 }
    311 
    312 void SyncBackendHost::ConfigureAutofillMigration() {
    313   if (GetAutofillMigrationState() == syncable::NOT_DETERMINED) {
    314     sync_api::ReadTransaction trans(GetUserShare());
    315     sync_api::ReadNode autofil_root_node(&trans);
    316 
    317     // Check for the presence of autofill node.
    318     if (!autofil_root_node.InitByTagLookup(browser_sync::kAutofillTag)) {
    319         SetAutofillMigrationState(syncable::INSUFFICIENT_INFO_TO_DETERMINE);
    320       return;
    321     }
    322 
    323     // Check for children under autofill node.
    324     if (autofil_root_node.GetFirstChildId() == static_cast<int64>(0)) {
    325       SetAutofillMigrationState(syncable::INSUFFICIENT_INFO_TO_DETERMINE);
    326       return;
    327     }
    328 
    329     sync_api::ReadNode autofill_profile_root_node(&trans);
    330 
    331     // Check for the presence of autofill profile root node.
    332     if (!autofill_profile_root_node.InitByTagLookup(
    333        browser_sync::kAutofillProfileTag)) {
    334       SetAutofillMigrationState(syncable::NOT_MIGRATED);
    335       return;
    336     }
    337 
    338     // If our state is not determined then we should not have the autofill
    339     // profile node.
    340     DCHECK(false);
    341 
    342     // just set it as not migrated.
    343     SetAutofillMigrationState(syncable::NOT_MIGRATED);
    344     return;
    345   }
    346 }
    347 
    348 SyncBackendHost::PendingConfigureDataTypesState::
    349 PendingConfigureDataTypesState() : deleted_type(false) {}
    350 
    351 SyncBackendHost::PendingConfigureDataTypesState::
    352 ~PendingConfigureDataTypesState() {}
    353 
    354 // static
    355 SyncBackendHost::PendingConfigureDataTypesState*
    356     SyncBackendHost::MakePendingConfigModeState(
    357         const DataTypeController::TypeMap& data_type_controllers,
    358         const syncable::ModelTypeSet& types,
    359         CancelableTask* ready_task,
    360         ModelSafeRoutingInfo* routing_info) {
    361   PendingConfigureDataTypesState* state = new PendingConfigureDataTypesState();
    362   for (DataTypeController::TypeMap::const_iterator it =
    363            data_type_controllers.begin();
    364        it != data_type_controllers.end(); ++it) {
    365     syncable::ModelType type = it->first;
    366     // If a type is not specified, remove it from the routing_info.
    367     if (types.count(type) == 0) {
    368       state->deleted_type = true;
    369       routing_info->erase(type);
    370     } else {
    371       // Add a newly specified data type as GROUP_PASSIVE into the
    372       // routing_info, if it does not already exist.
    373       if (routing_info->count(type) == 0) {
    374         (*routing_info)[type] = GROUP_PASSIVE;
    375         state->added_types.set(type);
    376       }
    377     }
    378   }
    379 
    380   state->ready_task.reset(ready_task);
    381   state->initial_types = types;
    382   return state;
    383 }
    384 
    385 void SyncBackendHost::ConfigureDataTypes(
    386     const DataTypeController::TypeMap& data_type_controllers,
    387     const syncable::ModelTypeSet& types,
    388     CancelableTask* ready_task) {
    389   // Only one configure is allowed at a time.
    390   DCHECK(!pending_config_mode_state_.get());
    391   DCHECK(!pending_download_state_.get());
    392   DCHECK(syncapi_initialized_);
    393 
    394   if (types.count(syncable::AUTOFILL_PROFILE) != 0) {
    395     ConfigureAutofillMigration();
    396   }
    397 
    398   {
    399     base::AutoLock lock(registrar_lock_);
    400     pending_config_mode_state_.reset(
    401         MakePendingConfigModeState(data_type_controllers, types, ready_task,
    402                                    &registrar_.routing_info));
    403   }
    404 
    405   StartConfiguration(NewCallback(core_.get(),
    406       &SyncBackendHost::Core::FinishConfigureDataTypes));
    407 }
    408 
    409 void SyncBackendHost::StartConfiguration(Callback0::Type* callback) {
    410   // Put syncer in the config mode. DTM will put us in normal mode once it is.
    411   // done. This is to ensure we dont do a normal sync when we are doing model
    412   // association.
    413   core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    414     core_.get(),&SyncBackendHost::Core::DoStartConfiguration, callback));
    415 }
    416 
    417 void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() {
    418   DCHECK_EQ(MessageLoop::current(), frontend_loop_);
    419   // Nudge the syncer. This is necessary for both datatype addition/deletion.
    420   //
    421   // Deletions need a nudge in order to ensure the deletion occurs in a timely
    422   // manner (see issue 56416).
    423   //
    424   // In the case of additions, on the next sync cycle, the syncer should
    425   // notice that the routing info has changed and start the process of
    426   // downloading updates for newly added data types.  Once this is
    427   // complete, the configure_state_.ready_task_ is run via an
    428   // OnInitializationComplete notification.
    429 
    430   if (pending_config_mode_state_->deleted_type) {
    431     core_thread_.message_loop()->PostTask(FROM_HERE,
    432         NewRunnableMethod(core_.get(),
    433         &SyncBackendHost::Core::DeferNudgeForCleanup));
    434   }
    435 
    436   if (pending_config_mode_state_->added_types.none() &&
    437       !core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) {
    438     LOG(WARNING) << "No new types, but initial sync not finished."
    439                  << "Possible sync db corruption / removal.";
    440     // TODO(tim): Log / UMA / count this somehow?
    441     // TODO(tim): If no added types, we could (should?) config only for
    442     // types that are needed... but this is a rare corruption edge case or
    443     // implies the user mucked around with their syncdb, so for now do all.
    444     pending_config_mode_state_->added_types =
    445         syncable::ModelTypeBitSetFromSet(
    446             pending_config_mode_state_->initial_types);
    447   }
    448 
    449   // If we've added types, we always want to request a nudge/config (even if
    450   // the initial sync is ended), in case we could not decrypt the data.
    451   if (pending_config_mode_state_->added_types.none()) {
    452     // No new types - just notify the caller that the types are available.
    453     pending_config_mode_state_->ready_task->Run();
    454   } else {
    455     pending_download_state_.reset(pending_config_mode_state_.release());
    456 
    457     syncable::ModelTypeBitSet types_copy(pending_download_state_->added_types);
    458     if (IsNigoriEnabled())
    459       types_copy.set(syncable::NIGORI);
    460     core_thread_.message_loop()->PostTask(FROM_HERE,
    461          NewRunnableMethod(core_.get(),
    462                            &SyncBackendHost::Core::DoRequestConfig,
    463                            types_copy));
    464   }
    465 
    466   pending_config_mode_state_.reset();
    467 
    468   // Notify the SyncManager about the new types.
    469   core_thread_.message_loop()->PostTask(FROM_HERE,
    470       NewRunnableMethod(core_.get(),
    471                         &SyncBackendHost::Core::DoUpdateEnabledTypes));
    472 }
    473 
    474 void SyncBackendHost::EncryptDataTypes(
    475     const syncable::ModelTypeSet& encrypted_types) {
    476   core_thread_.message_loop()->PostTask(FROM_HERE,
    477      NewRunnableMethod(core_.get(),
    478                        &SyncBackendHost::Core::DoEncryptDataTypes,
    479                        encrypted_types));
    480 }
    481 
    482 void SyncBackendHost::RequestNudge(const tracked_objects::Location& location) {
    483   core_thread_.message_loop()->PostTask(FROM_HERE,
    484       NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge,
    485                         location));
    486 }
    487 
    488 void SyncBackendHost::ActivateDataType(
    489     DataTypeController* data_type_controller,
    490     ChangeProcessor* change_processor) {
    491   base::AutoLock lock(registrar_lock_);
    492 
    493   // Ensure that the given data type is in the PASSIVE group.
    494   browser_sync::ModelSafeRoutingInfo::iterator i =
    495       registrar_.routing_info.find(data_type_controller->type());
    496   DCHECK(i != registrar_.routing_info.end());
    497   DCHECK((*i).second == GROUP_PASSIVE);
    498   syncable::ModelType type = data_type_controller->type();
    499   // Change the data type's routing info to its group.
    500   registrar_.routing_info[type] = data_type_controller->model_safe_group();
    501 
    502   // Add the data type's change processor to the list of change
    503   // processors so it can receive updates.
    504   DCHECK_EQ(processors_.count(type), 0U);
    505   processors_[type] = change_processor;
    506 }
    507 
    508 void SyncBackendHost::DeactivateDataType(
    509     DataTypeController* data_type_controller,
    510     ChangeProcessor* change_processor) {
    511   base::AutoLock lock(registrar_lock_);
    512   registrar_.routing_info.erase(data_type_controller->type());
    513 
    514   std::map<syncable::ModelType, ChangeProcessor*>::size_type erased =
    515       processors_.erase(data_type_controller->type());
    516   DCHECK_EQ(erased, 1U);
    517 }
    518 
    519 bool SyncBackendHost::RequestClearServerData() {
    520   core_thread_.message_loop()->PostTask(FROM_HERE,
    521      NewRunnableMethod(core_.get(),
    522      &SyncBackendHost::Core::DoRequestClearServerData));
    523   return true;
    524 }
    525 
    526 SyncBackendHost::Core::~Core() {
    527 }
    528 
    529 void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) {
    530   if (!host_ || !host_->frontend_)
    531     return;
    532 
    533   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
    534 
    535   if (processing_passphrase_) {
    536     VLOG(1) << "Core received OnPassphraseRequired while processing a "
    537             << "passphrase. Silently dropping.";
    538     return;
    539   }
    540   host_->frontend_->OnPassphraseRequired(for_decryption);
    541 }
    542 
    543 void SyncBackendHost::Core::NotifyPassphraseFailed() {
    544   if (!host_ || !host_->frontend_)
    545     return;
    546 
    547   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
    548 
    549   // When a passphrase fails, we just unset our waiting flag and trigger a
    550   // OnPassphraseRequired(true).
    551   processing_passphrase_ = false;
    552   host_->frontend_->OnPassphraseRequired(true);
    553 }
    554 
    555 void SyncBackendHost::Core::NotifyPassphraseAccepted(
    556     const std::string& bootstrap_token) {
    557   if (!host_ || !host_->frontend_)
    558     return;
    559 
    560   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
    561 
    562   processing_passphrase_ = false;
    563   host_->PersistEncryptionBootstrapToken(bootstrap_token);
    564   host_->frontend_->OnPassphraseAccepted();
    565 }
    566 
    567 void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) {
    568   if (!host_)
    569     return;
    570   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    571   TokenAvailableDetails details(GaiaConstants::kSyncService, token);
    572   NotificationService::current()->Notify(
    573       NotificationType::TOKEN_UPDATED,
    574       NotificationService::AllSources(),
    575       Details<const TokenAvailableDetails>(&details));
    576 }
    577 
    578 void SyncBackendHost::Core::NotifyEncryptionComplete(
    579     const syncable::ModelTypeSet& encrypted_types) {
    580   if (!host_)
    581     return;
    582   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
    583   host_->frontend_->OnEncryptionComplete(encrypted_types);
    584 }
    585 
    586 void SyncBackendHost::Core::FinishConfigureDataTypes() {
    587   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
    588       &SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop));
    589 }
    590 
    591 void SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop() {
    592   host_->FinishConfigureDataTypesOnFrontendLoop();
    593 }
    594 
    595 
    596 void SyncBackendHost::Core::CreateSyncNotifier(
    597     const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) {
    598   const std::string& client_info = webkit_glue::GetUserAgent(GURL());
    599   SyncNotifierFactory sync_notifier_factory(client_info);
    600   sync_notifier_.reset(sync_notifier_factory.CreateSyncNotifier(
    601       *CommandLine::ForCurrentProcess(),
    602       request_context_getter));
    603 }
    604 
    605 SyncBackendHost::Core::DoInitializeOptions::DoInitializeOptions(
    606     const GURL& service_url,
    607     sync_api::HttpPostProviderFactory* http_bridge_factory,
    608     const sync_api::SyncCredentials& credentials,
    609     bool delete_sync_data_folder,
    610     const std::string& restored_key_for_bootstrapping,
    611     bool setup_for_test_mode)
    612     : service_url(service_url),
    613       http_bridge_factory(http_bridge_factory),
    614       credentials(credentials),
    615       delete_sync_data_folder(delete_sync_data_folder),
    616       restored_key_for_bootstrapping(restored_key_for_bootstrapping),
    617       setup_for_test_mode(setup_for_test_mode) {
    618 }
    619 
    620 SyncBackendHost::Core::DoInitializeOptions::~DoInitializeOptions() {}
    621 
    622 sync_api::UserShare* SyncBackendHost::GetUserShare() const {
    623   DCHECK(syncapi_initialized_);
    624   return core_->syncapi()->GetUserShare();
    625 }
    626 
    627 SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() {
    628   DCHECK(syncapi_initialized_);
    629   return core_->syncapi()->GetDetailedStatus();
    630 }
    631 
    632 SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() {
    633   DCHECK(syncapi_initialized_);
    634   return core_->syncapi()->GetStatusSummary();
    635 }
    636 
    637 string16 SyncBackendHost::GetAuthenticatedUsername() const {
    638   DCHECK(syncapi_initialized_);
    639   return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername());
    640 }
    641 
    642 const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const {
    643   return last_auth_error_;
    644 }
    645 
    646 const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const {
    647   return last_snapshot_.get();
    648 }
    649 
    650 void SyncBackendHost::GetWorkers(std::vector<ModelSafeWorker*>* out) {
    651   base::AutoLock lock(registrar_lock_);
    652   out->clear();
    653   for (WorkerMap::const_iterator it = registrar_.workers.begin();
    654        it != registrar_.workers.end(); ++it) {
    655     out->push_back((*it).second);
    656   }
    657 }
    658 
    659 void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
    660   base::AutoLock lock(registrar_lock_);
    661   ModelSafeRoutingInfo copy(registrar_.routing_info);
    662   out->swap(copy);
    663 }
    664 
    665 bool SyncBackendHost::HasUnsyncedItems() const {
    666   DCHECK(syncapi_initialized_);
    667   return core_->syncapi()->HasUnsyncedItems();
    668 }
    669 
    670 SyncBackendHost::Core::Core(SyncBackendHost* backend)
    671     : host_(backend),
    672       syncapi_(new sync_api::SyncManager()),
    673       sync_manager_observer_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
    674       parent_router_(NULL),
    675       processing_passphrase_(false),
    676       deferred_nudge_for_cleanup_requested_(false) {
    677 }
    678 
    679 // Helper to construct a user agent string (ASCII) suitable for use by
    680 // the syncapi for any HTTP communication. This string is used by the sync
    681 // backend for classifying client types when calculating statistics.
    682 std::string MakeUserAgentForSyncapi() {
    683   std::string user_agent;
    684   user_agent = "Chrome ";
    685 #if defined(OS_WIN)
    686   user_agent += "WIN ";
    687 #elif defined(OS_LINUX)
    688   user_agent += "LINUX ";
    689 #elif defined(OS_FREEBSD)
    690   user_agent += "FREEBSD ";
    691 #elif defined(OS_OPENBSD)
    692   user_agent += "OPENBSD ";
    693 #elif defined(OS_MACOSX)
    694   user_agent += "MAC ";
    695 #endif
    696   chrome::VersionInfo version_info;
    697   if (!version_info.is_valid()) {
    698     DLOG(ERROR) << "Unable to create chrome::VersionInfo object";
    699     return user_agent;
    700   }
    701 
    702   user_agent += version_info.Version();
    703   user_agent += " (" + version_info.LastChange() + ")";
    704   if (!version_info.IsOfficialBuild())
    705     user_agent += "-devel";
    706   return user_agent;
    707 }
    708 
    709 void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) {
    710   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    711   processing_passphrase_ = false;
    712 
    713   // Blow away the partial or corrupt sync data folder before doing any more
    714   // initialization, if necessary.
    715   if (options.delete_sync_data_folder) {
    716     DeleteSyncDataFolder();
    717   }
    718 
    719   // Make sure that the directory exists before initializing the backend.
    720   // If it already exists, this will do no harm.
    721   bool success = file_util::CreateDirectory(host_->sync_data_folder_path());
    722   DCHECK(success);
    723 
    724   syncapi_->AddObserver(this);
    725   const FilePath& path_str = host_->sync_data_folder_path();
    726   success = syncapi_->Init(
    727       path_str,
    728       (options.service_url.host() + options.service_url.path()).c_str(),
    729       options.service_url.EffectiveIntPort(),
    730       options.service_url.SchemeIsSecure(),
    731       options.http_bridge_factory,
    732       host_,  // ModelSafeWorkerRegistrar.
    733       MakeUserAgentForSyncapi().c_str(),
    734       options.credentials,
    735       sync_notifier_.get(),
    736       options.restored_key_for_bootstrapping,
    737       options.setup_for_test_mode);
    738   DCHECK(success) << "Syncapi initialization failed!";
    739 }
    740 
    741 void SyncBackendHost::Core::DoUpdateCredentials(
    742     const SyncCredentials& credentials) {
    743   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    744   syncapi_->UpdateCredentials(credentials);
    745 }
    746 
    747 void SyncBackendHost::Core::DoUpdateEnabledTypes() {
    748   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    749   syncapi_->UpdateEnabledTypes();
    750 }
    751 
    752 void SyncBackendHost::Core::DoStartSyncing() {
    753   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    754   syncapi_->StartSyncing();
    755   if (deferred_nudge_for_cleanup_requested_)
    756     syncapi_->RequestNudge(FROM_HERE);
    757   deferred_nudge_for_cleanup_requested_ = false;
    758 }
    759 
    760 void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase,
    761                                             bool is_explicit) {
    762   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    763   syncapi_->SetPassphrase(passphrase, is_explicit);
    764 }
    765 
    766 bool SyncBackendHost::Core::processing_passphrase() const {
    767   DCHECK(MessageLoop::current() == host_->frontend_loop_);
    768   return processing_passphrase_;
    769 }
    770 
    771 void SyncBackendHost::Core::set_processing_passphrase() {
    772   DCHECK(MessageLoop::current() == host_->frontend_loop_);
    773   processing_passphrase_ = true;
    774 }
    775 
    776 void SyncBackendHost::Core::DoEncryptDataTypes(
    777     const syncable::ModelTypeSet& encrypted_types) {
    778   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    779   syncapi_->EncryptDataTypes(encrypted_types);
    780 }
    781 
    782 void SyncBackendHost::Core::DoRequestConfig(
    783     const syncable::ModelTypeBitSet& added_types) {
    784   syncapi_->RequestConfig(added_types);
    785 }
    786 
    787 void SyncBackendHost::Core::DoStartConfiguration(Callback0::Type* callback) {
    788   syncapi_->StartConfigurationMode(callback);
    789 }
    790 
    791 UIModelWorker* SyncBackendHost::ui_worker() {
    792   ModelSafeWorker* w = registrar_.workers[GROUP_UI];
    793   if (w == NULL)
    794     return NULL;
    795   if (w->GetModelSafeGroup() != GROUP_UI)
    796     NOTREACHED();
    797   return static_cast<UIModelWorker*>(w);
    798 }
    799 
    800 void SyncBackendHost::Core::DoShutdown(bool sync_disabled) {
    801   DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
    802 
    803   save_changes_timer_.Stop();
    804   syncapi_->Shutdown();  // Stops the SyncerThread.
    805   syncapi_->RemoveObserver(this);
    806   DisconnectChildJsEventRouter();
    807   host_->ui_worker()->OnSyncerShutdownComplete();
    808 
    809   if (sync_disabled)
    810     DeleteSyncDataFolder();
    811 
    812   host_ = NULL;
    813 }
    814 
    815 ChangeProcessor* SyncBackendHost::Core::GetProcessor(
    816     syncable::ModelType model_type) {
    817   std::map<syncable::ModelType, ChangeProcessor*>::const_iterator it =
    818       host_->processors_.find(model_type);
    819 
    820   // Until model association happens for a datatype, it will not appear in
    821   // the processors list.  During this time, it is OK to drop changes on
    822   // the floor (since model association has not happened yet).  When the
    823   // data type is activated, model association takes place then the change
    824   // processor is added to the processors_ list.  This all happens on
    825   // the UI thread so we will never drop any changes after model
    826   // association.
    827   if (it == host_->processors_.end())
    828     return NULL;
    829 
    830   if (!IsCurrentThreadSafeForModel(model_type)) {
    831     NOTREACHED() << "Changes applied on wrong thread.";
    832     return NULL;
    833   }
    834 
    835   // Now that we're sure we're on the correct thread, we can access the
    836   // ChangeProcessor.
    837   ChangeProcessor* processor = it->second;
    838 
    839   // Ensure the change processor is willing to accept changes.
    840   if (!processor->IsRunning())
    841     return NULL;
    842 
    843   return processor;
    844 }
    845 
    846 void SyncBackendHost::Core::OnChangesApplied(
    847     syncable::ModelType model_type,
    848     const sync_api::BaseTransaction* trans,
    849     const sync_api::SyncManager::ChangeRecord* changes,
    850     int change_count) {
    851   if (!host_ || !host_->frontend_) {
    852     DCHECK(false) << "OnChangesApplied called after Shutdown?";
    853     return;
    854   }
    855   ChangeProcessor* processor = GetProcessor(model_type);
    856   if (!processor)
    857     return;
    858 
    859   processor->ApplyChangesFromSyncModel(trans, changes, change_count);
    860 }
    861 
    862 void SyncBackendHost::Core::OnChangesComplete(
    863     syncable::ModelType model_type) {
    864   if (!host_ || !host_->frontend_) {
    865     DCHECK(false) << "OnChangesComplete called after Shutdown?";
    866     return;
    867   }
    868 
    869   ChangeProcessor* processor = GetProcessor(model_type);
    870   if (!processor)
    871     return;
    872 
    873   // This call just notifies the processor that it can commit, it already
    874   // buffered any changes it plans to makes so needs no further information.
    875   processor->CommitChangesFromSyncModel();
    876 }
    877 
    878 
    879 void SyncBackendHost::Core::OnSyncCycleCompleted(
    880     const SyncSessionSnapshot* snapshot) {
    881   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
    882       &Core::HandleSyncCycleCompletedOnFrontendLoop,
    883       new SyncSessionSnapshot(*snapshot)));
    884 }
    885 
    886 void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop(
    887     SyncSessionSnapshot* snapshot) {
    888   if (!host_ || !host_->frontend_)
    889     return;
    890   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
    891 
    892   host_->last_snapshot_.reset(snapshot);
    893 
    894   const syncable::ModelTypeSet& to_migrate =
    895       snapshot->syncer_status.types_needing_local_migration;
    896   if (!to_migrate.empty())
    897     host_->frontend_->OnMigrationNeededForTypes(to_migrate);
    898 
    899   // If we are waiting for a configuration change, check here to see
    900   // if this sync cycle has initialized all of the types we've been
    901   // waiting for.
    902   if (host_->pending_download_state_.get()) {
    903     bool found_all_added = true;
    904     for (syncable::ModelTypeSet::const_iterator it =
    905              host_->pending_download_state_->initial_types.begin();
    906          it != host_->pending_download_state_->initial_types.end();
    907          ++it) {
    908       if (host_->pending_download_state_->added_types.test(*it))
    909         found_all_added &= snapshot->initial_sync_ended.test(*it);
    910     }
    911     if (!found_all_added) {
    912       NOTREACHED() << "Update didn't return updates for all types requested.";
    913     } else {
    914       host_->pending_download_state_->ready_task->Run();
    915     }
    916     host_->pending_download_state_.reset();
    917   }
    918   host_->frontend_->OnSyncCycleCompleted();
    919 }
    920 
    921 void SyncBackendHost::Core::OnInitializationComplete() {
    922   if (!host_ || !host_->frontend_)
    923     return;  // We may have been told to Shutdown before initialization
    924              // completed.
    925 
    926   // We could be on some random sync backend thread, so MessageLoop::current()
    927   // can definitely be null in here.
    928   host_->frontend_loop_->PostTask(FROM_HERE,
    929       NewRunnableMethod(this,
    930                         &Core::HandleInitalizationCompletedOnFrontendLoop));
    931 
    932   // Initialization is complete, so we can schedule recurring SaveChanges.
    933   host_->core_thread_.message_loop()->PostTask(FROM_HERE,
    934       NewRunnableMethod(this, &Core::StartSavingChanges));
    935 }
    936 
    937 void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() {
    938   if (!host_)
    939     return;
    940   host_->HandleInitializationCompletedOnFrontendLoop();
    941 }
    942 
    943 void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() {
    944   if (!frontend_)
    945     return;
    946   syncapi_initialized_ = true;
    947   frontend_->OnBackendInitialized();
    948 }
    949 
    950 bool SyncBackendHost::Core::IsCurrentThreadSafeForModel(
    951     syncable::ModelType model_type) {
    952   base::AutoLock lock(host_->registrar_lock_);
    953 
    954   browser_sync::ModelSafeRoutingInfo::const_iterator routing_it =
    955       host_->registrar_.routing_info.find(model_type);
    956   if (routing_it == host_->registrar_.routing_info.end())
    957     return false;
    958   browser_sync::ModelSafeGroup group = routing_it->second;
    959   WorkerMap::const_iterator worker_it = host_->registrar_.workers.find(group);
    960   if (worker_it == host_->registrar_.workers.end())
    961     return false;
    962   ModelSafeWorker* worker = worker_it->second;
    963   return worker->CurrentThreadIsWorkThread();
    964 }
    965 
    966 void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) {
    967   // Post to our core loop so we can modify state. Could be on another thread.
    968   host_->frontend_loop_->PostTask(FROM_HERE,
    969       NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop,
    970       auth_error));
    971 }
    972 
    973 void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) {
    974   host_->frontend_loop_->PostTask(FROM_HERE,
    975       NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption));
    976 }
    977 
    978 void SyncBackendHost::Core::OnPassphraseFailed() {
    979   host_->frontend_loop_->PostTask(FROM_HERE,
    980       NewRunnableMethod(this, &Core::NotifyPassphraseFailed));
    981 }
    982 
    983 void SyncBackendHost::Core::OnPassphraseAccepted(
    984     const std::string& bootstrap_token) {
    985   host_->frontend_loop_->PostTask(FROM_HERE,
    986       NewRunnableMethod(this, &Core::NotifyPassphraseAccepted,
    987           bootstrap_token));
    988 }
    989 
    990 void SyncBackendHost::Core::OnStopSyncingPermanently() {
    991   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
    992       &Core::HandleStopSyncingPermanentlyOnFrontendLoop));
    993 }
    994 
    995 void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) {
    996   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
    997       &Core::NotifyUpdatedToken, token));
    998 }
    999 
   1000 void SyncBackendHost::Core::OnClearServerDataSucceeded() {
   1001   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
   1002       &Core::HandleClearServerDataSucceededOnFrontendLoop));
   1003 }
   1004 
   1005 void SyncBackendHost::Core::OnClearServerDataFailed() {
   1006   host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
   1007       &Core::HandleClearServerDataFailedOnFrontendLoop));
   1008 }
   1009 
   1010 void SyncBackendHost::Core::OnEncryptionComplete(
   1011     const syncable::ModelTypeSet& encrypted_types) {
   1012   host_->frontend_loop_->PostTask(
   1013       FROM_HERE,
   1014       NewRunnableMethod(this, &Core::NotifyEncryptionComplete,
   1015                         encrypted_types));
   1016 }
   1017 
   1018 void SyncBackendHost::Core::RouteJsEvent(
   1019     const std::string& name, const JsArgList& args,
   1020     const JsEventHandler* target) {
   1021   host_->frontend_loop_->PostTask(
   1022       FROM_HERE, NewRunnableMethod(
   1023           this, &Core::RouteJsEventOnFrontendLoop, name, args, target));
   1024 }
   1025 
   1026 void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
   1027   if (!host_ || !host_->frontend_)
   1028     return;
   1029   host_->frontend_->OnStopSyncingPermanently();
   1030 }
   1031 
   1032 void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() {
   1033   if (!host_ || !host_->frontend_)
   1034     return;
   1035   host_->frontend_->OnClearServerDataSucceeded();
   1036 }
   1037 
   1038 void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() {
   1039   if (!host_ || !host_->frontend_)
   1040     return;
   1041   host_->frontend_->OnClearServerDataFailed();
   1042 }
   1043 
   1044 void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop(
   1045     const GoogleServiceAuthError& new_auth_error) {
   1046   if (!host_ || !host_->frontend_)
   1047     return;
   1048 
   1049   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1050 
   1051   host_->last_auth_error_ = new_auth_error;
   1052   host_->frontend_->OnAuthError();
   1053 }
   1054 
   1055 void SyncBackendHost::Core::RouteJsEventOnFrontendLoop(
   1056     const std::string& name, const JsArgList& args,
   1057     const JsEventHandler* target) {
   1058   if (!host_ || !parent_router_)
   1059     return;
   1060 
   1061   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1062 
   1063   parent_router_->RouteJsEvent(name, args, target);
   1064 }
   1065 
   1066 void SyncBackendHost::Core::StartSavingChanges() {
   1067   save_changes_timer_.Start(
   1068       base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds),
   1069       this, &Core::SaveChanges);
   1070 }
   1071 
   1072 void SyncBackendHost::Core::DoRequestNudge(
   1073     const tracked_objects::Location& nudge_location) {
   1074   syncapi_->RequestNudge(nudge_location);
   1075 }
   1076 
   1077 void SyncBackendHost::Core::DoRequestClearServerData() {
   1078   syncapi_->RequestClearServerData();
   1079 }
   1080 
   1081 void SyncBackendHost::Core::SaveChanges() {
   1082   syncapi_->SaveChanges();
   1083 }
   1084 
   1085 void SyncBackendHost::Core::DeleteSyncDataFolder() {
   1086   if (file_util::DirectoryExists(host_->sync_data_folder_path())) {
   1087     if (!file_util::Delete(host_->sync_data_folder_path(), true))
   1088       LOG(DFATAL) << "Could not delete the Sync Data folder.";
   1089   }
   1090 }
   1091 
   1092 void SyncBackendHost::Core::SetParentJsEventRouter(JsEventRouter* router) {
   1093   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1094   DCHECK(router);
   1095   parent_router_ = router;
   1096   MessageLoop* core_message_loop = host_->core_thread_.message_loop();
   1097   CHECK(core_message_loop);
   1098   core_message_loop->PostTask(
   1099       FROM_HERE,
   1100       NewRunnableMethod(this,
   1101                         &SyncBackendHost::Core::ConnectChildJsEventRouter));
   1102 }
   1103 
   1104 void SyncBackendHost::Core::RemoveParentJsEventRouter() {
   1105   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1106   parent_router_ = NULL;
   1107   MessageLoop* core_message_loop = host_->core_thread_.message_loop();
   1108   CHECK(core_message_loop);
   1109   core_message_loop->PostTask(
   1110       FROM_HERE,
   1111       NewRunnableMethod(this,
   1112                         &SyncBackendHost::Core::DisconnectChildJsEventRouter));
   1113 }
   1114 
   1115 const JsEventRouter* SyncBackendHost::Core::GetParentJsEventRouter() const {
   1116   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1117   return parent_router_;
   1118 }
   1119 
   1120 void SyncBackendHost::Core::ProcessMessage(
   1121     const std::string& name, const JsArgList& args,
   1122     const JsEventHandler* sender) {
   1123   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
   1124   MessageLoop* core_message_loop = host_->core_thread_.message_loop();
   1125   CHECK(core_message_loop);
   1126   core_message_loop->PostTask(
   1127       FROM_HERE,
   1128       NewRunnableMethod(this,
   1129                         &SyncBackendHost::Core::DoProcessMessage,
   1130                         name, args, sender));
   1131 }
   1132 
   1133 void SyncBackendHost::Core::ConnectChildJsEventRouter() {
   1134   DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop());
   1135   // We need this check since AddObserver() can be called at most once
   1136   // for a given observer.
   1137   if (!syncapi_->GetJsBackend()->GetParentJsEventRouter()) {
   1138     syncapi_->GetJsBackend()->SetParentJsEventRouter(this);
   1139     syncapi_->AddObserver(&sync_manager_observer_);
   1140   }
   1141 }
   1142 
   1143 void SyncBackendHost::Core::DisconnectChildJsEventRouter() {
   1144   DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop());
   1145   syncapi_->GetJsBackend()->RemoveParentJsEventRouter();
   1146   syncapi_->RemoveObserver(&sync_manager_observer_);
   1147 }
   1148 
   1149 void SyncBackendHost::Core::DoProcessMessage(
   1150     const std::string& name, const JsArgList& args,
   1151     const JsEventHandler* sender) {
   1152   DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop());
   1153   syncapi_->GetJsBackend()->ProcessMessage(name, args, sender);
   1154 }
   1155 
   1156 void SyncBackendHost::Core::DeferNudgeForCleanup() {
   1157   DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop());
   1158   deferred_nudge_for_cleanup_requested_ = true;
   1159 }
   1160 
   1161 }  // namespace browser_sync
   1162