Only allow leveldb to use the minimum amount of file descriptors.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / local_file_sync_service.cc
blob13cbefb76a7fcb27f890c191ee325648d0ca17a2
1 // Copyright (c) 2012 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_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/local_change_processor.h"
12 #include "chrome/browser/sync_file_system/logger.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/site_instance.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "url/gurl.h"
18 #include "webkit/browser/fileapi/file_system_context.h"
19 #include "webkit/browser/fileapi/file_system_url.h"
20 #include "webkit/browser/fileapi/syncable/file_change.h"
21 #include "webkit/browser/fileapi/syncable/local_file_change_tracker.h"
22 #include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
23 #include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
25 using content::BrowserThread;
26 using fileapi::FileSystemURL;
28 namespace sync_file_system {
30 namespace {
32 void PrepareForProcessRemoteChangeCallbackAdapter(
33 const RemoteChangeProcessor::PrepareChangeCallback& callback,
34 SyncStatusCode status,
35 const LocalFileSyncInfo& sync_file_info) {
36 callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
39 } // namespace
41 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
42 : next_(change_count_map_.end()) {}
43 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
45 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
46 DCHECK(origin);
47 if (change_count_map_.empty())
48 return false;
49 Map::iterator begin = next_;
50 do {
51 if (next_ == change_count_map_.end())
52 next_ = change_count_map_.begin();
53 DCHECK_NE(0, next_->second);
54 *origin = next_++->first;
55 if (!ContainsKey(disabled_origins_, *origin))
56 return true;
57 } while (next_ != begin);
58 return false;
61 int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
62 int64 num_changes = 0;
63 for (Map::const_iterator iter = change_count_map_.begin();
64 iter != change_count_map_.end(); ++iter) {
65 if (ContainsKey(disabled_origins_, iter->first))
66 continue;
67 num_changes += iter->second;
69 return num_changes;
72 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
73 const GURL& origin, int64 changes) {
74 if (changes != 0) {
75 change_count_map_[origin] = changes;
76 return;
78 Map::iterator found = change_count_map_.find(origin);
79 if (found != change_count_map_.end()) {
80 if (next_ == found)
81 ++next_;
82 change_count_map_.erase(found);
86 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
87 const GURL& origin, bool enabled) {
88 if (enabled)
89 disabled_origins_.erase(origin);
90 else
91 disabled_origins_.insert(origin);
94 // LocalFileSyncService -------------------------------------------------------
96 LocalFileSyncService::LocalFileSyncService(Profile* profile)
97 : profile_(profile),
98 sync_context_(new LocalFileSyncContext(
99 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
100 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
101 .get())),
102 local_change_processor_(NULL) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104 sync_context_->AddOriginChangeObserver(this);
107 LocalFileSyncService::~LocalFileSyncService() {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
111 void LocalFileSyncService::Shutdown() {
112 sync_context_->RemoveOriginChangeObserver(this);
113 sync_context_->ShutdownOnUIThread();
114 profile_ = NULL;
117 void LocalFileSyncService::MaybeInitializeFileSystemContext(
118 const GURL& app_origin,
119 fileapi::FileSystemContext* file_system_context,
120 const SyncStatusCallback& callback) {
121 sync_context_->MaybeInitializeFileSystemContext(
122 app_origin, file_system_context,
123 base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext,
124 AsWeakPtr(), app_origin,
125 make_scoped_refptr(file_system_context), callback));
128 void LocalFileSyncService::AddChangeObserver(Observer* observer) {
129 change_observers_.AddObserver(observer);
132 void LocalFileSyncService::RegisterURLForWaitingSync(
133 const FileSystemURL& url,
134 const base::Closure& on_syncable_callback) {
135 sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback);
138 void LocalFileSyncService::ProcessLocalChange(
139 const SyncFileCallback& callback) {
140 DCHECK(local_change_processor_);
141 // Pick an origin to process next.
142 GURL origin;
143 if (!origin_change_map_.NextOriginToProcess(&origin)) {
144 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL());
145 return;
147 DCHECK(local_sync_callback_.is_null());
148 DCHECK(!origin.is_empty());
149 DCHECK(ContainsKey(origin_to_contexts_, origin));
151 DVLOG(1) << "Starting ProcessLocalChange";
153 local_sync_callback_ = callback;
155 sync_context_->GetFileForLocalSync(
156 origin_to_contexts_[origin],
157 base::Bind(&LocalFileSyncService::DidGetFileForLocalSync,
158 AsWeakPtr()));
161 void LocalFileSyncService::SetLocalChangeProcessor(
162 LocalChangeProcessor* processor) {
163 local_change_processor_ = processor;
166 void LocalFileSyncService::HasPendingLocalChanges(
167 const FileSystemURL& url,
168 const HasPendingLocalChangeCallback& callback) {
169 if (!ContainsKey(origin_to_contexts_, url.origin())) {
170 base::MessageLoopProxy::current()->PostTask(
171 FROM_HERE,
172 base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false));
173 return;
175 sync_context_->HasPendingLocalChanges(
176 origin_to_contexts_[url.origin()], url, callback);
179 void LocalFileSyncService::ClearSyncFlagForURL(
180 const FileSystemURL& url) {
181 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
182 sync_context_->ClearSyncFlagForURL(url);
185 void LocalFileSyncService::GetLocalFileMetadata(
186 const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
187 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
188 sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
189 url, callback);
192 void LocalFileSyncService::PrepareForProcessRemoteChange(
193 const FileSystemURL& url,
194 const PrepareChangeCallback& callback) {
195 DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
197 if (!ContainsKey(origin_to_contexts_, url.origin())) {
198 // This could happen if a remote sync is triggered for the app that hasn't
199 // been initialized in this service.
200 DCHECK(profile_);
201 // The given url.origin() must be for valid installed app.
202 ExtensionService* extension_service =
203 extensions::ExtensionSystem::Get(profile_)->extension_service();
204 const extensions::Extension* extension = extension_service->GetInstalledApp(
205 url.origin());
206 if (!extension) {
207 util::Log(
208 logging::LOG_WARNING,
209 FROM_HERE,
210 "PrepareForProcessRemoteChange called for non-existing origin: %s",
211 url.origin().spec().c_str());
213 // The extension has been uninstalled and this method is called
214 // before the remote changes for the origin are removed.
215 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
216 SyncFileMetadata(), FileChangeList());
217 return;
219 GURL site_url = extension_service->GetSiteForExtensionId(extension->id());
220 DCHECK(!site_url.is_empty());
221 scoped_refptr<fileapi::FileSystemContext> file_system_context =
222 content::BrowserContext::GetStoragePartitionForSite(
223 profile_, site_url)->GetFileSystemContext();
224 MaybeInitializeFileSystemContext(
225 url.origin(),
226 file_system_context.get(),
227 base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
228 AsWeakPtr(),
229 url,
230 file_system_context,
231 callback));
232 return;
235 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
236 sync_context_->PrepareForSync(
237 origin_to_contexts_[url.origin()], url,
238 base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
241 void LocalFileSyncService::ApplyRemoteChange(
242 const FileChange& change,
243 const base::FilePath& local_path,
244 const FileSystemURL& url,
245 const SyncStatusCallback& callback) {
246 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
247 sync_context_->ApplyRemoteChange(
248 origin_to_contexts_[url.origin()],
249 change, local_path, url, callback);
252 void LocalFileSyncService::ClearLocalChanges(
253 const FileSystemURL& url,
254 const base::Closure& completion_callback) {
255 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
256 sync_context_->ClearChangesForURL(origin_to_contexts_[url.origin()],
257 url, completion_callback);
260 void LocalFileSyncService::RecordFakeLocalChange(
261 const FileSystemURL& url,
262 const FileChange& change,
263 const SyncStatusCallback& callback) {
264 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
265 sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
266 url, change, callback);
269 void LocalFileSyncService::OnChangesAvailableInOrigins(
270 const std::set<GURL>& origins) {
271 bool need_notification = false;
272 for (std::set<GURL>::const_iterator iter = origins.begin();
273 iter != origins.end(); ++iter) {
274 const GURL& origin = *iter;
275 if (!ContainsKey(origin_to_contexts_, origin)) {
276 // This could happen if this is called for apps/origins that haven't
277 // been initialized yet, or for apps/origins that are disabled.
278 // (Local change tracker could call this for uninitialized origins
279 // while it's reading dirty files from the database in the
280 // initialization phase.)
281 pending_origins_with_changes_.insert(origin);
282 continue;
284 need_notification = true;
285 fileapi::FileSystemContext* context = origin_to_contexts_[origin];
286 DCHECK(context->change_tracker());
287 origin_change_map_.SetOriginChangeCount(
288 origin, context->change_tracker()->num_changes());
290 if (!need_notification)
291 return;
292 int64 num_changes = origin_change_map_.GetTotalChangeCount();
293 FOR_EACH_OBSERVER(Observer, change_observers_,
294 OnLocalChangeAvailable(num_changes));
297 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
298 if (!ContainsKey(origin_to_contexts_, origin))
299 return;
300 origin_change_map_.SetOriginEnabled(origin, enabled);
303 void LocalFileSyncService::DidInitializeFileSystemContext(
304 const GURL& app_origin,
305 fileapi::FileSystemContext* file_system_context,
306 const SyncStatusCallback& callback,
307 SyncStatusCode status) {
308 if (status != SYNC_STATUS_OK) {
309 callback.Run(status);
310 return;
312 DCHECK(file_system_context);
313 origin_to_contexts_[app_origin] = file_system_context;
315 if (pending_origins_with_changes_.find(app_origin) !=
316 pending_origins_with_changes_.end()) {
317 // We have remaining changes for the origin.
318 pending_origins_with_changes_.erase(app_origin);
319 DCHECK(file_system_context->change_tracker());
320 origin_change_map_.SetOriginChangeCount(
321 app_origin, file_system_context->change_tracker()->num_changes());
322 int64 num_changes = origin_change_map_.GetTotalChangeCount();
323 FOR_EACH_OBSERVER(Observer, change_observers_,
324 OnLocalChangeAvailable(num_changes));
326 callback.Run(status);
329 void LocalFileSyncService::DidInitializeForRemoteSync(
330 const FileSystemURL& url,
331 fileapi::FileSystemContext* file_system_context,
332 const PrepareChangeCallback& callback,
333 SyncStatusCode status) {
334 if (status != SYNC_STATUS_OK) {
335 DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
336 << url.DebugString() << " status=" << status
337 << " (" << SyncStatusCodeToString(status) << ")";
338 callback.Run(status, SyncFileMetadata(), FileChangeList());
339 return;
341 origin_to_contexts_[url.origin()] = file_system_context;
342 PrepareForProcessRemoteChange(url, callback);
345 void LocalFileSyncService::RunLocalSyncCallback(
346 SyncStatusCode status,
347 const FileSystemURL& url) {
348 DVLOG(1) << "Local sync is finished with: " << status
349 << " on " << url.DebugString();
350 DCHECK(!local_sync_callback_.is_null());
351 SyncFileCallback callback = local_sync_callback_;
352 local_sync_callback_.Reset();
353 callback.Run(status, url);
356 void LocalFileSyncService::DidGetFileForLocalSync(
357 SyncStatusCode status,
358 const LocalFileSyncInfo& sync_file_info) {
359 DCHECK(!local_sync_callback_.is_null());
360 if (status != SYNC_STATUS_OK) {
361 RunLocalSyncCallback(status, sync_file_info.url);
362 return;
364 if (sync_file_info.changes.empty()) {
365 // There's a slight chance this could happen.
366 SyncFileCallback callback = local_sync_callback_;
367 local_sync_callback_.Reset();
368 ProcessLocalChange(callback);
369 return;
372 DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
373 << " change:" << sync_file_info.changes.front().DebugString();
375 local_change_processor_->ApplyLocalChange(
376 sync_file_info.changes.front(),
377 sync_file_info.local_file_path,
378 sync_file_info.metadata,
379 sync_file_info.url,
380 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
381 AsWeakPtr(),
382 sync_file_info,
383 sync_file_info.changes.front(),
384 sync_file_info.changes.PopAndGetNewList()));
387 void LocalFileSyncService::ProcessNextChangeForURL(
388 const LocalFileSyncInfo& sync_file_info,
389 const FileChange& last_change,
390 const FileChangeList& changes,
391 SyncStatusCode status) {
392 DVLOG(1) << "Processed one local change: "
393 << sync_file_info.url.DebugString()
394 << " change:" << last_change.DebugString()
395 << " status:" << status;
397 if (status == SYNC_FILE_ERROR_NOT_FOUND &&
398 last_change.change() == FileChange::FILE_CHANGE_DELETE) {
399 // This must be ok (and could happen).
400 status = SYNC_STATUS_OK;
403 // TODO(kinuko,tzik): Handle other errors that should not be considered
404 // a sync error.
406 const FileSystemURL& url = sync_file_info.url;
407 if (status != SYNC_STATUS_OK || changes.empty()) {
408 if (status == SYNC_STATUS_OK || status == SYNC_STATUS_HAS_CONFLICT) {
409 // Clear the recorded changes for the URL if the sync was successfull
410 // OR has failed due to conflict (so that we won't stick to the same
411 // conflicting file again and again).
412 DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
413 sync_context_->ClearChangesForURL(
414 origin_to_contexts_[url.origin()], url,
415 base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
416 AsWeakPtr(), status, url));
417 return;
419 RunLocalSyncCallback(status, url);
420 return;
423 local_change_processor_->ApplyLocalChange(
424 changes.front(),
425 sync_file_info.local_file_path,
426 sync_file_info.metadata,
427 url,
428 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
429 AsWeakPtr(), sync_file_info,
430 changes.front(), changes.PopAndGetNewList()));
433 } // namespace sync_file_system