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