Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / provided_file_system.cc
blobc4e6cd3759f34b13f74ee283d6e2403f2c9abb67
1 // Copyright 2014 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/chromeos/file_system_provider/provided_file_system.h"
7 #include <vector>
9 #include "base/debug/trace_event.h"
10 #include "base/files/file.h"
11 #include "chrome/browser/chromeos/file_system_provider/notification_manager.h"
12 #include "chrome/browser/chromeos/file_system_provider/operations/abort.h"
13 #include "chrome/browser/chromeos/file_system_provider/operations/add_watcher.h"
14 #include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
15 #include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
16 #include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
17 #include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
18 #include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
19 #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
20 #include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
21 #include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
22 #include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
23 #include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
24 #include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
25 #include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
26 #include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
27 #include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
28 #include "chrome/browser/chromeos/file_system_provider/request_manager.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/common/extensions/api/file_system_provider.h"
31 #include "extensions/browser/event_router.h"
33 namespace net {
34 class IOBuffer;
35 } // namespace net
37 namespace chromeos {
38 namespace file_system_provider {
39 namespace {
41 // Discards the result of Abort() when called from the destructor.
42 void EmptyStatusCallback(base::File::Error /* result */) {
45 // Discards the error code and always calls the callback with a success.
46 void AlwaysSuccessCallback(
47 const storage::AsyncFileUtil::StatusCallback& callback,
48 base::File::Error /* result */) {
49 callback.Run(base::File::FILE_OK);
52 } // namespace
54 AutoUpdater::AutoUpdater(const base::Closure& update_callback)
55 : update_callback_(update_callback),
56 created_callbacks_(0),
57 pending_callbacks_(0) {
60 base::Closure AutoUpdater::CreateCallback() {
61 ++created_callbacks_;
62 ++pending_callbacks_;
63 return base::Bind(&AutoUpdater::OnPendingCallback, this);
66 void AutoUpdater::OnPendingCallback() {
67 DCHECK_LT(0, pending_callbacks_);
68 if (--pending_callbacks_ == 0)
69 update_callback_.Run();
72 AutoUpdater::~AutoUpdater() {
73 // If no callbacks are created, then we need to invoke updating in the
74 // destructor.
75 if (!created_callbacks_)
76 update_callback_.Run();
77 else if (pending_callbacks_)
78 LOG(ERROR) << "Not all callbacks called. This may happen on shutdown.";
81 ProvidedFileSystem::ProvidedFileSystem(
82 Profile* profile,
83 const ProvidedFileSystemInfo& file_system_info)
84 : profile_(profile),
85 event_router_(extensions::EventRouter::Get(profile)), // May be NULL.
86 file_system_info_(file_system_info),
87 notification_manager_(
88 new NotificationManager(profile_, file_system_info_)),
89 request_manager_(new RequestManager(notification_manager_.get())),
90 weak_ptr_factory_(this) {
93 ProvidedFileSystem::~ProvidedFileSystem() {
94 const std::vector<int> request_ids = request_manager_->GetActiveRequestIds();
95 for (size_t i = 0; i < request_ids.size(); ++i) {
96 Abort(request_ids[i], base::Bind(&EmptyStatusCallback));
100 void ProvidedFileSystem::SetEventRouterForTesting(
101 extensions::EventRouter* event_router) {
102 event_router_ = event_router;
105 void ProvidedFileSystem::SetNotificationManagerForTesting(
106 scoped_ptr<NotificationManagerInterface> notification_manager) {
107 notification_manager_ = notification_manager.Pass();
108 request_manager_.reset(new RequestManager(notification_manager_.get()));
111 ProvidedFileSystem::AbortCallback ProvidedFileSystem::RequestUnmount(
112 const storage::AsyncFileUtil::StatusCallback& callback) {
113 const int request_id = request_manager_->CreateRequest(
114 REQUEST_UNMOUNT,
115 scoped_ptr<RequestManager::HandlerInterface>(
116 new operations::Unmount(event_router_, file_system_info_, callback)));
117 if (!request_id) {
118 callback.Run(base::File::FILE_ERROR_SECURITY);
119 return AbortCallback();
122 return base::Bind(
123 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
126 ProvidedFileSystem::AbortCallback ProvidedFileSystem::GetMetadata(
127 const base::FilePath& entry_path,
128 MetadataFieldMask fields,
129 const GetMetadataCallback& callback) {
130 const int request_id = request_manager_->CreateRequest(
131 GET_METADATA,
132 scoped_ptr<RequestManager::HandlerInterface>(new operations::GetMetadata(
133 event_router_, file_system_info_, entry_path, fields, callback)));
134 if (!request_id) {
135 callback.Run(make_scoped_ptr<EntryMetadata>(NULL),
136 base::File::FILE_ERROR_SECURITY);
137 return AbortCallback();
140 return base::Bind(
141 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
144 ProvidedFileSystem::AbortCallback ProvidedFileSystem::ReadDirectory(
145 const base::FilePath& directory_path,
146 const storage::AsyncFileUtil::ReadDirectoryCallback& callback) {
147 const int request_id = request_manager_->CreateRequest(
148 READ_DIRECTORY,
149 scoped_ptr<RequestManager::HandlerInterface>(
150 new operations::ReadDirectory(
151 event_router_, file_system_info_, directory_path, callback)));
152 if (!request_id) {
153 callback.Run(base::File::FILE_ERROR_SECURITY,
154 storage::AsyncFileUtil::EntryList(),
155 false /* has_more */);
156 return AbortCallback();
159 return base::Bind(
160 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
163 ProvidedFileSystem::AbortCallback ProvidedFileSystem::ReadFile(
164 int file_handle,
165 net::IOBuffer* buffer,
166 int64 offset,
167 int length,
168 const ReadChunkReceivedCallback& callback) {
169 TRACE_EVENT1(
170 "file_system_provider", "ProvidedFileSystem::ReadFile", "length", length);
171 const int request_id = request_manager_->CreateRequest(
172 READ_FILE,
173 make_scoped_ptr<RequestManager::HandlerInterface>(
174 new operations::ReadFile(event_router_,
175 file_system_info_,
176 file_handle,
177 buffer,
178 offset,
179 length,
180 callback)));
181 if (!request_id) {
182 callback.Run(0 /* chunk_length */,
183 false /* has_more */,
184 base::File::FILE_ERROR_SECURITY);
185 return AbortCallback();
188 return base::Bind(
189 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
192 ProvidedFileSystem::AbortCallback ProvidedFileSystem::OpenFile(
193 const base::FilePath& file_path,
194 OpenFileMode mode,
195 const OpenFileCallback& callback) {
196 const int request_id = request_manager_->CreateRequest(
197 OPEN_FILE,
198 scoped_ptr<RequestManager::HandlerInterface>(new operations::OpenFile(
199 event_router_, file_system_info_, file_path, mode, callback)));
200 if (!request_id) {
201 callback.Run(0 /* file_handle */, base::File::FILE_ERROR_SECURITY);
202 return AbortCallback();
205 return base::Bind(
206 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
209 ProvidedFileSystem::AbortCallback ProvidedFileSystem::CloseFile(
210 int file_handle,
211 const storage::AsyncFileUtil::StatusCallback& callback) {
212 const int request_id = request_manager_->CreateRequest(
213 CLOSE_FILE,
214 scoped_ptr<RequestManager::HandlerInterface>(new operations::CloseFile(
215 event_router_, file_system_info_, file_handle, callback)));
216 if (!request_id) {
217 callback.Run(base::File::FILE_ERROR_SECURITY);
218 return AbortCallback();
221 return base::Bind(
222 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
225 ProvidedFileSystem::AbortCallback ProvidedFileSystem::CreateDirectory(
226 const base::FilePath& directory_path,
227 bool recursive,
228 const storage::AsyncFileUtil::StatusCallback& callback) {
229 const int request_id = request_manager_->CreateRequest(
230 CREATE_DIRECTORY,
231 scoped_ptr<RequestManager::HandlerInterface>(
232 new operations::CreateDirectory(event_router_,
233 file_system_info_,
234 directory_path,
235 recursive,
236 callback)));
237 if (!request_id) {
238 callback.Run(base::File::FILE_ERROR_SECURITY);
239 return AbortCallback();
242 return base::Bind(
243 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
246 ProvidedFileSystem::AbortCallback ProvidedFileSystem::DeleteEntry(
247 const base::FilePath& entry_path,
248 bool recursive,
249 const storage::AsyncFileUtil::StatusCallback& callback) {
250 const int request_id = request_manager_->CreateRequest(
251 DELETE_ENTRY,
252 scoped_ptr<RequestManager::HandlerInterface>(new operations::DeleteEntry(
253 event_router_, file_system_info_, entry_path, recursive, callback)));
254 if (!request_id) {
255 callback.Run(base::File::FILE_ERROR_SECURITY);
256 return AbortCallback();
259 return base::Bind(
260 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
263 ProvidedFileSystem::AbortCallback ProvidedFileSystem::CreateFile(
264 const base::FilePath& file_path,
265 const storage::AsyncFileUtil::StatusCallback& callback) {
266 const int request_id = request_manager_->CreateRequest(
267 CREATE_FILE,
268 scoped_ptr<RequestManager::HandlerInterface>(new operations::CreateFile(
269 event_router_, file_system_info_, file_path, callback)));
270 if (!request_id) {
271 callback.Run(base::File::FILE_ERROR_SECURITY);
272 return AbortCallback();
275 return base::Bind(
276 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
279 ProvidedFileSystem::AbortCallback ProvidedFileSystem::CopyEntry(
280 const base::FilePath& source_path,
281 const base::FilePath& target_path,
282 const storage::AsyncFileUtil::StatusCallback& callback) {
283 const int request_id = request_manager_->CreateRequest(
284 COPY_ENTRY,
285 scoped_ptr<RequestManager::HandlerInterface>(
286 new operations::CopyEntry(event_router_,
287 file_system_info_,
288 source_path,
289 target_path,
290 callback)));
291 if (!request_id) {
292 callback.Run(base::File::FILE_ERROR_SECURITY);
293 return AbortCallback();
296 return base::Bind(
297 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
300 ProvidedFileSystem::AbortCallback ProvidedFileSystem::WriteFile(
301 int file_handle,
302 net::IOBuffer* buffer,
303 int64 offset,
304 int length,
305 const storage::AsyncFileUtil::StatusCallback& callback) {
306 TRACE_EVENT1("file_system_provider",
307 "ProvidedFileSystem::WriteFile",
308 "length",
309 length);
310 const int request_id = request_manager_->CreateRequest(
311 WRITE_FILE,
312 make_scoped_ptr<RequestManager::HandlerInterface>(
313 new operations::WriteFile(event_router_,
314 file_system_info_,
315 file_handle,
316 make_scoped_refptr(buffer),
317 offset,
318 length,
319 callback)));
320 if (!request_id) {
321 callback.Run(base::File::FILE_ERROR_SECURITY);
322 return AbortCallback();
325 return base::Bind(
326 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
329 ProvidedFileSystem::AbortCallback ProvidedFileSystem::MoveEntry(
330 const base::FilePath& source_path,
331 const base::FilePath& target_path,
332 const storage::AsyncFileUtil::StatusCallback& callback) {
333 const int request_id = request_manager_->CreateRequest(
334 MOVE_ENTRY,
335 scoped_ptr<RequestManager::HandlerInterface>(
336 new operations::MoveEntry(event_router_,
337 file_system_info_,
338 source_path,
339 target_path,
340 callback)));
341 if (!request_id) {
342 callback.Run(base::File::FILE_ERROR_SECURITY);
343 return AbortCallback();
346 return base::Bind(
347 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
350 ProvidedFileSystem::AbortCallback ProvidedFileSystem::Truncate(
351 const base::FilePath& file_path,
352 int64 length,
353 const storage::AsyncFileUtil::StatusCallback& callback) {
354 const int request_id = request_manager_->CreateRequest(
355 TRUNCATE,
356 scoped_ptr<RequestManager::HandlerInterface>(new operations::Truncate(
357 event_router_, file_system_info_, file_path, length, callback)));
358 if (!request_id) {
359 callback.Run(base::File::FILE_ERROR_SECURITY);
360 return AbortCallback();
363 return base::Bind(
364 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
367 ProvidedFileSystem::AbortCallback ProvidedFileSystem::AddWatcher(
368 const GURL& origin,
369 const base::FilePath& entry_path,
370 bool recursive,
371 bool persistent,
372 const storage::AsyncFileUtil::StatusCallback& callback,
373 const storage::WatcherManager::NotificationCallback&
374 notification_callback) {
375 // TODO(mtomasz): Wrap the entire method body with an asynchronous queue to
376 // avoid races.
377 if (persistent && (!file_system_info_.supports_notify_tag() ||
378 !notification_callback.is_null())) {
379 OnAddWatcherCompleted(entry_path,
380 recursive,
381 Subscriber(),
382 callback,
383 base::File::FILE_ERROR_INVALID_OPERATION);
384 return AbortCallback();
387 // Create a candidate subscriber. This could be done in OnAddWatcherCompleted,
388 // but base::Bind supports only up to 7 arguments.
389 Subscriber subscriber;
390 subscriber.origin = origin;
391 subscriber.persistent = persistent;
392 subscriber.notification_callback = notification_callback;
394 const WatcherKey key(entry_path, recursive);
395 const Watchers::const_iterator it = watchers_.find(key);
396 if (it != watchers_.end()) {
397 const bool exists =
398 it->second.subscribers.find(origin) != it->second.subscribers.end();
399 OnAddWatcherCompleted(
400 entry_path,
401 recursive,
402 subscriber,
403 callback,
404 exists ? base::File::FILE_ERROR_EXISTS : base::File::FILE_OK);
405 return AbortCallback();
408 const int request_id = request_manager_->CreateRequest(
409 ADD_WATCHER,
410 scoped_ptr<RequestManager::HandlerInterface>(new operations::AddWatcher(
411 event_router_,
412 file_system_info_,
413 entry_path,
414 recursive,
415 base::Bind(&ProvidedFileSystem::OnAddWatcherCompleted,
416 weak_ptr_factory_.GetWeakPtr(),
417 entry_path,
418 recursive,
419 subscriber,
420 callback))));
422 if (!request_id) {
423 OnAddWatcherCompleted(entry_path,
424 recursive,
425 subscriber,
426 callback,
427 base::File::FILE_ERROR_SECURITY);
428 return AbortCallback();
431 return base::Bind(
432 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
435 void ProvidedFileSystem::RemoveWatcher(
436 const GURL& origin,
437 const base::FilePath& entry_path,
438 bool recursive,
439 const storage::AsyncFileUtil::StatusCallback& callback) {
440 const WatcherKey key(entry_path, recursive);
441 const Watchers::iterator it = watchers_.find(key);
442 if (it == watchers_.end() ||
443 it->second.subscribers.find(origin) == it->second.subscribers.end()) {
444 callback.Run(base::File::FILE_ERROR_NOT_FOUND);
445 return;
448 // Delete the subscriber in advance, since the list of watchers is owned by
449 // the C++ layer, not by the extension.
450 it->second.subscribers.erase(origin);
452 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
453 observers_,
454 OnWatcherListChanged(file_system_info_, watchers_));
456 // If there are other subscribers, then do not remove the obsererver, but
457 // simply return a success.
458 if (it->second.subscribers.size()) {
459 callback.Run(base::File::FILE_OK);
460 return;
463 // Delete the watcher in advance.
464 watchers_.erase(it);
466 // Even if the extension returns an error, the callback is called with base::
467 // File::FILE_OK. The reason for that is that the entry is not watche anymore
468 // anyway, as it's removed in advance.
469 const int request_id = request_manager_->CreateRequest(
470 REMOVE_WATCHER,
471 scoped_ptr<RequestManager::HandlerInterface>(
472 new operations::RemoveWatcher(
473 event_router_,
474 file_system_info_,
475 entry_path,
476 recursive,
477 base::Bind(&AlwaysSuccessCallback, callback))));
478 if (!request_id)
479 callback.Run(base::File::FILE_OK);
482 const ProvidedFileSystemInfo& ProvidedFileSystem::GetFileSystemInfo() const {
483 return file_system_info_;
486 RequestManager* ProvidedFileSystem::GetRequestManager() {
487 return request_manager_.get();
490 Watchers* ProvidedFileSystem::GetWatchers() {
491 return &watchers_;
494 void ProvidedFileSystem::AddObserver(ProvidedFileSystemObserver* observer) {
495 DCHECK(observer);
496 observers_.AddObserver(observer);
499 void ProvidedFileSystem::RemoveObserver(ProvidedFileSystemObserver* observer) {
500 DCHECK(observer);
501 observers_.RemoveObserver(observer);
504 void ProvidedFileSystem::Notify(
505 const base::FilePath& entry_path,
506 bool recursive,
507 storage::WatcherManager::ChangeType change_type,
508 scoped_ptr<ProvidedFileSystemObserver::Changes> changes,
509 const std::string& tag,
510 const storage::AsyncFileUtil::StatusCallback& callback) {
511 const WatcherKey key(entry_path, recursive);
512 const auto& watcher_it = watchers_.find(key);
513 if (watcher_it == watchers_.end()) {
514 callback.Run(base::File::FILE_ERROR_NOT_FOUND);
515 return;
518 // The tag must be provided if and only if it's explicitly supported.
519 if (file_system_info_.supports_notify_tag() == tag.empty()) {
520 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
521 return;
524 // It's illegal to provide a tag which is not unique.
525 if (!tag.empty() && tag == watcher_it->second.last_tag) {
526 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
527 return;
530 // The object is owned by AutoUpdated, so the reference is valid as long as
531 // callbacks created with AutoUpdater::CreateCallback().
532 const ProvidedFileSystemObserver::Changes& changes_ref = *changes.get();
534 scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater(
535 base::Bind(&ProvidedFileSystem::OnNotifyCompleted,
536 weak_ptr_factory_.GetWeakPtr(), entry_path, recursive,
537 change_type, base::Passed(&changes), tag, callback)));
539 // Call all notification callbacks (if any).
540 for (const auto& subscriber_it : watcher_it->second.subscribers) {
541 const storage::WatcherManager::NotificationCallback& notification_callback =
542 subscriber_it.second.notification_callback;
543 if (!notification_callback.is_null())
544 notification_callback.Run(change_type);
547 // Notify all observers.
548 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
549 observers_,
550 OnWatcherChanged(file_system_info_,
551 watcher_it->second,
552 change_type,
553 changes_ref,
554 auto_updater->CreateCallback()));
557 base::WeakPtr<ProvidedFileSystemInterface> ProvidedFileSystem::GetWeakPtr() {
558 return weak_ptr_factory_.GetWeakPtr();
561 void ProvidedFileSystem::Abort(
562 int operation_request_id,
563 const storage::AsyncFileUtil::StatusCallback& callback) {
564 request_manager_->RejectRequest(operation_request_id,
565 make_scoped_ptr(new RequestValue()),
566 base::File::FILE_ERROR_ABORT);
567 if (!request_manager_->CreateRequest(
568 ABORT,
569 scoped_ptr<RequestManager::HandlerInterface>(
570 new operations::Abort(event_router_,
571 file_system_info_,
572 operation_request_id,
573 callback)))) {
574 callback.Run(base::File::FILE_ERROR_SECURITY);
578 void ProvidedFileSystem::OnAddWatcherCompleted(
579 const base::FilePath& entry_path,
580 bool recursive,
581 const Subscriber& subscriber,
582 const storage::AsyncFileUtil::StatusCallback& callback,
583 base::File::Error result) {
584 if (result != base::File::FILE_OK) {
585 callback.Run(result);
586 return;
589 const WatcherKey key(entry_path, recursive);
590 const Watchers::iterator it = watchers_.find(key);
591 if (it != watchers_.end()) {
592 callback.Run(base::File::FILE_OK);
593 return;
596 // TODO(mtomasz): Add a queue to prevent races.
597 Watcher* const watcher = &watchers_[key];
598 watcher->entry_path = entry_path;
599 watcher->recursive = recursive;
600 watcher->subscribers[subscriber.origin] = subscriber;
602 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
603 observers_,
604 OnWatcherListChanged(file_system_info_, watchers_));
606 callback.Run(base::File::FILE_OK);
609 void ProvidedFileSystem::OnNotifyCompleted(
610 const base::FilePath& entry_path,
611 bool recursive,
612 storage::WatcherManager::ChangeType change_type,
613 scoped_ptr<ProvidedFileSystemObserver::Changes> /* changes */,
614 const std::string& tag,
615 const storage::AsyncFileUtil::StatusCallback& callback) {
616 const WatcherKey key(entry_path, recursive);
617 const Watchers::iterator it = watchers_.find(key);
618 // Check if the entry is still watched.
619 if (it == watchers_.end()) {
620 callback.Run(base::File::FILE_ERROR_NOT_FOUND);
621 return;
624 // TODO(mtomasz): Add an async queue around notify and other watcher related
625 // methods so there is no race.
627 it->second.last_tag = tag;
629 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
630 observers_,
631 OnWatcherTagUpdated(file_system_info_, it->second));
633 // If the watched entry is deleted, then remove the watcher.
634 if (change_type == storage::WatcherManager::DELETED) {
635 // Make a copy, since the |it| iterator will get invalidated on the last
636 // subscriber.
637 Subscribers subscribers = it->second.subscribers;
638 for (const auto& subscriber_it : subscribers) {
639 RemoveWatcher(subscriber_it.second.origin,
640 entry_path,
641 recursive,
642 base::Bind(&EmptyStatusCallback));
646 callback.Run(base::File::FILE_OK);
649 } // namespace file_system_provider
650 } // namespace chromeos