Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / sync_file_system / local / local_file_sync_service.cc
blobb9c76f7469adef148d748559f66844d76d851da5
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/single_thread_task_runner.h"
8 #include "base/stl_util.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/sync_file_system/file_change.h"
13 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
14 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
15 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
16 #include "chrome/browser/sync_file_system/local_change_processor.h"
17 #include "chrome/browser/sync_file_system/logger.h"
18 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/site_instance.h"
22 #include "content/public/browser/storage_partition.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/common/extension_set.h"
25 #include "storage/browser/blob/scoped_file.h"
26 #include "storage/browser/fileapi/file_system_context.h"
27 #include "storage/browser/fileapi/file_system_url.h"
28 #include "url/gurl.h"
30 using content::BrowserThread;
31 using storage::FileSystemURL;
33 namespace sync_file_system {
35 namespace {
37 void PrepareForProcessRemoteChangeCallbackAdapter(
38 const RemoteChangeProcessor::PrepareChangeCallback& callback,
39 SyncStatusCode status,
40 const LocalFileSyncInfo& sync_file_info,
41 storage::ScopedFile snapshot) {
42 callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
45 void InvokeCallbackOnNthInvocation(int* count, const base::Closure& callback) {
46 --*count;
47 if (*count <= 0)
48 callback.Run();
51 } // namespace
53 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
54 : next_(change_count_map_.end()) {}
55 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
57 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
58 DCHECK(origin);
59 if (change_count_map_.empty())
60 return false;
61 Map::iterator begin = next_;
62 do {
63 if (next_ == change_count_map_.end())
64 next_ = change_count_map_.begin();
65 DCHECK_NE(0, next_->second);
66 *origin = next_++->first;
67 if (!ContainsKey(disabled_origins_, *origin))
68 return true;
69 } while (next_ != begin);
70 return false;
73 int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
74 int64 num_changes = 0;
75 for (Map::const_iterator iter = change_count_map_.begin();
76 iter != change_count_map_.end(); ++iter) {
77 if (ContainsKey(disabled_origins_, iter->first))
78 continue;
79 num_changes += iter->second;
81 return num_changes;
84 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
85 const GURL& origin, int64 changes) {
86 if (changes != 0) {
87 change_count_map_[origin] = changes;
88 return;
90 Map::iterator found = change_count_map_.find(origin);
91 if (found != change_count_map_.end()) {
92 if (next_ == found)
93 ++next_;
94 change_count_map_.erase(found);
98 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
99 const GURL& origin, bool enabled) {
100 if (enabled)
101 disabled_origins_.erase(origin);
102 else
103 disabled_origins_.insert(origin);
106 // LocalFileSyncService -------------------------------------------------------
108 scoped_ptr<LocalFileSyncService> LocalFileSyncService::Create(
109 Profile* profile) {
110 return make_scoped_ptr(new LocalFileSyncService(profile, nullptr));
113 scoped_ptr<LocalFileSyncService> LocalFileSyncService::CreateForTesting(
114 Profile* profile,
115 leveldb::Env* env) {
116 scoped_ptr<LocalFileSyncService> sync_service(
117 new LocalFileSyncService(profile, env));
118 sync_service->sync_context_->set_mock_notify_changes_duration_in_sec(0);
119 return sync_service.Pass();
122 LocalFileSyncService::~LocalFileSyncService() {
123 DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 void LocalFileSyncService::Shutdown() {
127 sync_context_->RemoveOriginChangeObserver(this);
128 sync_context_->ShutdownOnUIThread();
129 profile_ = nullptr;
132 void LocalFileSyncService::MaybeInitializeFileSystemContext(
133 const GURL& app_origin,
134 storage::FileSystemContext* file_system_context,
135 const SyncStatusCallback& callback) {
136 sync_context_->MaybeInitializeFileSystemContext(
137 app_origin, file_system_context,
138 base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext,
139 AsWeakPtr(), app_origin,
140 make_scoped_refptr(file_system_context), callback));
143 void LocalFileSyncService::AddChangeObserver(Observer* observer) {
144 change_observers_.AddObserver(observer);
147 void LocalFileSyncService::RegisterURLForWaitingSync(
148 const FileSystemURL& url,
149 const base::Closure& on_syncable_callback) {
150 sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
153 void LocalFileSyncService::ProcessLocalChange(
154 const SyncFileCallback& callback) {
155 // Pick an origin to process next.
156 GURL origin;
157 if (!origin_change_map_.NextOriginToProcess(&origin)) {
158 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
159 return;
161 DCHECK(!origin.is_empty());
162 DCHECK(ContainsKey(origin_to_contexts_, origin));
164 DVLOG(1) << "Starting ProcessLocalChange";
166 sync_context_->GetFileForLocalSync(
167 origin_to_contexts_[origin],
168 base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
169 AsWeakPtr(), callback));
172 void LocalFileSyncService::SetLocalChangeProcessor(
173 LocalChangeProcessor* local_change_processor) {
174 local_change_processor_ = local_change_processor;
177 void LocalFileSyncService::SetLocalChangeProcessorCallback(
178 const GetLocalChangeProcessorCallback& get_local_change_processor) {
179 get_local_change_processor_ = get_local_change_processor;
182 void LocalFileSyncService::HasPendingLocalChanges(
183 const FileSystemURL& url,
184 const HasPendingLocalChangeCallback& callback) {
185 if (!ContainsKey(origin_to_contexts_, url.origin())) {
186 base::ThreadTaskRunnerHandle::Get()->PostTask(
187 FROM_HERE,
188 base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
189 return;
191 sync_context_->HasPendingLocalChanges(
192 origin_to_contexts_[url.origin()], url, callback);
195 void LocalFileSyncService::PromoteDemotedChanges(
196 const base::Closure& callback) {
197 if (origin_to_contexts_.empty()) {
198 callback.Run();
199 return;
202 base::Closure completion_callback =
203 base::Bind(&InvokeCallbackOnNthInvocation,
204 base::Owned(new int(origin_to_contexts_.size() + 1)),
205 callback);
206 for (OriginToContext::iterator iter = origin_to_contexts_.begin();
207 iter != origin_to_contexts_.end(); ++iter)
208 sync_context_->PromoteDemotedChanges(iter->first, iter->second,
209 completion_callback);
210 completion_callback.Run();
213 void LocalFileSyncService::GetLocalFileMetadata(
214 const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
215 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
216 sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
217 url, callback);
220 void LocalFileSyncService::PrepareForProcessRemoteChange(
221 const FileSystemURL& url,
222 const PrepareChangeCallback& callback) {
223 DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
225 if (!ContainsKey(origin_to_contexts_, url.origin())) {
226 // This could happen if a remote sync is triggered for the app that hasn't
227 // been initialized in this service.
228 DCHECK(profile_);
229 // The given url.origin() must be for valid installed app.
230 const extensions::Extension* extension =
231 extensions::ExtensionRegistry::Get(profile_)
232 ->enabled_extensions().GetAppByURL(url.origin());
233 if (!extension) {
234 util::Log(
235 logging::LOG_WARNING,
236 FROM_HERE,
237 "PrepareForProcessRemoteChange called for non-existing origin: %s",
238 url.origin().spec().c_str());
240 // The extension has been uninstalled and this method is called
241 // before the remote changes for the origin are removed.
242 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
243 SyncFileMetadata(), FileChangeList());
244 return;
246 GURL site_url =
247 extensions::util::GetSiteForExtensionId(extension->id(), profile_);
248 DCHECK(!site_url.is_empty());
249 scoped_refptr<storage::FileSystemContext> file_system_context =
250 content::BrowserContext::GetStoragePartitionForSite(profile_, site_url)
251 ->GetFileSystemContext();
252 MaybeInitializeFileSystemContext(
253 url.origin(),
254 file_system_context.get(),
255 base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
256 AsWeakPtr(),
257 url,
258 file_system_context,
259 callback));
260 return;
263 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
264 sync_context_->PrepareForSync(
265 origin_to_contexts_[url.origin()], url,
266 LocalFileSyncContext::SYNC_EXCLUSIVE,
267 base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
270 void LocalFileSyncService::ApplyRemoteChange(
271 const FileChange& change,
272 const base::FilePath& local_path,
273 const FileSystemURL& url,
274 const SyncStatusCallback& callback) {
275 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
276 util::Log(logging::LOG_VERBOSE, FROM_HERE,
277 "[Remote -> Local] ApplyRemoteChange: %s on %s",
278 change.DebugString().c_str(),
279 url.DebugString().c_str());
281 sync_context_->ApplyRemoteChange(
282 origin_to_contexts_[url.origin()],
283 change, local_path, url,
284 base::Bind(&LocalFileSyncService::DidApplyRemoteChange, AsWeakPtr(),
285 callback));
288 void LocalFileSyncService::FinalizeRemoteSync(
289 const FileSystemURL& url,
290 bool clear_local_changes,
291 const base::Closure& completion_callback) {
292 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
293 sync_context_->FinalizeExclusiveSync(
294 origin_to_contexts_[url.origin()],
295 url, clear_local_changes, completion_callback);
298 void LocalFileSyncService::RecordFakeLocalChange(
299 const FileSystemURL& url,
300 const FileChange& change,
301 const SyncStatusCallback& callback) {
302 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
303 sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
304 url, change, callback);
307 void LocalFileSyncService::OnChangesAvailableInOrigins(
308 const std::set<GURL>& origins) {
309 bool need_notification = false;
310 for (std::set<GURL>::const_iterator iter = origins.begin();
311 iter != origins.end(); ++iter) {
312 const GURL& origin = *iter;
313 if (!ContainsKey(origin_to_contexts_, origin)) {
314 // This could happen if this is called for apps/origins that haven't
315 // been initialized yet, or for apps/origins that are disabled.
316 // (Local change tracker could call this for uninitialized origins
317 // while it's reading dirty files from the database in the
318 // initialization phase.)
319 pending_origins_with_changes_.insert(origin);
320 continue;
322 need_notification = true;
323 SyncFileSystemBackend* backend =
324 SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
325 DCHECK(backend);
326 DCHECK(backend->change_tracker());
327 origin_change_map_.SetOriginChangeCount(
328 origin, backend->change_tracker()->num_changes());
330 if (!need_notification)
331 return;
332 int64 num_changes = origin_change_map_.GetTotalChangeCount();
333 FOR_EACH_OBSERVER(Observer, change_observers_,
334 OnLocalChangeAvailable(num_changes));
337 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
338 if (!ContainsKey(origin_to_contexts_, origin))
339 return;
340 origin_change_map_.SetOriginEnabled(origin, enabled);
343 LocalFileSyncService::LocalFileSyncService(Profile* profile,
344 leveldb::Env* env_override)
345 : profile_(profile),
346 sync_context_(new LocalFileSyncContext(
347 profile_->GetPath(),
348 env_override,
349 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
350 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
351 .get())),
352 local_change_processor_(nullptr) {
353 DCHECK_CURRENTLY_ON(BrowserThread::UI);
354 sync_context_->AddOriginChangeObserver(this);
357 void LocalFileSyncService::DidInitializeFileSystemContext(
358 const GURL& app_origin,
359 storage::FileSystemContext* file_system_context,
360 const SyncStatusCallback& callback,
361 SyncStatusCode status) {
362 if (status != SYNC_STATUS_OK) {
363 callback.Run(status);
364 return;
366 DCHECK(file_system_context);
367 origin_to_contexts_[app_origin] = file_system_context;
369 if (pending_origins_with_changes_.find(app_origin) !=
370 pending_origins_with_changes_.end()) {
371 // We have remaining changes for the origin.
372 pending_origins_with_changes_.erase(app_origin);
373 SyncFileSystemBackend* backend =
374 SyncFileSystemBackend::GetBackend(file_system_context);
375 DCHECK(backend);
376 DCHECK(backend->change_tracker());
377 origin_change_map_.SetOriginChangeCount(
378 app_origin, backend->change_tracker()->num_changes());
379 int64 num_changes = origin_change_map_.GetTotalChangeCount();
380 FOR_EACH_OBSERVER(Observer, change_observers_,
381 OnLocalChangeAvailable(num_changes));
383 callback.Run(status);
386 void LocalFileSyncService::DidInitializeForRemoteSync(
387 const FileSystemURL& url,
388 storage::FileSystemContext* file_system_context,
389 const PrepareChangeCallback& callback,
390 SyncStatusCode status) {
391 if (status != SYNC_STATUS_OK) {
392 DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
393 << url.DebugString() << " status=" << status
394 << " (" << SyncStatusCodeToString(status) << ")";
395 callback.Run(status, SyncFileMetadata(), FileChangeList());
396 return;
398 origin_to_contexts_[url.origin()] = file_system_context;
399 PrepareForProcessRemoteChange(url, callback);
402 void LocalFileSyncService::DidApplyRemoteChange(
403 const SyncStatusCallback& callback,
404 SyncStatusCode status) {
405 util::Log(logging::LOG_VERBOSE, FROM_HERE,
406 "[Remote -> Local] ApplyRemoteChange finished --> %s",
407 SyncStatusCodeToString(status));
408 callback.Run(status);
411 void LocalFileSyncService::DidGetFileForLocalSync(
412 const SyncFileCallback& callback,
413 SyncStatusCode status,
414 const LocalFileSyncInfo& sync_file_info,
415 storage::ScopedFile snapshot) {
416 if (status != SYNC_STATUS_OK) {
417 callback.Run(status, sync_file_info.url);
418 return;
420 if (sync_file_info.changes.empty()) {
421 // There's a slight chance this could happen.
422 ProcessLocalChange(callback);
423 return;
426 FileChange next_change = sync_file_info.changes.front();
427 DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
428 << " change:" << next_change.DebugString();
430 GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
431 next_change,
432 sync_file_info.local_file_path,
433 sync_file_info.metadata,
434 sync_file_info.url,
435 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
436 AsWeakPtr(), callback,
437 base::Passed(&snapshot), sync_file_info,
438 next_change, sync_file_info.changes.PopAndGetNewList()));
441 void LocalFileSyncService::ProcessNextChangeForURL(
442 const SyncFileCallback& callback,
443 storage::ScopedFile snapshot,
444 const LocalFileSyncInfo& sync_file_info,
445 const FileChange& processed_change,
446 const FileChangeList& changes,
447 SyncStatusCode status) {
448 DVLOG(1) << "Processed one local change: "
449 << sync_file_info.url.DebugString()
450 << " change:" << processed_change.DebugString()
451 << " status:" << status;
453 if (status == SYNC_STATUS_RETRY) {
454 GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
455 processed_change,
456 sync_file_info.local_file_path,
457 sync_file_info.metadata,
458 sync_file_info.url,
459 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
460 AsWeakPtr(), callback, base::Passed(&snapshot),
461 sync_file_info, processed_change, changes));
462 return;
465 if (status == SYNC_FILE_ERROR_NOT_FOUND &&
466 processed_change.change() == FileChange::FILE_CHANGE_DELETE) {
467 // This must be ok (and could happen).
468 status = SYNC_STATUS_OK;
471 const FileSystemURL& url = sync_file_info.url;
472 if (status != SYNC_STATUS_OK || changes.empty()) {
473 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
474 sync_context_->FinalizeSnapshotSync(
475 origin_to_contexts_[url.origin()], url, status,
476 base::Bind(callback, status, url));
477 return;
480 FileChange next_change = changes.front();
481 GetLocalChangeProcessor(url)->ApplyLocalChange(
482 changes.front(),
483 sync_file_info.local_file_path,
484 sync_file_info.metadata,
485 url,
486 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
487 AsWeakPtr(), callback,
488 base::Passed(&snapshot), sync_file_info,
489 next_change, changes.PopAndGetNewList()));
492 LocalChangeProcessor* LocalFileSyncService::GetLocalChangeProcessor(
493 const FileSystemURL& url) {
494 if (!get_local_change_processor_.is_null())
495 return get_local_change_processor_.Run(url.origin());
496 DCHECK(local_change_processor_);
497 return local_change_processor_;
500 } // namespace sync_file_system