blob: bbcc2d0661ce1114e5880e97282a5995f7519b65 [file] [log] [blame]
Patrick Monette6c6de3882019-10-09 02:59:321// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Patrick Monette9a905082020-01-07 18:37:355#include "components/performance_manager/worker_watcher.h"
Patrick Monette6c6de3882019-10-09 02:59:326
7#include <utility>
8#include <vector>
9
10#include "components/performance_manager/frame_node_source.h"
11#include "components/performance_manager/graph/frame_node_impl.h"
12#include "components/performance_manager/graph/worker_node_impl.h"
13#include "components/performance_manager/performance_manager_impl.h"
14#include "components/performance_manager/process_node_source.h"
Patrick Monette6c6de3882019-10-09 02:59:3215
16namespace performance_manager {
17
18namespace {
19
20// Helper function to add |worker_node| as a child to |frame_node| on the PM
21// sequence.
22void AddWorkerToFrameNode(FrameNodeImpl* frame_node,
23 WorkerNodeImpl* worker_node,
24 GraphImpl* graph) {
25 worker_node->AddClientFrame(frame_node);
26}
27
28// Helper function to remove |worker_node| from |frame_node| on the PM sequence.
29void RemoveWorkerFromFrameNode(FrameNodeImpl* frame_node,
30 WorkerNodeImpl* worker_node,
31 GraphImpl* graph) {
32 worker_node->RemoveClientFrame(frame_node);
33}
34
35// Helper function to remove all |worker_nodes| from |frame_node| on the PM
36// sequence.
37void RemoveWorkersFromFrameNode(
38 FrameNodeImpl* frame_node,
39 const base::flat_set<WorkerNodeImpl*>& worker_nodes,
40 GraphImpl* graph) {
41 for (auto* worker_node : worker_nodes)
42 worker_node->RemoveClientFrame(frame_node);
43}
44
Patrick Monetteba8336672020-02-24 21:27:5445// Helper function that posts a task on the PM sequence that will invoke
46// OnFinalResponseURLDetermined() on |worker_node|.
47void SetFinalResponseURL(WorkerNodeImpl* worker_node, const GURL& url) {
48 PerformanceManagerImpl::CallOnGraphImpl(
49 FROM_HERE,
50 base::BindOnce(
51 [](WorkerNodeImpl* worker_node, const GURL& url, GraphImpl* graph) {
52 worker_node->OnFinalResponseURLDetermined(url);
53 },
54 worker_node, url));
55}
56
Patrick Monette6c6de3882019-10-09 02:59:3257} // namespace
58
Patrick Monette9a905082020-01-07 18:37:3559WorkerWatcher::WorkerWatcher(
Patrick Monette6c6de3882019-10-09 02:59:3260 const std::string& browser_context_id,
Patrick Monette3117283f2020-02-07 15:49:1561 content::DedicatedWorkerService* dedicated_worker_service,
Patrick Monette6c6de3882019-10-09 02:59:3262 content::SharedWorkerService* shared_worker_service,
63 ProcessNodeSource* process_node_source,
64 FrameNodeSource* frame_node_source)
65 : browser_context_id_(browser_context_id),
Patrick Monette3117283f2020-02-07 15:49:1566 dedicated_worker_service_observer_(this),
Patrick Monette6c6de3882019-10-09 02:59:3267 shared_worker_service_observer_(this),
68 process_node_source_(process_node_source),
69 frame_node_source_(frame_node_source) {
Patrick Monette3117283f2020-02-07 15:49:1570 DCHECK(dedicated_worker_service);
Patrick Monette6c6de3882019-10-09 02:59:3271 DCHECK(shared_worker_service);
72 DCHECK(process_node_source_);
73 DCHECK(frame_node_source_);
Patrick Monette3117283f2020-02-07 15:49:1574 dedicated_worker_service_observer_.Add(dedicated_worker_service);
Patrick Monette6c6de3882019-10-09 02:59:3275 shared_worker_service_observer_.Add(shared_worker_service);
76}
77
Patrick Monette9a905082020-01-07 18:37:3578WorkerWatcher::~WorkerWatcher() {
Patrick Monette6c6de3882019-10-09 02:59:3279 DCHECK(frame_node_child_workers_.empty());
Patrick Monette3117283f2020-02-07 15:49:1580 DCHECK(dedicated_worker_nodes_.empty());
81 DCHECK(!dedicated_worker_service_observer_.IsObservingSources());
Patrick Monette9a905082020-01-07 18:37:3582 DCHECK(shared_worker_nodes_.empty());
Patrick Monette6c6de3882019-10-09 02:59:3283 DCHECK(!shared_worker_service_observer_.IsObservingSources());
84}
85
Patrick Monette9a905082020-01-07 18:37:3586void WorkerWatcher::TearDown() {
Patrick Monette6c6de3882019-10-09 02:59:3287 // First clear client-child relations between frames and workers.
88 for (auto& kv : frame_node_child_workers_) {
Patrick Monette7364e6972020-01-09 22:56:0289 const content::GlobalFrameRoutingId& render_frame_host_id = kv.first;
Patrick Monette6c6de3882019-10-09 02:59:3290 base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
91
Patrick Monette7364e6972020-01-09 22:56:0292 frame_node_source_->UnsubscribeFromFrameNode(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:3293
94 // Disconnect all child workers from |frame_node|.
Patrick Monette7364e6972020-01-09 22:56:0295 FrameNodeImpl* frame_node =
96 frame_node_source_->GetFrameNode(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:3297 DCHECK(frame_node);
98 DCHECK(!child_workers.empty());
99 PerformanceManagerImpl::CallOnGraphImpl(
100 FROM_HERE,
101 base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
102 }
103 frame_node_child_workers_.clear();
104
105 // Then clean all the worker nodes.
106 std::vector<std::unique_ptr<NodeBase>> nodes;
Patrick Monette3117283f2020-02-07 15:49:15107 nodes.reserve(dedicated_worker_nodes_.size() + shared_worker_nodes_.size());
108 for (auto& node : dedicated_worker_nodes_)
109 nodes.push_back(std::move(node.second));
110 dedicated_worker_nodes_.clear();
Patrick Monette9a905082020-01-07 18:37:35111 for (auto& node : shared_worker_nodes_)
Patrick Monette6c6de3882019-10-09 02:59:32112 nodes.push_back(std::move(node.second));
Patrick Monette9a905082020-01-07 18:37:35113 shared_worker_nodes_.clear();
Patrick Monette6c6de3882019-10-09 02:59:32114
115 PerformanceManagerImpl::GetInstance()->BatchDeleteNodes(std::move(nodes));
116
Patrick Monette3117283f2020-02-07 15:49:15117 dedicated_worker_service_observer_.RemoveAll();
Patrick Monette6c6de3882019-10-09 02:59:32118 shared_worker_service_observer_.RemoveAll();
119}
120
Patrick Monette9a905082020-01-07 18:37:35121void WorkerWatcher::OnWorkerStarted(
Patrick Monette3117283f2020-02-07 15:49:15122 content::DedicatedWorkerId dedicated_worker_id,
123 int worker_process_id,
124 content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
125 // TODO(https://crbug.com/993029): Plumb through the URL and the DevTools
126 // token.
127 auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
128 browser_context_id_, WorkerNode::WorkerType::kDedicated,
129 process_node_source_->GetProcessNode(worker_process_id),
130 base::UnguessableToken::Create());
131 bool inserted = dedicated_worker_nodes_
132 .emplace(dedicated_worker_id, std::move(worker_node))
133 .second;
134 DCHECK(inserted);
135
136 // TODO(pmonette): Connect |worker_node| to its client frame.
137}
138
139void WorkerWatcher::OnBeforeWorkerTerminated(
140 content::DedicatedWorkerId dedicated_worker_id,
141 content::GlobalFrameRoutingId ancestor_render_frame_host_id) {
142 auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
143 DCHECK(it != dedicated_worker_nodes_.end());
144
145 auto worker_node = std::move(it->second);
146 // TODO(pmonette): Disconnect |worker_node| from its client frame.
147#if DCHECK_IS_ON()
148 DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
149#endif // DCHECK_IS_ON()
150 PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
151
152 dedicated_worker_nodes_.erase(it);
153}
154
Patrick Monetteba8336672020-02-24 21:27:54155void WorkerWatcher::OnFinalResponseURLDetermined(
156 content::DedicatedWorkerId dedicated_worker_id,
157 const GURL& url) {
158 SetFinalResponseURL(GetDedicatedWorkerNode(dedicated_worker_id), url);
159}
160
Patrick Monette3117283f2020-02-07 15:49:15161void WorkerWatcher::OnWorkerStarted(
Patrick Monettea376d3b02020-02-24 18:33:39162 content::SharedWorkerId shared_worker_id,
Patrick Monette6c6de3882019-10-09 02:59:32163 int worker_process_id,
164 const base::UnguessableToken& dev_tools_token) {
165 auto worker_node = PerformanceManagerImpl::GetInstance()->CreateWorkerNode(
166 browser_context_id_, WorkerNode::WorkerType::kShared,
Patrick Monetteeb6c1eee2020-02-04 20:42:48167 process_node_source_->GetProcessNode(worker_process_id), dev_tools_token);
Patrick Monette6c6de3882019-10-09 02:59:32168 bool inserted =
Patrick Monettea376d3b02020-02-24 18:33:39169 shared_worker_nodes_.emplace(shared_worker_id, std::move(worker_node))
170 .second;
Patrick Monette6c6de3882019-10-09 02:59:32171 DCHECK(inserted);
172}
173
Patrick Monette9a905082020-01-07 18:37:35174void WorkerWatcher::OnBeforeWorkerTerminated(
Patrick Monettea376d3b02020-02-24 18:33:39175 content::SharedWorkerId shared_worker_id) {
176 auto it = shared_worker_nodes_.find(shared_worker_id);
Patrick Monette9a905082020-01-07 18:37:35177 DCHECK(it != shared_worker_nodes_.end());
Patrick Monette6c6de3882019-10-09 02:59:32178
179 auto worker_node = std::move(it->second);
180#if DCHECK_IS_ON()
181 DCHECK(!base::Contains(clients_to_remove_, worker_node.get()));
182#endif // DCHECK_IS_ON()
183 PerformanceManagerImpl::GetInstance()->DeleteNode(std::move(worker_node));
184
Patrick Monette9a905082020-01-07 18:37:35185 shared_worker_nodes_.erase(it);
Patrick Monette6c6de3882019-10-09 02:59:32186}
187
Patrick Monetteba8336672020-02-24 21:27:54188void WorkerWatcher::OnFinalResponseURLDetermined(
189 content::SharedWorkerId shared_worker_id,
190 const GURL& url) {
191 SetFinalResponseURL(GetSharedWorkerNode(shared_worker_id), url);
192}
193
Patrick Monette7364e6972020-01-09 22:56:02194void WorkerWatcher::OnClientAdded(
Patrick Monettea376d3b02020-02-24 18:33:39195 content::SharedWorkerId shared_worker_id,
Patrick Monette7364e6972020-01-09 22:56:02196 content::GlobalFrameRoutingId render_frame_host_id) {
Patrick Monettea376d3b02020-02-24 18:33:39197 AddClientFrame(GetSharedWorkerNode(shared_worker_id), render_frame_host_id);
Patrick Monette897d08b2020-02-04 14:24:00198}
199
Marc Treib70854a72020-02-04 17:19:52200void WorkerWatcher::OnClientRemoved(
Patrick Monettea376d3b02020-02-24 18:33:39201 content::SharedWorkerId shared_worker_id,
Marc Treib70854a72020-02-04 17:19:52202 content::GlobalFrameRoutingId render_frame_host_id) {
Patrick Monettea376d3b02020-02-24 18:33:39203 RemoveClientFrame(GetSharedWorkerNode(shared_worker_id),
204 render_frame_host_id);
Patrick Monette3117283f2020-02-07 15:49:15205}
206
207void WorkerWatcher::AddClientFrame(
208 WorkerNodeImpl* worker_node,
209 content::GlobalFrameRoutingId client_render_frame_host_id) {
Patrick Monette897d08b2020-02-04 14:24:00210 FrameNodeImpl* frame_node =
Patrick Monette3117283f2020-02-07 15:49:15211 frame_node_source_->GetFrameNode(client_render_frame_host_id);
212 DCHECK(frame_node);
213
214 // Connect the nodes in the PM graph.
215 PerformanceManagerImpl::CallOnGraphImpl(
216 FROM_HERE,
217 base::BindOnce(&AddWorkerToFrameNode, frame_node, worker_node));
218
219 // Keep track of the shared workers that this frame is a client to.
220 if (AddChildWorker(client_render_frame_host_id, worker_node)) {
221 frame_node_source_->SubscribeToFrameNode(
222 client_render_frame_host_id,
223 base::BindOnce(&WorkerWatcher::OnBeforeFrameNodeRemoved,
224 base::Unretained(this), client_render_frame_host_id));
225 }
226}
227
228void WorkerWatcher::RemoveClientFrame(
229 WorkerNodeImpl* worker_node,
230 content::GlobalFrameRoutingId client_render_frame_host_id) {
231 FrameNodeImpl* frame_node =
232 frame_node_source_->GetFrameNode(client_render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32233
234 // It's possible that the frame was destroyed before receiving the
235 // OnClientRemoved() for all of its child shared worker. Nothing to do in
236 // that case because OnBeforeFrameNodeRemoved() took care of removing this
237 // client from its child worker nodes.
238 if (!frame_node) {
239#if DCHECK_IS_ON()
240 // These debug only checks ensure that this code path is only taken if
241 // OnBeforeFrameNodeRemoved() was already called for that frame.
242 auto it = clients_to_remove_.find(worker_node);
243 DCHECK(it != clients_to_remove_.end());
244
245 int& count = it->second;
246 DCHECK_GT(count, 0);
247 --count;
248
249 if (count == 0)
250 clients_to_remove_.erase(it);
251#endif // DCHECK_IS_ON()
252 return;
253 }
254
255 // Disconnect the node.
256 PerformanceManagerImpl::CallOnGraphImpl(
257 FROM_HERE,
258 base::BindOnce(&RemoveWorkerFromFrameNode, frame_node, worker_node));
259
260 // Remove |worker_node| from the set of workers that this frame is a client
261 // of.
Patrick Monette3117283f2020-02-07 15:49:15262 if (RemoveChildWorker(client_render_frame_host_id, worker_node))
263 frame_node_source_->UnsubscribeFromFrameNode(client_render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32264}
265
Patrick Monette7364e6972020-01-09 22:56:02266void WorkerWatcher::OnBeforeFrameNodeRemoved(
267 content::GlobalFrameRoutingId render_frame_host_id,
268 FrameNodeImpl* frame_node) {
269 auto it = frame_node_child_workers_.find(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32270 DCHECK(it != frame_node_child_workers_.end());
271
272 // Clean up all child workers of this frame node.
273 base::flat_set<WorkerNodeImpl*> child_workers = std::move(it->second);
274 frame_node_child_workers_.erase(it);
275
276 // Disconnect all child workers from |frame_node|.
277 DCHECK(!child_workers.empty());
278 PerformanceManagerImpl::CallOnGraphImpl(
279 FROM_HERE,
280 base::BindOnce(&RemoveWorkersFromFrameNode, frame_node, child_workers));
281
282#if DCHECK_IS_ON()
283 for (WorkerNodeImpl* worker_node : child_workers) {
284 // Now expect that this frame will be removed as a client for each worker
285 // in |child_workers|.
286 // Note: the [] operator is intentionally used to default initialize the
287 // count to zero if needed.
288 clients_to_remove_[worker_node]++;
289 }
290#endif // DCHECK_IS_ON()
291}
292
Patrick Monette7364e6972020-01-09 22:56:02293bool WorkerWatcher::AddChildWorker(
294 content::GlobalFrameRoutingId render_frame_host_id,
295 WorkerNodeImpl* child_worker_node) {
Patrick Monette6c6de3882019-10-09 02:59:32296 auto insertion_result =
Patrick Monette7364e6972020-01-09 22:56:02297 frame_node_child_workers_.insert({render_frame_host_id, {}});
Patrick Monette6c6de3882019-10-09 02:59:32298
299 auto& child_workers = insertion_result.first->second;
300 bool inserted = child_workers.insert(child_worker_node).second;
301 DCHECK(inserted);
302
303 return insertion_result.second;
304}
305
Patrick Monette7364e6972020-01-09 22:56:02306bool WorkerWatcher::RemoveChildWorker(
307 content::GlobalFrameRoutingId render_frame_host_id,
308 WorkerNodeImpl* child_worker_node) {
309 auto it = frame_node_child_workers_.find(render_frame_host_id);
Patrick Monette6c6de3882019-10-09 02:59:32310 DCHECK(it != frame_node_child_workers_.end());
311 auto& child_workers = it->second;
312
313 size_t removed = child_workers.erase(child_worker_node);
314 DCHECK_EQ(removed, 1u);
315
316 if (child_workers.empty()) {
317 frame_node_child_workers_.erase(it);
318 return true;
319 }
320 return false;
321}
322
Patrick Monette3117283f2020-02-07 15:49:15323WorkerNodeImpl* WorkerWatcher::GetDedicatedWorkerNode(
324 content::DedicatedWorkerId dedicated_worker_id) {
325 auto it = dedicated_worker_nodes_.find(dedicated_worker_id);
326 if (it == dedicated_worker_nodes_.end()) {
327 NOTREACHED();
328 return nullptr;
329 }
330 return it->second.get();
331}
332
Patrick Monette9a905082020-01-07 18:37:35333WorkerNodeImpl* WorkerWatcher::GetSharedWorkerNode(
Patrick Monettea376d3b02020-02-24 18:33:39334 content::SharedWorkerId shared_worker_id) {
335 auto it = shared_worker_nodes_.find(shared_worker_id);
Patrick Monette9a905082020-01-07 18:37:35336 if (it == shared_worker_nodes_.end()) {
Patrick Monette6c6de3882019-10-09 02:59:32337 NOTREACHED();
338 return nullptr;
339 }
340 return it->second.get();
341}
342
Patrick Monette6c6de3882019-10-09 02:59:32343} // namespace performance_manager