blob: 1687f65aa8f7b9bad315a2536acc5c7ce2a4e3e2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
#include "base/debug/trace_event.h"
#include "base/files/file.h"
#include "base/stl_util.h"
namespace chromeos {
namespace file_system_provider {
namespace {
// Timeout in seconds, before a request is considered as stale and hence
// aborted.
const int kDefaultTimeout = 10;
} // namespace
std::string RequestTypeToString(RequestType type) {
switch (type) {
case REQUEST_UNMOUNT:
return "REQUEST_UNMOUNT";
case GET_METADATA:
return "GET_METADATA";
case READ_DIRECTORY:
return "READ_DIRECTORY";
case OPEN_FILE:
return "OPEN_FILE";
case CLOSE_FILE:
return "CLOSE_FILE";
case READ_FILE:
return "READ_FILE";
case CREATE_DIRECTORY:
return "CREATE_DIRECTORY";
case DELETE_ENTRY:
return "DELETE_ENTRY";
case CREATE_FILE:
return "CREATE_FILE";
case COPY_ENTRY:
return "COPY_ENTRY";
case MOVE_ENTRY:
return "MOVE_ENTRY";
case TRUNCATE:
return "TRUNCATE";
case WRITE_FILE:
return "WRITE_FILE";
case ABORT:
return "ABORT";
case OBSERVE_DIRECTORY:
return "OBSERVE_DIRECTORY";
case UNOBSERVE_ENTRY:
return "UNOBSERVE_ENTRY";
case TESTING:
return "TESTING";
}
NOTREACHED();
return "";
}
RequestManager::RequestManager(
NotificationManagerInterface* notification_manager)
: notification_manager_(notification_manager),
next_id_(1),
timeout_(base::TimeDelta::FromSeconds(kDefaultTimeout)),
weak_ptr_factory_(this) {
}
RequestManager::~RequestManager() {
// Abort all of the active requests.
RequestMap::iterator it = requests_.begin();
while (it != requests_.end()) {
const int request_id = it->first;
++it;
RejectRequest(request_id,
scoped_ptr<RequestValue>(new RequestValue()),
base::File::FILE_ERROR_ABORT);
}
DCHECK_EQ(0u, requests_.size());
STLDeleteValues(&requests_);
}
int RequestManager::CreateRequest(RequestType type,
scoped_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_ASYNC_BEGIN1("file_system_provider",
"RequestManager::Request",
request_id,
"type",
type);
Request* request = new Request;
request->handler = handler.Pass();
requests_[request_id] = request;
ResetTimer(request_id);
FOR_EACH_OBSERVER(Observer, observers_, 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 (!request->handler->Execute(request_id)) {
DestroyRequest(request_id);
return 0;
}
FOR_EACH_OBSERVER(Observer, observers_, OnRequestExecuted(request_id));
return request_id;
}
bool RequestManager::FulfillRequest(int request_id,
scoped_ptr<RequestValue> response,
bool has_more) {
CHECK(response.get());
RequestMap::iterator request_it = requests_.find(request_id);
if (request_it == requests_.end())
return false;
FOR_EACH_OBSERVER(Observer,
observers_,
OnRequestFulfilled(request_id, *response.get(), has_more));
request_it->second->handler->OnSuccess(request_id, response.Pass(), has_more);
if (!has_more) {
DestroyRequest(request_id);
} else {
if (notification_manager_)
notification_manager_->HideUnresponsiveNotification(request_id);
ResetTimer(request_id);
}
return true;
}
bool RequestManager::RejectRequest(int request_id,
scoped_ptr<RequestValue> response,
base::File::Error error) {
CHECK(response.get());
RequestMap::iterator request_it = requests_.find(request_id);
if (request_it == requests_.end())
return false;
FOR_EACH_OBSERVER(Observer,
observers_,
OnRequestRejected(request_id, *response.get(), error));
request_it->second->handler->OnError(request_id, response.Pass(), error);
DestroyRequest(request_id);
return true;
}
void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) {
timeout_ = timeout;
}
std::vector<int> RequestManager::GetActiveRequestIds() const {
std::vector<int> result;
for (RequestMap::const_iterator 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_EACH_OBSERVER(Observer, observers_, OnRequestTimeouted(request_id));
if (!notification_manager_) {
RejectRequest(request_id,
scoped_ptr<RequestValue>(new RequestValue()),
base::File::FILE_ERROR_ABORT);
return;
}
notification_manager_->ShowUnresponsiveNotification(
request_id,
base::Bind(&RequestManager::OnUnresponsiveNotificationResult,
weak_ptr_factory_.GetWeakPtr(),
request_id));
}
void RequestManager::OnUnresponsiveNotificationResult(
int request_id,
NotificationManagerInterface::NotificationResult result) {
RequestMap::iterator request_it = requests_.find(request_id);
if (request_it == requests_.end())
return;
if (result == NotificationManagerInterface::CONTINUE) {
ResetTimer(request_id);
return;
}
RejectRequest(request_id,
scoped_ptr<RequestValue>(new RequestValue()),
base::File::FILE_ERROR_ABORT);
}
void RequestManager::ResetTimer(int request_id) {
RequestMap::iterator request_it = requests_.find(request_id);
if (request_it == requests_.end())
return;
request_it->second->timeout_timer.Start(
FROM_HERE,
timeout_,
base::Bind(&RequestManager::OnRequestTimeout,
weak_ptr_factory_.GetWeakPtr(),
request_id));
}
void RequestManager::DestroyRequest(int request_id) {
RequestMap::iterator request_it = requests_.find(request_id);
if (request_it == requests_.end())
return;
delete request_it->second;
requests_.erase(request_it);
if (notification_manager_)
notification_manager_->HideUnresponsiveNotification(request_id);
FOR_EACH_OBSERVER(Observer, observers_, OnRequestDestroyed(request_id));
TRACE_EVENT_ASYNC_END0(
"file_system_provider", "RequestManager::Request", request_id);
}
} // namespace file_system_provider
} // namespace chromeos