| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 #ifdef CHROME_PERSONALIZATION | |
| 6 | |
| 7 #include "base/file_version_info.h" | |
| 8 #include "base/file_util.h" | |
| 9 #include "base/string_util.h" | |
| 10 #include "chrome/browser/sync/glue/sync_backend_host.h" | |
| 11 #include "chrome/browser/sync/glue/http_bridge.h" | |
| 12 #include "chrome/browser/sync/glue/bookmark_model_worker.h" | |
| 13 #include "webkit/glue/webkit_glue.h" | |
| 14 | |
| 15 static const char kSwitchSyncServiceURL[] = "sync-url"; | |
| 16 static const char kSwitchSyncServicePort[] = "sync-port"; | |
| 17 static const int kSaveChangesIntervalSeconds = 10; | |
| 18 static const char kGaiaServiceId[] = "chromiumsync"; | |
| 19 static const char kGaiaSourceForChrome[] = "ChromiumBrowser"; | |
| 20 static const FilePath::CharType kSyncDataFolderName[] = | |
| 21 FILE_PATH_LITERAL("Sync Data"); | |
| 22 | |
| 23 namespace browser_sync { | |
| 24 | |
| 25 SyncBackendHost::SyncBackendHost(SyncFrontend* frontend, | |
| 26 const FilePath& profile_path) | |
| 27 : core_thread_("Chrome_SyncCoreThread"), | |
| 28 frontend_loop_(MessageLoop::current()), | |
| 29 bookmark_model_worker_(NULL), | |
| 30 frontend_(frontend), | |
| 31 sync_data_folder_path_(profile_path.Append(kSyncDataFolderName)), | |
| 32 last_auth_error_(AUTH_ERROR_NONE) { | |
| 33 core_ = new Core(this); | |
| 34 } | |
| 35 | |
| 36 SyncBackendHost::~SyncBackendHost() { | |
| 37 DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor."; | |
| 38 } | |
| 39 | |
| 40 void SyncBackendHost::Initialize(const GURL& sync_service_url) { | |
| 41 if (!core_thread_.Start()) | |
| 42 return; | |
| 43 | |
| 44 bookmark_model_worker_ = new BookmarkModelWorker(frontend_loop_); | |
| 45 core_thread_.message_loop()->PostTask(FROM_HERE, | |
| 46 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, | |
| 47 sync_service_url, bookmark_model_worker_, true)); | |
| 48 } | |
| 49 | |
| 50 void SyncBackendHost::Authenticate(const std::string& username, | |
| 51 const std::string& password) { | |
| 52 core_thread_.message_loop()->PostTask(FROM_HERE, | |
| 53 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoAuthenticate, | |
| 54 username, password)); | |
| 55 } | |
| 56 | |
| 57 void SyncBackendHost::Shutdown(bool sync_disabled) { | |
| 58 // Thread shutdown should occur in the following order: | |
| 59 // - SyncerThread | |
| 60 // - CoreThread | |
| 61 // - UI Thread (stops some time after we return from this call). | |
| 62 core_thread_.message_loop()->PostTask(FROM_HERE, | |
| 63 NewRunnableMethod(core_.get(), | |
| 64 &SyncBackendHost::Core::DoShutdown, | |
| 65 sync_disabled)); | |
| 66 | |
| 67 // Before joining the core_thread_, we wait for the BookmarkModelWorker to | |
| 68 // give us the green light that it is not depending on the frontend_loop_ to | |
| 69 // process any more tasks. Stop() blocks until this termination condition | |
| 70 // is true. | |
| 71 bookmark_model_worker_->Stop(); | |
| 72 | |
| 73 // Stop will return once the thread exits, which will be after DoShutdown | |
| 74 // runs. DoShutdown needs to run from core_thread_ because the sync backend | |
| 75 // requires any thread that opened sqlite handles to relinquish them | |
| 76 // personally. We need to join threads, because otherwise the main Chrome | |
| 77 // thread (ui loop) can exit before DoShutdown finishes, at which point | |
| 78 // virtually anything the sync backend does (or the post-back to | |
| 79 // frontend_loop_ by our Core) will epically fail because the CRT won't be | |
| 80 // initialized. For now this only ever happens at sync-enabled-Chrome exit, | |
| 81 // meaning bug 1482548 applies to prolonged "waiting" that may occur in | |
| 82 // DoShutdown. | |
| 83 core_thread_.Stop(); | |
| 84 | |
| 85 bookmark_model_worker_ = NULL; | |
| 86 frontend_ = NULL; | |
| 87 core_ = NULL; // Releases reference to core_. | |
| 88 } | |
| 89 | |
| 90 void SyncBackendHost::Core::NotifyFrontend(FrontendNotification notification) { | |
| 91 if (!host_ || !host_->frontend_) { | |
| 92 return; // This can happen in testing because the UI loop processes tasks | |
| 93 // after an instance of SyncBackendHost was destroyed. In real | |
| 94 // life this doesn't happen. | |
| 95 } | |
| 96 switch(notification) { | |
| 97 case INITIALIZED: | |
| 98 host_->frontend_->OnBackendInitialized(); | |
| 99 return; | |
| 100 case SYNC_CYCLE_COMPLETED: | |
| 101 host_->frontend_->OnSyncCycleCompleted(); | |
| 102 return; | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 SyncBackendHost::UserShareHandle SyncBackendHost::GetUserShareHandle() const { | |
| 107 return core_->syncapi()->GetUserShare(); | |
| 108 } | |
| 109 | |
| 110 SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() { | |
| 111 return core_->syncapi()->GetDetailedStatus(); | |
| 112 } | |
| 113 | |
| 114 SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() { | |
| 115 return core_->syncapi()->GetStatusSummary(); | |
| 116 } | |
| 117 | |
| 118 string16 SyncBackendHost::GetAuthenticatedUsername() const { | |
| 119 return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername()); | |
| 120 } | |
| 121 | |
| 122 AuthErrorState SyncBackendHost::GetAuthErrorState() const { | |
| 123 return last_auth_error_; | |
| 124 } | |
| 125 | |
| 126 SyncBackendHost::Core::Core(SyncBackendHost* backend) | |
| 127 : host_(backend), | |
| 128 syncapi_(new sync_api::SyncManager()) { | |
| 129 } | |
| 130 | |
| 131 // Helper to construct a user agent string (ASCII) suitable for use by | |
| 132 // the syncapi for any HTTP communication. This string is used by the sync | |
| 133 // backend for classifying client types when calculating statistics. | |
| 134 std::string MakeUserAgentForSyncapi() { | |
| 135 std::string user_agent; | |
| 136 user_agent = "Chrome "; | |
| 137 #if defined (OS_WIN) | |
| 138 user_agent += "WIN "; | |
| 139 #elif defined (OS_LINUX) | |
| 140 user_agent += "LINUX "; | |
| 141 #elif defined (OS_MACOSX) | |
| 142 user_agent += "MAC "; | |
| 143 #endif | |
| 144 scoped_ptr<FileVersionInfo> version_info( | |
| 145 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | |
| 146 if (version_info == NULL) { | |
| 147 DLOG(ERROR) << "Unable to create FileVersionInfo object"; | |
| 148 return user_agent; | |
| 149 } | |
| 150 | |
| 151 user_agent += WideToASCII(version_info->product_version()); | |
| 152 user_agent += " (" + WideToASCII(version_info->last_change()) + ")"; | |
| 153 if (!version_info->is_official_build()) | |
| 154 user_agent += "-devel"; | |
| 155 return user_agent; | |
| 156 } | |
| 157 | |
| 158 void SyncBackendHost::Core::DoInitialize( | |
| 159 const GURL& service_url, | |
| 160 BookmarkModelWorker* bookmark_model_worker, | |
| 161 bool attempt_last_user_authentication) { | |
| 162 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); | |
| 163 | |
| 164 // Make sure that the directory exists before initializing the backend. | |
| 165 // If it already exists, this will do no harm. | |
| 166 bool success = file_util::CreateDirectory(host_->sync_data_folder_path()); | |
| 167 DCHECK(success); | |
| 168 | |
| 169 syncapi_->SetObserver(this); | |
| 170 string16 path_str; | |
| 171 #if defined (OS_WIN) | |
| 172 path_str = host_->sync_data_folder_path().value(); | |
| 173 #elif (defined (OS_LINUX) || defined (OS_MACOSX)) | |
| 174 path_str = UTF8ToUTF16(sync_data_folder_path().value()); | |
| 175 #endif | |
| 176 success = syncapi_->Init(path_str.c_str(), | |
| 177 (service_url.host() + service_url.path()).c_str(), | |
| 178 service_url.EffectiveIntPort(), | |
| 179 kGaiaServiceId, | |
| 180 kGaiaSourceForChrome, | |
| 181 service_url.SchemeIsSecure(), | |
| 182 new HttpBridgeFactory(), | |
| 183 new HttpBridgeFactory(), | |
| 184 bookmark_model_worker, | |
| 185 attempt_last_user_authentication, | |
| 186 MakeUserAgentForSyncapi().c_str()); | |
| 187 DCHECK(success) << "Syncapi initialization failed!"; | |
| 188 } | |
| 189 | |
| 190 void SyncBackendHost::Core::DoAuthenticate(const std::string& username, | |
| 191 const std::string& password) { | |
| 192 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); | |
| 193 syncapi_->Authenticate(username.c_str(), password.c_str()); | |
| 194 } | |
| 195 | |
| 196 void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { | |
| 197 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); | |
| 198 | |
| 199 save_changes_timer_.Stop(); | |
| 200 syncapi_->Shutdown(); // Stops the SyncerThread. | |
| 201 syncapi_->RemoveObserver(); | |
| 202 host_->bookmark_model_worker_->OnSyncerShutdownComplete(); | |
| 203 | |
| 204 if (sync_disabled && | |
| 205 file_util::DirectoryExists(host_->sync_data_folder_path())) { | |
| 206 // Delete the sync data folder to cleanup backend data. | |
| 207 bool success = file_util::Delete(host_->sync_data_folder_path(), true); | |
| 208 DCHECK(success); | |
| 209 } | |
| 210 | |
| 211 host_ = NULL; | |
| 212 } | |
| 213 | |
| 214 static AuthErrorState AuthProblemToAuthError( | |
| 215 const sync_api::SyncManager::AuthProblem& auth_problem) { | |
| 216 switch(auth_problem) { | |
| 217 case sync_api::SyncManager::AUTH_PROBLEM_NONE: | |
| 218 return AUTH_ERROR_NONE; | |
| 219 case sync_api::SyncManager::AUTH_PROBLEM_INVALID_GAIA_CREDENTIALS: | |
| 220 return AUTH_ERROR_INVALID_GAIA_CREDENTIALS; | |
| 221 case sync_api::SyncManager::AUTH_PROBLEM_CONNECTION_FAILED: | |
| 222 return AUTH_ERROR_CONNECTION_FAILED; | |
| 223 case sync_api::SyncManager::AUTH_PROBLEM_USER_NOT_SIGNED_UP: | |
| 224 return AUTH_ERROR_USER_NOT_SIGNED_UP; | |
| 225 } | |
| 226 | |
| 227 NOTREACHED() << "Unknown AuthProblem."; | |
| 228 return AUTH_ERROR_NONE; | |
| 229 } | |
| 230 | |
| 231 void SyncBackendHost::Core::OnChangesApplied( | |
| 232 const sync_api::BaseTransaction* trans, | |
| 233 const sync_api::SyncManager::ChangeRecord* changes, | |
| 234 int change_count) { | |
| 235 if (!host_ || !host_->frontend_) { | |
| 236 DCHECK(false) << "OnChangesApplied called after Shutdown?"; | |
| 237 return; | |
| 238 } | |
| 239 | |
| 240 // ChangesApplied is the one exception that should come over from the sync | |
| 241 // backend already on the service_loop_ thanks to our BookmarkModelWorker. | |
| 242 // SyncFrontend changes exclusively on the UI loop, because it updates | |
| 243 // the bookmark model. As such, we don't need to worry about changes that | |
| 244 // have been made to the bookmark model but not yet applied to the sync | |
| 245 // model -- such changes only happen on the UI loop, and there's no | |
| 246 // contention. | |
| 247 if (host_->frontend_loop_ != MessageLoop::current()) { | |
| 248 // TODO(ncarter): Bug 1480644. Make this a DCHECK once syncapi filters | |
| 249 // out all irrelevant changes. | |
| 250 DLOG(WARNING) << "Could not update bookmark model from non-UI thread"; | |
| 251 return; | |
| 252 } | |
| 253 host_->frontend_->ApplyModelChanges(trans, changes, change_count); | |
| 254 } | |
| 255 | |
| 256 void SyncBackendHost::Core::OnSyncCycleCompleted() { | |
| 257 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | |
| 258 &Core::NotifyFrontend, SYNC_CYCLE_COMPLETED)); | |
| 259 } | |
| 260 | |
| 261 void SyncBackendHost::Core::OnInitializationComplete() { | |
| 262 if (!host_ || !host_->frontend_) | |
| 263 return; // We may have been told to Shutdown before initialization | |
| 264 // completed. | |
| 265 | |
| 266 // We could be on some random sync backend thread, so MessageLoop::current() | |
| 267 // can definitely be null in here. | |
| 268 host_->frontend_loop_->PostTask(FROM_HERE, | |
| 269 NewRunnableMethod(this, &Core::NotifyFrontend, INITIALIZED)); | |
| 270 | |
| 271 // Initialization is complete, so we can schedule recurring SaveChanges. | |
| 272 host_->core_thread_.message_loop()->PostTask(FROM_HERE, | |
| 273 NewRunnableMethod(this, &Core::StartSavingChanges)); | |
| 274 } | |
| 275 | |
| 276 void SyncBackendHost::Core::OnAuthProblem( | |
| 277 sync_api::SyncManager::AuthProblem auth_problem) { | |
| 278 // We could be on SyncEngine_AuthWatcherThread. Post to our core loop so | |
| 279 // we can modify state. | |
| 280 host_->frontend_loop_->PostTask(FROM_HERE, | |
| 281 NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, | |
| 282 AuthProblemToAuthError(auth_problem))); | |
| 283 } | |
| 284 | |
| 285 void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop( | |
| 286 AuthErrorState new_auth_error) { | |
| 287 if (!host_ || !host_->frontend_) | |
| 288 return; | |
| 289 | |
| 290 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); | |
| 291 | |
| 292 host_->last_auth_error_ = new_auth_error; | |
| 293 host_->frontend_->OnAuthError(); | |
| 294 } | |
| 295 | |
| 296 void SyncBackendHost::Core::StartSavingChanges() { | |
| 297 save_changes_timer_.Start( | |
| 298 base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds), | |
| 299 this, &Core::SaveChanges); | |
| 300 } | |
| 301 | |
| 302 void SyncBackendHost::Core::SaveChanges() { | |
| 303 syncapi_->SaveChanges(); | |
| 304 } | |
| 305 | |
| 306 } // namespace browser_sync | |
| 307 | |
| 308 #endif | |
| OLD | NEW |