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.
4
5#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
6
7#include "base/debug/trace_event.h"
8#include "base/files/file.h"
9#include "base/stl_util.h"
10
11namespace chromeos {
12namespace file_system_provider {
13namespace {
14
15// Timeout in seconds, before a request is considered as stale and hence
16// aborted.
17const int kDefaultTimeout = 10;
18
19}  // namespace
20
21std::string RequestTypeToString(RequestType type) {
22  switch (type) {
23    case REQUEST_UNMOUNT:
24      return "REQUEST_UNMOUNT";
25    case GET_METADATA:
26      return "GET_METADATA";
27    case READ_DIRECTORY:
28      return "READ_DIRECTORY";
29    case OPEN_FILE:
30      return "OPEN_FILE";
31    case CLOSE_FILE:
32      return "CLOSE_FILE";
33    case READ_FILE:
34      return "READ_FILE";
35    case CREATE_DIRECTORY:
36      return "CREATE_DIRECTORY";
37    case DELETE_ENTRY:
38      return "DELETE_ENTRY";
39    case CREATE_FILE:
40      return "CREATE_FILE";
41    case COPY_ENTRY:
42      return "COPY_ENTRY";
43    case MOVE_ENTRY:
44      return "MOVE_ENTRY";
45    case TRUNCATE:
46      return "TRUNCATE";
47    case WRITE_FILE:
48      return "WRITE_FILE";
49    case ABORT:
50      return "ABORT";
51    case TESTING:
52      return "TESTING";
53  }
54  NOTREACHED();
55  return "";
56}
57
58RequestManager::RequestManager(
59    NotificationManagerInterface* notification_manager)
60    : notification_manager_(notification_manager),
61      next_id_(1),
62      timeout_(base::TimeDelta::FromSeconds(kDefaultTimeout)),
63      weak_ptr_factory_(this) {
64}
65
66RequestManager::~RequestManager() {
67  // Abort all of the active requests.
68  RequestMap::iterator it = requests_.begin();
69  while (it != requests_.end()) {
70    const int request_id = it->first;
71    ++it;
72    RejectRequest(request_id,
73                  scoped_ptr<RequestValue>(new RequestValue()),
74                  base::File::FILE_ERROR_ABORT);
75  }
76
77  DCHECK_EQ(0u, requests_.size());
78  STLDeleteValues(&requests_);
79}
80
81int RequestManager::CreateRequest(RequestType type,
82                                  scoped_ptr<HandlerInterface> handler) {
83  // The request id is unique per request manager, so per service, thereof
84  // per profile.
85  int request_id = next_id_++;
86
87  // If cycled the int, then signal an error.
88  if (requests_.find(request_id) != requests_.end())
89    return 0;
90
91  TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
92                           "RequestManager::Request",
93                           request_id,
94                           "type",
95                           type);
96
97  Request* request = new Request;
98  request->handler = handler.Pass();
99  requests_[request_id] = request;
100  ResetTimer(request_id);
101
102  FOR_EACH_OBSERVER(Observer, observers_, OnRequestCreated(request_id, type));
103
104  // Execute the request implementation. In case of an execution failure,
105  // unregister and return 0. This may often happen, eg. if the providing
106  // extension is not listening for the request event being sent.
107  // In such case, we should abort as soon as possible.
108  if (!request->handler->Execute(request_id)) {
109    DestroyRequest(request_id);
110    return 0;
111  }
112
113  FOR_EACH_OBSERVER(Observer, observers_, OnRequestExecuted(request_id));
114
115  return request_id;
116}
117
118bool RequestManager::FulfillRequest(int request_id,
119                                    scoped_ptr<RequestValue> response,
120                                    bool has_more) {
121  CHECK(response.get());
122  RequestMap::iterator request_it = requests_.find(request_id);
123  if (request_it == requests_.end())
124    return false;
125
126  FOR_EACH_OBSERVER(Observer,
127                    observers_,
128                    OnRequestFulfilled(request_id, *response.get(), has_more));
129
130  request_it->second->handler->OnSuccess(request_id, response.Pass(), has_more);
131
132  if (!has_more) {
133    DestroyRequest(request_id);
134  } else {
135    if (notification_manager_)
136      notification_manager_->HideUnresponsiveNotification(request_id);
137    ResetTimer(request_id);
138  }
139
140  return true;
141}
142
143bool RequestManager::RejectRequest(int request_id,
144                                   scoped_ptr<RequestValue> response,
145                                   base::File::Error error) {
146  CHECK(response.get());
147  RequestMap::iterator request_it = requests_.find(request_id);
148  if (request_it == requests_.end())
149    return false;
150
151  FOR_EACH_OBSERVER(Observer,
152                    observers_,
153                    OnRequestRejected(request_id, *response.get(), error));
154  request_it->second->handler->OnError(request_id, response.Pass(), error);
155  DestroyRequest(request_id);
156
157  return true;
158}
159
160void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) {
161  timeout_ = timeout;
162}
163
164std::vector<int> RequestManager::GetActiveRequestIds() const {
165  std::vector<int> result;
166
167  for (RequestMap::const_iterator request_it = requests_.begin();
168       request_it != requests_.end();
169       ++request_it) {
170    result.push_back(request_it->first);
171  }
172
173  return result;
174}
175
176void RequestManager::AddObserver(Observer* observer) {
177  DCHECK(observer);
178  observers_.AddObserver(observer);
179}
180
181void RequestManager::RemoveObserver(Observer* observer) {
182  DCHECK(observer);
183  observers_.RemoveObserver(observer);
184}
185
186RequestManager::Request::Request() {}
187
188RequestManager::Request::~Request() {}
189
190void RequestManager::OnRequestTimeout(int request_id) {
191  FOR_EACH_OBSERVER(Observer, observers_, OnRequestTimeouted(request_id));
192
193  if (!notification_manager_) {
194    RejectRequest(request_id,
195                  scoped_ptr<RequestValue>(new RequestValue()),
196                  base::File::FILE_ERROR_ABORT);
197    return;
198  }
199
200  notification_manager_->ShowUnresponsiveNotification(
201      request_id,
202      base::Bind(&RequestManager::OnUnresponsiveNotificationResult,
203                 weak_ptr_factory_.GetWeakPtr(),
204                 request_id));
205}
206
207void RequestManager::OnUnresponsiveNotificationResult(
208    int request_id,
209    NotificationManagerInterface::NotificationResult result) {
210  RequestMap::iterator request_it = requests_.find(request_id);
211  if (request_it == requests_.end())
212    return;
213
214  if (result == NotificationManagerInterface::CONTINUE) {
215    ResetTimer(request_id);
216    return;
217  }
218
219  RejectRequest(request_id,
220                scoped_ptr<RequestValue>(new RequestValue()),
221                base::File::FILE_ERROR_ABORT);
222}
223
224void RequestManager::ResetTimer(int request_id) {
225  RequestMap::iterator request_it = requests_.find(request_id);
226  if (request_it == requests_.end())
227    return;
228
229  request_it->second->timeout_timer.Start(
230      FROM_HERE,
231      timeout_,
232      base::Bind(&RequestManager::OnRequestTimeout,
233                 weak_ptr_factory_.GetWeakPtr(),
234                 request_id));
235}
236
237void RequestManager::DestroyRequest(int request_id) {
238  RequestMap::iterator request_it = requests_.find(request_id);
239  if (request_it == requests_.end())
240    return;
241
242  delete request_it->second;
243  requests_.erase(request_it);
244
245  if (notification_manager_)
246    notification_manager_->HideUnresponsiveNotification(request_id);
247
248  FOR_EACH_OBSERVER(Observer, observers_, OnRequestDestroyed(request_id));
249
250  TRACE_EVENT_ASYNC_END0(
251      "file_system_provider", "RequestManager::Request", request_id);
252}
253
254}  // namespace file_system_provider
255}  // namespace chromeos
256