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