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
45static const int kSaveChangesIntervalSeconds = 10;
46static const FilePath::CharType kSyncDataFolderName[] =
47    FILE_PATH_LITERAL("Sync Data");
48
49using browser_sync::DataTypeController;
50using sync_notifier::SyncNotifierFactory;
51typedef TokenService::TokenAvailableDetails TokenAvailableDetails;
52
53typedef GoogleServiceAuthError AuthError;
54
55namespace browser_sync {
56
57using sessions::SyncSessionSnapshot;
58using sync_api::SyncCredentials;
59
60SyncBackendHost::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
72SyncBackendHost::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
81SyncBackendHost::~SyncBackendHost() {
82  DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor.";
83  DCHECK(registrar_.workers.empty());
84}
85
86void 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
153void SyncBackendHost::PersistEncryptionBootstrapToken(
154    const std::string& token) {
155  PrefService* prefs = profile_->GetPrefs();
156
157  prefs->SetString(prefs::kEncryptionBootstrapToken, token);
158  prefs->ScheduleSavePersistentPrefs();
159}
160
161std::string SyncBackendHost::RestoreEncryptionBootstrapToken() {
162  PrefService* prefs = profile_->GetPrefs();
163  std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken);
164  return token;
165}
166
167bool 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
176bool SyncBackendHost::IsUsingExplicitPassphrase() {
177  return IsNigoriEnabled() && syncapi_initialized_ &&
178      core_->syncapi()->InitialSyncEndedForAllEnabledTypes() &&
179      core_->syncapi()->IsUsingExplicitPassphrase();
180}
181
182bool SyncBackendHost::IsCryptographerReady(
183    const sync_api::BaseTransaction* trans) const {
184  return syncapi_initialized_ && trans->GetCryptographer()->is_ready();
185}
186
187JsBackend* SyncBackendHost::GetJsBackend() {
188  if (syncapi_initialized_) {
189    return core_.get();
190  } else {
191    NOTREACHED();
192    return NULL;
193  }
194}
195
196sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory(
197    net::URLRequestContextGetter* getter) {
198  return new HttpBridgeFactory(getter);
199}
200
201void 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
207void 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
214void SyncBackendHost::StartSyncingWithServer() {
215  core_thread_.message_loop()->PostTask(FROM_HERE,
216      NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
217}
218
219void 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
241void 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
291syncable::AutofillMigrationState
292    SyncBackendHost::GetAutofillMigrationState() {
293  return core_->syncapi()->GetAutofillMigrationState();
294}
295
296void SyncBackendHost::SetAutofillMigrationState(
297    syncable::AutofillMigrationState state) {
298  return core_->syncapi()->SetAutofillMigrationState(state);
299}
300
301syncable::AutofillMigrationDebugInfo
302    SyncBackendHost::GetAutofillMigrationDebugInfo() {
303  return core_->syncapi()->GetAutofillMigrationDebugInfo();
304}
305
306void 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
312void 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
348SyncBackendHost::PendingConfigureDataTypesState::
349PendingConfigureDataTypesState() : deleted_type(false) {}
350
351SyncBackendHost::PendingConfigureDataTypesState::
352~PendingConfigureDataTypesState() {}
353
354// static
355SyncBackendHost::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
385void 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
409void 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
417void 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
474void 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
482void 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
488void 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
508void 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
519bool SyncBackendHost::RequestClearServerData() {
520  core_thread_.message_loop()->PostTask(FROM_HERE,
521     NewRunnableMethod(core_.get(),
522     &SyncBackendHost::Core::DoRequestClearServerData));
523  return true;
524}
525
526SyncBackendHost::Core::~Core() {
527}
528
529void 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
543void 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
555void 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
567void 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
578void 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
586void SyncBackendHost::Core::FinishConfigureDataTypes() {
587  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
588      &SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop));
589}
590
591void SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop() {
592  host_->FinishConfigureDataTypesOnFrontendLoop();
593}
594
595
596void 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
605SyncBackendHost::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
620SyncBackendHost::Core::DoInitializeOptions::~DoInitializeOptions() {}
621
622sync_api::UserShare* SyncBackendHost::GetUserShare() const {
623  DCHECK(syncapi_initialized_);
624  return core_->syncapi()->GetUserShare();
625}
626
627SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() {
628  DCHECK(syncapi_initialized_);
629  return core_->syncapi()->GetDetailedStatus();
630}
631
632SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() {
633  DCHECK(syncapi_initialized_);
634  return core_->syncapi()->GetStatusSummary();
635}
636
637string16 SyncBackendHost::GetAuthenticatedUsername() const {
638  DCHECK(syncapi_initialized_);
639  return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername());
640}
641
642const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const {
643  return last_auth_error_;
644}
645
646const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const {
647  return last_snapshot_.get();
648}
649
650void 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
659void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
660  base::AutoLock lock(registrar_lock_);
661  ModelSafeRoutingInfo copy(registrar_.routing_info);
662  out->swap(copy);
663}
664
665bool SyncBackendHost::HasUnsyncedItems() const {
666  DCHECK(syncapi_initialized_);
667  return core_->syncapi()->HasUnsyncedItems();
668}
669
670SyncBackendHost::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.
682std::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
709void 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
741void SyncBackendHost::Core::DoUpdateCredentials(
742    const SyncCredentials& credentials) {
743  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
744  syncapi_->UpdateCredentials(credentials);
745}
746
747void SyncBackendHost::Core::DoUpdateEnabledTypes() {
748  DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
749  syncapi_->UpdateEnabledTypes();
750}
751
752void 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
760void 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
766bool SyncBackendHost::Core::processing_passphrase() const {
767  DCHECK(MessageLoop::current() == host_->frontend_loop_);
768  return processing_passphrase_;
769}
770
771void SyncBackendHost::Core::set_processing_passphrase() {
772  DCHECK(MessageLoop::current() == host_->frontend_loop_);
773  processing_passphrase_ = true;
774}
775
776void 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
782void SyncBackendHost::Core::DoRequestConfig(
783    const syncable::ModelTypeBitSet& added_types) {
784  syncapi_->RequestConfig(added_types);
785}
786
787void SyncBackendHost::Core::DoStartConfiguration(Callback0::Type* callback) {
788  syncapi_->StartConfigurationMode(callback);
789}
790
791UIModelWorker* 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
800void 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
815ChangeProcessor* 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
846void 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
862void 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
879void 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
886void 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
921void 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
937void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() {
938  if (!host_)
939    return;
940  host_->HandleInitializationCompletedOnFrontendLoop();
941}
942
943void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() {
944  if (!frontend_)
945    return;
946  syncapi_initialized_ = true;
947  frontend_->OnBackendInitialized();
948}
949
950bool 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
966void 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
973void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) {
974  host_->frontend_loop_->PostTask(FROM_HERE,
975      NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption));
976}
977
978void SyncBackendHost::Core::OnPassphraseFailed() {
979  host_->frontend_loop_->PostTask(FROM_HERE,
980      NewRunnableMethod(this, &Core::NotifyPassphraseFailed));
981}
982
983void 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
990void SyncBackendHost::Core::OnStopSyncingPermanently() {
991  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
992      &Core::HandleStopSyncingPermanentlyOnFrontendLoop));
993}
994
995void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) {
996  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
997      &Core::NotifyUpdatedToken, token));
998}
999
1000void SyncBackendHost::Core::OnClearServerDataSucceeded() {
1001  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
1002      &Core::HandleClearServerDataSucceededOnFrontendLoop));
1003}
1004
1005void SyncBackendHost::Core::OnClearServerDataFailed() {
1006  host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
1007      &Core::HandleClearServerDataFailedOnFrontendLoop));
1008}
1009
1010void 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
1018void 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
1026void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
1027  if (!host_ || !host_->frontend_)
1028    return;
1029  host_->frontend_->OnStopSyncingPermanently();
1030}
1031
1032void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() {
1033  if (!host_ || !host_->frontend_)
1034    return;
1035  host_->frontend_->OnClearServerDataSucceeded();
1036}
1037
1038void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() {
1039  if (!host_ || !host_->frontend_)
1040    return;
1041  host_->frontend_->OnClearServerDataFailed();
1042}
1043
1044void 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
1055void 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
1066void SyncBackendHost::Core::StartSavingChanges() {
1067  save_changes_timer_.Start(
1068      base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds),
1069      this, &Core::SaveChanges);
1070}
1071
1072void SyncBackendHost::Core::DoRequestNudge(
1073    const tracked_objects::Location& nudge_location) {
1074  syncapi_->RequestNudge(nudge_location);
1075}
1076
1077void SyncBackendHost::Core::DoRequestClearServerData() {
1078  syncapi_->RequestClearServerData();
1079}
1080
1081void SyncBackendHost::Core::SaveChanges() {
1082  syncapi_->SaveChanges();
1083}
1084
1085void 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
1092void 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
1104void 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
1115const JsEventRouter* SyncBackendHost::Core::GetParentJsEventRouter() const {
1116  DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
1117  return parent_router_;
1118}
1119
1120void 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
1133void 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
1143void 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
1149void 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
1156void 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