| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/file_system_provider/request_manager.h" |
| |
| #include "base/files/file.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "third_party/perfetto/include/perfetto/tracing/track.h" |
| |
| namespace ash::file_system_provider { |
| namespace { |
| |
| } // namespace |
| |
| RequestManager::RequestManager( |
| Profile* profile, |
| NotificationManagerInterface* notification_manager, |
| base::TimeDelta timeout) |
| : profile_(profile), |
| notification_manager_(notification_manager), |
| next_id_(1), |
| timeout_(timeout) {} |
| |
| RequestManager::~RequestManager() { |
| // Abort all of the active requests. |
| auto it = requests_.begin(); |
| while (it != requests_.end()) { |
| const int request_id = it->first; |
| ++it; |
| RejectRequestInternal(request_id, RequestValue(), |
| base::File::FILE_ERROR_ABORT, |
| OperationCompletion::kAbortedInternally); |
| } |
| |
| DCHECK_EQ(0u, requests_.size()); |
| } |
| |
| int RequestManager::CreateRequest(RequestType type, |
| std::unique_ptr<HandlerInterface> handler) { |
| // The request id is unique per request manager, so per service, thereof |
| // per profile. |
| int request_id = next_id_++; |
| |
| // If cycled the int, then signal an error. |
| if (requests_.find(request_id) != requests_.end()) |
| return 0; |
| |
| TRACE_EVENT_BEGIN("file_system_provider", "RequestManager::Request", |
| perfetto::Track(request_id), "type", type); |
| |
| std::unique_ptr<Request> request = std::make_unique<Request>(); |
| request->handler = std::move(handler); |
| requests_[request_id] = std::move(request); |
| ResetTimer(request_id); |
| |
| for (auto& observer : observers_) |
| observer.OnRequestCreated(request_id, type); |
| |
| // Execute the request implementation. In case of an execution failure, |
| // unregister and return 0. This may often happen, eg. if the providing |
| // extension is not listening for the request event being sent. |
| // In such case, we should abort as soon as possible. |
| if (!requests_[request_id]->handler->Execute(request_id)) { |
| DestroyRequest(request_id, OperationCompletion::kCompletedNormally); |
| return 0; |
| } |
| |
| for (auto& observer : observers_) |
| observer.OnRequestExecuted(request_id); |
| |
| return request_id; |
| } |
| |
| base::File::Error RequestManager::FulfillRequest(int request_id, |
| const RequestValue& response, |
| bool has_more) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) |
| return base::File::FILE_ERROR_NOT_FOUND; |
| |
| for (auto& observer : observers_) |
| observer.OnRequestFulfilled(request_id, response, has_more); |
| |
| const Request& request = *request_it->second; |
| request.handler->OnSuccess(request_id, response, has_more); |
| |
| if (!has_more) { |
| DestroyRequest(request_id, request.shown_unresponsive_notification |
| ? OperationCompletion::kCompletedAfterWarning |
| : OperationCompletion::kCompletedNormally); |
| } else { |
| if (notification_manager_) |
| notification_manager_->HideUnresponsiveNotification(request_id); |
| ResetTimer(request_id); |
| } |
| |
| return base::File::FILE_OK; |
| } |
| |
| base::File::Error RequestManager::RejectRequest(int request_id, |
| const RequestValue& response, |
| base::File::Error error) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) { |
| return base::File::FILE_ERROR_NOT_FOUND; |
| } |
| const Request& request = *request_it->second; |
| return RejectRequestInternal(request_id, response, error, |
| request.shown_unresponsive_notification |
| ? OperationCompletion::kCompletedAfterWarning |
| : OperationCompletion::kCompletedNormally); |
| } |
| |
| void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) { |
| timeout_ = timeout; |
| } |
| |
| std::vector<int> RequestManager::GetActiveRequestIds() const { |
| std::vector<int> result; |
| |
| for (const auto& request : requests_) { |
| result.push_back(request.first); |
| } |
| |
| return result; |
| } |
| |
| void RequestManager::AddObserver(Observer* observer) { |
| DCHECK(observer); |
| observers_.AddObserver(observer); |
| } |
| |
| void RequestManager::RemoveObserver(Observer* observer) { |
| DCHECK(observer); |
| observers_.RemoveObserver(observer); |
| } |
| |
| RequestManager::Request::Request() = default; |
| |
| RequestManager::Request::~Request() = default; |
| |
| void RequestManager::OnRequestTimeout(int request_id) { |
| for (auto& observer : observers_) |
| observer.OnRequestTimedOut(request_id); |
| |
| if (!notification_manager_) { |
| RejectRequestInternal(request_id, RequestValue(), |
| base::File::FILE_ERROR_ABORT, |
| OperationCompletion::kAbortedInternally); |
| return; |
| } |
| |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) { |
| return; |
| } |
| |
| request_it->second->shown_unresponsive_notification = true; |
| notification_manager_->ShowUnresponsiveNotification( |
| request_id, |
| base::BindOnce(&RequestManager::OnUnresponsiveNotificationResult, |
| weak_ptr_factory_.GetWeakPtr(), request_id)); |
| } |
| |
| void RequestManager::OnUnresponsiveNotificationResult( |
| int request_id, |
| NotificationManagerInterface::NotificationResult result) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) |
| return; |
| |
| if (result == NotificationManagerInterface::CONTINUE) { |
| ResetTimer(request_id); |
| return; |
| } |
| |
| RejectRequestInternal(request_id, RequestValue(), |
| base::File::FILE_ERROR_ABORT, |
| OperationCompletion::kAbortedFromNotification); |
| } |
| |
| void RequestManager::ResetTimer(int request_id) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) |
| return; |
| |
| request_it->second->timeout_timer.Start( |
| FROM_HERE, timeout_, |
| base::BindOnce(&RequestManager::OnRequestTimeout, |
| weak_ptr_factory_.GetWeakPtr(), request_id)); |
| } |
| |
| base::File::Error RequestManager::RejectRequestInternal( |
| int request_id, |
| const RequestValue& response, |
| base::File::Error error, |
| OperationCompletion completion) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) { |
| return base::File::FILE_ERROR_NOT_FOUND; |
| } |
| |
| if (error == base::File::FILE_ERROR_ABORT) { |
| request_it->second->handler->OnAbort(request_id); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.OnRequestRejected(request_id, response, error); |
| } |
| request_it->second->handler->OnError(request_id, response, error); |
| DestroyRequest(request_id, completion); |
| |
| return base::File::FILE_OK; |
| } |
| |
| void RequestManager::DestroyRequest(int request_id, |
| OperationCompletion completion) { |
| auto request_it = requests_.find(request_id); |
| if (request_it == requests_.end()) |
| return; |
| |
| requests_.erase(request_it); |
| |
| if (notification_manager_) |
| notification_manager_->HideUnresponsiveNotification(request_id); |
| |
| for (auto& observer : observers_) |
| observer.OnRequestDestroyed(request_id, completion); |
| |
| TRACE_EVENT_END("file_system_provider", perfetto::Track(request_id)); |
| } |
| |
| } // namespace ash::file_system_provider |