Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / local / local_file_sync_service.cc
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.
4
5 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
6
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/fileapi/file_system_context.h"
26 #include "storage/browser/fileapi/file_system_url.h"
27 #include "storage/common/blob/scoped_file.h"
28 #include "url/gurl.h"
29
30 using content::BrowserThread;
31 using storage::FileSystemURL;
32
33 namespace sync_file_system {
34
35 namespace {
36
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);
43 }
44
45 void InvokeCallbackOnNthInvocation(int* count, const base::Closure& callback) {
46   --*count;
47   if (*count <= 0)
48     callback.Run();
49 }
50
51 }  // namespace
52
53 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
54     : next_(change_count_map_.end()) {}
55 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
56
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;
71 }
72
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;
80   }
81   return num_changes;
82 }
83
84 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
85     const GURL& origin, int64 changes) {
86   if (changes != 0) {
87     change_count_map_[origin] = changes;
88     return;
89   }
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);
95   }
96 }
97
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);
104 }
105
106 // LocalFileSyncService -------------------------------------------------------
107
108 scoped_ptr<LocalFileSyncService> LocalFileSyncService::Create(
109     Profile* profile) {
110   return make_scoped_ptr(new LocalFileSyncService(profile, NULL));
111 }
112
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();
120 }
121
122 LocalFileSyncService::~LocalFileSyncService() {
123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
124 }
125
126 void LocalFileSyncService::Shutdown() {
127   sync_context_->RemoveOriginChangeObserver(this);
128   sync_context_->ShutdownOnUIThread();
129   profile_ = NULL;
130 }
131
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));
141 }
142
143 void LocalFileSyncService::AddChangeObserver(Observer* observer) {
144   change_observers_.AddObserver(observer);
145 }
146
147 void LocalFileSyncService::RegisterURLForWaitingSync(
148     const FileSystemURL& url,
149     const base::Closure& on_syncable_callback) {
150   sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
151 }
152
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;
160   }
161   DCHECK(local_sync_callback_.is_null());
162   DCHECK(!origin.is_empty());
163   DCHECK(ContainsKey(origin_to_contexts_, origin));
164
165   DVLOG(1) << "Starting ProcessLocalChange";
166
167   local_sync_callback_ = callback;
168
169   sync_context_->GetFileForLocalSync(
170       origin_to_contexts_[origin],
171       base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
172                  AsWeakPtr()));
173 }
174
175 void LocalFileSyncService::SetLocalChangeProcessor(
176     LocalChangeProcessor* local_change_processor) {
177   local_change_processor_ = local_change_processor;
178 }
179
180 void LocalFileSyncService::SetLocalChangeProcessorCallback(
181     const GetLocalChangeProcessorCallback& get_local_change_processor) {
182   get_local_change_processor_ = get_local_change_processor;
183 }
184
185 void LocalFileSyncService::HasPendingLocalChanges(
186     const FileSystemURL& url,
187     const HasPendingLocalChangeCallback& callback) {
188   if (!ContainsKey(origin_to_contexts_, url.origin())) {
189     base::ThreadTaskRunnerHandle::Get()->PostTask(
190         FROM_HERE,
191         base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
192     return;
193   }
194   sync_context_->HasPendingLocalChanges(
195       origin_to_contexts_[url.origin()], url, callback);
196 }
197
198 void LocalFileSyncService::PromoteDemotedChanges(
199     const base::Closure& callback) {
200   if (origin_to_contexts_.empty()) {
201     callback.Run();
202     return;
203   }
204
205   base::Closure completion_callback =
206       base::Bind(&InvokeCallbackOnNthInvocation,
207                  base::Owned(new int(origin_to_contexts_.size())), callback);
208   for (OriginToContext::iterator iter = origin_to_contexts_.begin();
209        iter != origin_to_contexts_.end(); ++iter)
210     sync_context_->PromoteDemotedChanges(iter->first, iter->second,
211                                          completion_callback);
212 }
213
214 void LocalFileSyncService::GetLocalFileMetadata(
215     const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
216   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
217   sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
218                                  url, callback);
219 }
220
221 void LocalFileSyncService::PrepareForProcessRemoteChange(
222     const FileSystemURL& url,
223     const PrepareChangeCallback& callback) {
224   DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
225
226   if (!ContainsKey(origin_to_contexts_, url.origin())) {
227     // This could happen if a remote sync is triggered for the app that hasn't
228     // been initialized in this service.
229     DCHECK(profile_);
230     // The given url.origin() must be for valid installed app.
231     const extensions::Extension* extension =
232         extensions::ExtensionRegistry::Get(profile_)
233             ->enabled_extensions().GetAppByURL(url.origin());
234     if (!extension) {
235       util::Log(
236           logging::LOG_WARNING,
237           FROM_HERE,
238           "PrepareForProcessRemoteChange called for non-existing origin: %s",
239           url.origin().spec().c_str());
240
241       // The extension has been uninstalled and this method is called
242       // before the remote changes for the origin are removed.
243       callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
244                    SyncFileMetadata(), FileChangeList());
245       return;
246     }
247     GURL site_url =
248         extensions::util::GetSiteForExtensionId(extension->id(), profile_);
249     DCHECK(!site_url.is_empty());
250     scoped_refptr<storage::FileSystemContext> file_system_context =
251         content::BrowserContext::GetStoragePartitionForSite(profile_, site_url)
252             ->GetFileSystemContext();
253     MaybeInitializeFileSystemContext(
254         url.origin(),
255         file_system_context.get(),
256         base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
257                    AsWeakPtr(),
258                    url,
259                    file_system_context,
260                    callback));
261     return;
262   }
263
264   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
265   sync_context_->PrepareForSync(
266       origin_to_contexts_[url.origin()], url,
267       LocalFileSyncContext::SYNC_EXCLUSIVE,
268       base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
269 }
270
271 void LocalFileSyncService::ApplyRemoteChange(
272     const FileChange& change,
273     const base::FilePath& local_path,
274     const FileSystemURL& url,
275     const SyncStatusCallback& callback) {
276   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
277   util::Log(logging::LOG_VERBOSE, FROM_HERE,
278             "[Remote -> Local] ApplyRemoteChange: %s on %s",
279             change.DebugString().c_str(),
280             url.DebugString().c_str());
281
282   sync_context_->ApplyRemoteChange(
283       origin_to_contexts_[url.origin()],
284       change, local_path, url,
285       base::Bind(&LocalFileSyncService::DidApplyRemoteChange, AsWeakPtr(),
286                  callback));
287 }
288
289 void LocalFileSyncService::FinalizeRemoteSync(
290     const FileSystemURL& url,
291     bool clear_local_changes,
292     const base::Closure& completion_callback) {
293   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
294   sync_context_->FinalizeExclusiveSync(
295       origin_to_contexts_[url.origin()],
296       url, clear_local_changes, completion_callback);
297 }
298
299 void LocalFileSyncService::RecordFakeLocalChange(
300     const FileSystemURL& url,
301     const FileChange& change,
302     const SyncStatusCallback& callback) {
303   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
304   sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
305                                        url, change, callback);
306 }
307
308 void LocalFileSyncService::OnChangesAvailableInOrigins(
309     const std::set<GURL>& origins) {
310   bool need_notification = false;
311   for (std::set<GURL>::const_iterator iter = origins.begin();
312        iter != origins.end(); ++iter) {
313     const GURL& origin = *iter;
314     if (!ContainsKey(origin_to_contexts_, origin)) {
315       // This could happen if this is called for apps/origins that haven't
316       // been initialized yet, or for apps/origins that are disabled.
317       // (Local change tracker could call this for uninitialized origins
318       // while it's reading dirty files from the database in the
319       // initialization phase.)
320       pending_origins_with_changes_.insert(origin);
321       continue;
322     }
323     need_notification = true;
324     SyncFileSystemBackend* backend =
325         SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
326     DCHECK(backend);
327     DCHECK(backend->change_tracker());
328     origin_change_map_.SetOriginChangeCount(
329         origin, backend->change_tracker()->num_changes());
330   }
331   if (!need_notification)
332     return;
333   int64 num_changes = origin_change_map_.GetTotalChangeCount();
334   FOR_EACH_OBSERVER(Observer, change_observers_,
335                     OnLocalChangeAvailable(num_changes));
336 }
337
338 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
339   if (!ContainsKey(origin_to_contexts_, origin))
340     return;
341   origin_change_map_.SetOriginEnabled(origin, enabled);
342 }
343
344 LocalFileSyncService::LocalFileSyncService(Profile* profile,
345                                            leveldb::Env* env_override)
346     : profile_(profile),
347       sync_context_(new LocalFileSyncContext(
348           profile_->GetPath(),
349           env_override,
350           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
351           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
352               .get())),
353       local_change_processor_(NULL) {
354   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355   sync_context_->AddOriginChangeObserver(this);
356 }
357
358 void LocalFileSyncService::DidInitializeFileSystemContext(
359     const GURL& app_origin,
360     storage::FileSystemContext* file_system_context,
361     const SyncStatusCallback& callback,
362     SyncStatusCode status) {
363   if (status != SYNC_STATUS_OK) {
364     callback.Run(status);
365     return;
366   }
367   DCHECK(file_system_context);
368   origin_to_contexts_[app_origin] = file_system_context;
369
370   if (pending_origins_with_changes_.find(app_origin) !=
371       pending_origins_with_changes_.end()) {
372     // We have remaining changes for the origin.
373     pending_origins_with_changes_.erase(app_origin);
374     SyncFileSystemBackend* backend =
375         SyncFileSystemBackend::GetBackend(file_system_context);
376     DCHECK(backend);
377     DCHECK(backend->change_tracker());
378     origin_change_map_.SetOriginChangeCount(
379         app_origin, backend->change_tracker()->num_changes());
380     int64 num_changes = origin_change_map_.GetTotalChangeCount();
381     FOR_EACH_OBSERVER(Observer, change_observers_,
382                       OnLocalChangeAvailable(num_changes));
383   }
384   callback.Run(status);
385 }
386
387 void LocalFileSyncService::DidInitializeForRemoteSync(
388     const FileSystemURL& url,
389     storage::FileSystemContext* file_system_context,
390     const PrepareChangeCallback& callback,
391     SyncStatusCode status) {
392   if (status != SYNC_STATUS_OK) {
393     DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
394              << url.DebugString() << " status=" << status
395              << " (" << SyncStatusCodeToString(status) << ")";
396     callback.Run(status, SyncFileMetadata(), FileChangeList());
397     return;
398   }
399   origin_to_contexts_[url.origin()] = file_system_context;
400   PrepareForProcessRemoteChange(url, callback);
401 }
402
403 void LocalFileSyncService::RunLocalSyncCallback(
404     SyncStatusCode status,
405     const FileSystemURL& url) {
406   DVLOG(1) << "Local sync is finished with: " << status
407            << " on " << url.DebugString();
408   DCHECK(!local_sync_callback_.is_null());
409   SyncFileCallback callback = local_sync_callback_;
410   local_sync_callback_.Reset();
411   callback.Run(status, url);
412 }
413
414 void LocalFileSyncService::DidApplyRemoteChange(
415     const SyncStatusCallback& callback,
416     SyncStatusCode status) {
417   util::Log(logging::LOG_VERBOSE, FROM_HERE,
418             "[Remote -> Local] ApplyRemoteChange finished --> %s",
419             SyncStatusCodeToString(status));
420   callback.Run(status);
421 }
422
423 void LocalFileSyncService::DidGetFileForLocalSync(
424     SyncStatusCode status,
425     const LocalFileSyncInfo& sync_file_info,
426     storage::ScopedFile snapshot) {
427   DCHECK(!local_sync_callback_.is_null());
428   if (status != SYNC_STATUS_OK) {
429     RunLocalSyncCallback(status, sync_file_info.url);
430     return;
431   }
432   if (sync_file_info.changes.empty()) {
433     // There's a slight chance this could happen.
434     SyncFileCallback callback = local_sync_callback_;
435     local_sync_callback_.Reset();
436     ProcessLocalChange(callback);
437     return;
438   }
439
440   FileChange next_change = sync_file_info.changes.front();
441   DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
442            << " change:" << next_change.DebugString();
443
444   GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
445       next_change,
446       sync_file_info.local_file_path,
447       sync_file_info.metadata,
448       sync_file_info.url,
449       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
450                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
451                  next_change, sync_file_info.changes.PopAndGetNewList()));
452 }
453
454 void LocalFileSyncService::ProcessNextChangeForURL(
455     storage::ScopedFile snapshot,
456     const LocalFileSyncInfo& sync_file_info,
457     const FileChange& processed_change,
458     const FileChangeList& changes,
459     SyncStatusCode status) {
460   DVLOG(1) << "Processed one local change: "
461            << sync_file_info.url.DebugString()
462            << " change:" << processed_change.DebugString()
463            << " status:" << status;
464
465   if (status == SYNC_STATUS_RETRY) {
466     GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
467         processed_change,
468         sync_file_info.local_file_path,
469         sync_file_info.metadata,
470         sync_file_info.url,
471         base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
472                    AsWeakPtr(), base::Passed(&snapshot),
473                    sync_file_info, processed_change, changes));
474     return;
475   }
476
477   if (status == SYNC_FILE_ERROR_NOT_FOUND &&
478       processed_change.change() == FileChange::FILE_CHANGE_DELETE) {
479     // This must be ok (and could happen).
480     status = SYNC_STATUS_OK;
481   }
482
483   const FileSystemURL& url = sync_file_info.url;
484   if (status != SYNC_STATUS_OK || changes.empty()) {
485     DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
486     sync_context_->FinalizeSnapshotSync(
487         origin_to_contexts_[url.origin()], url, status,
488         base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
489                    AsWeakPtr(), status, url));
490     return;
491   }
492
493   FileChange next_change = changes.front();
494   GetLocalChangeProcessor(url)->ApplyLocalChange(
495       changes.front(),
496       sync_file_info.local_file_path,
497       sync_file_info.metadata,
498       url,
499       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
500                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
501                  next_change, changes.PopAndGetNewList()));
502 }
503
504 LocalChangeProcessor* LocalFileSyncService::GetLocalChangeProcessor(
505     const FileSystemURL& url) {
506   if (!get_local_change_processor_.is_null())
507     return get_local_change_processor_.Run(url.origin());
508   DCHECK(local_change_processor_);
509   return local_change_processor_;
510 }
511
512 }  // namespace sync_file_system