- add sources.
[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/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"
22 #include "url/gurl.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"
26
27 using content::BrowserThread;
28 using fileapi::FileSystemURL;
29
30 namespace sync_file_system {
31
32 namespace {
33
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);
40 }
41
42 }  // namespace
43
44 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
45     : next_(change_count_map_.end()) {}
46 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
47
48 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
49   DCHECK(origin);
50   if (change_count_map_.empty())
51     return false;
52   Map::iterator begin = next_;
53   do {
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))
59       return true;
60   } while (next_ != begin);
61   return false;
62 }
63
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))
69       continue;
70     num_changes += iter->second;
71   }
72   return num_changes;
73 }
74
75 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
76     const GURL& origin, int64 changes) {
77   if (changes != 0) {
78     change_count_map_[origin] = changes;
79     return;
80   }
81   Map::iterator found = change_count_map_.find(origin);
82   if (found != change_count_map_.end()) {
83     if (next_ == found)
84       ++next_;
85     change_count_map_.erase(found);
86   }
87 }
88
89 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
90     const GURL& origin, bool enabled) {
91   if (enabled)
92     disabled_origins_.erase(origin);
93   else
94     disabled_origins_.insert(origin);
95 }
96
97 // LocalFileSyncService -------------------------------------------------------
98
99 LocalFileSyncService::LocalFileSyncService(Profile* profile)
100     : profile_(profile),
101       sync_context_(new LocalFileSyncContext(
102           profile_->GetPath(),
103           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
104           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
105               .get())),
106       local_change_processor_(NULL) {
107   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108   sync_context_->AddOriginChangeObserver(this);
109 }
110
111 LocalFileSyncService::~LocalFileSyncService() {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113 }
114
115 void LocalFileSyncService::Shutdown() {
116   sync_context_->RemoveOriginChangeObserver(this);
117   sync_context_->ShutdownOnUIThread();
118   profile_ = NULL;
119 }
120
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));
130 }
131
132 void LocalFileSyncService::AddChangeObserver(Observer* observer) {
133   change_observers_.AddObserver(observer);
134 }
135
136 void LocalFileSyncService::RegisterURLForWaitingSync(
137     const FileSystemURL& url,
138     const base::Closure& on_syncable_callback) {
139   sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
140 }
141
142 void LocalFileSyncService::ProcessLocalChange(
143     const SyncFileCallback& callback) {
144   DCHECK(local_change_processor_);
145   // Pick an origin to process next.
146   GURL origin;
147   if (!origin_change_map_.NextOriginToProcess(&origin)) {
148     callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
149     return;
150   }
151   DCHECK(local_sync_callback_.is_null());
152   DCHECK(!origin.is_empty());
153   DCHECK(ContainsKey(origin_to_contexts_, origin));
154
155   DVLOG(1) << "Starting ProcessLocalChange";
156
157   local_sync_callback_ = callback;
158
159   sync_context_->GetFileForLocalSync(
160       origin_to_contexts_[origin],
161       base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
162                  AsWeakPtr()));
163 }
164
165 void LocalFileSyncService::SetLocalChangeProcessor(
166     LocalChangeProcessor* processor) {
167   local_change_processor_ = processor;
168 }
169
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(
175         FROM_HERE,
176         base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
177     return;
178   }
179   sync_context_->HasPendingLocalChanges(
180       origin_to_contexts_[url.origin()], url, callback);
181 }
182
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()],
187                                  url, callback);
188 }
189
190 void LocalFileSyncService::PrepareForProcessRemoteChange(
191     const FileSystemURL& url,
192     const PrepareChangeCallback& callback) {
193   DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
194
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.
198     DCHECK(profile_);
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(
203         url.origin());
204     if (!extension) {
205       util::Log(
206           logging::LOG_WARNING,
207           FROM_HERE,
208           "PrepareForProcessRemoteChange called for non-existing origin: %s",
209           url.origin().spec().c_str());
210
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());
215       return;
216     }
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(
223         url.origin(),
224         file_system_context.get(),
225         base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
226                    AsWeakPtr(),
227                    url,
228                    file_system_context,
229                    callback));
230     return;
231   }
232
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));
238 }
239
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);
249 }
250
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);
259 }
260
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);
268 }
269
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);
283       continue;
284     }
285     need_notification = true;
286     SyncFileSystemBackend* backend =
287         SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
288     DCHECK(backend);
289     DCHECK(backend->change_tracker());
290     origin_change_map_.SetOriginChangeCount(
291         origin, backend->change_tracker()->num_changes());
292   }
293   if (!need_notification)
294     return;
295   int64 num_changes = origin_change_map_.GetTotalChangeCount();
296   FOR_EACH_OBSERVER(Observer, change_observers_,
297                     OnLocalChangeAvailable(num_changes));
298 }
299
300 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
301   if (!ContainsKey(origin_to_contexts_, origin))
302     return;
303   origin_change_map_.SetOriginEnabled(origin, enabled);
304 }
305
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);
313     return;
314   }
315   DCHECK(file_system_context);
316   origin_to_contexts_[app_origin] = file_system_context;
317
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);
324     DCHECK(backend);
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));
331   }
332   callback.Run(status);
333 }
334
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());
345     return;
346   }
347   origin_to_contexts_[url.origin()] = file_system_context;
348   PrepareForProcessRemoteChange(url, callback);
349 }
350
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);
360 }
361
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);
369     return;
370   }
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);
376     return;
377   }
378
379   FileChange next_change = sync_file_info.changes.front();
380   DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
381            << " change:" << next_change.DebugString();
382
383   local_change_processor_->ApplyLocalChange(
384       next_change,
385       sync_file_info.local_file_path,
386       sync_file_info.metadata,
387       sync_file_info.url,
388       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
389                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
390                  next_change, sync_file_info.changes.PopAndGetNewList()));
391 }
392
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;
403
404   if (status == SYNC_STATUS_RETRY) {
405     local_change_processor_->ApplyLocalChange(
406         processed_change,
407         sync_file_info.local_file_path,
408         sync_file_info.metadata,
409         sync_file_info.url,
410         base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
411                    AsWeakPtr(), base::Passed(&snapshot),
412                    sync_file_info, processed_change, changes));
413     return;
414   }
415
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;
420   }
421
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));
429     return;
430   }
431
432   FileChange next_change = changes.front();
433   local_change_processor_->ApplyLocalChange(
434       changes.front(),
435       sync_file_info.local_file_path,
436       sync_file_info.metadata,
437       url,
438       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
439                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
440                  next_change, changes.PopAndGetNewList()));
441 }
442
443 }  // namespace sync_file_system