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 |