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"
18 namespace file_system_provider
{
21 // Timeout in seconds, before a request is considered as stale and hence
23 const int kDefaultTimeout
= 10;
27 RequestManager::RequestManager(
29 const std::string
& extension_id
,
30 NotificationManagerInterface
* notification_manager
)
32 extension_id_(extension_id
),
33 notification_manager_(notification_manager
),
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
;
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
58 int request_id
= next_id_
++;
60 // If cycled the int, then signal an error.
61 if (requests_
.find(request_id
) != requests_
.end())
64 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
65 "RequestManager::Request",
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
);
86 FOR_EACH_OBSERVER(Observer
, observers_
, OnRequestExecuted(request_id
));
91 base::File::Error
RequestManager::FulfillRequest(
93 scoped_ptr
<RequestValue
> response
,
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
,
102 OnRequestFulfilled(request_id
, *response
.get(), has_more
));
104 request_it
->second
->handler
->OnSuccess(request_id
, response
.Pass(), has_more
);
107 DestroyRequest(request_id
);
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(
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
,
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
) {
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();
145 result
.push_back(request_it
->first
);
151 void RequestManager::AddObserver(Observer
* observer
) {
153 observers_
.AddObserver(observer
);
156 void RequestManager::RemoveObserver(Observer
* 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
);
175 if (!IsInteractingWithUser()) {
176 notification_manager_
->ShowUnresponsiveNotification(
178 base::Bind(&RequestManager::OnUnresponsiveNotificationResult
,
179 weak_ptr_factory_
.GetWeakPtr(), request_id
));
181 ResetTimer(request_id
);
185 void RequestManager::OnUnresponsiveNotificationResult(
187 NotificationManagerInterface::NotificationResult result
) {
188 RequestMap::iterator request_it
= requests_
.find(request_id
);
189 if (request_it
== requests_
.end())
192 if (result
== NotificationManagerInterface::CONTINUE
) {
193 ResetTimer(request_id
);
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())
207 request_it
->second
->timeout_timer
.Start(
210 base::Bind(&RequestManager::OnRequestTimeout
,
211 weak_ptr_factory_
.GetWeakPtr(),
215 bool RequestManager::IsInteractingWithUser() const {
216 // First try for app windows. If not found, then fall back to browser windows
219 const extensions::AppWindowRegistry
* const registry
=
220 extensions::AppWindowRegistry::Get(profile_
);
222 if (registry
->GetCurrentAppWindowForApp(extension_id_
))
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();
233 const TabStripModel
* const tabs
= browser
->tab_strip_model();
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_
) {
249 void RequestManager::DestroyRequest(int request_id
) {
250 RequestMap::iterator request_it
= requests_
.find(request_id
);
251 if (request_it
== requests_
.end())
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