1 // Copyright 2015 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 "content/browser/service_worker/service_worker_client_utils.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <tuple>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/location.h"
15 #include "base/macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/task/post_task.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "content/browser/frame_host/frame_tree_node.h"
20 #include "content/browser/frame_host/navigator.h"
21 #include "content/browser/frame_host/render_frame_host_impl.h"
22 #include "content/browser/service_worker/service_worker_container_host.h"
23 #include "content/browser/service_worker/service_worker_context_core.h"
24 #include "content/browser/service_worker/service_worker_context_wrapper.h"
25 #include "content/browser/service_worker/service_worker_version.h"
26 #include "content/browser/storage_partition_impl.h"
27 #include "content/browser/web_contents/web_contents_impl.h"
28 #include "content/public/browser/browser_task_traits.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/content_browser_client.h"
31 #include "content/public/browser/navigation_handle.h"
32 #include "content/public/browser/page_navigator.h"
33 #include "content/public/browser/payment_app_provider.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/render_widget_host_view.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_observer.h"
38 #include "content/public/common/child_process_host.h"
39 #include "content/public/common/content_client.h"
40 #include "content/public/common/page_visibility_state.h"
41 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom.h"
42 #include "ui/base/window_open_disposition.h"
43 #include "url/gurl.h"
44 #include "url/origin.h"
45 
46 // TODO(https://crbug.com/824858): Much of this file, which dealt with thread
47 // hops between UI and IO, can likely be simplified when the service worker core
48 // thread moves to the UI thread.
49 
50 namespace content {
51 namespace service_worker_client_utils {
52 
53 namespace {
54 
55 using OpenURLCallback = base::OnceCallback<void(int, int)>;
56 
57 // The OpenURLObserver class is a WebContentsObserver that will wait for a
58 // WebContents to be initialized, run the |callback| passed to its constructor
59 // then self destroy.
60 // The callback will receive the process and frame ids. If something went wrong
61 // those will be (kInvalidUniqueID, MSG_ROUTING_NONE).
62 // The callback will be called on the core thread.
63 class OpenURLObserver : public WebContentsObserver {
64  public:
OpenURLObserver(WebContents * web_contents,int frame_tree_node_id,OpenURLCallback callback)65   OpenURLObserver(WebContents* web_contents,
66                   int frame_tree_node_id,
67                   OpenURLCallback callback)
68       : WebContentsObserver(web_contents),
69         frame_tree_node_id_(frame_tree_node_id),
70         callback_(std::move(callback)) {}
71 
DidFinishNavigation(NavigationHandle * navigation_handle)72   void DidFinishNavigation(NavigationHandle* navigation_handle) override {
73     DCHECK(web_contents());
74     if (!navigation_handle->HasCommitted()) {
75       // Return error.
76       RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
77       return;
78     }
79 
80     if (navigation_handle->GetFrameTreeNodeId() != frame_tree_node_id_) {
81       // Return error.
82       RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
83       return;
84     }
85 
86     RenderFrameHost* render_frame_host =
87         navigation_handle->GetRenderFrameHost();
88     RunCallback(render_frame_host->GetProcess()->GetID(),
89                 render_frame_host->GetRoutingID());
90   }
91 
RenderProcessGone(base::TerminationStatus status)92   void RenderProcessGone(base::TerminationStatus status) override {
93     RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
94   }
95 
WebContentsDestroyed()96   void WebContentsDestroyed() override {
97     RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
98   }
99 
100  private:
RunCallback(int render_process_id,int render_frame_id)101   void RunCallback(int render_process_id, int render_frame_id) {
102     // After running the callback, |this| will stop observing, thus
103     // web_contents() should return nullptr and |RunCallback| should no longer
104     // be called. Then, |this| will self destroy.
105     DCHECK(web_contents());
106     DCHECK(callback_);
107 
108     base::PostTask(FROM_HERE, {ServiceWorkerContext::GetCoreThreadId()},
109                    base::BindOnce(std::move(callback_), render_process_id,
110                                   render_frame_id));
111     Observe(nullptr);
112     base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
113   }
114 
115   int frame_tree_node_id_;
116   OpenURLCallback callback_;
117 
118   DISALLOW_COPY_AND_ASSIGN(OpenURLObserver);
119 };
120 
GetWindowClientInfoOnUI(int render_process_id,int render_frame_id,base::TimeTicks create_time,const std::string & client_uuid)121 blink::mojom::ServiceWorkerClientInfoPtr GetWindowClientInfoOnUI(
122     int render_process_id,
123     int render_frame_id,
124     base::TimeTicks create_time,
125     const std::string& client_uuid) {
126   DCHECK_CURRENTLY_ON(BrowserThread::UI);
127   RenderFrameHostImpl* render_frame_host =
128       RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
129   if (!render_frame_host)
130     return nullptr;
131 
132   // Treat items in backforward cache as not existing.
133   if (render_frame_host->is_in_back_forward_cache())
134     return nullptr;
135 
136   // TODO(mlamouri,michaeln): it is possible to end up collecting information
137   // for a frame that is actually being navigated and isn't exactly what we are
138   // expecting.
139   PageVisibilityState visibility = render_frame_host->GetVisibilityState();
140   bool page_hidden = visibility != PageVisibilityState::kVisible;
141   return blink::mojom::ServiceWorkerClientInfo::New(
142       render_frame_host->GetLastCommittedURL(),
143       render_frame_host->GetParent()
144           ? blink::mojom::RequestContextFrameType::kNested
145           : blink::mojom::RequestContextFrameType::kTopLevel,
146       client_uuid, blink::mojom::ServiceWorkerClientType::kWindow, page_hidden,
147       render_frame_host->IsFocused(),
148       render_frame_host->IsFrozen()
149           ? blink::mojom::ServiceWorkerClientLifecycleState::kFrozen
150           : blink::mojom::ServiceWorkerClientLifecycleState::kActive,
151       render_frame_host->frame_tree_node()->last_focus_time(), create_time);
152 }
153 
FocusOnUI(int render_process_id,int render_frame_id,base::TimeTicks create_time,const std::string & client_uuid)154 blink::mojom::ServiceWorkerClientInfoPtr FocusOnUI(
155     int render_process_id,
156     int render_frame_id,
157     base::TimeTicks create_time,
158     const std::string& client_uuid) {
159   DCHECK_CURRENTLY_ON(BrowserThread::UI);
160   RenderFrameHostImpl* render_frame_host =
161       RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
162   WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
163       WebContents::FromRenderFrameHost(render_frame_host));
164 
165   if (!render_frame_host || !web_contents)
166     return nullptr;
167 
168   FrameTreeNode* frame_tree_node = render_frame_host->frame_tree_node();
169 
170   // Focus the frame in the frame tree node, in case it has changed.
171   frame_tree_node->frame_tree()->SetFocusedFrame(
172       frame_tree_node, render_frame_host->GetSiteInstance());
173 
174   // Focus the frame's view to make sure the frame is now considered as focused.
175   render_frame_host->GetView()->Focus();
176 
177   // Move the web contents to the foreground.
178   web_contents->Activate();
179 
180   return GetWindowClientInfoOnUI(render_process_id, render_frame_id,
181                                  create_time, client_uuid);
182 }
183 
184 // This is only called for main frame navigations in OpenWindowOnUI().
DidOpenURLOnUI(WindowType type,OpenURLCallback callback,WebContents * web_contents)185 void DidOpenURLOnUI(WindowType type,
186                     OpenURLCallback callback,
187                     WebContents* web_contents) {
188   DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 
190   if (!web_contents) {
191     RunOrPostTaskOnThread(
192         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
193         base::BindOnce(std::move(callback), ChildProcessHost::kInvalidUniqueID,
194                        MSG_ROUTING_NONE));
195     return;
196   }
197 
198   // ContentBrowserClient::OpenURL calls ui::BaseWindow::Show which
199   // makes the destination window the main+key window, but won't make Chrome
200   // the active application (https://crbug.com/470830). Since OpenWindow is
201   // always called from a user gesture (e.g. notification click), we should
202   // explicitly activate the window, which brings Chrome to the front.
203   static_cast<WebContentsImpl*>(web_contents)->Activate();
204 
205   RenderFrameHostImpl* rfhi =
206       static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
207   new OpenURLObserver(web_contents,
208                       rfhi->frame_tree_node()->frame_tree_node_id(),
209                       std::move(callback));
210 
211 #ifndef TOOLKIT_QT
212   if (type == WindowType::PAYMENT_HANDLER_WINDOW) {
213     // Set the opened web_contents to payment app provider to manage its life
214     // cycle.
215     PaymentAppProvider::GetInstance()->SetOpenedWindow(web_contents);
216   }
217 #endif
218 }
219 
OpenWindowOnUI(const GURL & url,const GURL & script_url,int worker_id,int worker_process_id,const scoped_refptr<ServiceWorkerContextWrapper> & context_wrapper,WindowType type,OpenURLCallback callback)220 void OpenWindowOnUI(
221     const GURL& url,
222     const GURL& script_url,
223     int worker_id,
224     int worker_process_id,
225     const scoped_refptr<ServiceWorkerContextWrapper>& context_wrapper,
226     WindowType type,
227     OpenURLCallback callback) {
228   DCHECK_CURRENTLY_ON(BrowserThread::UI);
229 
230   RenderProcessHost* render_process_host =
231       RenderProcessHost::FromID(worker_process_id);
232   if (render_process_host->IsForGuestsOnly()) {
233     RunOrPostTaskOnThread(
234         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
235         base::BindOnce(std::move(callback), ChildProcessHost::kInvalidUniqueID,
236                        MSG_ROUTING_NONE));
237     return;
238   }
239 
240   SiteInstance* site_instance =
241       context_wrapper->process_manager()->GetSiteInstanceForWorker(worker_id);
242   if (!site_instance) {
243     // Worker isn't running anymore. Fail.
244     RunOrPostTaskOnThread(
245         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
246         base::BindOnce(std::move(callback), ChildProcessHost::kInvalidUniqueID,
247                        MSG_ROUTING_NONE));
248     return;
249   }
250 
251   // The following code is a rough copy of NavigatorImpl::RequestOpenURL. That
252   // function can't be used directly since there is no render frame host yet
253   // that the navigation will occur in.
254 
255   OpenURLParams params(
256       url,
257       Referrer::SanitizeForRequest(
258           url, Referrer(script_url, network::mojom::ReferrerPolicy::kDefault)),
259       type == WindowType::PAYMENT_HANDLER_WINDOW
260           ? WindowOpenDisposition::NEW_POPUP
261           : WindowOpenDisposition::NEW_FOREGROUND_TAB,
262       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, true /* is_renderer_initiated */);
263   params.open_app_window_if_possible = type == WindowType::NEW_TAB_WINDOW;
264   params.initiator_origin = url::Origin::Create(script_url.GetOrigin());
265 
266   // End of RequestOpenURL copy.
267 
268   GetContentClient()->browser()->OpenURL(
269       site_instance, params,
270       base::AdaptCallbackForRepeating(
271           base::BindOnce(&DidOpenURLOnUI, type, std::move(callback))));
272 }
273 
NavigateClientOnUI(const GURL & url,const GURL & script_url,int process_id,int frame_id,OpenURLCallback callback)274 void NavigateClientOnUI(const GURL& url,
275                         const GURL& script_url,
276                         int process_id,
277                         int frame_id,
278                         OpenURLCallback callback) {
279   DCHECK_CURRENTLY_ON(BrowserThread::UI);
280 
281   RenderFrameHostImpl* rfhi = RenderFrameHostImpl::FromID(process_id, frame_id);
282   WebContents* web_contents = WebContents::FromRenderFrameHost(rfhi);
283 
284   if (!rfhi || !web_contents) {
285     RunOrPostTaskOnThread(
286         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
287         base::BindOnce(std::move(callback), ChildProcessHost::kInvalidUniqueID,
288                        MSG_ROUTING_NONE));
289     return;
290   }
291 
292   // Reject the navigate() call if there is an ongoing browser-initiated
293   // navigation. Not rejecting it would allow websites to prevent the user from
294   // navigating away. See https://crbug.com/930154.
295   NavigationRequest* ongoing_navigation_request =
296       rfhi->frame_tree_node()->frame_tree()->root()->navigation_request();
297   if (ongoing_navigation_request &&
298       ongoing_navigation_request->browser_initiated()) {
299     RunOrPostTaskOnThread(
300         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
301         base::BindOnce(std::move(callback), ChildProcessHost::kInvalidUniqueID,
302                        MSG_ROUTING_NONE));
303     return;
304   }
305 
306   int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id();
307   Navigator* navigator = rfhi->frame_tree_node()->navigator();
308   navigator->RequestOpenURL(
309       rfhi, url, url::Origin::Create(script_url), nullptr /* post_body */,
310       std::string() /* extra_headers */,
311       Referrer::SanitizeForRequest(
312           url, Referrer(script_url, network::mojom::ReferrerPolicy::kDefault)),
313       WindowOpenDisposition::CURRENT_TAB,
314       false /* should_replace_current_entry */, false /* user_gesture */,
315       blink::TriggeringEventInfo::kUnknown, std::string() /* href_translate */,
316       nullptr /* blob_url_loader_factory */);
317   new OpenURLObserver(web_contents, frame_tree_node_id, std::move(callback));
318 }
319 
AddWindowClient(const ServiceWorkerContainerHost * container_host,std::vector<std::tuple<int,int,base::TimeTicks,std::string>> * client_info)320 void AddWindowClient(
321     const ServiceWorkerContainerHost* container_host,
322     std::vector<std::tuple<int, int, base::TimeTicks, std::string>>*
323         client_info) {
324   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
325   if (container_host->client_type() !=
326       blink::mojom::ServiceWorkerClientType::kWindow) {
327     return;
328   }
329   if (!container_host->is_execution_ready())
330     return;
331   client_info->push_back(std::make_tuple(
332       container_host->process_id(), container_host->frame_id(),
333       container_host->create_time(), container_host->client_uuid()));
334 }
335 
AddNonWindowClient(const ServiceWorkerContainerHost * container_host,blink::mojom::ServiceWorkerClientType client_type,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> * out_clients)336 void AddNonWindowClient(
337     const ServiceWorkerContainerHost* container_host,
338     blink::mojom::ServiceWorkerClientType client_type,
339     std::vector<blink::mojom::ServiceWorkerClientInfoPtr>* out_clients) {
340   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
341   blink::mojom::ServiceWorkerClientType host_client_type =
342       container_host->client_type();
343   if (host_client_type == blink::mojom::ServiceWorkerClientType::kWindow)
344     return;
345   if (client_type != blink::mojom::ServiceWorkerClientType::kAll &&
346       client_type != host_client_type)
347     return;
348   if (!container_host->is_execution_ready())
349     return;
350 
351   // TODO(dtapuska): Need to get frozen state for dedicated workers from
352   // DedicatedWorkerHost. crbug.com/968417
353   auto client_info = blink::mojom::ServiceWorkerClientInfo::New(
354       container_host->url(), blink::mojom::RequestContextFrameType::kNone,
355       container_host->client_uuid(), host_client_type,
356       /*page_hidden=*/true,
357       /*is_focused=*/false,
358       blink::mojom::ServiceWorkerClientLifecycleState::kActive,
359       base::TimeTicks(), container_host->create_time());
360   out_clients->push_back(std::move(client_info));
361 }
362 
OnGetWindowClientsOnUI(const std::vector<std::tuple<int,int,base::TimeTicks,std::string>> & clients_info,const GURL & script_url,blink::mojom::ServiceWorkerHost::GetClientsCallback callback,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> out_clients)363 void OnGetWindowClientsOnUI(
364     // The tuple contains process_id, frame_id, create_time, client_uuid.
365     const std::vector<std::tuple<int, int, base::TimeTicks, std::string>>&
366         clients_info,
367     const GURL& script_url,
368     blink::mojom::ServiceWorkerHost::GetClientsCallback callback,
369     std::vector<blink::mojom::ServiceWorkerClientInfoPtr> out_clients) {
370   DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 
372   for (const auto& it : clients_info) {
373     blink::mojom::ServiceWorkerClientInfoPtr info = GetWindowClientInfoOnUI(
374         std::get<0>(it), std::get<1>(it), std::get<2>(it), std::get<3>(it));
375 
376     // If the request to the container_host returned a null
377     // ServiceWorkerClientInfo, that means that it wasn't possible to associate
378     // it with a valid RenderFrameHost. It might be because the frame was killed
379     // or navigated in between.
380     if (!info)
381       continue;
382     DCHECK(!info->client_uuid.empty());
383 
384     // We can get info for a frame that was navigating end ended up with a
385     // different URL than expected. In such case, we should make sure to not
386     // expose cross-origin WindowClient.
387     if (info->url.GetOrigin() != script_url.GetOrigin())
388       continue;
389 
390     out_clients.push_back(std::move(info));
391   }
392 
393   RunOrPostTaskOnThread(
394       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
395       base::BindOnce(std::move(callback), std::move(out_clients)));
396 }
397 
398 struct ServiceWorkerClientInfoSort {
operator ()content::service_worker_client_utils::__anon6b84fec20111::ServiceWorkerClientInfoSort399   bool operator()(const blink::mojom::ServiceWorkerClientInfoPtr& a,
400                   const blink::mojom::ServiceWorkerClientInfoPtr& b) const {
401     // Clients for windows should be appeared earlier.
402     if (a->client_type == blink::mojom::ServiceWorkerClientType::kWindow &&
403         b->client_type != blink::mojom::ServiceWorkerClientType::kWindow) {
404       return true;
405     }
406     if (a->client_type != blink::mojom::ServiceWorkerClientType::kWindow &&
407         b->client_type == blink::mojom::ServiceWorkerClientType::kWindow) {
408       return false;
409     }
410 
411     // Clients focused recently should be appeared earlier.
412     if (a->last_focus_time != b->last_focus_time)
413       return a->last_focus_time > b->last_focus_time;
414 
415     // Clients created before should be appeared earlier.
416     return a->creation_time < b->creation_time;
417   }
418 };
419 
DidGetClients(blink::mojom::ServiceWorkerHost::GetClientsCallback callback,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients)420 void DidGetClients(
421     blink::mojom::ServiceWorkerHost::GetClientsCallback callback,
422     std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients) {
423   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
424 
425   std::sort(clients.begin(), clients.end(), ServiceWorkerClientInfoSort());
426 
427   std::move(callback).Run(std::move(clients));
428 }
429 
GetNonWindowClients(const base::WeakPtr<ServiceWorkerVersion> & controller,blink::mojom::ServiceWorkerClientQueryOptionsPtr options,blink::mojom::ServiceWorkerHost::GetClientsCallback callback,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients)430 void GetNonWindowClients(
431     const base::WeakPtr<ServiceWorkerVersion>& controller,
432     blink::mojom::ServiceWorkerClientQueryOptionsPtr options,
433     blink::mojom::ServiceWorkerHost::GetClientsCallback callback,
434     std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients) {
435   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
436   if (!options->include_uncontrolled) {
437     for (const auto& controllee : controller->controllee_map())
438       AddNonWindowClient(controllee.second, options->client_type, &clients);
439   } else if (controller->context()) {
440     GURL origin = controller->script_url().GetOrigin();
441     for (auto it = controller->context()->GetClientContainerHostIterator(
442              origin, false /* include_reserved_clients */,
443              false /* include_back_forward_cached_clients */);
444          !it->IsAtEnd(); it->Advance()) {
445       AddNonWindowClient(it->GetContainerHost(), options->client_type,
446                          &clients);
447     }
448   }
449   DidGetClients(std::move(callback), std::move(clients));
450 }
451 
DidGetWindowClients(const base::WeakPtr<ServiceWorkerVersion> & controller,blink::mojom::ServiceWorkerClientQueryOptionsPtr options,blink::mojom::ServiceWorkerHost::GetClientsCallback callback,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients)452 void DidGetWindowClients(
453     const base::WeakPtr<ServiceWorkerVersion>& controller,
454     blink::mojom::ServiceWorkerClientQueryOptionsPtr options,
455     blink::mojom::ServiceWorkerHost::GetClientsCallback callback,
456     std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients) {
457   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
458   if (options->client_type == blink::mojom::ServiceWorkerClientType::kAll) {
459     GetNonWindowClients(controller, std::move(options), std::move(callback),
460                         std::move(clients));
461     return;
462   }
463   DidGetClients(std::move(callback), std::move(clients));
464 }
465 
GetWindowClients(const base::WeakPtr<ServiceWorkerVersion> & controller,blink::mojom::ServiceWorkerClientQueryOptionsPtr options,blink::mojom::ServiceWorkerHost::GetClientsCallback callback,std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients)466 void GetWindowClients(
467     const base::WeakPtr<ServiceWorkerVersion>& controller,
468     blink::mojom::ServiceWorkerClientQueryOptionsPtr options,
469     blink::mojom::ServiceWorkerHost::GetClientsCallback callback,
470     std::vector<blink::mojom::ServiceWorkerClientInfoPtr> clients) {
471   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
472   DCHECK(options->client_type ==
473              blink::mojom::ServiceWorkerClientType::kWindow ||
474          options->client_type == blink::mojom::ServiceWorkerClientType::kAll);
475 
476   std::vector<std::tuple<int, int, base::TimeTicks, std::string>> clients_info;
477   if (!options->include_uncontrolled) {
478     for (const auto& controllee : controller->controllee_map())
479       AddWindowClient(controllee.second, &clients_info);
480   } else if (controller->context()) {
481     GURL origin = controller->script_url().GetOrigin();
482     for (auto it = controller->context()->GetClientContainerHostIterator(
483              origin, false /* include_reserved_clients */,
484              false /* include_back_forward_cached_clients */);
485          !it->IsAtEnd(); it->Advance()) {
486       AddWindowClient(it->GetContainerHost(), &clients_info);
487     }
488   }
489 
490   if (clients_info.empty()) {
491     DidGetWindowClients(controller, std::move(options), std::move(callback),
492                         std::move(clients));
493     return;
494   }
495 
496   RunOrPostTaskOnThread(
497       FROM_HERE, BrowserThread::UI,
498       base::BindOnce(&OnGetWindowClientsOnUI, clients_info,
499                      controller->script_url(),
500                      base::BindOnce(&DidGetWindowClients, controller,
501                                     std::move(options), std::move(callback)),
502                      std::move(clients)));
503 }
504 
DidGetExecutionReadyClient(const base::WeakPtr<ServiceWorkerContextCore> & context,const std::string & client_uuid,const GURL & sane_origin,NavigationCallback callback)505 void DidGetExecutionReadyClient(
506     const base::WeakPtr<ServiceWorkerContextCore>& context,
507     const std::string& client_uuid,
508     const GURL& sane_origin,
509     NavigationCallback callback) {
510   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
511 
512   if (!context) {
513     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
514                             nullptr /* client_info */);
515     return;
516   }
517 
518   ServiceWorkerContainerHost* container_host =
519       context->GetContainerHostByClientID(client_uuid);
520   if (!container_host || !container_host->is_execution_ready()) {
521     // The page was destroyed before it became execution ready.  Tell the
522     // renderer the page opened but it doesn't have access to it.
523     std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
524                             nullptr /* client_info */);
525     return;
526   }
527 
528   CHECK_EQ(container_host->url().GetOrigin(), sane_origin);
529 
530   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
531     blink::mojom::ServiceWorkerClientInfoPtr info = GetWindowClientInfoOnUI(
532         container_host->process_id(), container_host->frame_id(),
533         container_host->create_time(), container_host->client_uuid());
534     std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
535                             std::move(info));
536 
537   } else {
538     base::PostTaskAndReplyWithResult(
539         FROM_HERE, {BrowserThread::UI},
540         base::BindOnce(&GetWindowClientInfoOnUI, container_host->process_id(),
541                        container_host->frame_id(),
542                        container_host->create_time(),
543                        container_host->client_uuid()),
544         base::BindOnce(std::move(callback),
545                        blink::ServiceWorkerStatusCode::kOk));
546   }
547 }
548 
549 }  // namespace
550 
FocusWindowClient(ServiceWorkerContainerHost * container_host,ClientCallback callback)551 void FocusWindowClient(ServiceWorkerContainerHost* container_host,
552                        ClientCallback callback) {
553   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
554   DCHECK_EQ(blink::mojom::ServiceWorkerClientType::kWindow,
555             container_host->client_type());
556 
557   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
558     blink::mojom::ServiceWorkerClientInfoPtr info =
559         FocusOnUI(container_host->process_id(), container_host->frame_id(),
560                   container_host->create_time(), container_host->client_uuid());
561     std::move(callback).Run(std::move(info));
562   } else {
563     base::PostTaskAndReplyWithResult(
564         FROM_HERE, {BrowserThread::UI},
565         base::BindOnce(&FocusOnUI, container_host->process_id(),
566                        container_host->frame_id(),
567                        container_host->create_time(),
568                        container_host->client_uuid()),
569         std::move(callback));
570   }
571 }
572 
OpenWindow(const GURL & url,const GURL & script_url,int worker_id,int worker_process_id,const base::WeakPtr<ServiceWorkerContextCore> & context,WindowType type,NavigationCallback callback)573 void OpenWindow(const GURL& url,
574                 const GURL& script_url,
575                 int worker_id,
576                 int worker_process_id,
577                 const base::WeakPtr<ServiceWorkerContextCore>& context,
578                 WindowType type,
579                 NavigationCallback callback) {
580   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
581   RunOrPostTaskOnThread(
582       FROM_HERE, BrowserThread::UI,
583       base::BindOnce(
584           &OpenWindowOnUI, url, script_url, worker_id, worker_process_id,
585           base::WrapRefCounted(context->wrapper()), type,
586           base::BindOnce(&DidNavigate, context, script_url.GetOrigin(),
587                          std::move(callback))));
588 }
589 
NavigateClient(const GURL & url,const GURL & script_url,int process_id,int frame_id,const base::WeakPtr<ServiceWorkerContextCore> & context,NavigationCallback callback)590 void NavigateClient(const GURL& url,
591                     const GURL& script_url,
592                     int process_id,
593                     int frame_id,
594                     const base::WeakPtr<ServiceWorkerContextCore>& context,
595                     NavigationCallback callback) {
596   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
597 
598   RunOrPostTaskOnThread(
599       FROM_HERE, BrowserThread::UI,
600       base::BindOnce(
601           &NavigateClientOnUI, url, script_url, process_id, frame_id,
602           base::BindOnce(&DidNavigate, context, script_url.GetOrigin(),
603                          std::move(callback))));
604 }
605 
GetClient(ServiceWorkerContainerHost * container_host,ClientCallback callback)606 void GetClient(ServiceWorkerContainerHost* container_host,
607                ClientCallback callback) {
608   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
609   DCHECK(container_host->IsContainerForClient());
610 
611   blink::mojom::ServiceWorkerClientType client_type =
612       container_host->client_type();
613   if (client_type == blink::mojom::ServiceWorkerClientType::kWindow) {
614     if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
615       blink::mojom::ServiceWorkerClientInfoPtr info = GetWindowClientInfoOnUI(
616           container_host->process_id(), container_host->frame_id(),
617           container_host->create_time(), container_host->client_uuid());
618       base::ThreadTaskRunnerHandle::Get()->PostTask(
619           FROM_HERE, base::BindOnce(std::move(callback), std::move(info)));
620       return;
621     }
622     base::PostTaskAndReplyWithResult(
623         FROM_HERE, BrowserThread::UI,
624         base::BindOnce(&GetWindowClientInfoOnUI, container_host->process_id(),
625                        container_host->frame_id(),
626                        container_host->create_time(),
627                        container_host->client_uuid()),
628         std::move(callback));
629     return;
630   }
631 
632   // TODO(dtapuska): Need to get frozen state for dedicated workers from
633   // DedicatedWorkerHost. crbug.com/968417
634   auto client_info = blink::mojom::ServiceWorkerClientInfo::New(
635       container_host->url(), blink::mojom::RequestContextFrameType::kNone,
636       container_host->client_uuid(), container_host->client_type(),
637       /*page_hidden=*/true,
638       /*is_focused=*/false,
639       blink::mojom::ServiceWorkerClientLifecycleState::kActive,
640       base::TimeTicks(), container_host->create_time());
641   base::ThreadTaskRunnerHandle::Get()->PostTask(
642       FROM_HERE, base::BindOnce(std::move(callback), std::move(client_info)));
643 }
644 
GetClients(const base::WeakPtr<ServiceWorkerVersion> & controller,blink::mojom::ServiceWorkerClientQueryOptionsPtr options,blink::mojom::ServiceWorkerHost::GetClientsCallback callback)645 void GetClients(const base::WeakPtr<ServiceWorkerVersion>& controller,
646                 blink::mojom::ServiceWorkerClientQueryOptionsPtr options,
647                 blink::mojom::ServiceWorkerHost::GetClientsCallback callback) {
648   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
649 
650   auto clients = std::vector<blink::mojom::ServiceWorkerClientInfoPtr>();
651   if (!controller->HasControllee() && !options->include_uncontrolled) {
652     DidGetClients(std::move(callback), std::move(clients));
653     return;
654   }
655 
656   // For Window clients we want to query the info on the UI thread first.
657   if (options->client_type == blink::mojom::ServiceWorkerClientType::kWindow ||
658       options->client_type == blink::mojom::ServiceWorkerClientType::kAll) {
659     GetWindowClients(controller, std::move(options), std::move(callback),
660                      std::move(clients));
661     return;
662   }
663 
664   GetNonWindowClients(controller, std::move(options), std::move(callback),
665                       std::move(clients));
666 }
667 
DidNavigate(const base::WeakPtr<ServiceWorkerContextCore> & context,const GURL & origin,NavigationCallback callback,int render_process_id,int render_frame_id)668 void DidNavigate(const base::WeakPtr<ServiceWorkerContextCore>& context,
669                  const GURL& origin,
670                  NavigationCallback callback,
671                  int render_process_id,
672                  int render_frame_id) {
673   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
674 
675   if (!context) {
676     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
677                             nullptr /* client_info */);
678     return;
679   }
680 
681   if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
682       render_frame_id == MSG_ROUTING_NONE) {
683     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorFailed,
684                             nullptr /* client_info */);
685     return;
686   }
687 
688   for (std::unique_ptr<ServiceWorkerContextCore::ContainerHostIterator> it =
689            context->GetClientContainerHostIterator(
690                origin, true /* include_reserved_clients */,
691                false /* include_back_forward_cached_clients */);
692        !it->IsAtEnd(); it->Advance()) {
693     ServiceWorkerContainerHost* container_host = it->GetContainerHost();
694     DCHECK(container_host->IsContainerForClient());
695     if (container_host->process_id() != render_process_id ||
696         container_host->frame_id() != render_frame_id) {
697       continue;
698     }
699     // DidNavigate must be called with a preparation complete client (the
700     // navigation was committed), but the client might not be execution ready
701     // yet (Blink hasn't yet created the Document).
702     DCHECK(container_host->is_response_committed());
703     if (!container_host->is_execution_ready()) {
704       container_host->AddExecutionReadyCallback(base::BindOnce(
705           &DidGetExecutionReadyClient, context, container_host->client_uuid(),
706           origin, std::move(callback)));
707       return;
708     }
709 
710     DidGetExecutionReadyClient(context, container_host->client_uuid(), origin,
711                                std::move(callback));
712     return;
713   }
714 
715   // If here, it means that no container_host was found, in which case, the
716   // renderer should still be informed that the window was opened.
717   std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
718                           nullptr /* client_info */);
719 }
720 
721 }  // namespace service_worker_client_utils
722 }  // namespace content
723