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