Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / provided_file_system.cc
blob6c3ff26521e5ff14f3cdf3f25cda2a2dc861d003
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/files/file.h"
10 #include "base/trace_event/trace_event.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/configure.h"
16 #include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
17 #include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
18 #include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
19 #include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
20 #include "chrome/browser/chromeos/file_system_provider/operations/execute_action.h"
21 #include "chrome/browser/chromeos/file_system_provider/operations/get_actions.h"
22 #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
23 #include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
24 #include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
25 #include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
26 #include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
27 #include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
28 #include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
29 #include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
30 #include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
31 #include "chrome/browser/chromeos/file_system_provider/request_manager.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/common/extensions/api/file_system_provider.h"
34 #include "extensions/browser/event_router.h"
36 namespace net {
37 class IOBuffer;
38 } // namespace net
40 namespace chromeos {
41 namespace file_system_provider {
42 namespace {
44 // Discards the result of Abort() when called from the destructor.
45 void EmptyStatusCallback(base::File::Error /* result */) {
48 } // namespace
50 AutoUpdater::AutoUpdater(const base::Closure& update_callback)
51 : update_callback_(update_callback),
52 created_callbacks_(0),
53 pending_callbacks_(0) {
56 base::Closure AutoUpdater::CreateCallback() {
57 ++created_callbacks_;
58 ++pending_callbacks_;
59 return base::Bind(&AutoUpdater::OnPendingCallback, this);
62 void AutoUpdater::OnPendingCallback() {
63 DCHECK_LT(0, pending_callbacks_);
64 if (--pending_callbacks_ == 0)
65 update_callback_.Run();
68 AutoUpdater::~AutoUpdater() {
69 // If no callbacks are created, then we need to invoke updating in the
70 // destructor.
71 if (!created_callbacks_)
72 update_callback_.Run();
73 else if (pending_callbacks_)
74 LOG(ERROR) << "Not all callbacks called. This may happen on shutdown.";
77 struct ProvidedFileSystem::AddWatcherInQueueArgs {
78 AddWatcherInQueueArgs(size_t token,
79 const GURL& origin,
80 const base::FilePath& entry_path,
81 bool recursive,
82 bool persistent,
83 const storage::AsyncFileUtil::StatusCallback& callback,
84 const storage::WatcherManager::NotificationCallback&
85 notification_callback)
86 : token(token),
87 origin(origin),
88 entry_path(entry_path),
89 recursive(recursive),
90 persistent(persistent),
91 callback(callback),
92 notification_callback(notification_callback) {}
93 ~AddWatcherInQueueArgs() {}
95 const size_t token;
96 const GURL origin;
97 const base::FilePath entry_path;
98 const bool recursive;
99 const bool persistent;
100 const storage::AsyncFileUtil::StatusCallback callback;
101 const storage::WatcherManager::NotificationCallback notification_callback;
104 struct ProvidedFileSystem::NotifyInQueueArgs {
105 NotifyInQueueArgs(size_t token,
106 const base::FilePath& entry_path,
107 bool recursive,
108 storage::WatcherManager::ChangeType change_type,
109 scoped_ptr<ProvidedFileSystemObserver::Changes> changes,
110 const std::string& tag,
111 const storage::AsyncFileUtil::StatusCallback& callback)
112 : token(token),
113 entry_path(entry_path),
114 recursive(recursive),
115 change_type(change_type),
116 changes(changes.Pass()),
117 tag(tag),
118 callback(callback) {}
119 ~NotifyInQueueArgs() {}
121 const size_t token;
122 const base::FilePath entry_path;
123 const bool recursive;
124 const storage::WatcherManager::ChangeType change_type;
125 const scoped_ptr<ProvidedFileSystemObserver::Changes> changes;
126 const std::string tag;
127 const storage::AsyncFileUtil::StatusCallback callback;
129 private:
130 DISALLOW_COPY_AND_ASSIGN(NotifyInQueueArgs);
133 ProvidedFileSystem::ProvidedFileSystem(
134 Profile* profile,
135 const ProvidedFileSystemInfo& file_system_info)
136 : profile_(profile),
137 event_router_(extensions::EventRouter::Get(profile)), // May be NULL.
138 file_system_info_(file_system_info),
139 notification_manager_(
140 new NotificationManager(profile_, file_system_info_)),
141 request_manager_(new RequestManager(profile,
142 file_system_info.extension_id(),
143 notification_manager_.get())),
144 watcher_queue_(1),
145 weak_ptr_factory_(this) {
148 ProvidedFileSystem::~ProvidedFileSystem() {
149 const std::vector<int> request_ids = request_manager_->GetActiveRequestIds();
150 for (size_t i = 0; i < request_ids.size(); ++i) {
151 Abort(request_ids[i]);
155 void ProvidedFileSystem::SetEventRouterForTesting(
156 extensions::EventRouter* event_router) {
157 event_router_ = event_router;
160 void ProvidedFileSystem::SetNotificationManagerForTesting(
161 scoped_ptr<NotificationManagerInterface> notification_manager) {
162 notification_manager_ = notification_manager.Pass();
163 request_manager_.reset(new RequestManager(
164 profile_, file_system_info_.extension_id(), notification_manager_.get()));
167 AbortCallback ProvidedFileSystem::RequestUnmount(
168 const storage::AsyncFileUtil::StatusCallback& callback) {
169 const int request_id = request_manager_->CreateRequest(
170 REQUEST_UNMOUNT,
171 scoped_ptr<RequestManager::HandlerInterface>(
172 new operations::Unmount(event_router_, file_system_info_, callback)));
173 if (!request_id) {
174 callback.Run(base::File::FILE_ERROR_SECURITY);
175 return AbortCallback();
178 return base::Bind(
179 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
182 AbortCallback ProvidedFileSystem::GetMetadata(
183 const base::FilePath& entry_path,
184 MetadataFieldMask fields,
185 const GetMetadataCallback& callback) {
186 const int request_id = request_manager_->CreateRequest(
187 GET_METADATA,
188 scoped_ptr<RequestManager::HandlerInterface>(new operations::GetMetadata(
189 event_router_, file_system_info_, entry_path, fields, callback)));
190 if (!request_id) {
191 callback.Run(make_scoped_ptr<EntryMetadata>(NULL),
192 base::File::FILE_ERROR_SECURITY);
193 return AbortCallback();
196 return base::Bind(
197 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
200 AbortCallback ProvidedFileSystem::GetActions(
201 const base::FilePath& entry_path,
202 const GetActionsCallback& callback) {
203 const int request_id = request_manager_->CreateRequest(
204 GET_ACTIONS,
205 scoped_ptr<RequestManager::HandlerInterface>(new operations::GetActions(
206 event_router_, file_system_info_, entry_path, callback)));
207 if (!request_id) {
208 callback.Run(Actions(), base::File::FILE_ERROR_SECURITY);
209 return AbortCallback();
212 return base::Bind(&ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(),
213 request_id);
216 AbortCallback ProvidedFileSystem::ExecuteAction(
217 const base::FilePath& entry_path,
218 const std::string& action_id,
219 const storage::AsyncFileUtil::StatusCallback& callback) {
220 const int request_id = request_manager_->CreateRequest(
221 EXECUTE_ACTION,
222 scoped_ptr<RequestManager::HandlerInterface>(
223 new operations::ExecuteAction(event_router_, file_system_info_,
224 entry_path, action_id, callback)));
225 if (!request_id) {
226 callback.Run(base::File::FILE_ERROR_SECURITY);
227 return AbortCallback();
230 return base::Bind(&ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(),
231 request_id);
234 AbortCallback ProvidedFileSystem::ReadDirectory(
235 const base::FilePath& directory_path,
236 const storage::AsyncFileUtil::ReadDirectoryCallback& callback) {
237 const int request_id = request_manager_->CreateRequest(
238 READ_DIRECTORY,
239 scoped_ptr<RequestManager::HandlerInterface>(
240 new operations::ReadDirectory(
241 event_router_, file_system_info_, directory_path, callback)));
242 if (!request_id) {
243 callback.Run(base::File::FILE_ERROR_SECURITY,
244 storage::AsyncFileUtil::EntryList(),
245 false /* has_more */);
246 return AbortCallback();
249 return base::Bind(
250 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
253 AbortCallback ProvidedFileSystem::ReadFile(
254 int file_handle,
255 net::IOBuffer* buffer,
256 int64 offset,
257 int length,
258 const ReadChunkReceivedCallback& callback) {
259 TRACE_EVENT1(
260 "file_system_provider", "ProvidedFileSystem::ReadFile", "length", length);
261 const int request_id = request_manager_->CreateRequest(
262 READ_FILE,
263 make_scoped_ptr<RequestManager::HandlerInterface>(
264 new operations::ReadFile(event_router_,
265 file_system_info_,
266 file_handle,
267 buffer,
268 offset,
269 length,
270 callback)));
271 if (!request_id) {
272 callback.Run(0 /* chunk_length */,
273 false /* has_more */,
274 base::File::FILE_ERROR_SECURITY);
275 return AbortCallback();
278 return base::Bind(
279 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
282 AbortCallback ProvidedFileSystem::OpenFile(const base::FilePath& file_path,
283 OpenFileMode mode,
284 const OpenFileCallback& callback) {
285 const int request_id = request_manager_->CreateRequest(
286 OPEN_FILE,
287 scoped_ptr<RequestManager::HandlerInterface>(new operations::OpenFile(
288 event_router_, file_system_info_, file_path, mode,
289 base::Bind(&ProvidedFileSystem::OnOpenFileCompleted,
290 weak_ptr_factory_.GetWeakPtr(), file_path, mode,
291 callback))));
292 if (!request_id) {
293 callback.Run(0 /* file_handle */, base::File::FILE_ERROR_SECURITY);
294 return AbortCallback();
297 return base::Bind(&ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(),
298 request_id);
301 AbortCallback ProvidedFileSystem::CloseFile(
302 int file_handle,
303 const storage::AsyncFileUtil::StatusCallback& callback) {
304 const int request_id = request_manager_->CreateRequest(
305 CLOSE_FILE,
306 scoped_ptr<RequestManager::HandlerInterface>(new operations::CloseFile(
307 event_router_, file_system_info_, file_handle,
308 base::Bind(&ProvidedFileSystem::OnCloseFileCompleted,
309 weak_ptr_factory_.GetWeakPtr(), file_handle, callback))));
310 if (!request_id) {
311 callback.Run(base::File::FILE_ERROR_SECURITY);
312 return AbortCallback();
315 return base::Bind(
316 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
319 AbortCallback ProvidedFileSystem::CreateDirectory(
320 const base::FilePath& directory_path,
321 bool recursive,
322 const storage::AsyncFileUtil::StatusCallback& callback) {
323 const int request_id = request_manager_->CreateRequest(
324 CREATE_DIRECTORY,
325 scoped_ptr<RequestManager::HandlerInterface>(
326 new operations::CreateDirectory(event_router_,
327 file_system_info_,
328 directory_path,
329 recursive,
330 callback)));
331 if (!request_id) {
332 callback.Run(base::File::FILE_ERROR_SECURITY);
333 return AbortCallback();
336 return base::Bind(
337 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
340 AbortCallback ProvidedFileSystem::DeleteEntry(
341 const base::FilePath& entry_path,
342 bool recursive,
343 const storage::AsyncFileUtil::StatusCallback& callback) {
344 const int request_id = request_manager_->CreateRequest(
345 DELETE_ENTRY,
346 scoped_ptr<RequestManager::HandlerInterface>(new operations::DeleteEntry(
347 event_router_, file_system_info_, entry_path, recursive, callback)));
348 if (!request_id) {
349 callback.Run(base::File::FILE_ERROR_SECURITY);
350 return AbortCallback();
353 return base::Bind(
354 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
357 AbortCallback ProvidedFileSystem::CreateFile(
358 const base::FilePath& file_path,
359 const storage::AsyncFileUtil::StatusCallback& callback) {
360 const int request_id = request_manager_->CreateRequest(
361 CREATE_FILE,
362 scoped_ptr<RequestManager::HandlerInterface>(new operations::CreateFile(
363 event_router_, file_system_info_, file_path, callback)));
364 if (!request_id) {
365 callback.Run(base::File::FILE_ERROR_SECURITY);
366 return AbortCallback();
369 return base::Bind(
370 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
373 AbortCallback ProvidedFileSystem::CopyEntry(
374 const base::FilePath& source_path,
375 const base::FilePath& target_path,
376 const storage::AsyncFileUtil::StatusCallback& callback) {
377 const int request_id = request_manager_->CreateRequest(
378 COPY_ENTRY,
379 scoped_ptr<RequestManager::HandlerInterface>(
380 new operations::CopyEntry(event_router_,
381 file_system_info_,
382 source_path,
383 target_path,
384 callback)));
385 if (!request_id) {
386 callback.Run(base::File::FILE_ERROR_SECURITY);
387 return AbortCallback();
390 return base::Bind(
391 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
394 AbortCallback ProvidedFileSystem::WriteFile(
395 int file_handle,
396 net::IOBuffer* buffer,
397 int64 offset,
398 int length,
399 const storage::AsyncFileUtil::StatusCallback& callback) {
400 TRACE_EVENT1("file_system_provider",
401 "ProvidedFileSystem::WriteFile",
402 "length",
403 length);
404 const int request_id = request_manager_->CreateRequest(
405 WRITE_FILE,
406 make_scoped_ptr<RequestManager::HandlerInterface>(
407 new operations::WriteFile(event_router_,
408 file_system_info_,
409 file_handle,
410 make_scoped_refptr(buffer),
411 offset,
412 length,
413 callback)));
414 if (!request_id) {
415 callback.Run(base::File::FILE_ERROR_SECURITY);
416 return AbortCallback();
419 return base::Bind(
420 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
423 AbortCallback ProvidedFileSystem::MoveEntry(
424 const base::FilePath& source_path,
425 const base::FilePath& target_path,
426 const storage::AsyncFileUtil::StatusCallback& callback) {
427 const int request_id = request_manager_->CreateRequest(
428 MOVE_ENTRY,
429 scoped_ptr<RequestManager::HandlerInterface>(
430 new operations::MoveEntry(event_router_,
431 file_system_info_,
432 source_path,
433 target_path,
434 callback)));
435 if (!request_id) {
436 callback.Run(base::File::FILE_ERROR_SECURITY);
437 return AbortCallback();
440 return base::Bind(
441 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
444 AbortCallback ProvidedFileSystem::Truncate(
445 const base::FilePath& file_path,
446 int64 length,
447 const storage::AsyncFileUtil::StatusCallback& callback) {
448 const int request_id = request_manager_->CreateRequest(
449 TRUNCATE,
450 scoped_ptr<RequestManager::HandlerInterface>(new operations::Truncate(
451 event_router_, file_system_info_, file_path, length, callback)));
452 if (!request_id) {
453 callback.Run(base::File::FILE_ERROR_SECURITY);
454 return AbortCallback();
457 return base::Bind(
458 &ProvidedFileSystem::Abort, weak_ptr_factory_.GetWeakPtr(), request_id);
461 AbortCallback ProvidedFileSystem::AddWatcher(
462 const GURL& origin,
463 const base::FilePath& entry_path,
464 bool recursive,
465 bool persistent,
466 const storage::AsyncFileUtil::StatusCallback& callback,
467 const storage::WatcherManager::NotificationCallback&
468 notification_callback) {
469 const size_t token = watcher_queue_.NewToken();
470 watcher_queue_.Enqueue(
471 token, base::Bind(&ProvidedFileSystem::AddWatcherInQueue,
472 base::Unretained(this), // Outlived by the queue.
473 AddWatcherInQueueArgs(token, origin, entry_path,
474 recursive, persistent, callback,
475 notification_callback)));
476 return AbortCallback();
479 void ProvidedFileSystem::RemoveWatcher(
480 const GURL& origin,
481 const base::FilePath& entry_path,
482 bool recursive,
483 const storage::AsyncFileUtil::StatusCallback& callback) {
484 const size_t token = watcher_queue_.NewToken();
485 watcher_queue_.Enqueue(
486 token, base::Bind(&ProvidedFileSystem::RemoveWatcherInQueue,
487 base::Unretained(this), // Outlived by the queue.
488 token, origin, entry_path, recursive, callback));
491 const ProvidedFileSystemInfo& ProvidedFileSystem::GetFileSystemInfo() const {
492 return file_system_info_;
495 RequestManager* ProvidedFileSystem::GetRequestManager() {
496 return request_manager_.get();
499 Watchers* ProvidedFileSystem::GetWatchers() {
500 return &watchers_;
503 const OpenedFiles& ProvidedFileSystem::GetOpenedFiles() const {
504 return opened_files_;
507 void ProvidedFileSystem::AddObserver(ProvidedFileSystemObserver* observer) {
508 DCHECK(observer);
509 observers_.AddObserver(observer);
512 void ProvidedFileSystem::RemoveObserver(ProvidedFileSystemObserver* observer) {
513 DCHECK(observer);
514 observers_.RemoveObserver(observer);
517 void ProvidedFileSystem::Notify(
518 const base::FilePath& entry_path,
519 bool recursive,
520 storage::WatcherManager::ChangeType change_type,
521 scoped_ptr<ProvidedFileSystemObserver::Changes> changes,
522 const std::string& tag,
523 const storage::AsyncFileUtil::StatusCallback& callback) {
524 const size_t token = watcher_queue_.NewToken();
525 watcher_queue_.Enqueue(
526 token, base::Bind(&ProvidedFileSystem::NotifyInQueue,
527 base::Unretained(this), // Outlived by the queue.
528 base::Passed(make_scoped_ptr(new NotifyInQueueArgs(
529 token, entry_path, recursive, change_type,
530 changes.Pass(), tag, callback)))));
533 void ProvidedFileSystem::Configure(
534 const storage::AsyncFileUtil::StatusCallback& callback) {
535 const int request_id = request_manager_->CreateRequest(
536 CONFIGURE,
537 scoped_ptr<RequestManager::HandlerInterface>(new operations::Configure(
538 event_router_, file_system_info_, callback)));
539 if (!request_id)
540 callback.Run(base::File::FILE_ERROR_SECURITY);
543 void ProvidedFileSystem::Abort(int operation_request_id) {
544 if (!request_manager_->CreateRequest(
545 ABORT,
546 scoped_ptr<RequestManager::HandlerInterface>(new operations::Abort(
547 event_router_, file_system_info_, operation_request_id,
548 base::Bind(&ProvidedFileSystem::OnAbortCompleted,
549 weak_ptr_factory_.GetWeakPtr(),
550 operation_request_id))))) {
551 // If the aborting event is not handled, then the operation should simply
552 // be not aborted. Instead we'll wait until it completes.
553 LOG(ERROR) << "Failed to create an abort request.";
557 void ProvidedFileSystem::OnAbortCompleted(int operation_request_id,
558 base::File::Error result) {
559 if (result != base::File::FILE_OK) {
560 // If an error in aborting happens, then do not abort the request in the
561 // request manager, as the operation is supposed to complete. The only case
562 // it wouldn't complete is if there is a bug in the extension code, and
563 // the extension never calls the callback. We consiously *do not* handle
564 // bugs in extensions here.
565 return;
567 request_manager_->RejectRequest(operation_request_id,
568 make_scoped_ptr(new RequestValue()),
569 base::File::FILE_ERROR_ABORT);
572 AbortCallback ProvidedFileSystem::AddWatcherInQueue(
573 const AddWatcherInQueueArgs& args) {
574 if (args.persistent && (!file_system_info_.supports_notify_tag() ||
575 !args.notification_callback.is_null())) {
576 OnAddWatcherInQueueCompleted(args.token, args.entry_path, args.recursive,
577 Subscriber(), args.callback,
578 base::File::FILE_ERROR_INVALID_OPERATION);
579 return AbortCallback();
582 // Create a candidate subscriber. This could be done in OnAddWatcherCompleted,
583 // but base::Bind supports only up to 7 arguments.
584 Subscriber subscriber;
585 subscriber.origin = args.origin;
586 subscriber.persistent = args.persistent;
587 subscriber.notification_callback = args.notification_callback;
589 const WatcherKey key(args.entry_path, args.recursive);
590 const Watchers::const_iterator it = watchers_.find(key);
591 if (it != watchers_.end()) {
592 const bool exists = it->second.subscribers.find(args.origin) !=
593 it->second.subscribers.end();
594 OnAddWatcherInQueueCompleted(
595 args.token, args.entry_path, args.recursive, subscriber, args.callback,
596 exists ? base::File::FILE_ERROR_EXISTS : base::File::FILE_OK);
597 return AbortCallback();
600 const int request_id = request_manager_->CreateRequest(
601 ADD_WATCHER,
602 scoped_ptr<RequestManager::HandlerInterface>(new operations::AddWatcher(
603 event_router_, file_system_info_, args.entry_path, args.recursive,
604 base::Bind(&ProvidedFileSystem::OnAddWatcherInQueueCompleted,
605 weak_ptr_factory_.GetWeakPtr(), args.token,
606 args.entry_path, args.recursive, subscriber,
607 args.callback))));
609 if (!request_id) {
610 OnAddWatcherInQueueCompleted(args.token, args.entry_path, args.recursive,
611 subscriber, args.callback,
612 base::File::FILE_ERROR_SECURITY);
615 return AbortCallback();
618 AbortCallback ProvidedFileSystem::RemoveWatcherInQueue(
619 size_t token,
620 const GURL& origin,
621 const base::FilePath& entry_path,
622 bool recursive,
623 const storage::AsyncFileUtil::StatusCallback& callback) {
624 const WatcherKey key(entry_path, recursive);
625 const Watchers::iterator it = watchers_.find(key);
626 if (it == watchers_.end() ||
627 it->second.subscribers.find(origin) == it->second.subscribers.end()) {
628 OnRemoveWatcherInQueueCompleted(token, origin, key, callback,
629 false /* extension_response */,
630 base::File::FILE_ERROR_NOT_FOUND);
631 return AbortCallback();
634 // If there are other subscribers, then do not remove the observer, but simply
635 // return a success.
636 if (it->second.subscribers.size() > 1) {
637 OnRemoveWatcherInQueueCompleted(token, origin, key, callback,
638 false /* extension_response */,
639 base::File::FILE_OK);
640 return AbortCallback();
643 // Otherwise, emit an event, and remove the watcher.
644 request_manager_->CreateRequest(
645 REMOVE_WATCHER,
646 scoped_ptr<RequestManager::HandlerInterface>(
647 new operations::RemoveWatcher(
648 event_router_, file_system_info_, entry_path, recursive,
649 base::Bind(&ProvidedFileSystem::OnRemoveWatcherInQueueCompleted,
650 weak_ptr_factory_.GetWeakPtr(), token, origin, key,
651 callback, true /* extension_response */))));
653 return AbortCallback();
656 AbortCallback ProvidedFileSystem::NotifyInQueue(
657 scoped_ptr<NotifyInQueueArgs> args) {
658 const WatcherKey key(args->entry_path, args->recursive);
659 const auto& watcher_it = watchers_.find(key);
660 if (watcher_it == watchers_.end()) {
661 OnNotifyInQueueCompleted(args.Pass(), base::File::FILE_ERROR_NOT_FOUND);
662 return AbortCallback();
665 // The tag must be provided if and only if it's explicitly supported.
666 if (file_system_info_.supports_notify_tag() == args->tag.empty()) {
667 OnNotifyInQueueCompleted(args.Pass(),
668 base::File::FILE_ERROR_INVALID_OPERATION);
669 return AbortCallback();
672 // It's illegal to provide a tag which is not unique.
673 if (!args->tag.empty() && args->tag == watcher_it->second.last_tag) {
674 OnNotifyInQueueCompleted(args.Pass(),
675 base::File::FILE_ERROR_INVALID_OPERATION);
676 return AbortCallback();
679 // The object is owned by AutoUpdated, so the reference is valid as long as
680 // callbacks created with AutoUpdater::CreateCallback().
681 const ProvidedFileSystemObserver::Changes& changes_ref = *args->changes.get();
682 const storage::WatcherManager::ChangeType change_type = args->change_type;
684 scoped_refptr<AutoUpdater> auto_updater(
685 new AutoUpdater(base::Bind(&ProvidedFileSystem::OnNotifyInQueueCompleted,
686 weak_ptr_factory_.GetWeakPtr(),
687 base::Passed(&args), base::File::FILE_OK)));
689 // Call all notification callbacks (if any).
690 for (const auto& subscriber_it : watcher_it->second.subscribers) {
691 const storage::WatcherManager::NotificationCallback& notification_callback =
692 subscriber_it.second.notification_callback;
693 if (!notification_callback.is_null())
694 notification_callback.Run(change_type);
697 // Notify all observers.
698 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
699 observers_,
700 OnWatcherChanged(file_system_info_,
701 watcher_it->second,
702 change_type,
703 changes_ref,
704 auto_updater->CreateCallback()));
706 return AbortCallback();
709 base::WeakPtr<ProvidedFileSystemInterface> ProvidedFileSystem::GetWeakPtr() {
710 return weak_ptr_factory_.GetWeakPtr();
713 void ProvidedFileSystem::OnAddWatcherInQueueCompleted(
714 size_t token,
715 const base::FilePath& entry_path,
716 bool recursive,
717 const Subscriber& subscriber,
718 const storage::AsyncFileUtil::StatusCallback& callback,
719 base::File::Error result) {
720 if (result != base::File::FILE_OK) {
721 callback.Run(result);
722 watcher_queue_.Complete(token);
723 return;
726 const WatcherKey key(entry_path, recursive);
727 const Watchers::iterator it = watchers_.find(key);
728 if (it != watchers_.end()) {
729 callback.Run(base::File::FILE_OK);
730 watcher_queue_.Complete(token);
731 return;
734 Watcher* const watcher = &watchers_[key];
735 watcher->entry_path = entry_path;
736 watcher->recursive = recursive;
737 watcher->subscribers[subscriber.origin] = subscriber;
739 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
740 observers_,
741 OnWatcherListChanged(file_system_info_, watchers_));
743 callback.Run(base::File::FILE_OK);
744 watcher_queue_.Complete(token);
747 void ProvidedFileSystem::OnRemoveWatcherInQueueCompleted(
748 size_t token,
749 const GURL& origin,
750 const WatcherKey& key,
751 const storage::AsyncFileUtil::StatusCallback& callback,
752 bool extension_response,
753 base::File::Error result) {
754 if (!extension_response && result != base::File::FILE_OK) {
755 watcher_queue_.Complete(token);
756 callback.Run(result);
757 return;
760 // Even if the extension returns an error, the callback is called with base::
761 // File::FILE_OK.
762 const auto it = watchers_.find(key);
763 DCHECK(it != watchers_.end());
764 DCHECK(it->second.subscribers.find(origin) != it->second.subscribers.end());
766 it->second.subscribers.erase(origin);
768 FOR_EACH_OBSERVER(ProvidedFileSystemObserver, observers_,
769 OnWatcherListChanged(file_system_info_, watchers_));
771 // If there are no more subscribers, then remove the watcher.
772 if (!it->second.subscribers.size())
773 watchers_.erase(it);
775 callback.Run(base::File::FILE_OK);
776 watcher_queue_.Complete(token);
779 void ProvidedFileSystem::OnNotifyInQueueCompleted(
780 scoped_ptr<NotifyInQueueArgs> args,
781 base::File::Error result) {
782 if (result != base::File::FILE_OK) {
783 args->callback.Run(result);
784 watcher_queue_.Complete(args->token);
785 return;
788 // Check if the entry is still watched.
789 const WatcherKey key(args->entry_path, args->recursive);
790 const Watchers::iterator it = watchers_.find(key);
791 if (it == watchers_.end()) {
792 args->callback.Run(base::File::FILE_ERROR_NOT_FOUND);
793 watcher_queue_.Complete(args->token);
794 return;
797 it->second.last_tag = args->tag;
799 FOR_EACH_OBSERVER(ProvidedFileSystemObserver,
800 observers_,
801 OnWatcherTagUpdated(file_system_info_, it->second));
803 // If the watched entry is deleted, then remove the watcher.
804 if (args->change_type == storage::WatcherManager::DELETED) {
805 // Make a copy, since the |it| iterator will get invalidated on the last
806 // subscriber.
807 Subscribers subscribers = it->second.subscribers;
808 for (const auto& subscriber_it : subscribers) {
809 RemoveWatcher(subscriber_it.second.origin, args->entry_path,
810 args->recursive, base::Bind(&EmptyStatusCallback));
814 args->callback.Run(base::File::FILE_OK);
815 watcher_queue_.Complete(args->token);
818 void ProvidedFileSystem::OnOpenFileCompleted(const base::FilePath& file_path,
819 OpenFileMode mode,
820 const OpenFileCallback& callback,
821 int file_handle,
822 base::File::Error result) {
823 if (result != base::File::FILE_OK) {
824 callback.Run(file_handle, result);
825 return;
828 opened_files_[file_handle] = OpenedFile(file_path, mode);
829 callback.Run(file_handle, base::File::FILE_OK);
832 void ProvidedFileSystem::OnCloseFileCompleted(
833 int file_handle,
834 const storage::AsyncFileUtil::StatusCallback& callback,
835 base::File::Error result) {
836 // Closing files is final. Even if an error happened, we remove it from the
837 // list of opened files.
838 opened_files_.erase(file_handle);
839 callback.Run(result);
842 } // namespace file_system_provider
843 } // namespace chromeos