blob: 2c1a81e814b0f348e2ef436289d4e444611c1ebc [file] [log] [blame]
// 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"
namespace ash {
namespace file_system_provider {
namespace {
// Timeout in minutes, before a request is considered as stale and hence
// aborted.
const int kDefaultTimeout = 5;
} // namespace
RequestManager::RequestManager(
Profile* profile,
NotificationManagerInterface* notification_manager)
: profile_(profile),
notification_manager_(notification_manager),
next_id_(1),
timeout_(base::Minutes(kDefaultTimeout)) {}
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;
RejectRequest(request_id, RequestValue(), base::File::FILE_ERROR_ABORT);
}
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_NESTABLE_ASYNC_BEGIN1("file_system_provider",
"RequestManager::Request",
TRACE_ID_LOCAL(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);
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);
request_it->second->handler->OnSuccess(request_id, response, has_more);
if (!has_more) {
DestroyRequest(request_id);
} 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;
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);
return base::File::FILE_OK;
}
void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) {
timeout_ = timeout;
}
std::vector<int> RequestManager::GetActiveRequestIds() const {
std::vector<int> result;
for (auto request_it = requests_.begin(); request_it != requests_.end();
++request_it) {
result.push_back(request_it->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() {}
RequestManager::Request::~Request() {}
void RequestManager::OnRequestTimeout(int request_id) {
for (auto& observer : observers_)
observer.OnRequestTimeouted(request_id);
if (!notification_manager_) {
RejectRequest(request_id, RequestValue(), base::File::FILE_ERROR_ABORT);
return;
}
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;
}
RejectRequest(request_id, RequestValue(), base::File::FILE_ERROR_ABORT);
}
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));
}
void RequestManager::DestroyRequest(int request_id) {
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);
TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
"RequestManager::Request",
TRACE_ID_LOCAL(request_id));
}
} // namespace file_system_provider
} // namespace ash