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