1 // Copyright 2013 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.
5 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
7 #include "base/stl_util.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/extensions/extension_system.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/sync_file_system/file_change.h"
12 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
13 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
14 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
15 #include "chrome/browser/sync_file_system/local_change_processor.h"
16 #include "chrome/browser/sync_file_system/logger.h"
17 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/site_instance.h"
21 #include "content/public/browser/storage_partition.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_url.h"
25 #include "webkit/common/blob/scoped_file.h"
27 using content::BrowserThread;
28 using fileapi::FileSystemURL;
30 namespace sync_file_system {
34 void PrepareForProcessRemoteChangeCallbackAdapter(
35 const RemoteChangeProcessor::PrepareChangeCallback& callback,
36 SyncStatusCode status,
37 const LocalFileSyncInfo& sync_file_info,
38 webkit_blob::ScopedFile snapshot) {
39 callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
44 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
45 : next_(change_count_map_.end()) {}
46 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
48 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
50 if (change_count_map_.empty())
52 Map::iterator begin = next_;
54 if (next_ == change_count_map_.end())
55 next_ = change_count_map_.begin();
56 DCHECK_NE(0, next_->second);
57 *origin = next_++->first;
58 if (!ContainsKey(disabled_origins_, *origin))
60 } while (next_ != begin);
64 int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
65 int64 num_changes = 0;
66 for (Map::const_iterator iter = change_count_map_.begin();
67 iter != change_count_map_.end(); ++iter) {
68 if (ContainsKey(disabled_origins_, iter->first))
70 num_changes += iter->second;
75 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
76 const GURL& origin, int64 changes) {
78 change_count_map_[origin] = changes;
81 Map::iterator found = change_count_map_.find(origin);
82 if (found != change_count_map_.end()) {
85 change_count_map_.erase(found);
89 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
90 const GURL& origin, bool enabled) {
92 disabled_origins_.erase(origin);
94 disabled_origins_.insert(origin);
97 // LocalFileSyncService -------------------------------------------------------
99 LocalFileSyncService::LocalFileSyncService(Profile* profile)
101 sync_context_(new LocalFileSyncContext(
103 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
104 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
106 local_change_processor_(NULL) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108 sync_context_->AddOriginChangeObserver(this);
111 LocalFileSyncService::~LocalFileSyncService() {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115 void LocalFileSyncService::Shutdown() {
116 sync_context_->RemoveOriginChangeObserver(this);
117 sync_context_->ShutdownOnUIThread();
121 void LocalFileSyncService::MaybeInitializeFileSystemContext(
122 const GURL& app_origin,
123 fileapi::FileSystemContext* file_system_context,
124 const SyncStatusCallback& callback) {
125 sync_context_->MaybeInitializeFileSystemContext(
126 app_origin, file_system_context,
127 base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext,
128 AsWeakPtr(), app_origin,
129 make_scoped_refptr(file_system_context), callback));
132 void LocalFileSyncService::AddChangeObserver(Observer* observer) {
133 change_observers_.AddObserver(observer);
136 void LocalFileSyncService::RegisterURLForWaitingSync(
137 const FileSystemURL& url,
138 const base::Closure& on_syncable_callback) {
139 sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
142 void LocalFileSyncService::ProcessLocalChange(
143 const SyncFileCallback& callback) {
144 DCHECK(local_change_processor_);
145 // Pick an origin to process next.
147 if (!origin_change_map_.NextOriginToProcess(&origin)) {
148 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
151 DCHECK(local_sync_callback_.is_null());
152 DCHECK(!origin.is_empty());
153 DCHECK(ContainsKey(origin_to_contexts_, origin));
155 DVLOG(1) << "Starting ProcessLocalChange";
157 local_sync_callback_ = callback;
159 sync_context_->GetFileForLocalSync(
160 origin_to_contexts_[origin],
161 base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
165 void LocalFileSyncService::SetLocalChangeProcessor(
166 LocalChangeProcessor* processor) {
167 local_change_processor_ = processor;
170 void LocalFileSyncService::HasPendingLocalChanges(
171 const FileSystemURL& url,
172 const HasPendingLocalChangeCallback& callback) {
173 if (!ContainsKey(origin_to_contexts_, url.origin())) {
174 base::MessageLoopProxy::current()->PostTask(
176 base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
179 sync_context_->HasPendingLocalChanges(
180 origin_to_contexts_[url.origin()], url, callback);
183 void LocalFileSyncService::GetLocalFileMetadata(
184 const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
185 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
186 sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
190 void LocalFileSyncService::PrepareForProcessRemoteChange(
191 const FileSystemURL& url,
192 const PrepareChangeCallback& callback) {
193 DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
195 if (!ContainsKey(origin_to_contexts_, url.origin())) {
196 // This could happen if a remote sync is triggered for the app that hasn't
197 // been initialized in this service.
199 // The given url.origin() must be for valid installed app.
200 ExtensionService* extension_service =
201 extensions::ExtensionSystem::Get(profile_)->extension_service();
202 const extensions::Extension* extension = extension_service->GetInstalledApp(
206 logging::LOG_WARNING,
208 "PrepareForProcessRemoteChange called for non-existing origin: %s",
209 url.origin().spec().c_str());
211 // The extension has been uninstalled and this method is called
212 // before the remote changes for the origin are removed.
213 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
214 SyncFileMetadata(), FileChangeList());
217 GURL site_url = extension_service->GetSiteForExtensionId(extension->id());
218 DCHECK(!site_url.is_empty());
219 scoped_refptr<fileapi::FileSystemContext> file_system_context =
220 content::BrowserContext::GetStoragePartitionForSite(
221 profile_, site_url)->GetFileSystemContext();
222 MaybeInitializeFileSystemContext(
224 file_system_context.get(),
225 base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
233 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
234 sync_context_->PrepareForSync(
235 origin_to_contexts_[url.origin()], url,
236 LocalFileSyncContext::SYNC_EXCLUSIVE,
237 base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
240 void LocalFileSyncService::ApplyRemoteChange(
241 const FileChange& change,
242 const base::FilePath& local_path,
243 const FileSystemURL& url,
244 const SyncStatusCallback& callback) {
245 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
246 sync_context_->ApplyRemoteChange(
247 origin_to_contexts_[url.origin()],
248 change, local_path, url, callback);
251 void LocalFileSyncService::FinalizeRemoteSync(
252 const FileSystemURL& url,
253 bool clear_local_changes,
254 const base::Closure& completion_callback) {
255 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
256 sync_context_->FinalizeExclusiveSync(
257 origin_to_contexts_[url.origin()],
258 url, clear_local_changes, completion_callback);
261 void LocalFileSyncService::RecordFakeLocalChange(
262 const FileSystemURL& url,
263 const FileChange& change,
264 const SyncStatusCallback& callback) {
265 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
266 sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
267 url, change, callback);
270 void LocalFileSyncService::OnChangesAvailableInOrigins(
271 const std::set<GURL>& origins) {
272 bool need_notification = false;
273 for (std::set<GURL>::const_iterator iter = origins.begin();
274 iter != origins.end(); ++iter) {
275 const GURL& origin = *iter;
276 if (!ContainsKey(origin_to_contexts_, origin)) {
277 // This could happen if this is called for apps/origins that haven't
278 // been initialized yet, or for apps/origins that are disabled.
279 // (Local change tracker could call this for uninitialized origins
280 // while it's reading dirty files from the database in the
281 // initialization phase.)
282 pending_origins_with_changes_.insert(origin);
285 need_notification = true;
286 SyncFileSystemBackend* backend =
287 SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
289 DCHECK(backend->change_tracker());
290 origin_change_map_.SetOriginChangeCount(
291 origin, backend->change_tracker()->num_changes());
293 if (!need_notification)
295 int64 num_changes = origin_change_map_.GetTotalChangeCount();
296 FOR_EACH_OBSERVER(Observer, change_observers_,
297 OnLocalChangeAvailable(num_changes));
300 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
301 if (!ContainsKey(origin_to_contexts_, origin))
303 origin_change_map_.SetOriginEnabled(origin, enabled);
306 void LocalFileSyncService::DidInitializeFileSystemContext(
307 const GURL& app_origin,
308 fileapi::FileSystemContext* file_system_context,
309 const SyncStatusCallback& callback,
310 SyncStatusCode status) {
311 if (status != SYNC_STATUS_OK) {
312 callback.Run(status);
315 DCHECK(file_system_context);
316 origin_to_contexts_[app_origin] = file_system_context;
318 if (pending_origins_with_changes_.find(app_origin) !=
319 pending_origins_with_changes_.end()) {
320 // We have remaining changes for the origin.
321 pending_origins_with_changes_.erase(app_origin);
322 SyncFileSystemBackend* backend =
323 SyncFileSystemBackend::GetBackend(file_system_context);
325 DCHECK(backend->change_tracker());
326 origin_change_map_.SetOriginChangeCount(
327 app_origin, backend->change_tracker()->num_changes());
328 int64 num_changes = origin_change_map_.GetTotalChangeCount();
329 FOR_EACH_OBSERVER(Observer, change_observers_,
330 OnLocalChangeAvailable(num_changes));
332 callback.Run(status);
335 void LocalFileSyncService::DidInitializeForRemoteSync(
336 const FileSystemURL& url,
337 fileapi::FileSystemContext* file_system_context,
338 const PrepareChangeCallback& callback,
339 SyncStatusCode status) {
340 if (status != SYNC_STATUS_OK) {
341 DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
342 << url.DebugString() << " status=" << status
343 << " (" << SyncStatusCodeToString(status) << ")";
344 callback.Run(status, SyncFileMetadata(), FileChangeList());
347 origin_to_contexts_[url.origin()] = file_system_context;
348 PrepareForProcessRemoteChange(url, callback);
351 void LocalFileSyncService::RunLocalSyncCallback(
352 SyncStatusCode status,
353 const FileSystemURL& url) {
354 DVLOG(1) << "Local sync is finished with: " << status
355 << " on " << url.DebugString();
356 DCHECK(!local_sync_callback_.is_null());
357 SyncFileCallback callback = local_sync_callback_;
358 local_sync_callback_.Reset();
359 callback.Run(status, url);
362 void LocalFileSyncService::DidGetFileForLocalSync(
363 SyncStatusCode status,
364 const LocalFileSyncInfo& sync_file_info,
365 webkit_blob::ScopedFile snapshot) {
366 DCHECK(!local_sync_callback_.is_null());
367 if (status != SYNC_STATUS_OK) {
368 RunLocalSyncCallback(status, sync_file_info.url);
371 if (sync_file_info.changes.empty()) {
372 // There's a slight chance this could happen.
373 SyncFileCallback callback = local_sync_callback_;
374 local_sync_callback_.Reset();
375 ProcessLocalChange(callback);
379 FileChange next_change = sync_file_info.changes.front();
380 DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
381 << " change:" << next_change.DebugString();
383 local_change_processor_->ApplyLocalChange(
385 sync_file_info.local_file_path,
386 sync_file_info.metadata,
388 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
389 AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
390 next_change, sync_file_info.changes.PopAndGetNewList()));
393 void LocalFileSyncService::ProcessNextChangeForURL(
394 webkit_blob::ScopedFile snapshot,
395 const LocalFileSyncInfo& sync_file_info,
396 const FileChange& processed_change,
397 const FileChangeList& changes,
398 SyncStatusCode status) {
399 DVLOG(1) << "Processed one local change: "
400 << sync_file_info.url.DebugString()
401 << " change:" << processed_change.DebugString()
402 << " status:" << status;
404 if (status == SYNC_STATUS_RETRY) {
405 local_change_processor_->ApplyLocalChange(
407 sync_file_info.local_file_path,
408 sync_file_info.metadata,
410 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
411 AsWeakPtr(), base::Passed(&snapshot),
412 sync_file_info, processed_change, changes));
416 if (status == SYNC_FILE_ERROR_NOT_FOUND &&
417 processed_change.change() == FileChange::FILE_CHANGE_DELETE) {
418 // This must be ok (and could happen).
419 status = SYNC_STATUS_OK;
422 const FileSystemURL& url = sync_file_info.url;
423 if (status != SYNC_STATUS_OK || changes.empty()) {
424 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
425 sync_context_->FinalizeSnapshotSync(
426 origin_to_contexts_[url.origin()], url, status,
427 base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
428 AsWeakPtr(), status, url));
432 FileChange next_change = changes.front();
433 local_change_processor_->ApplyLocalChange(
435 sync_file_info.local_file_path,
436 sync_file_info.metadata,
438 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
439 AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
440 next_change, changes.PopAndGetNewList()));
443 } // namespace sync_file_system