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
SyncBackendHost(Profile * profile)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
SyncBackendHost()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
~SyncBackendHost()81 SyncBackendHost::~SyncBackendHost() {
82 DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor.";
83 DCHECK(registrar_.workers.empty());
84 }
85
Initialize(SyncFrontend * frontend,const GURL & sync_service_url,const syncable::ModelTypeSet & types,net::URLRequestContextGetter * baseline_context_getter,const SyncCredentials & credentials,bool delete_sync_data_folder)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
PersistEncryptionBootstrapToken(const std::string & token)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
RestoreEncryptionBootstrapToken()161 std::string SyncBackendHost::RestoreEncryptionBootstrapToken() {
162 PrefService* prefs = profile_->GetPrefs();
163 std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken);
164 return token;
165 }
166
IsNigoriEnabled() const167 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
IsUsingExplicitPassphrase()176 bool SyncBackendHost::IsUsingExplicitPassphrase() {
177 return IsNigoriEnabled() && syncapi_initialized_ &&
178 core_->syncapi()->InitialSyncEndedForAllEnabledTypes() &&
179 core_->syncapi()->IsUsingExplicitPassphrase();
180 }
181
IsCryptographerReady(const sync_api::BaseTransaction * trans) const182 bool SyncBackendHost::IsCryptographerReady(
183 const sync_api::BaseTransaction* trans) const {
184 return syncapi_initialized_ && trans->GetCryptographer()->is_ready();
185 }
186
GetJsBackend()187 JsBackend* SyncBackendHost::GetJsBackend() {
188 if (syncapi_initialized_) {
189 return core_.get();
190 } else {
191 NOTREACHED();
192 return NULL;
193 }
194 }
195
MakeHttpBridgeFactory(net::URLRequestContextGetter * getter)196 sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory(
197 net::URLRequestContextGetter* getter) {
198 return new HttpBridgeFactory(getter);
199 }
200
InitCore(const Core::DoInitializeOptions & options)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
UpdateCredentials(const SyncCredentials & credentials)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
StartSyncingWithServer()214 void SyncBackendHost::StartSyncingWithServer() {
215 core_thread_.message_loop()->PostTask(FROM_HERE,
216 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
217 }
218
SetPassphrase(const std::string & passphrase,bool is_explicit)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
Shutdown(bool sync_disabled)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
GetAutofillMigrationState()292 SyncBackendHost::GetAutofillMigrationState() {
293 return core_->syncapi()->GetAutofillMigrationState();
294 }
295
SetAutofillMigrationState(syncable::AutofillMigrationState state)296 void SyncBackendHost::SetAutofillMigrationState(
297 syncable::AutofillMigrationState state) {
298 return core_->syncapi()->SetAutofillMigrationState(state);
299 }
300
301 syncable::AutofillMigrationDebugInfo
GetAutofillMigrationDebugInfo()302 SyncBackendHost::GetAutofillMigrationDebugInfo() {
303 return core_->syncapi()->GetAutofillMigrationDebugInfo();
304 }
305
SetAutofillMigrationDebugInfo(syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set,const syncable::AutofillMigrationDebugInfo & info)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
ConfigureAutofillMigration()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::
PendingConfigureDataTypesState()349 PendingConfigureDataTypesState() : deleted_type(false) {}
350
351 SyncBackendHost::PendingConfigureDataTypesState::
~PendingConfigureDataTypesState()352 ~PendingConfigureDataTypesState() {}
353
354 // static
355 SyncBackendHost::PendingConfigureDataTypesState*
MakePendingConfigModeState(const DataTypeController::TypeMap & data_type_controllers,const syncable::ModelTypeSet & types,CancelableTask * ready_task,ModelSafeRoutingInfo * routing_info)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
ConfigureDataTypes(const DataTypeController::TypeMap & data_type_controllers,const syncable::ModelTypeSet & types,CancelableTask * ready_task)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 ®istrar_.routing_info));
403 }
404
405 StartConfiguration(NewCallback(core_.get(),
406 &SyncBackendHost::Core::FinishConfigureDataTypes));
407 }
408
StartConfiguration(Callback0::Type * callback)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
FinishConfigureDataTypesOnFrontendLoop()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
EncryptDataTypes(const syncable::ModelTypeSet & encrypted_types)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
RequestNudge(const tracked_objects::Location & location)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
ActivateDataType(DataTypeController * data_type_controller,ChangeProcessor * change_processor)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
DeactivateDataType(DataTypeController * data_type_controller,ChangeProcessor * change_processor)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
RequestClearServerData()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
~Core()526 SyncBackendHost::Core::~Core() {
527 }
528
NotifyPassphraseRequired(bool for_decryption)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
NotifyPassphraseFailed()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
NotifyPassphraseAccepted(const std::string & bootstrap_token)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
NotifyUpdatedToken(const std::string & token)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
NotifyEncryptionComplete(const syncable::ModelTypeSet & encrypted_types)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
FinishConfigureDataTypes()586 void SyncBackendHost::Core::FinishConfigureDataTypes() {
587 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
588 &SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop));
589 }
590
FinishConfigureDataTypesOnFrontendLoop()591 void SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop() {
592 host_->FinishConfigureDataTypesOnFrontendLoop();
593 }
594
595
CreateSyncNotifier(const scoped_refptr<net::URLRequestContextGetter> & request_context_getter)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
DoInitializeOptions(const GURL & service_url,sync_api::HttpPostProviderFactory * http_bridge_factory,const sync_api::SyncCredentials & credentials,bool delete_sync_data_folder,const std::string & restored_key_for_bootstrapping,bool setup_for_test_mode)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
~DoInitializeOptions()620 SyncBackendHost::Core::DoInitializeOptions::~DoInitializeOptions() {}
621
GetUserShare() const622 sync_api::UserShare* SyncBackendHost::GetUserShare() const {
623 DCHECK(syncapi_initialized_);
624 return core_->syncapi()->GetUserShare();
625 }
626
GetDetailedStatus()627 SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() {
628 DCHECK(syncapi_initialized_);
629 return core_->syncapi()->GetDetailedStatus();
630 }
631
GetStatusSummary()632 SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() {
633 DCHECK(syncapi_initialized_);
634 return core_->syncapi()->GetStatusSummary();
635 }
636
GetAuthenticatedUsername() const637 string16 SyncBackendHost::GetAuthenticatedUsername() const {
638 DCHECK(syncapi_initialized_);
639 return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername());
640 }
641
GetAuthError() const642 const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const {
643 return last_auth_error_;
644 }
645
GetLastSessionSnapshot() const646 const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const {
647 return last_snapshot_.get();
648 }
649
GetWorkers(std::vector<ModelSafeWorker * > * out)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
GetModelSafeRoutingInfo(ModelSafeRoutingInfo * out)659 void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
660 base::AutoLock lock(registrar_lock_);
661 ModelSafeRoutingInfo copy(registrar_.routing_info);
662 out->swap(copy);
663 }
664
HasUnsyncedItems() const665 bool SyncBackendHost::HasUnsyncedItems() const {
666 DCHECK(syncapi_initialized_);
667 return core_->syncapi()->HasUnsyncedItems();
668 }
669
Core(SyncBackendHost * backend)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.
MakeUserAgentForSyncapi()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
DoInitialize(const DoInitializeOptions & options)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
DoUpdateCredentials(const SyncCredentials & credentials)741 void SyncBackendHost::Core::DoUpdateCredentials(
742 const SyncCredentials& credentials) {
743 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
744 syncapi_->UpdateCredentials(credentials);
745 }
746
DoUpdateEnabledTypes()747 void SyncBackendHost::Core::DoUpdateEnabledTypes() {
748 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
749 syncapi_->UpdateEnabledTypes();
750 }
751
DoStartSyncing()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
DoSetPassphrase(const std::string & passphrase,bool is_explicit)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
processing_passphrase() const766 bool SyncBackendHost::Core::processing_passphrase() const {
767 DCHECK(MessageLoop::current() == host_->frontend_loop_);
768 return processing_passphrase_;
769 }
770
set_processing_passphrase()771 void SyncBackendHost::Core::set_processing_passphrase() {
772 DCHECK(MessageLoop::current() == host_->frontend_loop_);
773 processing_passphrase_ = true;
774 }
775
DoEncryptDataTypes(const syncable::ModelTypeSet & encrypted_types)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
DoRequestConfig(const syncable::ModelTypeBitSet & added_types)782 void SyncBackendHost::Core::DoRequestConfig(
783 const syncable::ModelTypeBitSet& added_types) {
784 syncapi_->RequestConfig(added_types);
785 }
786
DoStartConfiguration(Callback0::Type * callback)787 void SyncBackendHost::Core::DoStartConfiguration(Callback0::Type* callback) {
788 syncapi_->StartConfigurationMode(callback);
789 }
790
ui_worker()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
DoShutdown(bool sync_disabled)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
GetProcessor(syncable::ModelType model_type)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
OnChangesApplied(syncable::ModelType model_type,const sync_api::BaseTransaction * trans,const sync_api::SyncManager::ChangeRecord * changes,int change_count)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
OnChangesComplete(syncable::ModelType model_type)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
OnSyncCycleCompleted(const SyncSessionSnapshot * snapshot)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
HandleSyncCycleCompletedOnFrontendLoop(SyncSessionSnapshot * snapshot)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
OnInitializationComplete()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
HandleInitalizationCompletedOnFrontendLoop()937 void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() {
938 if (!host_)
939 return;
940 host_->HandleInitializationCompletedOnFrontendLoop();
941 }
942
HandleInitializationCompletedOnFrontendLoop()943 void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() {
944 if (!frontend_)
945 return;
946 syncapi_initialized_ = true;
947 frontend_->OnBackendInitialized();
948 }
949
IsCurrentThreadSafeForModel(syncable::ModelType model_type)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
OnAuthError(const AuthError & auth_error)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
OnPassphraseRequired(bool for_decryption)973 void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) {
974 host_->frontend_loop_->PostTask(FROM_HERE,
975 NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption));
976 }
977
OnPassphraseFailed()978 void SyncBackendHost::Core::OnPassphraseFailed() {
979 host_->frontend_loop_->PostTask(FROM_HERE,
980 NewRunnableMethod(this, &Core::NotifyPassphraseFailed));
981 }
982
OnPassphraseAccepted(const std::string & bootstrap_token)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
OnStopSyncingPermanently()990 void SyncBackendHost::Core::OnStopSyncingPermanently() {
991 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
992 &Core::HandleStopSyncingPermanentlyOnFrontendLoop));
993 }
994
OnUpdatedToken(const std::string & token)995 void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) {
996 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
997 &Core::NotifyUpdatedToken, token));
998 }
999
OnClearServerDataSucceeded()1000 void SyncBackendHost::Core::OnClearServerDataSucceeded() {
1001 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
1002 &Core::HandleClearServerDataSucceededOnFrontendLoop));
1003 }
1004
OnClearServerDataFailed()1005 void SyncBackendHost::Core::OnClearServerDataFailed() {
1006 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
1007 &Core::HandleClearServerDataFailedOnFrontendLoop));
1008 }
1009
OnEncryptionComplete(const syncable::ModelTypeSet & encrypted_types)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
RouteJsEvent(const std::string & name,const JsArgList & args,const JsEventHandler * target)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
HandleStopSyncingPermanentlyOnFrontendLoop()1026 void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
1027 if (!host_ || !host_->frontend_)
1028 return;
1029 host_->frontend_->OnStopSyncingPermanently();
1030 }
1031
HandleClearServerDataSucceededOnFrontendLoop()1032 void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() {
1033 if (!host_ || !host_->frontend_)
1034 return;
1035 host_->frontend_->OnClearServerDataSucceeded();
1036 }
1037
HandleClearServerDataFailedOnFrontendLoop()1038 void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() {
1039 if (!host_ || !host_->frontend_)
1040 return;
1041 host_->frontend_->OnClearServerDataFailed();
1042 }
1043
HandleAuthErrorEventOnFrontendLoop(const GoogleServiceAuthError & new_auth_error)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
RouteJsEventOnFrontendLoop(const std::string & name,const JsArgList & args,const JsEventHandler * target)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
StartSavingChanges()1066 void SyncBackendHost::Core::StartSavingChanges() {
1067 save_changes_timer_.Start(
1068 base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds),
1069 this, &Core::SaveChanges);
1070 }
1071
DoRequestNudge(const tracked_objects::Location & nudge_location)1072 void SyncBackendHost::Core::DoRequestNudge(
1073 const tracked_objects::Location& nudge_location) {
1074 syncapi_->RequestNudge(nudge_location);
1075 }
1076
DoRequestClearServerData()1077 void SyncBackendHost::Core::DoRequestClearServerData() {
1078 syncapi_->RequestClearServerData();
1079 }
1080
SaveChanges()1081 void SyncBackendHost::Core::SaveChanges() {
1082 syncapi_->SaveChanges();
1083 }
1084
DeleteSyncDataFolder()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
SetParentJsEventRouter(JsEventRouter * router)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
RemoveParentJsEventRouter()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
GetParentJsEventRouter() const1115 const JsEventRouter* SyncBackendHost::Core::GetParentJsEventRouter() const {
1116 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
1117 return parent_router_;
1118 }
1119
ProcessMessage(const std::string & name,const JsArgList & args,const JsEventHandler * sender)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
ConnectChildJsEventRouter()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
DisconnectChildJsEventRouter()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
DoProcessMessage(const std::string & name,const JsArgList & args,const JsEventHandler * sender)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
DeferNudgeForCleanup()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