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 <utility>
8
9 #include "base/bind.h"
10 #include "base/files/file.h"
11 #include "base/trace_event/trace_event.h"
12 #include "chrome/browser/extensions/window_controller_list.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "extensions/browser/app_window/app_window_registry.h"
17 #include "extensions/common/constants.h"
18
19 namespace chromeos {
20 namespace file_system_provider {
21 namespace {
22
23 // Timeout in seconds, before a request is considered as stale and hence
24 // aborted.
25 const int kDefaultTimeout = 10;
26
27 } // namespace
28
RequestManager(Profile * profile,const std::string & provider_id,NotificationManagerInterface * notification_manager)29 RequestManager::RequestManager(
30 Profile* profile,
31 const std::string& provider_id,
32 NotificationManagerInterface* notification_manager)
33 : profile_(profile),
34 provider_id_(provider_id),
35 notification_manager_(notification_manager),
36 next_id_(1),
37 timeout_(base::TimeDelta::FromSeconds(kDefaultTimeout)) {}
38
~RequestManager()39 RequestManager::~RequestManager() {
40 // Abort all of the active requests.
41 auto it = requests_.begin();
42 while (it != requests_.end()) {
43 const int request_id = it->first;
44 ++it;
45 RejectRequest(request_id, std::make_unique<RequestValue>(),
46 base::File::FILE_ERROR_ABORT);
47 }
48
49 DCHECK_EQ(0u, requests_.size());
50 }
51
CreateRequest(RequestType type,std::unique_ptr<HandlerInterface> handler)52 int RequestManager::CreateRequest(RequestType type,
53 std::unique_ptr<HandlerInterface> handler) {
54 // The request id is unique per request manager, so per service, thereof
55 // per profile.
56 int request_id = next_id_++;
57
58 // If cycled the int, then signal an error.
59 if (requests_.find(request_id) != requests_.end())
60 return 0;
61
62 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("file_system_provider",
63 "RequestManager::Request",
64 TRACE_ID_LOCAL(request_id), "type", type);
65
66 std::unique_ptr<Request> request = std::make_unique<Request>();
67 request->handler = std::move(handler);
68 requests_[request_id] = std::move(request);
69 ResetTimer(request_id);
70
71 for (auto& observer : observers_)
72 observer.OnRequestCreated(request_id, type);
73
74 // Execute the request implementation. In case of an execution failure,
75 // unregister and return 0. This may often happen, eg. if the providing
76 // extension is not listening for the request event being sent.
77 // In such case, we should abort as soon as possible.
78 if (!requests_[request_id]->handler->Execute(request_id)) {
79 DestroyRequest(request_id);
80 return 0;
81 }
82
83 for (auto& observer : observers_)
84 observer.OnRequestExecuted(request_id);
85
86 return request_id;
87 }
88
FulfillRequest(int request_id,std::unique_ptr<RequestValue> response,bool has_more)89 base::File::Error RequestManager::FulfillRequest(
90 int request_id,
91 std::unique_ptr<RequestValue> response,
92 bool has_more) {
93 CHECK(response.get());
94 auto request_it = requests_.find(request_id);
95 if (request_it == requests_.end())
96 return base::File::FILE_ERROR_NOT_FOUND;
97
98 for (auto& observer : observers_)
99 observer.OnRequestFulfilled(request_id, *response.get(), has_more);
100
101 request_it->second->handler->OnSuccess(request_id, std::move(response),
102 has_more);
103
104 if (!has_more) {
105 DestroyRequest(request_id);
106 } else {
107 if (notification_manager_)
108 notification_manager_->HideUnresponsiveNotification(request_id);
109 ResetTimer(request_id);
110 }
111
112 return base::File::FILE_OK;
113 }
114
RejectRequest(int request_id,std::unique_ptr<RequestValue> response,base::File::Error error)115 base::File::Error RequestManager::RejectRequest(
116 int request_id,
117 std::unique_ptr<RequestValue> response,
118 base::File::Error error) {
119 CHECK(response.get());
120 auto request_it = requests_.find(request_id);
121 if (request_it == requests_.end())
122 return base::File::FILE_ERROR_NOT_FOUND;
123
124 for (auto& observer : observers_)
125 observer.OnRequestRejected(request_id, *response.get(), error);
126 request_it->second->handler->OnError(request_id, std::move(response), error);
127 DestroyRequest(request_id);
128
129 return base::File::FILE_OK;
130 }
131
SetTimeoutForTesting(const base::TimeDelta & timeout)132 void RequestManager::SetTimeoutForTesting(const base::TimeDelta& timeout) {
133 timeout_ = timeout;
134 }
135
GetActiveRequestIds() const136 std::vector<int> RequestManager::GetActiveRequestIds() const {
137 std::vector<int> result;
138
139 for (auto request_it = requests_.begin(); request_it != requests_.end();
140 ++request_it) {
141 result.push_back(request_it->first);
142 }
143
144 return result;
145 }
146
AddObserver(Observer * observer)147 void RequestManager::AddObserver(Observer* observer) {
148 DCHECK(observer);
149 observers_.AddObserver(observer);
150 }
151
RemoveObserver(Observer * observer)152 void RequestManager::RemoveObserver(Observer* observer) {
153 DCHECK(observer);
154 observers_.RemoveObserver(observer);
155 }
156
Request()157 RequestManager::Request::Request() {}
158
~Request()159 RequestManager::Request::~Request() {}
160
OnRequestTimeout(int request_id)161 void RequestManager::OnRequestTimeout(int request_id) {
162 for (auto& observer : observers_)
163 observer.OnRequestTimeouted(request_id);
164
165 if (!notification_manager_) {
166 RejectRequest(request_id, std::make_unique<RequestValue>(),
167 base::File::FILE_ERROR_ABORT);
168 return;
169 }
170
171 if (!IsInteractingWithUser()) {
172 notification_manager_->ShowUnresponsiveNotification(
173 request_id,
174 base::BindOnce(&RequestManager::OnUnresponsiveNotificationResult,
175 weak_ptr_factory_.GetWeakPtr(), request_id));
176 } else {
177 ResetTimer(request_id);
178 }
179 }
180
OnUnresponsiveNotificationResult(int request_id,NotificationManagerInterface::NotificationResult result)181 void RequestManager::OnUnresponsiveNotificationResult(
182 int request_id,
183 NotificationManagerInterface::NotificationResult result) {
184 auto request_it = requests_.find(request_id);
185 if (request_it == requests_.end())
186 return;
187
188 if (result == NotificationManagerInterface::CONTINUE) {
189 ResetTimer(request_id);
190 return;
191 }
192
193 RejectRequest(request_id, std::make_unique<RequestValue>(),
194 base::File::FILE_ERROR_ABORT);
195 }
196
ResetTimer(int request_id)197 void RequestManager::ResetTimer(int request_id) {
198 auto request_it = requests_.find(request_id);
199 if (request_it == requests_.end())
200 return;
201
202 request_it->second->timeout_timer.Start(
203 FROM_HERE, timeout_,
204 base::BindOnce(&RequestManager::OnRequestTimeout,
205 weak_ptr_factory_.GetWeakPtr(), request_id));
206 }
207
IsInteractingWithUser() const208 bool RequestManager::IsInteractingWithUser() const {
209 // First try for app windows. If not found, then fall back to browser windows
210 // and tabs.
211
212 const extensions::AppWindowRegistry* const registry =
213 extensions::AppWindowRegistry::Get(profile_);
214 DCHECK(registry);
215 if (registry->GetCurrentAppWindowForApp(provider_id_))
216 return true;
217
218 // This loop is heavy, but it's not called often. Only when a request timeouts
219 // which is at most once every 10 seconds per request (except tests).
220 const extensions::WindowControllerList::ControllerList& windows =
221 extensions::WindowControllerList::GetInstance()->windows();
222 for (auto* window : windows) {
223 const Browser* const browser = window->GetBrowser();
224 if (!browser)
225 continue;
226 const TabStripModel* const tabs = browser->tab_strip_model();
227 DCHECK(tabs);
228 for (int i = 0; i < tabs->count(); ++i) {
229 content::WebContents* const web_contents = tabs->GetWebContentsAt(i);
230 const GURL& url = web_contents->GetURL();
231 if (url.SchemeIs(extensions::kExtensionScheme) &&
232 url.host_piece() == provider_id_) {
233 return true;
234 }
235 }
236 }
237
238 return false;
239 }
240
DestroyRequest(int request_id)241 void RequestManager::DestroyRequest(int request_id) {
242 auto request_it = requests_.find(request_id);
243 if (request_it == requests_.end())
244 return;
245
246 requests_.erase(request_it);
247
248 if (notification_manager_)
249 notification_manager_->HideUnresponsiveNotification(request_id);
250
251 for (auto& observer : observers_)
252 observer.OnRequestDestroyed(request_id);
253
254 TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
255 "RequestManager::Request",
256 TRACE_ID_LOCAL(request_id));
257 }
258
259 } // namespace file_system_provider
260 } // namespace chromeos
261