Commit 08f415bf authored by Sigurdur Asgeirsson's avatar Sigurdur Asgeirsson Committed by Chromium LUCI CQ

PM: Cope with MIA service worker (worker) clients.

It appears that dead shared and/or dedicated worker clients of
a shared worker may have outstanding client registrations as the
service worker starts.

Bug: 1168113
Change-Id: Ie19f3fb90c9252502a29eadfa1bec52acd6e5f64
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2637256
Commit-Queue: Sigurður Ásgeirsson <siggi@chromium.org>
Reviewed-by: default avatarPatrick Monette <pmonette@chromium.org>
Auto-Submit: Sigurður Ásgeirsson <siggi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845396}
parent 9fa16da2
......@@ -40,3 +40,20 @@ blink::SharedWorkerToken ServiceWorkerClient::GetSharedWorkerToken() const {
DCHECK_EQ(type_, blink::mojom::ServiceWorkerClientType::kSharedWorker);
return worker_token_.GetAs<blink::SharedWorkerToken>();
}
bool ServiceWorkerClient::operator<(const ServiceWorkerClient& o) const {
if (type() == o.type()) {
switch (type()) {
case blink::mojom::ServiceWorkerClientType::kWindow:
return GetRenderFrameHostId() < o.GetRenderFrameHostId();
case blink::mojom::ServiceWorkerClientType::kDedicatedWorker:
return GetDedicatedWorkerToken() < o.GetDedicatedWorkerToken();
case blink::mojom::ServiceWorkerClientType::kSharedWorker:
return GetSharedWorkerToken() < o.GetSharedWorkerToken();
case blink::mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
return false;
}
}
return type() < o.type();
}
......@@ -33,6 +33,8 @@ class ServiceWorkerClient {
blink::DedicatedWorkerToken GetDedicatedWorkerToken() const;
blink::SharedWorkerToken GetSharedWorkerToken() const;
bool operator<(const ServiceWorkerClient& other) const;
private:
// The client type.
blink::mojom::ServiceWorkerClientType type_;
......
......@@ -131,7 +131,8 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
// Posts a task to the PM graph to connect/disconnect |worker_node| with the
// If a node with |client_dedicated_worker_token| exists, 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,
......@@ -140,8 +141,9 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
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|.
// If a node with |client_shared_worker_token| exists, posts a task to
// the PM graph to connect/disconnect |worker_node| with the
// dedicated worker node associated to |client_shared_worker_token|.
void ConnectSharedWorkerClient(
WorkerNodeImpl* worker_node,
blink::SharedWorkerToken client_shared_worker_token);
......@@ -181,12 +183,17 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
bool* was_last_child_worker_connection);
// Helper functions to retrieve an existing worker node.
// Return the requested node, or nullptr if no such node registered.
WorkerNodeImpl* GetDedicatedWorkerNode(
const blink::DedicatedWorkerToken& dedicated_worker_token);
WorkerNodeImpl* GetSharedWorkerNode(
const blink::SharedWorkerToken& shared_worker_token);
WorkerNodeImpl* GetServiceWorkerNode(int64_t version_id);
#if DCHECK_IS_ON()
bool IsServiceWorkerNode(WorkerNodeImpl* worker_node);
#endif
SEQUENCE_CHECKER(sequence_checker_);
// The ID of the BrowserContext who owns the shared worker service.
......@@ -247,11 +254,12 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
frame_node_child_worker_connections_;
// Maps each dedicated worker to all its child workers.
base::flat_map<blink::DedicatedWorkerToken, base::flat_set<WorkerNodeImpl*>>
using WorkerNodeSet = base::flat_set<WorkerNodeImpl*>;
base::flat_map<blink::DedicatedWorkerToken, WorkerNodeSet>
dedicated_worker_child_workers_;
// Maps each shared worker to all its child workers.
base::flat_map<blink::SharedWorkerToken, base::flat_set<WorkerNodeImpl*>>
base::flat_map<blink::SharedWorkerToken, WorkerNodeSet>
shared_worker_child_workers_;
#if DCHECK_IS_ON()
......@@ -260,6 +268,11 @@ class WorkerWatcher : public content::DedicatedWorkerService::Observer,
// before OnClientRemoved(), or when it wasn't possible to initially attach
// a client frame node to a worker.
base::flat_map<WorkerNodeImpl*, int> detached_frame_count_per_worker_;
// Keeps track of shared and dedicated workers that were not present when
// a service worker tried to add a client relationship for them.
base::flat_map<WorkerNodeImpl*, base::flat_set<ServiceWorkerClient>>
missing_service_worker_clients_;
#endif // DCHECK_IS_ON()
DISALLOW_COPY_AND_ASSIGN(WorkerWatcher);
......
......@@ -1359,6 +1359,84 @@ TEST_F(WorkerWatcherTest, SharedWorkerCrossProcessClient) {
shared_worker_service()->DestroySharedWorker(shared_worker_token);
}
// Tests that the WorkerWatcher can handle the case where the service worker
// starts after it has been assigned a worker client, but the client has
// already died by the time the service worker starts.
TEST_F(WorkerWatcherTest, SharedWorkerStartsWithDeadWorkerClients) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kServiceWorkerRelationshipsInGraph);
int render_process_id = process_node_source()->CreateProcessNode();
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()->CreateServiceWorker();
// Create a worker client of each type and connect them to the service worker.
// 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));
// Destroy the workers before the service worker starts.
shared_worker_service()->DestroySharedWorker(shared_worker_token);
dedicated_worker_service()->DestroyDedicatedWorker(dedicated_worker_token);
// 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)](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(service_worker_node->child_workers().empty());
}));
// Disconnect the non-existent clients.
service_worker_context()->RemoveClient(service_worker_version_id,
shared_worker_client_uuid);
service_worker_context()->RemoveClient(service_worker_version_id,
dedicated_worker_client_uuid);
// No changes in the graph.
CallOnGraphAndWait(base::BindLambdaForTesting(
[process_node = process_node_source()->GetProcessNode(render_process_id),
service_worker_node =
GetServiceWorkerNode(service_worker_version_id)](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(service_worker_node->child_workers().empty());
}));
// Stop and destroy the service worker.
service_worker_context()->StopServiceWorker(service_worker_version_id);
service_worker_context()->DestroyServiceWorker(service_worker_version_id);
}
TEST_F(WorkerWatcherTest, SharedWorkerDiesAsServiceWorkerClient) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
......
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