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