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

[PM] Add support for dedicated workers

The WorkerWatcher observes dedicated workers through the
DedicatedWorkerService interface.

Change-Id: If62d911e500def7ebb905311485a8d89eeaca8df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992208Reviewed-by: default avatarSigurður Ásgeirsson <siggi@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736411}
parent ffe4fb6e
......@@ -4,6 +4,8 @@
#include "components/performance_manager/performance_manager_registry_impl.h"
#include <utility>
#include "base/stl_util.h"
#include "components/performance_manager/performance_manager_tab_helper.h"
#include "components/performance_manager/public/performance_manager.h"
......@@ -90,12 +92,14 @@ void PerformanceManagerRegistryImpl::CreateProcessNodeForRenderProcessHost(
void PerformanceManagerRegistryImpl::NotifyBrowserContextAdded(
content::BrowserContext* browser_context) {
content::StoragePartition* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(browser_context);
std::unique_ptr<WorkerWatcher> worker_watcher =
std::make_unique<WorkerWatcher>(
browser_context->UniqueId(),
content::BrowserContext::GetDefaultStoragePartition(browser_context)
->GetSharedWorkerService(),
&process_node_source_, &frame_node_source_);
storage_partition->GetDedicatedWorkerService(),
storage_partition->GetSharedWorkerService(), &process_node_source_,
&frame_node_source_);
auto result = browser_contexts_with_worker_watcher_.insert(
{browser_context->UniqueId(), std::move(worker_watcher)});
DCHECK(result.second);
......
......@@ -330,13 +330,7 @@ int64_t PerformanceManagerTabHelper::LastNavigationId() const {
FrameNodeImpl* PerformanceManagerTabHelper::GetFrameNode(
content::RenderFrameHost* render_frame_host) {
auto it = frames_.find(render_frame_host);
if (it == frames_.end()) {
// Avoid dereferencing an invalid iterator because it produces hard to debug
// crashes.
NOTREACHED();
return nullptr;
}
return it->second.get();
return it != frames_.end() ? it->second.get() : nullptr;
}
void PerformanceManagerTabHelper::AddObserver(Observer* observer) {
......
......@@ -83,7 +83,8 @@ class PerformanceManagerTabHelper
void SetUkmSourceIdForTesting(ukm::SourceId id) { ukm_source_id_ = id; }
// Retrieves the frame node associated with |render_frame_host|.
// Retrieves the frame node associated with |render_frame_host|. Returns
// nullptr if none exist for that frame.
FrameNodeImpl* GetFrameNode(content::RenderFrameHost* render_frame_host);
class Observer : public base::CheckedObserver {
......
......@@ -223,4 +223,28 @@ TEST_F(PerformanceManagerTabHelperTest, PageIsAudible) {
ExpectPageIsAudible(false);
}
TEST_F(PerformanceManagerTabHelperTest, GetFrameNode) {
SetContents(CreateTestWebContents());
auto* tab_helper =
PerformanceManagerTabHelper::FromWebContents(web_contents());
ASSERT_TRUE(tab_helper);
// GetFrameNode() can return nullptr. In this test, it is achieved by using an
// empty RenderFrameHost.
auto* empty_frame = web_contents()->GetMainFrame();
DCHECK(empty_frame);
auto* empty_frame_node = tab_helper->GetFrameNode(empty_frame);
EXPECT_FALSE(empty_frame_node);
// This navigation will create a frame node.
auto* new_frame = content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL(kParentUrl));
DCHECK(new_frame);
auto* new_frame_node = tab_helper->GetFrameNode(new_frame);
EXPECT_TRUE(new_frame_node);
}
} // namespace performance_manager
......@@ -47,21 +47,27 @@ void RemoveWorkersFromFrameNode(
WorkerWatcher::WorkerWatcher(
const std::string& browser_context_id,
content::DedicatedWorkerService* dedicated_worker_service,
content::SharedWorkerService* shared_worker_service,
ProcessNodeSource* process_node_source,
FrameNodeSource* frame_node_source)
: browser_context_id_(browser_context_id),
dedicated_worker_service_observer_(this),
shared_worker_service_observer_(this),
process_node_source_(process_node_source),
frame_node_source_(frame_node_source) {
DCHECK(dedicated_worker_service);
DCHECK(shared_worker_service);
DCHECK(process_node_source_);
DCHECK(frame_node_source_);
dedicated_worker_service_observer_.Add(dedicated_worker_service);
shared_worker_service_observer_.Add(shared_worker_service);
}
WorkerWatcher::~WorkerWatcher() {
DCHECK(frame_node_child_workers_.empty());
DCHECK(dedicated_worker_nodes_.empty());
DCHECK(!dedicated_worker_service_observer_.IsObservingSources());
DCHECK(shared_worker_nodes_.empty());
DCHECK(!shared_worker_service_observer_.IsObservingSources());
}
......@@ -87,16 +93,57 @@ void WorkerWatcher::TearDown() {
// Then clean all the worker nodes.
std::vector<std::unique_ptr<NodeBase>> nodes;
nodes.reserve(shared_worker_nodes_.size());
nodes.reserve(dedicated_worker_nodes_.size() + shared_worker_nodes_.size());
for (auto& node : dedicated_worker_nodes_)
nodes.push_back(std::move(node.second));
dedicated_worker_nodes_.clear();
for (auto& node : shared_worker_nodes_)
nodes.push_back(std::move(node.second));
shared_worker_nodes_.clear();
PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
dedicated_worker_service_observer_.RemoveAll();
shared_worker_service_observer_.RemoveAll();
}
void WorkerWatcher::OnWorkerStarted(
content::DedicatedWorkerId dedicated_worker_id,
int worker_process_id,
content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
// TODO(https://crbug.com/993029): Plumb through the URL and the DevTools
// token.
auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
browser_context_id_, WorkerNode::WorkerType::kDedicated,
process_node_source_->GetProcessNode(worker_process_id), GURL(),
base::UnguessableToken::Create());
bool inserted = dedicated_worker_nodes_
.emplace(dedicated_worker_id, std::move(worker_node))
.second;
DCHECK(inserted);
AddClientFrame(GetDedicatedWorkerNode(dedicated_worker_id),
ancestor_render_frame_host_id);
}
void WorkerWatcher::OnBeforeWorkerTerminated(
content::DedicatedWorkerId dedicated_worker_id,
content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
RemoveClientFrame(GetDedicatedWorkerNode(dedicated_worker_id),
ancestor_render_frame_host_id);
auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
DCHECK(it != dedicated_worker_nodes_.end());
auto worker_node = std::move(it->second);
#if DCHECK_IS_ON()
DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
#endif // DCHECK_IS_ON()
PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
dedicated_worker_nodes_.erase(it);
}
void WorkerWatcher::OnWorkerStarted(
const content::SharedWorkerInstance& instance,
int worker_process_id,
......@@ -127,31 +174,41 @@ void WorkerWatcher::OnBeforeWorkerTerminated(
void WorkerWatcher::OnClientAdded(
const content::SharedWorkerInstance& instance,
content::GlobalFrameRoutingId render_frame_host_id) {
AddClientFrame(GetSharedWorkerNode(instance), render_frame_host_id);
}
void WorkerWatcher::OnClientRemoved(
const content::SharedWorkerInstance& instance,
content::GlobalFrameRoutingId render_frame_host_id) {
RemoveClientFrame(GetSharedWorkerNode(instance), render_frame_host_id);
}
void WorkerWatcher::AddClientFrame(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id) {
FrameNodeImpl* frame_node =
frame_node_source_->GetFrameNode(render_frame_host_id);
frame_node_source_->GetFrameNode(client_render_frame_host_id);
DCHECK(frame_node);
// Connect the nodes in the PM graph.
WorkerNodeImpl* worker_node = GetSharedWorkerNode(instance);
PerformanceManagerImpl::CallOnGraphImpl(
FROM_HERE,
base::BindOnce(&AddWorkerToFrameNode, frame_node, worker_node));
// Keep track of the shared workers that this frame is a client to.
if (AddChildWorker(render_frame_host_id, worker_node)) {
if (AddChildWorker(client_render_frame_host_id, worker_node)) {
frame_node_source_->SubscribeToFrameNode(
render_frame_host_id,
client_render_frame_host_id,
base::BindOnce(&WorkerWatcher::OnBeforeFrameNodeRemoved,
base::Unretained(this), render_frame_host_id));
base::Unretained(this), client_render_frame_host_id));
}
}
void WorkerWatcher::OnClientRemoved(
const content::SharedWorkerInstance& instance,
content::GlobalFrameRoutingId render_frame_host_id) {
WorkerNodeImpl* worker_node = GetSharedWorkerNode(instance);
void WorkerWatcher::RemoveClientFrame(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id) {
FrameNodeImpl* frame_node =
frame_node_source_->GetFrameNode(render_frame_host_id);
frame_node_source_->GetFrameNode(client_render_frame_host_id);
// It's possible that the frame was destroyed before receiving the
// OnClientRemoved() for all of its child shared worker. Nothing to do in
......@@ -181,8 +238,8 @@ void WorkerWatcher::OnClientRemoved(
// Remove |worker_node| from the set of workers that this frame is a client
// of.
if (RemoveChildWorker(render_frame_host_id, worker_node))
frame_node_source_->UnsubscribeFromFrameNode(render_frame_host_id);
if (RemoveChildWorker(client_render_frame_host_id, worker_node))
frame_node_source_->UnsubscribeFromFrameNode(client_render_frame_host_id);
}
void WorkerWatcher::OnBeforeFrameNodeRemoved(
......@@ -242,6 +299,16 @@ bool WorkerWatcher::RemoveChildWorker(
return false;
}
WorkerNodeImpl* WorkerWatcher::GetDedicatedWorkerNode(
content::DedicatedWorkerId dedicated_worker_id) {
auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
if (it == dedicated_worker_nodes_.end()) {
NOTREACHED();
return nullptr;
}
return it->second.get();
}
WorkerNodeImpl* WorkerWatcher::GetSharedWorkerNode(
const content::SharedWorkerInstance& instance) {
auto it = shared_worker_nodes_.find(instance);
......
......@@ -13,6 +13,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/scoped_observer.h"
#include "content/public/browser/dedicated_worker_service.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/shared_worker_service.h"
......@@ -30,11 +31,12 @@ 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 dedicated workers and service
// workers.
class WorkerWatcher : public content::SharedWorkerService::Observer {
// TODO(https://crbug.com/993029): Add support for service workers.
class WorkerWatcher : public content::DedicatedWorkerService::Observer,
public content::SharedWorkerService::Observer {
public:
WorkerWatcher(const std::string& browser_context_id,
content::DedicatedWorkerService* dedicated_worker_service,
content::SharedWorkerService* shared_worker_service,
ProcessNodeSource* process_node_source,
FrameNodeSource* frame_node_source);
......@@ -44,6 +46,15 @@ class WorkerWatcher : public content::SharedWorkerService::Observer {
// destroyed on the PM graph.
void TearDown();
// content::DedicatedWorkerService::Observer:
void OnWorkerStarted(
content::DedicatedWorkerId dedicated_worker_id,
int worker_process_id,
content::GlobalFrameRoutingId ancestor_render_frame_host_id) override;
void OnBeforeWorkerTerminated(
content::DedicatedWorkerId dedicated_worker_id,
content::GlobalFrameRoutingId ancestor_render_frame_host_id) override;
// content::SharedWorkerService::Observer:
void OnWorkerStarted(const content::SharedWorkerInstance& instance,
int worker_process_id,
......@@ -60,6 +71,13 @@ class WorkerWatcher : public content::SharedWorkerService::Observer {
private:
friend class WorkerWatcherTest;
void AddClientFrame(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
void RemoveClientFrame(
WorkerNodeImpl* worker_node,
content::GlobalFrameRoutingId client_render_frame_host_id);
void OnBeforeFrameNodeRemoved(
content::GlobalFrameRoutingId render_frame_host_id,
FrameNodeImpl* frame_node);
......@@ -70,12 +88,19 @@ class WorkerWatcher : public content::SharedWorkerService::Observer {
WorkerNodeImpl* child_worker_node);
// Helper function to retrieve an existing shared worker node.
WorkerNodeImpl* GetDedicatedWorkerNode(
content::DedicatedWorkerId dedicated_worker_id);
WorkerNodeImpl* GetSharedWorkerNode(
const content::SharedWorkerInstance& instance);
// The ID of the BrowserContext who owns the shared worker service.
const std::string browser_context_id_;
// Observes the DedicatedWorkerService for this browser context.
ScopedObserver<content::DedicatedWorkerService,
content::DedicatedWorkerService::Observer>
dedicated_worker_service_observer_;
// Observes the SharedWorkerService for this browser context.
ScopedObserver<content::SharedWorkerService,
content::SharedWorkerService::Observer>
......@@ -88,6 +113,10 @@ class WorkerWatcher : public content::SharedWorkerService::Observer {
// frame ID. Also allows to subscribe to a frame's deletion notification.
FrameNodeSource* const frame_node_source_;
// Maps each dedicated worker ID to its worker node.
base::flat_map<content::DedicatedWorkerId, std::unique_ptr<WorkerNodeImpl>>
dedicated_worker_nodes_;
// Maps each SharedWorkerInstance to its worker node.
base::flat_map<content::SharedWorkerInstance, std::unique_ptr<WorkerNodeImpl>>
shared_worker_nodes_;
......
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