Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / request_manager.cc
blob1c4035b0041dd443e156afb9d109071b9cbfc372
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/request_manager.h"
7 #include "base/files/file.h"
8 #include "base/stl_util.h"
9 #include "base/trace_event/trace_event.h"
10 #include "chrome/browser/extensions/window_controller_list.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "extensions/browser/app_window/app_window_registry.h"
15 #include "extensions/common/constants.h"
17 namespace chromeos {
18 namespace file_system_provider {
19 namespace {
21 // Timeout in seconds, before a request is considered as stale and hence
22 // aborted.
23 const int kDefaultTimeout = 10;
25 } // namespace
27 RequestManager::RequestManager(
28 Profile* profile,
29 const std::string& extension_id,
30 NotificationManagerInterface* notification_manager)
31 : profile_(profile),
32 extension_id_(extension_id),
33 notification_manager_(notification_manager),
34 next_id_(1),
35 timeout_(base::TimeDelta::FromSeconds(kDefaultTimeout)),
36 weak_ptr_factory_(this) {
39 RequestManager::~RequestManager() {
40 // Abort all of the active requests.
41 RequestMap::iterator it = requests_.begin();
42 while (it != requests_.end()) {
43 const int request_id = it->first;
44 ++it;
45 RejectRequest(request_id,
46 scoped_ptr<RequestValue>(new RequestValue()),
47 base::File::FILE_ERROR_ABORT);
50 DCHECK_EQ(0u, requests_.size());
51 STLDeleteValues(&requests_);
54 int RequestManager::CreateRequest(RequestType type,
55 scoped_ptr<HandlerInterface> handler) {
56 // The request id is unique per request manager, so per service, thereof
57 // per profile.
58 int request_id = next_id_++;
60 // If cycled the int, then signal an error.
61 if (requests_.find(request_id) != requests_.end())
62 return 0;
64 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
65 "RequestManager::Request",
66 request_id,
67 "type",
68 type);
70 Request* request = new Request;
71 request->handler = handler.Pass();
72 requests_[request_id] = request;
73 ResetTimer(request_id);
75 FOR_EACH_OBSERVER(Observer, observers_, OnRequestCreated(request_id, type));
77 // Execute the request implementation. In case of an execution failure,
78 // unregister and return 0. This may often happen, eg. if the providing
79 // extension is not listening for the request event being sent.
80 // In such case, we should abort as soon as possible.
81 if (!request->handler->Execute(request_id)) {
82 DestroyRequest(request_id);
83 return 0;
86 FOR_EACH_OBSERVER(Observer, observers_, OnRequestExecuted(request_id));
88 return request_id;
91 base::File::Error RequestManager::FulfillRequest(
92 int request_id,
93 scoped_ptr<RequestValue> response,
94 bool has_more) {
95 CHECK(response.get());
96 RequestMap::iterator request_it = requests_.find(request_id);
97 if (request_it == requests_.end())
98 return base::File::FILE_ERROR_NOT_FOUND;
100 FOR_EACH_OBSERVER(Observer,
101 observers_,
102 OnRequestFulfilled(request_id, *response.get(), has_more));
104 request_it->second->handler->OnSuccess(request_id, response.Pass(), has_more);
106 if (!has_more) {
107 DestroyRequest(request_id);
108 } else {
109 if (notification_manager_)
110 notification_manager_->HideUnresponsiveNotification(request_id);
111 ResetTimer(request_id);
114 return base::File::FILE_OK;
117 base::File::Error RequestManager::RejectRequest(
118 int request_id,
119 scoped_ptr<RequestValue> response,
120 base::File::Error error) {
121 CHECK(response.get());
122 RequestMap::iterator request_it = requests_.find(request_id);
123 if (request_it == requests_.end())
124 return base::File::FILE_ERROR_NOT_FOUND;
126 FOR_EACH_OBSERVER(Observer,
127 observers_,
128 OnRequestRejected(request_id, *response.get(), error));
129 request_it->second->handler->OnError(request_id, response.Pass(), error);
130 DestroyRequest(request_id);
132 return base::File::FILE_OK;
135 void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) {
136 timeout_ = timeout;
139 std::vector<int> RequestManager::GetActiveRequestIds() const {
140 std::vector<int> result;
142 for (RequestMap::const_iterator request_it = requests_.begin();
143 request_it != requests_.end();
144 ++request_it) {
145 result.push_back(request_it->first);
148 return result;
151 void RequestManager::AddObserver(Observer* observer) {
152 DCHECK(observer);
153 observers_.AddObserver(observer);
156 void RequestManager::RemoveObserver(Observer* observer) {
157 DCHECK(observer);
158 observers_.RemoveObserver(observer);
161 RequestManager::Request::Request() {}
163 RequestManager::Request::~Request() {}
165 void RequestManager::OnRequestTimeout(int request_id) {
166 FOR_EACH_OBSERVER(Observer, observers_, OnRequestTimeouted(request_id));
168 if (!notification_manager_) {
169 RejectRequest(request_id,
170 scoped_ptr<RequestValue>(new RequestValue()),
171 base::File::FILE_ERROR_ABORT);
172 return;
175 if (!IsInteractingWithUser()) {
176 notification_manager_->ShowUnresponsiveNotification(
177 request_id,
178 base::Bind(&RequestManager::OnUnresponsiveNotificationResult,
179 weak_ptr_factory_.GetWeakPtr(), request_id));
180 } else {
181 ResetTimer(request_id);
185 void RequestManager::OnUnresponsiveNotificationResult(
186 int request_id,
187 NotificationManagerInterface::NotificationResult result) {
188 RequestMap::iterator request_it = requests_.find(request_id);
189 if (request_it == requests_.end())
190 return;
192 if (result == NotificationManagerInterface::CONTINUE) {
193 ResetTimer(request_id);
194 return;
197 RejectRequest(request_id,
198 scoped_ptr<RequestValue>(new RequestValue()),
199 base::File::FILE_ERROR_ABORT);
202 void RequestManager::ResetTimer(int request_id) {
203 RequestMap::iterator request_it = requests_.find(request_id);
204 if (request_it == requests_.end())
205 return;
207 request_it->second->timeout_timer.Start(
208 FROM_HERE,
209 timeout_,
210 base::Bind(&RequestManager::OnRequestTimeout,
211 weak_ptr_factory_.GetWeakPtr(),
212 request_id));
215 bool RequestManager::IsInteractingWithUser() const {
216 // First try for app windows. If not found, then fall back to browser windows
217 // and tabs.
219 const extensions::AppWindowRegistry* const registry =
220 extensions::AppWindowRegistry::Get(profile_);
221 DCHECK(registry);
222 if (registry->GetCurrentAppWindowForApp(extension_id_))
223 return true;
225 // This loop is heavy, but it's not called often. Only when a request timeouts
226 // which is at most once every 10 seconds per request (except tests).
227 const extensions::WindowControllerList::ControllerList& windows =
228 extensions::WindowControllerList::GetInstance()->windows();
229 for (const auto& window : windows) {
230 const Browser* const browser = window->GetBrowser();
231 if (!browser)
232 continue;
233 const TabStripModel* const tabs = browser->tab_strip_model();
234 DCHECK(tabs);
235 for (int i = 0; i < tabs->count(); ++i) {
236 const content::WebContents* const web_contents =
237 tabs->GetWebContentsAt(i);
238 const GURL& url = web_contents->GetURL();
239 if (url.scheme() == extensions::kExtensionScheme &&
240 url.host() == extension_id_) {
241 return true;
246 return false;
249 void RequestManager::DestroyRequest(int request_id) {
250 RequestMap::iterator request_it = requests_.find(request_id);
251 if (request_it == requests_.end())
252 return;
254 delete request_it->second;
255 requests_.erase(request_it);
257 if (notification_manager_)
258 notification_manager_->HideUnresponsiveNotification(request_id);
260 FOR_EACH_OBSERVER(Observer, observers_, OnRequestDestroyed(request_id));
262 TRACE_EVENT_ASYNC_END0(
263 "file_system_provider", "RequestManager::Request", request_id);
266 } // namespace file_system_provider
267 } // namespace chromeos