Index: chrome/browser/sync/glue/sync_backend_host.cc |
=================================================================== |
--- chrome/browser/sync/glue/sync_backend_host.cc (revision 0) |
+++ chrome/browser/sync/glue/sync_backend_host.cc (revision 0) |
@@ -0,0 +1,308 @@ |
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifdef CHROME_PERSONALIZATION |
+ |
+#include "base/file_version_info.h" |
+#include "base/file_util.h" |
+#include "base/string_util.h" |
+#include "chrome/browser/sync/glue/sync_backend_host.h" |
+#include "chrome/browser/sync/glue/http_bridge.h" |
+#include "chrome/browser/sync/glue/bookmark_model_worker.h" |
+#include "webkit/glue/webkit_glue.h" |
+ |
+static const char kSwitchSyncServiceURL[] = "sync-url"; |
+static const char kSwitchSyncServicePort[] = "sync-port"; |
+static const int kSaveChangesIntervalSeconds = 10; |
+static const char kGaiaServiceId[] = "chromiumsync"; |
+static const char kGaiaSourceForChrome[] = "ChromiumBrowser"; |
+static const FilePath::CharType kSyncDataFolderName[] = |
+ FILE_PATH_LITERAL("Sync Data"); |
+ |
+namespace browser_sync { |
+ |
+SyncBackendHost::SyncBackendHost(SyncFrontend* frontend, |
+ const FilePath& profile_path) |
+ : core_thread_("Chrome_SyncCoreThread"), |
+ frontend_loop_(MessageLoop::current()), |
+ bookmark_model_worker_(NULL), |
+ frontend_(frontend), |
+ sync_data_folder_path_(profile_path.Append(kSyncDataFolderName)), |
+ last_auth_error_(AUTH_ERROR_NONE) { |
+ core_ = new Core(this); |
+} |
+ |
+SyncBackendHost::~SyncBackendHost() { |
+ DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor."; |
+} |
+ |
+void SyncBackendHost::Initialize(const GURL& sync_service_url) { |
+ if (!core_thread_.Start()) |
+ return; |
+ |
+ bookmark_model_worker_ = new BookmarkModelWorker(frontend_loop_); |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, |
+ sync_service_url, bookmark_model_worker_, true)); |
+} |
+ |
+void SyncBackendHost::Authenticate(const std::string& username, |
+ const std::string& password) { |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoAuthenticate, |
+ username, password)); |
+} |
+ |
+void SyncBackendHost::Shutdown(bool sync_disabled) { |
+ // Thread shutdown should occur in the following order: |
+ // - SyncerThread |
+ // - CoreThread |
+ // - UI Thread (stops some time after we return from this call). |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(core_.get(), |
+ &SyncBackendHost::Core::DoShutdown, |
+ sync_disabled)); |
+ |
+ // Before joining the core_thread_, we wait for the BookmarkModelWorker to |
+ // give us the green light that it is not depending on the frontend_loop_ to |
+ // process any more tasks. Stop() blocks until this termination condition |
+ // is true. |
+ bookmark_model_worker_->Stop(); |
+ |
+ // Stop will return once the thread exits, which will be after DoShutdown |
+ // runs. DoShutdown needs to run from core_thread_ because the sync backend |
+ // requires any thread that opened sqlite handles to relinquish them |
+ // personally. We need to join threads, because otherwise the main Chrome |
+ // thread (ui loop) can exit before DoShutdown finishes, at which point |
+ // virtually anything the sync backend does (or the post-back to |
+ // frontend_loop_ by our Core) will epically fail because the CRT won't be |
+ // initialized. For now this only ever happens at sync-enabled-Chrome exit, |
+ // meaning bug 1482548 applies to prolonged "waiting" that may occur in |
+ // DoShutdown. |
+ core_thread_.Stop(); |
+ |
+ bookmark_model_worker_ = NULL; |
+ frontend_ = NULL; |
+ core_ = NULL; // Releases reference to core_. |
+} |
+ |
+void SyncBackendHost::Core::NotifyFrontend(FrontendNotification notification) { |
+ if (!host_ || !host_->frontend_) { |
+ return; // This can happen in testing because the UI loop processes tasks |
+ // after an instance of SyncBackendHost was destroyed. In real |
+ // life this doesn't happen. |
+ } |
+ switch (notification) { |
+ case INITIALIZED: |
+ host_->frontend_->OnBackendInitialized(); |
+ return; |
+ case SYNC_CYCLE_COMPLETED: |
+ host_->frontend_->OnSyncCycleCompleted(); |
+ return; |
+ } |
+} |
+ |
+SyncBackendHost::UserShareHandle SyncBackendHost::GetUserShareHandle() const { |
+ return core_->syncapi()->GetUserShare(); |
+} |
+ |
+SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() { |
+ return core_->syncapi()->GetDetailedStatus(); |
+} |
+ |
+SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() { |
+ return core_->syncapi()->GetStatusSummary(); |
+} |
+ |
+string16 SyncBackendHost::GetAuthenticatedUsername() const { |
+ return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername()); |
+} |
+ |
+AuthErrorState SyncBackendHost::GetAuthErrorState() const { |
+ return last_auth_error_; |
+} |
+ |
+SyncBackendHost::Core::Core(SyncBackendHost* backend) |
+ : host_(backend), |
+ syncapi_(new sync_api::SyncManager()) { |
+} |
+ |
+// Helper to construct a user agent string (ASCII) suitable for use by |
+// the syncapi for any HTTP communication. This string is used by the sync |
+// backend for classifying client types when calculating statistics. |
+std::string MakeUserAgentForSyncapi() { |
+ std::string user_agent; |
+ user_agent = "Chrome "; |
+#if defined(OS_WIN) |
+ user_agent += "WIN "; |
+#elif defined(OS_LINUX) |
+ user_agent += "LINUX "; |
+#elif defined(OS_MACOSX) |
+ user_agent += "MAC "; |
+#endif |
+ scoped_ptr<FileVersionInfo> version_info( |
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
+ if (version_info == NULL) { |
+ DLOG(ERROR) << "Unable to create FileVersionInfo object"; |
+ return user_agent; |
+ } |
+ |
+ user_agent += WideToASCII(version_info->product_version()); |
+ user_agent += " (" + WideToASCII(version_info->last_change()) + ")"; |
+ if (!version_info->is_official_build()) |
+ user_agent += "-devel"; |
+ return user_agent; |
+} |
+ |
+void SyncBackendHost::Core::DoInitialize( |
+ const GURL& service_url, |
+ BookmarkModelWorker* bookmark_model_worker, |
+ bool attempt_last_user_authentication) { |
+ DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); |
+ |
+ // Make sure that the directory exists before initializing the backend. |
+ // If it already exists, this will do no harm. |
+ bool success = file_util::CreateDirectory(host_->sync_data_folder_path()); |
+ DCHECK(success); |
+ |
+ syncapi_->SetObserver(this); |
+ string16 path_str; |
+#if defined (OS_WIN) |
+ path_str = host_->sync_data_folder_path().value(); |
+#elif (defined(OS_LINUX) || defined(OS_MACOSX)) |
+ path_str = UTF8ToUTF16(sync_data_folder_path().value()); |
+#endif |
+ success = syncapi_->Init(path_str.c_str(), |
+ (service_url.host() + service_url.path()).c_str(), |
+ service_url.EffectiveIntPort(), |
+ kGaiaServiceId, |
+ kGaiaSourceForChrome, |
+ service_url.SchemeIsSecure(), |
+ new HttpBridgeFactory(), |
+ new HttpBridgeFactory(), |
+ bookmark_model_worker, |
+ attempt_last_user_authentication, |
+ MakeUserAgentForSyncapi().c_str()); |
+ DCHECK(success) << "Syncapi initialization failed!"; |
+} |
+ |
+void SyncBackendHost::Core::DoAuthenticate(const std::string& username, |
+ const std::string& password) { |
+ DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); |
+ syncapi_->Authenticate(username.c_str(), password.c_str()); |
+} |
+ |
+void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { |
+ DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); |
+ |
+ save_changes_timer_.Stop(); |
+ syncapi_->Shutdown(); // Stops the SyncerThread. |
+ syncapi_->RemoveObserver(); |
+ host_->bookmark_model_worker_->OnSyncerShutdownComplete(); |
+ |
+ if (sync_disabled && |
+ file_util::DirectoryExists(host_->sync_data_folder_path())) { |
+ // Delete the sync data folder to cleanup backend data. |
+ bool success = file_util::Delete(host_->sync_data_folder_path(), true); |
+ DCHECK(success); |
+ } |
+ |
+ host_ = NULL; |
+} |
+ |
+static AuthErrorState AuthProblemToAuthError( |
+ const sync_api::SyncManager::AuthProblem& auth_problem) { |
+ switch (auth_problem) { |
+ case sync_api::SyncManager::AUTH_PROBLEM_NONE: |
+ return AUTH_ERROR_NONE; |
+ case sync_api::SyncManager::AUTH_PROBLEM_INVALID_GAIA_CREDENTIALS: |
+ return AUTH_ERROR_INVALID_GAIA_CREDENTIALS; |
+ case sync_api::SyncManager::AUTH_PROBLEM_CONNECTION_FAILED: |
+ return AUTH_ERROR_CONNECTION_FAILED; |
+ case sync_api::SyncManager::AUTH_PROBLEM_USER_NOT_SIGNED_UP: |
+ return AUTH_ERROR_USER_NOT_SIGNED_UP; |
+ } |
+ |
+ NOTREACHED() << "Unknown AuthProblem."; |
+ return AUTH_ERROR_NONE; |
+} |
+ |
+void SyncBackendHost::Core::OnChangesApplied( |
+ const sync_api::BaseTransaction* trans, |
+ const sync_api::SyncManager::ChangeRecord* changes, |
+ int change_count) { |
+ if (!host_ || !host_->frontend_) { |
+ DCHECK(false) << "OnChangesApplied called after Shutdown?"; |
+ return; |
+ } |
+ |
+ // ChangesApplied is the one exception that should come over from the sync |
+ // backend already on the service_loop_ thanks to our BookmarkModelWorker. |
+ // SyncFrontend changes exclusively on the UI loop, because it updates |
+ // the bookmark model. As such, we don't need to worry about changes that |
+ // have been made to the bookmark model but not yet applied to the sync |
+ // model -- such changes only happen on the UI loop, and there's no |
+ // contention. |
+ if (host_->frontend_loop_ != MessageLoop::current()) { |
+ // TODO(ncarter): Bug 1480644. Make this a DCHECK once syncapi filters |
+ // out all irrelevant changes. |
+ DLOG(WARNING) << "Could not update bookmark model from non-UI thread"; |
+ return; |
+ } |
+ host_->frontend_->ApplyModelChanges(trans, changes, change_count); |
+} |
+ |
+void SyncBackendHost::Core::OnSyncCycleCompleted() { |
+ host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
+ &Core::NotifyFrontend, SYNC_CYCLE_COMPLETED)); |
+} |
+ |
+void SyncBackendHost::Core::OnInitializationComplete() { |
+ if (!host_ || !host_->frontend_) |
+ return; // We may have been told to Shutdown before initialization |
+ // completed. |
+ |
+ // We could be on some random sync backend thread, so MessageLoop::current() |
+ // can definitely be null in here. |
+ host_->frontend_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &Core::NotifyFrontend, INITIALIZED)); |
+ |
+ // Initialization is complete, so we can schedule recurring SaveChanges. |
+ host_->core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &Core::StartSavingChanges)); |
+} |
+ |
+void SyncBackendHost::Core::OnAuthProblem( |
+ sync_api::SyncManager::AuthProblem auth_problem) { |
+ // We could be on SyncEngine_AuthWatcherThread. Post to our core loop so |
+ // we can modify state. |
+ host_->frontend_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, |
+ AuthProblemToAuthError(auth_problem))); |
+} |
+ |
+void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop( |
+ AuthErrorState new_auth_error) { |
+ if (!host_ || !host_->frontend_) |
+ return; |
+ |
+ DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); |
+ |
+ host_->last_auth_error_ = new_auth_error; |
+ host_->frontend_->OnAuthError(); |
+} |
+ |
+void SyncBackendHost::Core::StartSavingChanges() { |
+ save_changes_timer_.Start( |
+ base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds), |
+ this, &Core::SaveChanges); |
+} |
+ |
+void SyncBackendHost::Core::SaveChanges() { |
+ syncapi_->SaveChanges(); |
+} |
+ |
+} // namespace browser_sync |
+ |
+#endif |
Property changes on: chrome\browser\sync\glue\sync_backend_host.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |