// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/performance_manager/worker_watcher.h"

#include <algorithm>
#include <string>

#include "base/memory/weak_ptr.h"
#include "base/strings/stringprintf.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/graph/worker_node.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/performance_manager/test_support/performance_manager_test_harness.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace performance_manager {

namespace {

// Generates a URL in a unique domain, which can be used to create identifiable
// origins or scope URL's for service workers.
GURL GenerateUniqueDomainUrl() {
  static int next_id = 0;
  return GURL(base::StringPrintf("https://www.foo%d.org", next_id++));
}

// Helper function to check that |worker_node| and |client_frame_node| are
// correctly hooked up together.
bool IsWorkerClient(base::WeakPtr<WorkerNode> worker_node,
                    base::WeakPtr<FrameNode> client_frame_node) {
  return std::ranges::contains(worker_node->GetClientFrames(),
                               client_frame_node.get()) &&
         std::ranges::contains(client_frame_node->GetChildWorkerNodes(),
                               worker_node.get());
}

// Helper function to check that |worker_node| and |client_worker_node| are
// correctly hooked up together.
bool IsWorkerClient(base::WeakPtr<WorkerNode> worker_node,
                    base::WeakPtr<WorkerNode> client_worker_node) {
  return std::ranges::contains(worker_node->GetClientWorkers(),
                               client_worker_node.get()) &&
         std::ranges::contains(client_worker_node->GetChildWorkers(),
                               worker_node.get());
}

// Helpers methods for retrieving nodes from the graph.

base::WeakPtr<ProcessNode> GetProcessNode(
    content::RenderProcessHost* render_process_host) {
  return PerformanceManager::GetProcessNodeForRenderProcessHost(
      render_process_host);
}

base::WeakPtr<FrameNode> GetFrameNode(
    content::RenderFrameHost* render_frame_host) {
  return PerformanceManager::GetFrameNodeForRenderFrameHost(render_frame_host);
}

base::WeakPtr<WorkerNode> GetWorkerNode(const blink::WorkerToken& token) {
  return PerformanceManager::GetWorkerNodeForToken(token);
}

}  // namespace

class WorkerWatcherTest : public PerformanceManagerTestHarness {};

// This test creates one dedicated worker with a frame client.
TEST_F(WorkerWatcherTest, SimpleDedicatedWorker) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create the worker.
  const auto origin = url::Origin::Create(GenerateUniqueDomainUrl());
  const blink::DedicatedWorkerToken token =
      dedicated_worker_factory()->CreateDedicatedWorker(
          process_node.get(), client_frame_node.get(), origin);

  base::WeakPtr<WorkerNode> worker_node = GetWorkerNode(token);
  ASSERT_TRUE(worker_node);

  // Check the worker node's properties on the graph.
  Graph* graph = PerformanceManager::GetGraph();
  EXPECT_EQ(worker_node->GetGraph(), graph);
  EXPECT_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
  EXPECT_EQ(worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(worker_node->GetOrigin(), origin);
  // Script URL not available until script loads.
  EXPECT_FALSE(worker_node->GetURL().is_valid());
  EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));

  dedicated_worker_factory()->DestroyDedicatedWorker(token);
}

TEST_F(WorkerWatcherTest, NestedDedicatedWorker) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> ancestor_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(ancestor_frame_node);

  // Create the parent worker.
  const auto parent_origin = url::Origin::Create(GenerateUniqueDomainUrl());
  const blink::DedicatedWorkerToken parent_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(
          process_node.get(), ancestor_frame_node.get(), parent_origin);

  base::WeakPtr<WorkerNode> parent_worker_node =
      GetWorkerNode(parent_worker_token);
  ASSERT_TRUE(parent_worker_node);

  // Create the nested worker with an opaque origin derived from the parent
  // origin.
  const auto nested_origin = url::Origin::Resolve(GURL(), parent_origin);
  const blink::DedicatedWorkerToken nested_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(
          process_node.get(), parent_worker_node.get(), nested_origin);

  base::WeakPtr<WorkerNode> nested_worker_node =
      GetWorkerNode(nested_worker_token);
  ASSERT_TRUE(nested_worker_node);

  // Check the worker node's properties on the graph.
  Graph* graph = PerformanceManager::GetGraph();
  EXPECT_EQ(nested_worker_node->GetGraph(), graph);
  EXPECT_EQ(nested_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kDedicated);
  ASSERT_TRUE(process_node);
  EXPECT_EQ(nested_worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(nested_worker_node->GetOrigin(), nested_origin);
  // Script URL not available until script loads.
  EXPECT_FALSE(nested_worker_node->GetURL().is_valid());
  // The ancestor frame is not directly a client of the nested worker.
  EXPECT_FALSE(IsWorkerClient(nested_worker_node, ancestor_frame_node));
  EXPECT_TRUE(IsWorkerClient(nested_worker_node, parent_worker_node));

  // Disconnect and clean up the dedicated workers.
  dedicated_worker_factory()->DestroyDedicatedWorker(nested_worker_token);
  EXPECT_FALSE(nested_worker_node);
  dedicated_worker_factory()->DestroyDedicatedWorker(parent_worker_token);
  EXPECT_FALSE(parent_worker_node);
}

// This test creates one shared worker with one client frame.
TEST_F(WorkerWatcherTest, SimpleSharedWorker) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);
  content::GlobalRenderFrameHostId client_render_frame_host_id =
      main_rfh()->GetGlobalId();

  // Create the worker.
  const auto origin = url::Origin::Create(GenerateUniqueDomainUrl());
  const blink::SharedWorkerToken shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get(), origin);

  // Connect the frame to the worker.
  shared_worker_factory()->AddClient(shared_worker_token,
                                     client_render_frame_host_id);

  // Check expectations on the graph.
  base::WeakPtr<WorkerNode> worker_node = GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(worker_node);

  // Check the worker node's properties on the graph.
  Graph* graph = PerformanceManager::GetGraph();
  EXPECT_EQ(worker_node->GetGraph(), graph);
  EXPECT_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kShared);
  EXPECT_EQ(worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(worker_node->GetOrigin(), origin);
  // Script URL not available until script loads.
  EXPECT_FALSE(worker_node->GetURL().is_valid());
  EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));

  // Clean up.
  shared_worker_factory()->RemoveClient(shared_worker_token,
                                        client_render_frame_host_id);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
}

// This test creates one service worker with one client frame.
TEST_F(WorkerWatcherTest, ServiceWorkerFrameClient) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create and start the service worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  const GURL worker_url = ServiceWorkerFactory::GenerateWorkerUrl();
  const GURL scope_url = GenerateUniqueDomainUrl();
  // Make sure origins created from `worker_url` and `scope_url` can't be
  // confused.
  EXPECT_NE(url::Origin::Create(worker_url), url::Origin::Create(scope_url));

  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(
          service_worker_version_id, process_node.get(), worker_url, scope_url);
  base::WeakPtr<WorkerNode> worker_node = GetWorkerNode(service_worker_token);
  ASSERT_TRUE(worker_node);

  // Add a window client of the service worker.
  std::string service_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(service_worker_client_uuid.empty());

  // Check the worker node's properties on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(worker_node->GetGraph(), graph);
  EXPECT_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kService);
  EXPECT_EQ(worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(worker_node->GetOrigin(), url::Origin::Create(scope_url));
  EXPECT_EQ(worker_node->GetURL(), worker_url);

  // The frame can not be connected to the service worker until its
  // RenderFrameHost is available, which happens when the navigation
  // commits.
  EXPECT_TRUE(worker_node->GetClientFrames().empty());

  // Now simulate the navigation commit.
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, service_worker_client_uuid,
      client_frame_node.get());

  // Check the worker node's properties on the graph.
  EXPECT_EQ(worker_node->GetGraph(), graph);
  EXPECT_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kService);
  EXPECT_EQ(worker_node->GetProcessNode(), process_node.get());
  EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node));

  // Disconnect and clean up the service worker.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         service_worker_client_uuid);
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);

  EXPECT_FALSE(worker_node);
}

// Ensures that the WorkerWatcher handles the case where a frame with a service
// worker is (briefly?) an uncommitted client of two versions. This presumably
// happens on version update or some such, or perhaps when a frame is a
// bona-fide client of two service workers. Apparently this happens quite
// rarely in the field.
TEST_F(WorkerWatcherTest, ServiceWorkerFrameClientOfTwoWorkers) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create and start both service workers.
  int64_t first_service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  const GURL first_worker_url = ServiceWorkerFactory::GenerateWorkerUrl();
  const GURL first_scope_url = GenerateUniqueDomainUrl();
  blink::ServiceWorkerToken first_service_worker_token =
      service_worker_factory()->StartServiceWorker(
          first_service_worker_version_id, process_node.get(), first_worker_url,
          first_scope_url);
  base::WeakPtr<WorkerNode> first_service_worker_node =
      GetWorkerNode(first_service_worker_token);
  ASSERT_TRUE(first_service_worker_node);

  int64_t second_service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  const GURL second_worker_url = ServiceWorkerFactory::GenerateWorkerUrl();
  const GURL second_scope_url = GenerateUniqueDomainUrl();
  blink::ServiceWorkerToken second_service_worker_token =
      service_worker_factory()->StartServiceWorker(
          second_service_worker_version_id, process_node.get(),
          second_worker_url, second_scope_url);
  base::WeakPtr<WorkerNode> second_service_worker_node =
      GetWorkerNode(second_service_worker_token);
  ASSERT_TRUE(second_service_worker_node);

  // Make sure origins created from `worker_url` and `scope_url` can't be
  // confused.
  EXPECT_NE(url::Origin::Create(first_worker_url),
            url::Origin::Create(first_scope_url));
  EXPECT_NE(url::Origin::Create(second_worker_url),
            url::Origin::Create(second_scope_url));

  // Add a window client of the service worker twice.
  std::string service_worker_client_uuid = service_worker_factory()->AddClient(
      first_service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(service_worker_client_uuid.empty());
  EXPECT_FALSE(service_worker_factory()
                   ->AddClient(second_service_worker_version_id,
                               client_frame_node.get(),
                               service_worker_client_uuid)
                   .empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(first_service_worker_node->GetGraph(), graph);
  EXPECT_EQ(first_service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(first_service_worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(first_service_worker_node->GetOrigin(),
            url::Origin::Create(first_scope_url));
  EXPECT_EQ(first_service_worker_node->GetURL(), first_worker_url);
  // The frame was never added as a client of the service worker.
  EXPECT_TRUE(first_service_worker_node->GetClientFrames().empty());

  EXPECT_EQ(second_service_worker_node->GetGraph(), graph);
  EXPECT_EQ(second_service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(second_service_worker_node->GetProcessNode(), process_node.get());
  EXPECT_EQ(second_service_worker_node->GetOrigin(),
            url::Origin::Create(second_scope_url));
  EXPECT_EQ(second_service_worker_node->GetURL(), second_worker_url);
  // The frame was never added as a client of the service worker.
  EXPECT_TRUE(second_service_worker_node->GetClientFrames().empty());

  // Disconnect and clean up the service worker.
  service_worker_factory()->RemoveClient(first_service_worker_version_id,
                                         service_worker_client_uuid);
  service_worker_factory()->StopServiceWorker(first_service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(
      first_service_worker_version_id);
  EXPECT_FALSE(first_service_worker_node);

  service_worker_factory()->RemoveClient(second_service_worker_version_id,
                                         service_worker_client_uuid);
  service_worker_factory()->StopServiceWorker(second_service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(
      second_service_worker_version_id);
  EXPECT_FALSE(second_service_worker_node);
}

// Ensures that the WorkerWatcher handles the case where a frame with a service
// worker has a double client relationship with a service worker.
// This appears to be happening out in the real world, if quite rarely.
// See https://crbug.com/1143281#c33.
TEST_F(WorkerWatcherTest, ServiceWorkerTwoFrameClientRelationships) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create and start a service worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  // Add a window client of the service worker.
  std::string first_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(first_client_uuid.empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  // The frame was not yet added as a client.
  EXPECT_TRUE(service_worker_node->GetClientFrames().empty());

  // Add a second client relationship between the same two entities.
  std::string second_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(second_client_uuid.empty());

  // Now simulate the navigation commit for the first client relationship.
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, first_client_uuid, client_frame_node.get());

  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(1u, service_worker_node->GetClientFrames().size());
  EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));

  // Now simulate the navigation commit for the second client relationship.
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, second_client_uuid, client_frame_node.get());

  // Verify that the graph is still the same.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(1u, service_worker_node->GetClientFrames().size());
  EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));

  // Remove the first client relationship.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         first_client_uuid);

  // Verify that the graph is still the same.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(1u, service_worker_node->GetClientFrames().size());
  EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));

  // Remove the second client relationship.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         second_client_uuid);

  // Verify that the frame is no longer a client of the service worker.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_TRUE(service_worker_node->GetClientFrames().empty());

  // Teardown.
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);
}

// 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) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create and start the service worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  // Add a window client of the service worker.
  std::string service_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(service_worker_client_uuid.empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(service_worker_node->GetProcessNode(), process_node.get());

  // The frame was never added as a client of the service worker.
  EXPECT_TRUE(service_worker_node->GetClientFrames().empty());

  // Disconnect and clean up the service worker.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         service_worker_client_uuid);
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);
}

TEST_F(WorkerWatcherTest, AllTypesOfServiceWorkerClients) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);

  // Create and start the service worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  // Create a client of each type and connect them to the service worker.

  // Frame client.
  std::string frame_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(frame_client_uuid.empty());
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, frame_client_uuid, client_frame_node.get());

  // Dedicated worker client.
  blink::DedicatedWorkerToken dedicated_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(
          process_node.get(), client_frame_node.get());
  base::WeakPtr<WorkerNode> dedicated_worker_node =
      GetWorkerNode(dedicated_worker_token);
  ASSERT_TRUE(dedicated_worker_node);
  std::string dedicated_worker_client_uuid =
      service_worker_factory()->AddClient(service_worker_version_id,
                                          dedicated_worker_node.get());
  ASSERT_FALSE(dedicated_worker_client_uuid.empty());

  // Shared worker client.
  blink::SharedWorkerToken shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  base::WeakPtr<WorkerNode> shared_worker_node =
      GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(shared_worker_node);
  std::string shared_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, shared_worker_node.get());
  ASSERT_FALSE(shared_worker_client_uuid.empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  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_factory()->RemoveClient(service_worker_version_id,
                                         shared_worker_client_uuid);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         dedicated_worker_client_uuid);
  dedicated_worker_factory()->DestroyDedicatedWorker(dedicated_worker_token);
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         frame_client_uuid);

  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);
}

// 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) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(frame_node);

  // Create the worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();

  // Create a client of each type and connect them to the service worker.

  // Frame client.
  std::string frame_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, frame_node.get());
  ASSERT_FALSE(frame_client_uuid.empty());
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, frame_client_uuid, frame_node.get());

  // Dedicated worker client.
  blink::DedicatedWorkerToken dedicated_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(process_node.get(),
                                                        frame_node.get());
  base::WeakPtr<WorkerNode> dedicated_worker_node =
      GetWorkerNode(dedicated_worker_token);
  ASSERT_TRUE(dedicated_worker_node);
  std::string dedicated_worker_client_uuid =
      service_worker_factory()->AddClient(service_worker_version_id,
                                          dedicated_worker_node.get());
  ASSERT_FALSE(dedicated_worker_client_uuid.empty());

  // Shared worker client.
  blink::SharedWorkerToken shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  base::WeakPtr<WorkerNode> shared_worker_node =
      GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(shared_worker_node);
  std::string shared_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, shared_worker_node.get());
  ASSERT_FALSE(shared_worker_client_uuid.empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  // The clients exists in the graph but they are not connected to the
  // service worker.
  EXPECT_EQ(frame_node->GetGraph(), graph);
  EXPECT_EQ(dedicated_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetGraph(), graph);

  // Note: Because a dedicated worker is always connected to a frame, this
  // frame node actually has |dedicated_worker_node| as its sole client.
  EXPECT_THAT(frame_node->GetChildWorkerNodes(),
              ::testing::ElementsAre(dedicated_worker_node.get()));
  EXPECT_TRUE(dedicated_worker_node->GetChildWorkers().empty());
  EXPECT_TRUE(shared_worker_node->GetChildWorkers().empty());

  // Now start the service worker.
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  // Check expectations on the graph.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(service_worker_node->GetProcessNode(), process_node.get());

  EXPECT_EQ(frame_node->GetGraph(), graph);
  EXPECT_EQ(dedicated_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetGraph(), graph);

  // 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_factory()->StopServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);

  // Check expectations on the graph.

  // The clients exists in the graph but they are not connected to the
  // service worker.
  EXPECT_EQ(frame_node->GetGraph(), graph);
  EXPECT_EQ(dedicated_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetGraph(), graph);

  // Note: Because a dedicated worker is always connected to a frame, this
  // frame node actually has |dedicated_worker_node| as its sole client.
  EXPECT_THAT(frame_node->GetChildWorkerNodes(),
              ::testing::ElementsAre(dedicated_worker_node.get()));
  EXPECT_TRUE(dedicated_worker_node->GetChildWorkers().empty());
  EXPECT_TRUE(shared_worker_node->GetChildWorkers().empty());

  // Disconnect and clean up the service worker and its clients
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         shared_worker_client_uuid);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         dedicated_worker_client_uuid);
  dedicated_worker_factory()->DestroyDedicatedWorker(dedicated_worker_token);
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         frame_client_uuid);

  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
}

// 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) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(frame_node);

  // Create the service worker.
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();

  // Create a worker client of each type and connect them to the service worker
  // before starting it.

  // Dedicated worker client.
  blink::DedicatedWorkerToken dedicated_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(process_node.get(),
                                                        frame_node.get());
  base::WeakPtr<WorkerNode> dedicated_worker_node =
      GetWorkerNode(dedicated_worker_token);
  ASSERT_TRUE(dedicated_worker_node);
  std::string dedicated_worker_client_uuid =
      service_worker_factory()->AddClient(service_worker_version_id,
                                          dedicated_worker_node.get());
  ASSERT_FALSE(dedicated_worker_client_uuid.empty());

  // Shared worker client.
  blink::SharedWorkerToken shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  base::WeakPtr<WorkerNode> shared_worker_node =
      GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(shared_worker_node);
  std::string shared_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, shared_worker_node.get());
  ASSERT_FALSE(shared_worker_client_uuid.empty());

  // Destroy the workers before the service worker starts.
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  EXPECT_FALSE(shared_worker_node);
  dedicated_worker_factory()->DestroyDedicatedWorker(dedicated_worker_token);
  EXPECT_FALSE(dedicated_worker_node);

  // Now start the service worker.
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(service_worker_node->GetProcessNode(), process_node.get());
  EXPECT_TRUE(service_worker_node->GetChildWorkers().empty());

  // Disconnect the non-existent clients.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         shared_worker_client_uuid);
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         dedicated_worker_client_uuid);

  // No changes in the graph.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_EQ(service_worker_node->GetProcessNode(), process_node.get());
  EXPECT_TRUE(service_worker_node->GetChildWorkers().empty());

  // Stop and destroy the service worker.
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);
}

TEST_F(WorkerWatcherTest, SharedWorkerDiesAsServiceWorkerClient) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  ASSERT_TRUE(process_node);

  // Create the shared and service workers.
  const blink::SharedWorkerToken& shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  base::WeakPtr<WorkerNode> shared_worker_node =
      GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(shared_worker_node);

  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);
  ASSERT_TRUE(service_worker_node);

  std::string service_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, shared_worker_node.get());
  ASSERT_FALSE(service_worker_client_uuid.empty());

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  EXPECT_EQ(shared_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kShared);
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_TRUE(IsWorkerClient(service_worker_node, shared_worker_node));

  // Destroy the shared worker while it still has a client registration
  // against the service worker.
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  ASSERT_FALSE(shared_worker_node);
  ASSERT_TRUE(service_worker_node);

  // Check expectations on the graph again.
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetWorkerType(),
            WorkerNode::WorkerType::kService);
  EXPECT_TRUE(service_worker_node->GetClientWorkers().empty());

  // Issue the trailing service worker client removal.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         service_worker_client_uuid);

  // Stop and destroy the service worker.
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  EXPECT_FALSE(service_worker_node);
}

TEST_F(WorkerWatcherTest, OneSharedWorkerTwoClients) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node_1 = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node_1 = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node_1);
  ASSERT_TRUE(client_frame_node_1);
  content::GlobalRenderFrameHostId client_render_frame_host_id_1 =
      main_rfh()->GetGlobalId();

  auto web_contents_2 = CreateTestWebContents();
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents_2.get(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node_2 =
      GetProcessNode(web_contents_2->GetPrimaryMainFrame()->GetProcess());
  base::WeakPtr<FrameNode> client_frame_node_2 =
      GetFrameNode(web_contents_2->GetPrimaryMainFrame());
  ASSERT_TRUE(process_node_2);
  ASSERT_TRUE(client_frame_node_2);
  content::GlobalRenderFrameHostId client_render_frame_host_id_2 =
      web_contents_2->GetPrimaryMainFrame()->GetGlobalId();

  ASSERT_NE(client_frame_node_1.get(), client_frame_node_2.get());

  // Create the worker.
  const blink::SharedWorkerToken& shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node_1.get());
  base::WeakPtr<WorkerNode> worker_node = GetWorkerNode(shared_worker_token);
  ASSERT_TRUE(worker_node);

  // Create 2 client frame nodes and connect them to the worker.
  shared_worker_factory()->AddClient(shared_worker_token,
                                     client_render_frame_host_id_1);
  shared_worker_factory()->AddClient(shared_worker_token,
                                     client_render_frame_host_id_2);

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();
  EXPECT_EQ(worker_node->GetGraph(), graph);
  EXPECT_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kShared);

  // Check frame 1.
  EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node_1));

  // Check frame 2.
  EXPECT_TRUE(IsWorkerClient(worker_node, client_frame_node_2));

  // Disconnect and clean up the shared worker.
  shared_worker_factory()->RemoveClient(shared_worker_token,
                                        client_render_frame_host_id_1);
  shared_worker_factory()->RemoveClient(shared_worker_token,
                                        client_render_frame_host_id_2);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  EXPECT_FALSE(worker_node);
}

TEST_F(WorkerWatcherTest, OneClientTwoSharedWorkers) {
  SetContents(CreateTestWebContents());

  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);
  content::GlobalRenderFrameHostId client_render_frame_host_id =
      main_rfh()->GetGlobalId();

  // Create the 2 workers and connect them to the frame.
  const blink::SharedWorkerToken& shared_worker_token_1 =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  shared_worker_factory()->AddClient(shared_worker_token_1,
                                     client_render_frame_host_id);
  base::WeakPtr<WorkerNode> shared_worker_node_1 =
      GetWorkerNode(shared_worker_token_1);
  ASSERT_TRUE(shared_worker_node_1);

  const blink::SharedWorkerToken& shared_worker_token_2 =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  shared_worker_factory()->AddClient(shared_worker_token_2,
                                     client_render_frame_host_id);
  base::WeakPtr<WorkerNode> shared_worker_node_2 =
      GetWorkerNode(shared_worker_token_2);
  ASSERT_TRUE(shared_worker_node_2);

  // Check expectations on the graph.
  Graph* graph = PerformanceManager::GetGraph();

  // Check worker 1.
  EXPECT_EQ(shared_worker_node_1->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node_1->GetWorkerType(),
            WorkerNode::WorkerType::kShared);
  EXPECT_TRUE(IsWorkerClient(shared_worker_node_1, client_frame_node));

  // Check worker 2.
  EXPECT_EQ(shared_worker_node_2->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node_2->GetWorkerType(),
            WorkerNode::WorkerType::kShared);
  EXPECT_TRUE(IsWorkerClient(shared_worker_node_2, client_frame_node));

  // Disconnect and clean up the shared workers.
  shared_worker_factory()->RemoveClient(shared_worker_token_1,
                                        client_render_frame_host_id);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token_1);
  EXPECT_FALSE(shared_worker_node_1);

  shared_worker_factory()->RemoveClient(shared_worker_token_2,
                                        client_render_frame_host_id);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token_2);
  EXPECT_FALSE(shared_worker_node_2);
}

TEST_F(WorkerWatcherTest, FrameDestroyed) {
  SetContents(CreateTestWebContents());
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.foo.com/"));

  // This test uses a navigation to destroy the frame. The BackForwardCache
  // would prevent that.
  content::DisableBackForwardCacheForTesting(
      web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);

  base::WeakPtr<ProcessNode> process_node = GetProcessNode(process());
  base::WeakPtr<FrameNode> client_frame_node = GetFrameNode(main_rfh());
  ASSERT_TRUE(process_node);
  ASSERT_TRUE(client_frame_node);
  content::GlobalRenderFrameHostId render_frame_host_id =
      main_rfh()->GetGlobalId();

  // Create a worker of each type.
  const blink::DedicatedWorkerToken& dedicated_worker_token =
      dedicated_worker_factory()->CreateDedicatedWorker(
          process_node.get(), client_frame_node.get());
  const blink::SharedWorkerToken& shared_worker_token =
      shared_worker_factory()->CreateSharedWorker(process_node.get());
  int64_t service_worker_version_id =
      service_worker_factory()->CreateServiceWorker();
  blink::ServiceWorkerToken service_worker_token =
      service_worker_factory()->StartServiceWorker(service_worker_version_id,
                                                   process_node.get());

  // Connect the frame to the shared worker and the service worker. Note that it
  // is already connected to the dedicated worker.
  shared_worker_factory()->AddClient(shared_worker_token, render_frame_host_id);
  std::string service_worker_client_uuid = service_worker_factory()->AddClient(
      service_worker_version_id, client_frame_node.get());
  ASSERT_FALSE(service_worker_client_uuid.empty());
  service_worker_factory()->OnControlleeNavigationCommitted(
      service_worker_version_id, service_worker_client_uuid,
      client_frame_node.get());

  // Check that everything is wired up correctly.
  Graph* graph = PerformanceManager::GetGraph();

  base::WeakPtr<WorkerNode> dedicated_worker_node =
      GetWorkerNode(dedicated_worker_token);
  base::WeakPtr<WorkerNode> shared_worker_node =
      GetWorkerNode(shared_worker_token);
  base::WeakPtr<WorkerNode> service_worker_node =
      GetWorkerNode(service_worker_token);

  ASSERT_TRUE(dedicated_worker_node);
  ASSERT_TRUE(shared_worker_node);
  ASSERT_TRUE(service_worker_node);

  EXPECT_EQ(dedicated_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_TRUE(IsWorkerClient(dedicated_worker_node, client_frame_node));
  EXPECT_TRUE(IsWorkerClient(shared_worker_node, client_frame_node));
  EXPECT_TRUE(IsWorkerClient(service_worker_node, client_frame_node));

  // Navigate the main frame to destroy it. But keep the renderer alive
  content::RenderProcessHost* render_process_host = process();
  render_process_host->AddPendingView();
  content::NavigationSimulator::NavigateAndCommitFromBrowser(
      web_contents(), GURL("https://www.bar.com/"));
  ASSERT_FALSE(client_frame_node);

  // Check that the workers are no longer connected to the deleted frame.
  ASSERT_TRUE(dedicated_worker_node);
  ASSERT_TRUE(shared_worker_node);
  ASSERT_TRUE(service_worker_node);
  EXPECT_EQ(dedicated_worker_node->GetGraph(), graph);
  EXPECT_EQ(shared_worker_node->GetGraph(), graph);
  EXPECT_EQ(service_worker_node->GetGraph(), graph);
  EXPECT_TRUE(dedicated_worker_node->GetClientFrames().empty());
  EXPECT_TRUE(shared_worker_node->GetClientFrames().empty());
  EXPECT_TRUE(service_worker_node->GetClientFrames().empty());

  // Clean up. The watcher is still expecting a worker removed notification.
  service_worker_factory()->RemoveClient(service_worker_version_id,
                                         service_worker_client_uuid);
  service_worker_factory()->StopServiceWorker(service_worker_version_id);
  service_worker_factory()->DestroyServiceWorker(service_worker_version_id);
  shared_worker_factory()->RemoveClient(shared_worker_token,
                                        render_frame_host_id);
  shared_worker_factory()->DestroySharedWorker(shared_worker_token);
  dedicated_worker_factory()->DestroyDedicatedWorker(dedicated_worker_token);

  EXPECT_FALSE(dedicated_worker_node);
  EXPECT_FALSE(shared_worker_node);
  EXPECT_FALSE(service_worker_node);

  render_process_host->RemovePendingView();
}

}  // namespace performance_manager
