Commit f9ad5552 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

[PM] Add service worker clients to the graph

Bug: 993029
Change-Id: I898a87e8ea515778f1edcb518ada06ddf3d8e8ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2355089Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800352}
parent f49e90c4
......@@ -128,6 +128,8 @@ static_library("performance_manager") {
"render_process_host_proxy.cc",
"render_process_user_data.cc",
"render_process_user_data.h",
"service_worker_client.cc",
"service_worker_client.h",
"service_worker_context_adapter.cc",
"service_worker_context_adapter.h",
"tab_helper_frame_node_source.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/service_worker_client.h"
ServiceWorkerClient::ServiceWorkerClient(
content::GlobalFrameRoutingId render_frame_host_id)
: type_(blink::mojom::ServiceWorkerClientType::kWindow),
render_frame_host_id_(render_frame_host_id) {}
ServiceWorkerClient::ServiceWorkerClient(
blink::DedicatedWorkerToken dedicated_worker_token)
: type_(blink::mojom::ServiceWorkerClientType::kDedicatedWorker),
worker_token_(dedicated_worker_token) {}
ServiceWorkerClient::ServiceWorkerClient(
blink::SharedWorkerToken shared_worker_token)
: type_(blink::mojom::ServiceWorkerClientType::kSharedWorker),
worker_token_(shared_worker_token) {}
ServiceWorkerClient::ServiceWorkerClient(const ServiceWorkerClient& other) =
default;
ServiceWorkerClient& ServiceWorkerClient::operator=(
const ServiceWorkerClient& other) = default;
ServiceWorkerClient::~ServiceWorkerClient() = default;
content::GlobalFrameRoutingId ServiceWorkerClient::GetRenderFrameHostId()
const {
DCHECK_EQ(type_, blink::mojom::ServiceWorkerClientType::kWindow);
return render_frame_host_id_;
}
blink::DedicatedWorkerToken ServiceWorkerClient::GetDedicatedWorkerToken()
const {
DCHECK_EQ(type_, blink::mojom::ServiceWorkerClientType::kDedicatedWorker);
return worker_token_.GetAs<blink::DedicatedWorkerToken>();
}
blink::SharedWorkerToken ServiceWorkerClient::GetSharedWorkerToken() const {
DCHECK_EQ(type_, blink::mojom::ServiceWorkerClientType::kSharedWorker);
return worker_token_.GetAs<blink::SharedWorkerToken>();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_SERVICE_WORKER_CLIENT_H_
#define COMPONENTS_PERFORMANCE_MANAGER_SERVICE_WORKER_CLIENT_H_
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/service_worker_client_info.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
// Represents a client of a service worker node.
//
// This class is essentially a tagged union where only the field corresponding
// to the |type()| can be accessed.
class ServiceWorkerClient {
public:
explicit ServiceWorkerClient(
content::GlobalFrameRoutingId render_frame_host_id);
explicit ServiceWorkerClient(
blink::DedicatedWorkerToken dedicated_worker_token);
explicit ServiceWorkerClient(blink::SharedWorkerToken shared_worker_token);
ServiceWorkerClient(const ServiceWorkerClient& other);
ServiceWorkerClient& operator=(const ServiceWorkerClient& other);
~ServiceWorkerClient();
blink::mojom::ServiceWorkerClientType type() const { return type_; }
content::GlobalFrameRoutingId GetRenderFrameHostId() const;
blink::DedicatedWorkerToken GetDedicatedWorkerToken() const;
blink::SharedWorkerToken GetSharedWorkerToken() const;
private:
// The client type.
blink::mojom::ServiceWorkerClientType type_;
union {
// The frame tree node ID, if this is a window client.
content::GlobalFrameRoutingId render_frame_host_id_;
// The token of the client, if this is a worker client.
content::DedicatedOrSharedWorkerToken worker_token_;
};
};
#endif // COMPONENTS_PERFORMANCE_MANAGER_SERVICE_WORKER_CLIENT_H_
......@@ -310,6 +310,15 @@ void ServiceWorkerContextAdapter::OnNoControllees(int64_t version_id,
observer.OnNoControllees(version_id, scope);
}
void ServiceWorkerContextAdapter::OnControlleeNavigationCommitted(
int64_t version_id,
const std::string& uuid,
content::GlobalFrameRoutingId render_frame_host_id) {
for (auto& observer : observer_list_)
observer.OnControlleeNavigationCommitted(version_id, uuid,
render_frame_host_id);
}
void ServiceWorkerContextAdapter::OnReportConsoleMessage(
int64_t version_id,
const GURL& scope,
......
......@@ -107,6 +107,10 @@ class ServiceWorkerContextAdapter
void OnControlleeRemoved(int64_t version_id,
const std::string& client_uuid) override;
void OnNoControllees(int64_t version_id, const GURL& scope) override;
void OnControlleeNavigationCommitted(
int64_t version_id,
const std::string& uuid,
content::GlobalFrameRoutingId render_frame_host_id) override;
void OnReportConsoleMessage(int64_t version_id,
const GURL& scope,
const content::ConsoleMessage& message) override;
......
......@@ -45,6 +45,26 @@ void DisconnectClientOnGraph(WorkerNodeImpl* worker_node,
base::Unretained(worker_node), client_frame_node));
}
// Helper function to add |client_worker_node| as a client of |worker_node| on
// the PM sequence.
void ConnectClientOnGraph(WorkerNodeImpl* worker_node,
WorkerNodeImpl* client_worker_node) {
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&WorkerNodeImpl::AddClientWorker,
base::Unretained(worker_node), client_worker_node));
}
// Helper function to remove |client_worker_node| as a client of |worker_node|
// on the PM sequence.
void DisconnectClientOnGraph(WorkerNodeImpl* worker_node,
WorkerNodeImpl* client_worker_node) {
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&WorkerNodeImpl::RemoveClientWorker,
base::Unretained(worker_node), client_worker_node));
}
// Helper function to remove |client_frame_node| as a client of all worker nodes
// in |worker_nodes| on the PM sequence.
void DisconnectClientsOnGraph(base::flat_set<WorkerNodeImpl*> worker_nodes,
......@@ -59,6 +79,20 @@ void DisconnectClientsOnGraph(base::flat_set<WorkerNodeImpl*> worker_nodes,
std::move(worker_nodes), client_frame_node));
}
// Helper function to remove |client_worker_node| as a client of all worker
// nodes in |worker_nodes| on the PM sequence.
void DisconnectClientsOnGraph(base::flat_set<WorkerNodeImpl*> worker_nodes,
WorkerNodeImpl* client_worker_node) {
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE, base::BindOnce(
[](base::flat_set<WorkerNodeImpl*> worker_nodes,
WorkerNodeImpl* client_worker_node) {
for (auto* worker_node : worker_nodes)
worker_node->RemoveClientWorker(client_worker_node);
},
std::move(worker_nodes), client_worker_node));
}
// Helper function that posts a task on the PM sequence that will invoke
// OnFinalResponseURLDetermined() on |worker_node|.
void SetFinalResponseURL(WorkerNodeImpl* worker_node, const GURL& url) {
......@@ -121,6 +155,32 @@ void WorkerWatcher::TearDown() {
}
frame_node_child_workers_.clear();
// Then clear client-child connections for dedicated workers.
for (auto& kv : dedicated_worker_child_workers_) {
const blink::DedicatedWorkerToken& dedicated_worker_token = kv.first;
base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
DCHECK(!child_workers.empty());
// Disconnect all child workers from |dedicated_worker_token|.
WorkerNodeImpl* dedicated_worker_node =
GetDedicatedWorkerNode(dedicated_worker_token);
DisconnectClientsOnGraph(std::move(child_workers), dedicated_worker_node);
}
dedicated_worker_child_workers_.clear();
// Finally, clear client-child connections for shared workers.
for (auto& kv : shared_worker_child_workers_) {
const blink::SharedWorkerToken& shared_worker_token = kv.first;
base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
DCHECK(!child_workers.empty());
// Disconnect all child workers from |shared_worker_token|.
WorkerNodeImpl* shared_worker_node =
GetSharedWorkerNode(shared_worker_token);
DisconnectClientsOnGraph(std::move(child_workers), shared_worker_node);
}
shared_worker_child_workers_.clear();
// Then clean all the worker nodes.
std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.reserve(dedicated_worker_nodes_.size() + shared_worker_nodes_.size() +
......@@ -160,8 +220,8 @@ void WorkerWatcher::OnWorkerCreated(
dedicated_worker_token, std::move(worker_node));
DCHECK(insertion_result.second);
ConnectClient(insertion_result.first->second.get(),
ancestor_render_frame_host_id);
ConnectFrameClient(insertion_result.first->second.get(),
ancestor_render_frame_host_id);
}
void WorkerWatcher::OnBeforeWorkerDestroyed(
......@@ -175,7 +235,7 @@ void WorkerWatcher::OnBeforeWorkerDestroyed(
auto worker_node = std::move(it->second);
// First disconnect the ancestor's frame node from this worker node.
DisconnectClient(worker_node.get(), ancestor_render_frame_host_id);
DisconnectFrameClient(worker_node.get(), ancestor_render_frame_host_id);
#if DCHECK_IS_ON()
DCHECK(!base::Contains(detached_frame_count_per_worker_, worker_node.get()));
......@@ -239,7 +299,8 @@ void WorkerWatcher::OnClientAdded(
content::GlobalFrameRoutingId render_frame_host_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ConnectClient(GetSharedWorkerNode(shared_worker_token), render_frame_host_id);
ConnectFrameClient(GetSharedWorkerNode(shared_worker_token),
render_frame_host_id);
}
void WorkerWatcher::OnClientRemoved(
......@@ -247,8 +308,8 @@ void WorkerWatcher::OnClientRemoved(
content::GlobalFrameRoutingId render_frame_host_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DisconnectClient(GetSharedWorkerNode(shared_worker_token),
render_frame_host_id);
DisconnectFrameClient(GetSharedWorkerNode(shared_worker_token),
render_frame_host_id);
}
void WorkerWatcher::OnVersionStartedRunning(
......@@ -256,13 +317,19 @@ void WorkerWatcher::OnVersionStartedRunning(
const content::ServiceWorkerRunningInfo& running_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto worker_node = PerformanceManagerImpl::CreateWorkerNode(
browser_context_id_, WorkerNode::WorkerType::kService,
process_node_source_->GetProcessNode(running_info.render_process_id),
running_info.token);
bool inserted =
service_worker_nodes_.emplace(version_id, std::move(worker_node)).second;
DCHECK(inserted);
auto insertion_result = service_worker_nodes_.emplace(
version_id,
PerformanceManagerImpl::CreateWorkerNode(
browser_context_id_, WorkerNode::WorkerType::kService,
process_node_source_->GetProcessNode(running_info.render_process_id),
running_info.token));
DCHECK(insertion_result.second);
// Exclusively for service workers, some notifications for clients
// (OnControlleeAdded) may have been received before the worker started.
// Add those clients to the service worker on the PM graph.
ConnectAllServiceWorkerClients(insertion_result.first->second.get(),
version_id);
}
void WorkerWatcher::OnVersionStoppedRunning(int64_t version_id) {
......@@ -271,16 +338,138 @@ void WorkerWatcher::OnVersionStoppedRunning(int64_t version_id) {
auto it = service_worker_nodes_.find(version_id);
DCHECK(it != service_worker_nodes_.end());
auto worker_node = std::move(it->second);
auto service_worker_node = std::move(it->second);
// First, disconnect all current clients of this service worker.
DisconnectAllServiceWorkerClients(service_worker_node.get(), version_id);
#if DCHECK_IS_ON()
DCHECK(!base::Contains(detached_frame_count_per_worker_, worker_node.get()));
DCHECK(!base::Contains(detached_frame_count_per_worker_,
service_worker_node.get()));
#endif // DCHECK_IS_ON()
PerformanceManagerImpl::DeleteNode(std::move(worker_node));
PerformanceManagerImpl::DeleteNode(std::move(service_worker_node));
service_worker_nodes_.erase(it);
}
void WorkerWatcher::ConnectClient(
void WorkerWatcher::OnControlleeAdded(
int64_t version_id,
const std::string& client_uuid,
const content::ServiceWorkerClientInfo& client_info) {
switch (client_info.type()) {
case blink::mojom::ServiceWorkerClientType::kWindow: {
// For window clients, it is necessary to wait until the navigation has
// committed to a render frame host.
bool inserted = client_frames_awaiting_commit_.insert(client_uuid).second;
DCHECK(inserted);
break;
}
case blink::mojom::ServiceWorkerClientType::kDedicatedWorker: {
blink::DedicatedWorkerToken dedicated_worker_token =
client_info.GetDedicatedWorkerToken();
bool inserted = service_worker_clients_[version_id]
.emplace(client_uuid, dedicated_worker_token)
.second;
DCHECK(inserted);
// If the service worker is already started, connect it to the client.
WorkerNodeImpl* service_worker_node = GetServiceWorkerNode(version_id);
if (service_worker_node)
ConnectDedicatedWorkerClient(service_worker_node,
dedicated_worker_token);
break;
}
case blink::mojom::ServiceWorkerClientType::kSharedWorker: {
blink::SharedWorkerToken shared_worker_token =
client_info.GetSharedWorkerToken();
bool inserted = service_worker_clients_[version_id]
.emplace(client_uuid, shared_worker_token)
.second;
DCHECK(inserted);
// If the service worker is already started, connect it to the client.
WorkerNodeImpl* service_worker_node = GetServiceWorkerNode(version_id);
if (service_worker_node)
ConnectSharedWorkerClient(service_worker_node, shared_worker_token);
break;
}
case blink::mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
break;
}
}
void WorkerWatcher::OnControlleeRemoved(int64_t version_id,
const std::string& client_uuid) {
// Nothing to do for a frame client whose navigation never committed.
size_t removed = client_frames_awaiting_commit_.erase(client_uuid);
if (removed) {
#if DCHECK_IS_ON()
// |client_uuid| should not be part of this service worker's clients.
auto it = service_worker_clients_.find(version_id);
if (it != service_worker_clients_.end())
DCHECK(!base::Contains(it->second, client_uuid));
#endif // DCHECK_IS_ON()
return;
}
// First get clients for this worker.
auto it = service_worker_clients_.find(version_id);
DCHECK(it != service_worker_clients_.end());
base::flat_map<std::string /*client_uuid*/, ServiceWorkerClient>& clients =
it->second;
auto it2 = clients.find(client_uuid);
DCHECK(it2 != clients.end());
const ServiceWorkerClient client = it2->second;
clients.erase(it2);
if (clients.empty())
service_worker_clients_.erase(it);
// Now disconnect the client if the service worker is still running.
WorkerNodeImpl* worker_node = GetServiceWorkerNode(version_id);
if (!worker_node)
return;
switch (client.type()) {
case blink::mojom::ServiceWorkerClientType::kWindow:
DisconnectFrameClient(worker_node, client.GetRenderFrameHostId());
break;
case blink::mojom::ServiceWorkerClientType::kDedicatedWorker:
DisconnectDedicatedWorkerClient(worker_node,
client.GetDedicatedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kSharedWorker:
DisconnectSharedWorkerClient(worker_node, client.GetSharedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
break;
}
}
void WorkerWatcher::OnControlleeNavigationCommitted(
int64_t version_id,
const std::string& client_uuid,
content::GlobalFrameRoutingId render_frame_host_id) {
size_t removed = client_frames_awaiting_commit_.erase(client_uuid);
DCHECK_EQ(removed, 1u);
bool inserted = service_worker_clients_[version_id]
.emplace(client_uuid, render_frame_host_id)
.second;
DCHECK(inserted);
WorkerNodeImpl* service_worker_node = GetServiceWorkerNode(version_id);
if (service_worker_node)
ConnectFrameClient(service_worker_node, render_frame_host_id);
}
void WorkerWatcher::ConnectFrameClient(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -294,8 +483,8 @@ void WorkerWatcher::ConnectClient(
if (!frame_node) {
RecordWorkerClientFound(false);
#if DCHECK_IS_ON()
// A call to DisconnectClient() is still expected to be received for this
// frame and worker pair.
// A call to DisconnectFrameClient() is still expected to be received for
// this worker and frame pair.
detached_frame_count_per_worker_[worker_node]++;
#endif // DCHECK_IS_ON()
return;
......@@ -314,7 +503,7 @@ void WorkerWatcher::ConnectClient(
}
}
void WorkerWatcher::DisconnectClient(
void WorkerWatcher::DisconnectFrameClient(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -332,8 +521,9 @@ void WorkerWatcher::DisconnectClient(
// possible to connect a worker to its client frame.
if (!frame_node) {
#if DCHECK_IS_ON()
// These debug only checks are used to ensure that this DisconnectClient()
// was still expected even though the client frame node no longer exist.
// These debug only checks are used to ensure that this
// DisconnectFrameClient() call was still expected even though the client
// frame node no longer exist.
auto it = detached_frame_count_per_worker_.find(worker_node);
DCHECK(it != detached_frame_count_per_worker_.end());
......@@ -355,6 +545,138 @@ void WorkerWatcher::DisconnectClient(
frame_node_source_->UnsubscribeFromFrameNode(client_render_frame_host_id);
}
void WorkerWatcher::ConnectDedicatedWorkerClient(
WorkerNodeImpl* worker_node,
blink::DedicatedWorkerToken client_dedicated_worker_token) {
DCHECK(worker_node);
ConnectClientOnGraph(worker_node,
GetDedicatedWorkerNode(client_dedicated_worker_token));
// Remember that |worker_node| is a child worker of this dedicated worker.
bool inserted = dedicated_worker_child_workers_[client_dedicated_worker_token]
.insert(worker_node)
.second;
DCHECK(inserted);
}
void WorkerWatcher::DisconnectDedicatedWorkerClient(
WorkerNodeImpl* worker_node,
blink::DedicatedWorkerToken client_dedicated_worker_token) {
DCHECK(worker_node);
// Remove |worker_node| from the set of child workers of this dedicated
// worker.
auto it = dedicated_worker_child_workers_.find(client_dedicated_worker_token);
DCHECK(it != dedicated_worker_child_workers_.end());
auto& child_workers = it->second;
size_t removed = child_workers.erase(worker_node);
DCHECK_EQ(removed, 1u);
if (child_workers.empty())
dedicated_worker_child_workers_.erase(it);
DisconnectClientOnGraph(
worker_node, GetDedicatedWorkerNode(client_dedicated_worker_token));
}
void WorkerWatcher::ConnectSharedWorkerClient(
WorkerNodeImpl* worker_node,
blink::SharedWorkerToken client_shared_worker_token) {
DCHECK(worker_node);
ConnectClientOnGraph(worker_node,
GetSharedWorkerNode(client_shared_worker_token));
// Remember that |worker_node| is a child worker of this shared worker.
bool inserted = shared_worker_child_workers_[client_shared_worker_token]
.insert(worker_node)
.second;
DCHECK(inserted);
}
void WorkerWatcher::DisconnectSharedWorkerClient(
WorkerNodeImpl* worker_node,
blink::SharedWorkerToken client_shared_worker_token) {
DCHECK(worker_node);
// Remove |worker_node| from the set of child workers of this shared worker.
auto it = shared_worker_child_workers_.find(client_shared_worker_token);
DCHECK(it != shared_worker_child_workers_.end());
auto& child_workers = it->second;
size_t removed = child_workers.erase(worker_node);
DCHECK_EQ(removed, 1u);
if (child_workers.empty())
shared_worker_child_workers_.erase(it);
DisconnectClientOnGraph(worker_node,
GetSharedWorkerNode(client_shared_worker_token));
}
void WorkerWatcher::ConnectAllServiceWorkerClients(
WorkerNodeImpl* service_worker_node,
int64_t version_id) {
// Nothing to do if there are no clients.
auto it = service_worker_clients_.find(version_id);
if (it == service_worker_clients_.end())
return;
for (const auto& kv : it->second) {
const ServiceWorkerClient& client = kv.second;
switch (client.type()) {
case blink::mojom::ServiceWorkerClientType::kWindow:
ConnectFrameClient(service_worker_node, client.GetRenderFrameHostId());
break;
case blink::mojom::ServiceWorkerClientType::kDedicatedWorker:
ConnectDedicatedWorkerClient(service_worker_node,
client.GetDedicatedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kSharedWorker:
ConnectSharedWorkerClient(service_worker_node,
client.GetSharedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
break;
}
}
}
void WorkerWatcher::DisconnectAllServiceWorkerClients(
WorkerNodeImpl* service_worker_node,
int64_t version_id) {
// Nothing to do if there are no clients.
auto it = service_worker_clients_.find(version_id);
if (it == service_worker_clients_.end())
return;
for (const auto& kv : it->second) {
const ServiceWorkerClient& client = kv.second;
switch (client.type()) {
case blink::mojom::ServiceWorkerClientType::kWindow:
DisconnectFrameClient(service_worker_node,
client.GetRenderFrameHostId());
break;
case blink::mojom::ServiceWorkerClientType::kDedicatedWorker:
DisconnectDedicatedWorkerClient(service_worker_node,
client.GetDedicatedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kSharedWorker:
DisconnectSharedWorkerClient(service_worker_node,
client.GetSharedWorkerToken());
break;
case blink::mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
break;
}
}
}
void WorkerWatcher::OnBeforeFrameNodeRemoved(
content::GlobalFrameRoutingId render_frame_host_id,
FrameNodeImpl* frame_node) {
......@@ -373,8 +695,8 @@ void WorkerWatcher::OnBeforeFrameNodeRemoved(
#if DCHECK_IS_ON()
for (WorkerNodeImpl* worker_node : child_workers) {
// A call to DisconnectClient() is still expected to be received for this
// frame to all workers in |child_workers|.
// A call to DisconnectFrameClient() is still expected to be received for
// this frame to all workers in |child_workers|.
// Note: the [] operator is intentionally used to default initialize the
// count to zero if needed.
detached_frame_count_per_worker_[worker_node]++;
......@@ -445,7 +767,6 @@ WorkerNodeImpl* WorkerWatcher::GetServiceWorkerNode(int64_t version_id) {
auto it = service_worker_nodes_.find(version_id);
if (it == service_worker_nodes_.end()) {
NOTREACHED();
return nullptr;
}
return it->second.get();
......
......@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "base/scoped_observer.h"
#include "base/sequence_checker.h"
#include "components/performance_manager/service_worker_client.h"
#include "content/public/browser/dedicated_worker_service.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/service_worker_context.h"
......@@ -31,7 +32,26 @@ class WorkerNodeImpl;
// This class keeps track of running workers of all types for a single browser
// context and handles the ownership of the worker nodes.
//
// TODO(https://crbug.com/993029): Add support for service workers.
// Most of the complexity of this class is tracking every worker's clients. Each
// type of worker handles them a bit differently.
//
// The simplest case is dedicated workers, where each worker always has exactly
// one frame client. Technically, it is possible to create a nested dedicated
// worker, but for now they are treated as child of the ancestor frame.
//
// Shared workers are quite similar to dedicated workers but they can have any
// number of clients. Also, a shared worker can temporarily appear to have no
// clients shortly after being created and just before being destroyed.
//
// Service workers are more complicated to handle. They also can have any number
// of clients, but they aren't only frames. They could also be dedicated worker
// and shared worker clients. These different types of client are tracked using
// the ServiceWorkerClient class. Also, because of the important role the
// service worker plays with frame navigations, the service worker can be
// created before its first client's navigation has committed to a
// RenderFrameHost. So when a OnControlleeAdded() notification is received for
// a client frame, it is necessary to wait until the render frame host was
// determined.
class WorkerWatcher : public content::DedicatedWorkerService::Observer,
public content::SharedWorkerService::Observer,
public content::ServiceWorkerContextObserver {
......@@ -77,20 +97,61 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
content::GlobalFrameRoutingId render_frame_host_id) override;
// content::ServiceWorkerContextObserver:
// Note: If you add a new function here, make sure it is also added to
// ServiceWorkerContextAdapter.
void OnVersionStartedRunning(
int64_t version_id,
const content::ServiceWorkerRunningInfo& running_info) override;
void OnVersionStoppedRunning(int64_t version_id) override;
void OnControlleeAdded(
int64_t version_id,
const std::string& client_uuid,
const content::ServiceWorkerClientInfo& client_info) override;
void OnControlleeRemoved(int64_t version_id,
const std::string& client_uuid) override;
void OnControlleeNavigationCommitted(
int64_t version_id,
const std::string& client_uuid,
content::GlobalFrameRoutingId render_frame_host_id) override;
private:
friend class WorkerWatcherTest;
void ConnectClient(WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
void DisconnectClient(
// Posts a task to the PM graph to connect/disconnect |worker_node| with the
// frame node associated to |client_render_frame_host_id|.
void ConnectFrameClient(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
void DisconnectFrameClient(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
// Posts a task to the PM graph to connect/disconnect |worker_node| with the
// dedicated worker node associated to |client_dedicated_worker_token|.
void ConnectDedicatedWorkerClient(
WorkerNodeImpl* worker_node,
blink::DedicatedWorkerToken client_dedicated_worker_token);
void DisconnectDedicatedWorkerClient(
WorkerNodeImpl* worker_node,
blink::DedicatedWorkerToken client_dedicated_worker_token);
// Posts a task to the PM graph to connect/disconnect |worker_node| with the
// shared worker node associated to |client_shared_worker_id|.
void ConnectSharedWorkerClient(
WorkerNodeImpl* worker_node,
blink::SharedWorkerToken client_shared_worker_id);
void DisconnectSharedWorkerClient(
WorkerNodeImpl* worker_node,
blink::SharedWorkerToken client_shared_worker_id);
// Posts a task to the PM graph to connect/disconnect |service_worker_node|
// with all of its existing clients. Called when a service worker starts/stops
// running.
void ConnectAllServiceWorkerClients(WorkerNodeImpl* service_worker_node,
int64_t version_id);
void DisconnectAllServiceWorkerClients(WorkerNodeImpl* service_worker_node,
int64_t version_id);
void OnBeforeFrameNodeRemoved(
content::GlobalFrameRoutingId render_frame_host_id,
FrameNodeImpl* frame_node);
......@@ -147,13 +208,31 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
base::flat_map<int64_t /*version_id*/, std::unique_ptr<WorkerNodeImpl>>
service_worker_nodes_;
// Maps each frame to the shared workers that this frame is a client of. This
// is used when a frame is torn down before the OnBeforeWorkerTerminated() is
// received, to ensure the deletion of the worker nodes in the right order
// (workers before frames).
// Keeps track of frame clients that are awaiting the navigation commit
// notification. Used for service workers only.
base::flat_set<std::string /*client_uuid*/> client_frames_awaiting_commit_;
// Maps each service worker to its clients.
base::flat_map<
int64_t /*version_id*/,
base::flat_map<std::string /*client_uuid*/, ServiceWorkerClient>>
service_worker_clients_;
// Maps each frame to the workers that this frame is a client of in the graph.
// This is used when a frame is torn down before the
// OnBeforeWorkerTerminated() is received, to ensure the deletion of the
// worker nodes in the right order (workers before frames).
base::flat_map<content::GlobalFrameRoutingId, base::flat_set<WorkerNodeImpl*>>
frame_node_child_workers_;
// Maps each dedicated worker to all its child workers.
base::flat_map<blink::DedicatedWorkerToken, base::flat_set<WorkerNodeImpl*>>
dedicated_worker_child_workers_;
// Maps each shared worker to all its child workers.
base::flat_map<blink::SharedWorkerToken, base::flat_set<WorkerNodeImpl*>>
shared_worker_child_workers_;
#if DCHECK_IS_ON()
// Keeps track of how many OnClientRemoved() calls are expected for an
// existing worker. This happens when OnBeforeFrameNodeRemoved() is invoked
......
......@@ -9,6 +9,7 @@
#include <vector>
#include "base/bind_helpers.h"
#include "base/guid.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
......@@ -23,6 +24,7 @@
#include "components/performance_manager/graph/worker_node_impl.h"
#include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/process_node_source.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/shared_worker_service.h"
#include "content/public/test/fake_service_worker_context.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -51,6 +53,14 @@ bool IsWorkerClient(WorkerNodeImpl* worker_node,
base::Contains(client_frame_node->child_worker_nodes(), worker_node);
}
// Helper function to check that |worker_node| and |client_worker_node| are
// correctly hooked up together.
bool IsWorkerClient(WorkerNodeImpl* worker_node,
WorkerNodeImpl* client_worker_node) {
return base::Contains(worker_node->client_workers(), client_worker_node) &&
base::Contains(client_worker_node->child_workers(), worker_node);
}
// TestDedicatedWorkerService --------------------------------------------------
// A test DedicatedWorkerService that allows to simulate creating and destroying
......@@ -158,14 +168,12 @@ class TestSharedWorkerService : public content::SharedWorkerService {
void DestroySharedWorker(const blink::SharedWorkerToken& shared_worker_token);
// Adds a new frame client to an existing worker.
void AddFrameClientToWorker(
const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id);
void AddClient(const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id);
// Removes an existing frame client from a worker.
void RemoveFrameClientFromWorker(
const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id);
void RemoveClient(const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id);
private:
base::ObserverList<Observer> observer_list_;
......@@ -239,7 +247,7 @@ void TestSharedWorkerService::DestroySharedWorker(
shared_worker_client_frames_.erase(it);
}
void TestSharedWorkerService::AddFrameClientToWorker(
void TestSharedWorkerService::AddClient(
const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id) {
// Add the frame to the set of clients for this worker.
......@@ -255,7 +263,7 @@ void TestSharedWorkerService::AddFrameClientToWorker(
observer.OnClientAdded(shared_worker_token, client_render_frame_host_id);
}
void TestSharedWorkerService::RemoveFrameClientFromWorker(
void TestSharedWorkerService::RemoveClient(
const blink::SharedWorkerToken& shared_worker_token,
content::GlobalFrameRoutingId client_render_frame_host_id) {
// Notify observers.
......@@ -290,21 +298,32 @@ class TestServiceWorkerContext : public content::FakeServiceWorkerContext {
void AddObserver(content::ServiceWorkerContextObserver* observer) override;
void RemoveObserver(content::ServiceWorkerContextObserver* observer) override;
// Starts a new service worker and returns its version ID.
int64_t StartServiceWorker(int worker_process_id);
// Creates a new service worker and returns its version ID.
int64_t CreateServiceWorker();
// Stops a service shared worker.
// Deletes an existing service worker.
void DestroyServiceWorker(int64_t version_id);
// Starts an existing service worker.
void StartServiceWorker(int64_t version_id, int worker_process_id);
// Destroys a service shared worker.
void StopServiceWorker(int64_t version_id);
// Adds a new frame client to an existing worker.
void AddFrameClientToWorker(
int64_t version_id,
content::GlobalFrameRoutingId client_render_frame_host_id);
// Adds a new client to an existing service worker and returns its generated
// client UUID.
std::string AddClient(int64_t version_id,
const content::ServiceWorkerClientInfo& client_info);
// Removes an existing frame client from a worker.
void RemoveFrameClientFromWorker(
// Removes an existing client from a worker.
void RemoveClient(int64_t version_id, const std::string& client_uuid);
// Simulates when the navigation commits, meaning that the RenderFrameHost is
// now available for a window client. Not valid for worker clients.
void OnControlleeNavigationCommitted(
int64_t version_id,
content::GlobalFrameRoutingId client_render_frame_host_id);
const std::string& client_uuid,
content::GlobalFrameRoutingId render_frame_host_id);
private:
base::ObserverList<content::ServiceWorkerContextObserver>::Unchecked
......@@ -313,10 +332,15 @@ class TestServiceWorkerContext : public content::FakeServiceWorkerContext {
// The ID that the next service worker will be assigned.
int64_t next_service_worker_instance_id_ = 0;
// Contains the set of clients for each running workers.
base::flat_map<int64_t /*version_id*/,
base::flat_set<content::GlobalFrameRoutingId>>
service_worker_client_frames_;
struct ServiceWorkerInfo {
bool is_running = false;
// Contains all the clients
base::flat_set<std::string /*client_uuid*/> clients;
};
base::flat_map<int64_t /*version_id*/, ServiceWorkerInfo>
service_worker_infos_;
};
TestServiceWorkerContext::TestServiceWorkerContext() = default;
......@@ -333,68 +357,107 @@ void TestServiceWorkerContext::RemoveObserver(
observer_list_.RemoveObserver(observer);
}
int64_t TestServiceWorkerContext::StartServiceWorker(int worker_process_id) {
int64_t TestServiceWorkerContext::CreateServiceWorker() {
// Create a new version ID and add it to the map.
GURL worker_url = GenerateWorkerUrl();
int64_t version_id = next_service_worker_instance_id_++;
bool inserted = service_worker_client_frames_.insert({version_id, {}}).second;
bool inserted = service_worker_infos_.insert({version_id, {}}).second;
DCHECK(inserted);
return version_id;
}
void TestServiceWorkerContext::DestroyServiceWorker(int64_t version_id) {
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
const ServiceWorkerInfo& info = it->second;
// Can only delete a service worker that isn't running and has no clients.
DCHECK(!info.is_running);
DCHECK(info.clients.empty());
// Remove the worker instance from the map.
service_worker_infos_.erase(it);
}
void TestServiceWorkerContext::StartServiceWorker(int64_t version_id,
int worker_process_id) {
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
ServiceWorkerInfo& info = it->second;
DCHECK(!info.is_running);
info.is_running = true;
// Notify observers.
GURL worker_url = GenerateWorkerUrl();
GURL scope_url;
for (auto& observer : observer_list_) {
observer.OnVersionStartedRunning(
version_id,
content::ServiceWorkerRunningInfo(worker_url, GURL(), worker_process_id,
blink::ServiceWorkerToken()));
}
return version_id;
}
void TestServiceWorkerContext::StopServiceWorker(int64_t version_id) {
auto it = service_worker_client_frames_.find(version_id);
DCHECK(it != service_worker_client_frames_.end());
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
ServiceWorkerInfo& info = it->second;
// A stopping worker should have no clients.
DCHECK(it->second.empty());
DCHECK(info.is_running);
info.is_running = false;
// Notify observers that the worker is terminating.
for (auto& observer : observer_list_)
observer.OnVersionStoppedRunning(version_id);
// Remove the worker instance from the map.
service_worker_client_frames_.erase(it);
}
void TestServiceWorkerContext::AddFrameClientToWorker(
std::string TestServiceWorkerContext::AddClient(
int64_t version_id,
content::GlobalFrameRoutingId client_render_frame_host_id) {
// Add the frame to the set of clients for this worker.
auto it = service_worker_client_frames_.find(version_id);
DCHECK(it != service_worker_client_frames_.end());
const content::ServiceWorkerClientInfo& client_info) {
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
ServiceWorkerInfo& info = it->second;
base::flat_set<content::GlobalFrameRoutingId>& client_frames = it->second;
bool inserted = client_frames.insert(client_render_frame_host_id).second;
std::string client_uuid = base::GenerateGUID();
bool inserted = info.clients.insert(client_uuid).second;
DCHECK(inserted);
// TODO(pmonette): Notify observers when the ServiceWorkerContextObserver
// interface supports it.
for (auto& observer : observer_list_)
observer.OnControlleeAdded(version_id, client_uuid, client_info);
return client_uuid;
}
void TestServiceWorkerContext::RemoveClient(int64_t version_id,
const std::string& client_uuid) {
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
ServiceWorkerInfo& info = it->second;
size_t removed = info.clients.erase(client_uuid);
DCHECK_EQ(removed, 1u);
for (auto& observer : observer_list_)
observer.OnControlleeRemoved(version_id, client_uuid);
}
void TestServiceWorkerContext::RemoveFrameClientFromWorker(
void TestServiceWorkerContext::OnControlleeNavigationCommitted(
int64_t version_id,
content::GlobalFrameRoutingId client_render_frame_host_id) {
// TODO(pmonette): Notify observers when the ServiceWorkerContextObserver
// interface supports it.
const std::string& client_uuid,
content::GlobalFrameRoutingId render_frame_host_id) {
auto it = service_worker_infos_.find(version_id);
DCHECK(it != service_worker_infos_.end());
ServiceWorkerInfo& info = it->second;
// Then remove the frame from the set of clients of this worker.
auto it = service_worker_client_frames_.find(version_id);
DCHECK(it != service_worker_client_frames_.end());
DCHECK(base::Contains(info.clients, client_uuid));
base::flat_set<content::GlobalFrameRoutingId>& client_frames = it->second;
size_t removed = client_frames.erase(client_render_frame_host_id);
DCHECK_EQ(removed, 1u);
for (auto& observer : observer_list_) {
observer.OnControlleeNavigationCommitted(version_id, client_uuid,
render_frame_host_id);
}
}
// TestProcessNodeSource -------------------------------------------------------
......@@ -528,8 +591,7 @@ void TestFrameNodeSource::SubscribeToFrameNode(
bool inserted =
frame_node_callbacks_
.insert(std::make_pair(
frame_node, std::move(on_before_frame_node_removed_callback)))
.emplace(frame_node, std::move(on_before_frame_node_removed_callback))
.second;
DCHECK(inserted);
}
......@@ -725,7 +787,7 @@ TEST_F(WorkerWatcherTest, SimpleDedicatedWorker) {
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
// Disconnect and clean up the dedicated worker.
dedicated_worker_service()->DestroyDedicatedWorker(token);
}
......@@ -744,8 +806,7 @@ TEST_F(WorkerWatcherTest, SimpleSharedWorker) {
shared_worker_service()->CreateSharedWorker(render_process_id);
// Connect the frame to the worker.
shared_worker_service()->AddFrameClientToWorker(shared_worker_token,
render_frame_host_id);
shared_worker_service()->AddClient(shared_worker_token, render_frame_host_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
......@@ -759,29 +820,51 @@ TEST_F(WorkerWatcherTest, SimpleSharedWorker) {
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token,
render_frame_host_id);
// Disconnect and clean up the shared worker.
shared_worker_service()->RemoveClient(shared_worker_token,
render_frame_host_id);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
}
// This test creates one service worker with one client frame.
TEST_F(WorkerWatcherTest, SimpleServiceWorker) {
TEST_F(WorkerWatcherTest, ServiceWorkerFrameClient) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the frame node.
// Create and start the service worker.
int64_t service_worker_version_id =
service_worker_context()->CreateServiceWorker();
service_worker_context()->StartServiceWorker(service_worker_version_id,
render_process_id);
// Add a frame tree node as a client of the service worker.
int frame_tree_node_id = GenerateNextId();
std::string service_worker_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(frame_tree_node_id));
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
worker_node =
GetServiceWorkerNode(service_worker_version_id)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kService);
EXPECT_EQ(worker_node->process_node(), process_node);
// The frame can not be connected to the service worker until its
// render frame host is available, which happens when the navigation
// commits.
EXPECT_TRUE(worker_node->client_frames().empty());
}));
// Now simulate the navigation commit.
content::GlobalFrameRoutingId render_frame_host_id =
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
// Create the worker.
int64_t service_worker_version_id =
service_worker_context()->StartServiceWorker(render_process_id);
// Connect the frame to the worker.
service_worker_context()->AddFrameClientToWorker(service_worker_version_id,
render_frame_host_id);
service_worker_context()->OnControlleeNavigationCommitted(
service_worker_version_id, service_worker_client_uuid,
render_frame_host_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
......@@ -792,15 +875,253 @@ TEST_F(WorkerWatcherTest, SimpleServiceWorker) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kService);
EXPECT_EQ(worker_node->process_node(), process_node);
// TODO(pmonette): Change the following to EXPECT_TRUE when the
// service worker node gets hooked up correctly.
EXPECT_FALSE(IsWorkerClient(worker_node, client_frame_node));
// Now is it correctly hooked up.
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
service_worker_context()->RemoveFrameClientFromWorker(
service_worker_version_id, render_frame_host_id);
// Disconnect and clean up the service worker.
service_worker_context()->RemoveClient(service_worker_version_id,
service_worker_client_uuid);
service_worker_context()->StopServiceWorker(service_worker_version_id);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
}
// Ensures that the WorkerWatcher handles the case where a frame with a service
// worker is created but it's navigation is never committed before the
// FrameTreeNode is destroyed.
TEST_F(WorkerWatcherTest, ServiceWorkerFrameClientDestroyedBeforeCommit) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create and start the service worker.
int64_t service_worker_version_id =
service_worker_context()->CreateServiceWorker();
service_worker_context()->StartServiceWorker(service_worker_version_id,
render_process_id);
// Add a frame tree node as a client of the service worker.
int frame_tree_node_id = GenerateNextId();
std::string service_worker_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(frame_tree_node_id));
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
worker_node =
GetServiceWorkerNode(service_worker_version_id)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(worker_node));
EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kService);
EXPECT_EQ(worker_node->process_node(), process_node);
// The frame was never added as a client of the service worker.
EXPECT_TRUE(worker_node->client_frames().empty());
}));
// Disconnect and clean up the service worker.
service_worker_context()->RemoveClient(service_worker_version_id,
service_worker_client_uuid);
service_worker_context()->StopServiceWorker(service_worker_version_id);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
}
TEST_F(WorkerWatcherTest, AllTypesOfServiceWorkerClients) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create and start the service worker.
int64_t service_worker_version_id =
service_worker_context()->CreateServiceWorker();
service_worker_context()->StartServiceWorker(service_worker_version_id,
render_process_id);
// Create a client of each type and connect them to the service worker.
// Frame client.
int frame_tree_node_id = GenerateNextId();
std::string frame_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(frame_tree_node_id));
content::GlobalFrameRoutingId render_frame_host_id =
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
service_worker_context()->OnControlleeNavigationCommitted(
service_worker_version_id, frame_client_uuid, render_frame_host_id);
// Dedicated worker client.
blink::DedicatedWorkerToken dedicated_worker_token =
dedicated_worker_service()->CreateDedicatedWorker(render_process_id,
render_frame_host_id);
std::string dedicated_worker_client_uuid =
service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(dedicated_worker_token));
// Shared worker client.
blink::SharedWorkerToken shared_worker_token =
shared_worker_service()->CreateSharedWorker(render_process_id);
std::string shared_worker_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(shared_worker_token));
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
service_worker_node = GetServiceWorkerNode(service_worker_version_id),
client_frame_node =
frame_node_source()->GetFrameNode(render_frame_host_id),
dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node =
GetSharedWorkerNode(shared_worker_token)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(service_worker_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, dedicated_worker_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, shared_worker_node));
}));
// Disconnect and clean up the service worker and its clients.
service_worker_context()->RemoveClient(service_worker_version_id,
shared_worker_client_uuid);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
service_worker_context()->RemoveClient(service_worker_version_id,
dedicated_worker_client_uuid);
dedicated_worker_service()->DestroyDedicatedWorker(dedicated_worker_token);
service_worker_context()->RemoveClient(service_worker_version_id,
frame_client_uuid);
service_worker_context()->StopServiceWorker(service_worker_version_id);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
}
// Tests that the WorkerWatcher can handle the case where the service worker
// starts after it has been assigned a client. In this case, the clients are not
// connected to the service worker until it starts. It also tests that when the
// service worker stops, its existing clients are also disconnected.
TEST_F(WorkerWatcherTest, ServiceWorkerStartsAndStopsWithExistingClients) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the worker.
int64_t service_worker_version_id =
service_worker_context()->CreateServiceWorker();
// Create a client of each type and connect them to the service worker.
// Frame client.
int frame_tree_node_id = GenerateNextId();
std::string frame_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(frame_tree_node_id));
content::GlobalFrameRoutingId render_frame_host_id =
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
service_worker_context()->OnControlleeNavigationCommitted(
service_worker_version_id, frame_client_uuid, render_frame_host_id);
// Dedicated worker client.
blink::DedicatedWorkerToken dedicated_worker_token =
dedicated_worker_service()->CreateDedicatedWorker(render_process_id,
render_frame_host_id);
std::string dedicated_worker_client_uuid =
service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(dedicated_worker_token));
// Shared worker client.
blink::SharedWorkerToken shared_worker_token =
shared_worker_service()->CreateSharedWorker(render_process_id);
std::string shared_worker_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(shared_worker_token));
// The service worker node doesn't even exist yet.
EXPECT_FALSE(GetServiceWorkerNode(service_worker_version_id));
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
frame_node = frame_node_source()->GetFrameNode(render_frame_host_id),
dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node =
GetSharedWorkerNode(shared_worker_token)](GraphImpl* graph) {
// The clients exists in the graph but they are not connected to the
// service worker.
EXPECT_TRUE(graph->NodeInGraph(frame_node));
EXPECT_TRUE(graph->NodeInGraph(dedicated_worker_node));
EXPECT_TRUE(graph->NodeInGraph(shared_worker_node));
// Note: Because a dedicated worker is always connected to a frame, this
// frame node actually has |dedicated_worker_node| as its sole client.
ASSERT_EQ(frame_node->child_worker_nodes().size(), 1u);
EXPECT_TRUE(base::Contains(frame_node->child_worker_nodes(),
dedicated_worker_node));
EXPECT_TRUE(dedicated_worker_node->child_workers().empty());
EXPECT_TRUE(shared_worker_node->child_workers().empty());
}));
// Now start the service worker.
service_worker_context()->StartServiceWorker(service_worker_version_id,
render_process_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
service_worker_node = GetServiceWorkerNode(service_worker_version_id),
frame_node = frame_node_source()->GetFrameNode(render_frame_host_id),
dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node =
GetSharedWorkerNode(shared_worker_token)](GraphImpl* graph) {
EXPECT_TRUE(graph->NodeInGraph(service_worker_node));
EXPECT_EQ(service_worker_node->worker_type(),
WorkerNode::WorkerType::kService);
EXPECT_EQ(service_worker_node->process_node(), process_node);
EXPECT_TRUE(graph->NodeInGraph(frame_node));
EXPECT_TRUE(graph->NodeInGraph(dedicated_worker_node));
EXPECT_TRUE(graph->NodeInGraph(shared_worker_node));
// Now is it correctly hooked up.
EXPECT_TRUE(IsWorkerClient(service_worker_node, frame_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, dedicated_worker_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, shared_worker_node));
}));
// Stop the service worker. All the clients will be disconnected.
service_worker_context()->StopServiceWorker(service_worker_version_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
frame_node = frame_node_source()->GetFrameNode(render_frame_host_id),
dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node =
GetSharedWorkerNode(shared_worker_token)](GraphImpl* graph) {
// The clients exists in the graph but they are not connected to the
// service worker.
EXPECT_TRUE(graph->NodeInGraph(frame_node));
EXPECT_TRUE(graph->NodeInGraph(dedicated_worker_node));
EXPECT_TRUE(graph->NodeInGraph(shared_worker_node));
// Note: Because a dedicated worker is always connected to a frame, this
// frame node actually has |dedicated_worker_node| as its sole client.
ASSERT_EQ(frame_node->child_worker_nodes().size(), 1u);
EXPECT_TRUE(base::Contains(frame_node->child_worker_nodes(),
dedicated_worker_node));
EXPECT_TRUE(dedicated_worker_node->child_workers().empty());
EXPECT_TRUE(shared_worker_node->child_workers().empty());
}));
// Disconnect and clean up the service worker and its clients
service_worker_context()->RemoveClient(service_worker_version_id,
shared_worker_client_uuid);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
service_worker_context()->RemoveClient(service_worker_version_id,
dedicated_worker_client_uuid);
dedicated_worker_service()->DestroyDedicatedWorker(dedicated_worker_token);
service_worker_context()->RemoveClient(service_worker_version_id,
frame_client_uuid);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
}
TEST_F(WorkerWatcherTest, SharedWorkerCrossProcessClient) {
......@@ -817,8 +1138,7 @@ TEST_F(WorkerWatcherTest, SharedWorkerCrossProcessClient) {
shared_worker_service()->CreateSharedWorker(worker_process_id);
// Connect the frame to the worker.
shared_worker_service()->AddFrameClientToWorker(shared_worker_token,
render_frame_host_id);
shared_worker_service()->AddClient(shared_worker_token, render_frame_host_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
......@@ -835,9 +1155,9 @@ TEST_F(WorkerWatcherTest, SharedWorkerCrossProcessClient) {
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token,
render_frame_host_id);
// Disconnect and clean up the shared worker.
shared_worker_service()->RemoveClient(shared_worker_token,
render_frame_host_id);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
}
......@@ -853,15 +1173,15 @@ TEST_F(WorkerWatcherTest, OneSharedWorkerTwoClients) {
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
shared_worker_service()->AddFrameClientToWorker(shared_worker_token,
render_frame_host_id_1);
shared_worker_service()->AddClient(shared_worker_token,
render_frame_host_id_1);
content::GlobalFrameRoutingId render_frame_host_id_2 =
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
shared_worker_service()->AddFrameClientToWorker(shared_worker_token,
render_frame_host_id_2);
shared_worker_service()->AddClient(shared_worker_token,
render_frame_host_id_2);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
......@@ -880,11 +1200,11 @@ TEST_F(WorkerWatcherTest, OneSharedWorkerTwoClients) {
EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node_2));
}));
// Disconnect and clean up the worker.
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token,
render_frame_host_id_1);
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token,
render_frame_host_id_2);
// Disconnect and clean up the shared worker.
shared_worker_service()->RemoveClient(shared_worker_token,
render_frame_host_id_1);
shared_worker_service()->RemoveClient(shared_worker_token,
render_frame_host_id_2);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
}
......@@ -900,13 +1220,13 @@ TEST_F(WorkerWatcherTest, OneClientTwoSharedWorkers) {
// Create the 2 workers and connect them to the frame.
const blink::SharedWorkerToken& shared_worker_token_1 =
shared_worker_service()->CreateSharedWorker(render_process_id);
shared_worker_service()->AddFrameClientToWorker(shared_worker_token_1,
render_frame_host_id);
shared_worker_service()->AddClient(shared_worker_token_1,
render_frame_host_id);
const blink::SharedWorkerToken& shared_worker_token_2 =
shared_worker_service()->CreateSharedWorker(render_process_id);
shared_worker_service()->AddFrameClientToWorker(shared_worker_token_2,
render_frame_host_id);
shared_worker_service()->AddClient(shared_worker_token_2,
render_frame_host_id);
// Check expectations on the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
......@@ -927,13 +1247,13 @@ TEST_F(WorkerWatcherTest, OneClientTwoSharedWorkers) {
EXPECT_TRUE(IsWorkerClient(worker_node_2, client_frame_node));
}));
// Disconnect and clean up the workers.
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token_1,
render_frame_host_id);
// Disconnect and clean up the shared workers.
shared_worker_service()->RemoveClient(shared_worker_token_1,
render_frame_host_id);
shared_worker_service()->DestroySharedWorker(shared_worker_token_1);
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token_2,
render_frame_host_id);
shared_worker_service()->RemoveClient(shared_worker_token_2,
render_frame_host_id);
shared_worker_service()->DestroySharedWorker(shared_worker_token_2);
}
......@@ -941,29 +1261,36 @@ TEST_F(WorkerWatcherTest, FrameDestroyed) {
int render_process_id = process_node_source()->CreateProcessNode();
// Create the frame node.
int frame_tree_node_id = GenerateNextId();
content::GlobalFrameRoutingId render_frame_host_id =
frame_node_source()->CreateFrameNode(
render_process_id,
process_node_source()->GetProcessNode(render_process_id));
// Create a worker of each type.
const blink::DedicatedWorkerToken& token =
const blink::DedicatedWorkerToken& dedicated_worker_token =
dedicated_worker_service()->CreateDedicatedWorker(render_process_id,
render_frame_host_id);
const blink::SharedWorkerToken& shared_worker_token =
shared_worker_service()->CreateSharedWorker(render_process_id);
int64_t service_worker_version_id =
service_worker_context()->StartServiceWorker(render_process_id);
// Connect the frame to the shared worker.
shared_worker_service()->AddFrameClientToWorker(shared_worker_token,
render_frame_host_id);
service_worker_context()->AddFrameClientToWorker(service_worker_version_id,
render_frame_host_id);
service_worker_context()->CreateServiceWorker();
service_worker_context()->StartServiceWorker(service_worker_version_id,
render_process_id);
// Connect the frame to the shared worker and the service worker. Note that it
// is already connected to the dedicated worker.
shared_worker_service()->AddClient(shared_worker_token, render_frame_host_id);
std::string service_worker_client_uuid = service_worker_context()->AddClient(
service_worker_version_id,
content::ServiceWorkerClientInfo(frame_tree_node_id));
service_worker_context()->OnControlleeNavigationCommitted(
service_worker_version_id, service_worker_client_uuid,
render_frame_host_id);
// Check that everything is wired up correctly.
CallOnGraphAndWait(base::BindLambdaForTesting(
[dedicated_worker_node = GetDedicatedWorkerNode(token),
[dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node = GetSharedWorkerNode(shared_worker_token),
service_worker_node = GetServiceWorkerNode(service_worker_version_id),
client_frame_node = frame_node_source()->GetFrameNode(
......@@ -973,16 +1300,14 @@ TEST_F(WorkerWatcherTest, FrameDestroyed) {
EXPECT_TRUE(graph->NodeInGraph(service_worker_node));
EXPECT_TRUE(IsWorkerClient(dedicated_worker_node, client_frame_node));
EXPECT_TRUE(IsWorkerClient(shared_worker_node, client_frame_node));
// TODO(pmonette): Change the following to EXPECT_TRUE when the
// service worker node gets hooked up correctly.
EXPECT_FALSE(IsWorkerClient(service_worker_node, client_frame_node));
EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));
}));
frame_node_source()->DeleteFrameNode(render_frame_host_id);
// Check that the worker is no longer connected to the deleted frame.
// Check that the workers are no longer connected to the deleted frame.
CallOnGraphAndWait(base::BindLambdaForTesting(
[dedicated_worker_node = GetDedicatedWorkerNode(token),
[dedicated_worker_node = GetDedicatedWorkerNode(dedicated_worker_token),
shared_worker_node = GetSharedWorkerNode(shared_worker_token),
service_worker_node =
GetServiceWorkerNode(service_worker_version_id)](GraphImpl* graph) {
......@@ -994,14 +1319,15 @@ TEST_F(WorkerWatcherTest, FrameDestroyed) {
EXPECT_TRUE(service_worker_node->client_frames().empty());
}));
// The watcher is still expecting a worker removed notification.
service_worker_context()->RemoveFrameClientFromWorker(
service_worker_version_id, render_frame_host_id);
// Clean up. The watcher is still expecting a worker removed notification.
service_worker_context()->RemoveClient(service_worker_version_id,
service_worker_client_uuid);
service_worker_context()->StopServiceWorker(service_worker_version_id);
shared_worker_service()->RemoveFrameClientFromWorker(shared_worker_token,
render_frame_host_id);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
shared_worker_service()->RemoveClient(shared_worker_token,
render_frame_host_id);
shared_worker_service()->DestroySharedWorker(shared_worker_token);
dedicated_worker_service()->DestroyDedicatedWorker(token);
dedicated_worker_service()->DestroyDedicatedWorker(dedicated_worker_token);
}
} // namespace performance_manager
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment