1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2012 Intel Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "third_party/blink/renderer/core/timing/window_performance.h"
33
34 #include "third_party/blink/public/common/features.h"
35 #include "third_party/blink/public/platform/platform.h"
36 #include "third_party/blink/public/platform/task_type.h"
37 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
38 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
39 #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
40 #include "third_party/blink/renderer/core/dom/document.h"
41 #include "third_party/blink/renderer/core/dom/qualified_name.h"
42 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
43 #include "third_party/blink/renderer/core/frame/local_frame.h"
44 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
45 #include "third_party/blink/renderer/core/inspector/console_message.h"
46 #include "third_party/blink/renderer/core/loader/document_loader.h"
47 #include "third_party/blink/renderer/core/page/chrome_client.h"
48 #include "third_party/blink/renderer/core/page/page.h"
49 #include "third_party/blink/renderer/core/page/page_hidden_state.h"
50 #include "third_party/blink/renderer/core/timing/largest_contentful_paint.h"
51 #include "third_party/blink/renderer/core/timing/layout_shift.h"
52 #include "third_party/blink/renderer/core/timing/performance_element_timing.h"
53 #include "third_party/blink/renderer/core/timing/performance_event_timing.h"
54 #include "third_party/blink/renderer/core/timing/performance_observer.h"
55 #include "third_party/blink/renderer/core/timing/performance_timing.h"
56 #include "third_party/blink/renderer/core/timing/visibility_state_entry.h"
57 #include "third_party/blink/renderer/platform/heap/persistent.h"
58 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
59 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
60 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
61 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
62 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
63
64 static constexpr base::TimeDelta kLongTaskObserverThreshold =
65 base::TimeDelta::FromMilliseconds(50);
66
67 namespace blink {
68
69 namespace {
70
GetFrameAttribute(HTMLFrameOwnerElement * frame_owner,const QualifiedName & attr_name)71 AtomicString GetFrameAttribute(HTMLFrameOwnerElement* frame_owner,
72 const QualifiedName& attr_name) {
73 AtomicString attr_value;
74 if (frame_owner->hasAttribute(attr_name)) {
75 attr_value = frame_owner->getAttribute(attr_name);
76 }
77 return attr_value;
78 }
79
GetFrameOwnerType(HTMLFrameOwnerElement * frame_owner)80 AtomicString GetFrameOwnerType(HTMLFrameOwnerElement* frame_owner) {
81 switch (frame_owner->OwnerType()) {
82 case mojom::blink::FrameOwnerElementType::kNone:
83 return "window";
84 case mojom::blink::FrameOwnerElementType::kIframe:
85 return "iframe";
86 case mojom::blink::FrameOwnerElementType::kObject:
87 return "object";
88 case mojom::blink::FrameOwnerElementType::kEmbed:
89 return "embed";
90 case mojom::blink::FrameOwnerElementType::kFrame:
91 return "frame";
92 case mojom::blink::FrameOwnerElementType::kPortal:
93 return "portal";
94 }
95 NOTREACHED();
96 return "";
97 }
98
GetFrameSrc(HTMLFrameOwnerElement * frame_owner)99 AtomicString GetFrameSrc(HTMLFrameOwnerElement* frame_owner) {
100 switch (frame_owner->OwnerType()) {
101 case mojom::blink::FrameOwnerElementType::kObject:
102 return GetFrameAttribute(frame_owner, html_names::kDataAttr);
103 default:
104 return GetFrameAttribute(frame_owner, html_names::kSrcAttr);
105 }
106 }
107
SelfKeyword()108 const AtomicString& SelfKeyword() {
109 DEFINE_STATIC_LOCAL(const AtomicString, kSelfAttribution, ("self"));
110 return kSelfAttribution;
111 }
112
SameOriginAncestorKeyword()113 const AtomicString& SameOriginAncestorKeyword() {
114 DEFINE_STATIC_LOCAL(const AtomicString, kSameOriginAncestorAttribution,
115 ("same-origin-ancestor"));
116 return kSameOriginAncestorAttribution;
117 }
118
SameOriginDescendantKeyword()119 const AtomicString& SameOriginDescendantKeyword() {
120 DEFINE_STATIC_LOCAL(const AtomicString, kSameOriginDescendantAttribution,
121 ("same-origin-descendant"));
122 return kSameOriginDescendantAttribution;
123 }
124
SameOriginKeyword()125 const AtomicString& SameOriginKeyword() {
126 DEFINE_STATIC_LOCAL(const AtomicString, kSameOriginAttribution,
127 ("same-origin"));
128 return kSameOriginAttribution;
129 }
130
SameOriginAttribution(Frame * observer_frame,Frame * culprit_frame)131 AtomicString SameOriginAttribution(Frame* observer_frame,
132 Frame* culprit_frame) {
133 DCHECK(IsMainThread());
134 if (observer_frame == culprit_frame)
135 return SelfKeyword();
136 if (observer_frame->Tree().IsDescendantOf(culprit_frame))
137 return SameOriginAncestorKeyword();
138 if (culprit_frame->Tree().IsDescendantOf(observer_frame))
139 return SameOriginDescendantKeyword();
140 return SameOriginKeyword();
141 }
142
143 } // namespace
144
145 constexpr size_t kDefaultVisibilityStateEntrySize = 50;
146
ToTimeOrigin(LocalDOMWindow * window)147 static base::TimeTicks ToTimeOrigin(LocalDOMWindow* window) {
148 DocumentLoader* loader = window->GetFrame()->Loader().GetDocumentLoader();
149 return loader->GetTiming().ReferenceMonotonicTime();
150 }
151
WindowPerformance(LocalDOMWindow * window)152 WindowPerformance::WindowPerformance(LocalDOMWindow* window)
153 : Performance(ToTimeOrigin(window),
154 window->GetTaskRunner(TaskType::kPerformanceTimeline)),
155 ExecutionContextClient(window),
156 PageVisibilityObserver(window->GetFrame()->GetPage()) {
157 DCHECK(window);
158 DCHECK(window->GetFrame()->GetPerformanceMonitor());
159 window->GetFrame()->GetPerformanceMonitor()->Subscribe(
160 PerformanceMonitor::kLongTask, kLongTaskObserverThreshold, this);
161 if (RuntimeEnabledFeatures::VisibilityStateEntryEnabled()) {
162 DCHECK(GetPage());
163 AddVisibilityStateEntry(GetPage()->IsPageVisible(), base::TimeTicks());
164 }
165 }
166
167 WindowPerformance::~WindowPerformance() = default;
168
GetExecutionContext() const169 ExecutionContext* WindowPerformance::GetExecutionContext() const {
170 return ExecutionContextClient::GetExecutionContext();
171 }
172
timing() const173 PerformanceTiming* WindowPerformance::timing() const {
174 if (!timing_)
175 timing_ = MakeGarbageCollected<PerformanceTiming>(DomWindow());
176
177 return timing_.Get();
178 }
179
navigation() const180 PerformanceNavigation* WindowPerformance::navigation() const {
181 if (!navigation_)
182 navigation_ = MakeGarbageCollected<PerformanceNavigation>(DomWindow());
183
184 return navigation_.Get();
185 }
186
memory() const187 MemoryInfo* WindowPerformance::memory() const {
188 // The performance.memory() API has been improved so that we report precise
189 // values when the process is locked to a site. The intent (which changed
190 // course over time about what changes would be implemented) can be found at
191 // https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/no00RdMnGio,
192 // and the relevant bug is https://crbug.com/807651.
193 return MakeGarbageCollected<MemoryInfo>(
194 Platform::Current()->IsLockedToSite()
195 ? MemoryInfo::Precision::Precise
196 : MemoryInfo::Precision::Bucketized);
197 }
198
199 PerformanceNavigationTiming*
CreateNavigationTimingInstance()200 WindowPerformance::CreateNavigationTimingInstance() {
201 if (!DomWindow())
202 return nullptr;
203 DocumentLoader* document_loader = DomWindow()->document()->Loader();
204 ResourceTimingInfo* info = document_loader->GetNavigationTimingInfo();
205 if (!info)
206 return nullptr;
207 HeapVector<Member<PerformanceServerTiming>> server_timing =
208 PerformanceServerTiming::ParseServerTiming(*info);
209 if (!server_timing.IsEmpty())
210 document_loader->CountUse(WebFeature::kPerformanceServerTiming);
211
212 return MakeGarbageCollected<PerformanceNavigationTiming>(
213 DomWindow(), info, time_origin_, std::move(server_timing));
214 }
215
BuildJSONValue(V8ObjectBuilder & builder) const216 void WindowPerformance::BuildJSONValue(V8ObjectBuilder& builder) const {
217 Performance::BuildJSONValue(builder);
218 builder.Add("timing", timing());
219 builder.Add("navigation", navigation());
220 }
221
Trace(Visitor * visitor) const222 void WindowPerformance::Trace(Visitor* visitor) const {
223 visitor->Trace(event_timings_);
224 visitor->Trace(first_pointer_down_event_timing_);
225 visitor->Trace(event_counts_);
226 visitor->Trace(navigation_);
227 visitor->Trace(timing_);
228 Performance::Trace(visitor);
229 PerformanceMonitor::Client::Trace(visitor);
230 ExecutionContextClient::Trace(visitor);
231 PageVisibilityObserver::Trace(visitor);
232 }
233
CanAccessOrigin(Frame * frame1,Frame * frame2)234 static bool CanAccessOrigin(Frame* frame1, Frame* frame2) {
235 const SecurityOrigin* security_origin1 =
236 frame1->GetSecurityContext()->GetSecurityOrigin();
237 const SecurityOrigin* security_origin2 =
238 frame2->GetSecurityContext()->GetSecurityOrigin();
239 return security_origin1->CanAccess(security_origin2);
240 }
241
242 /**
243 * Report sanitized name based on cross-origin policy.
244 * See detailed Security doc here: http://bit.ly/2duD3F7
245 */
246 // static
SanitizedAttribution(ExecutionContext * task_context,bool has_multiple_contexts,LocalFrame * observer_frame)247 std::pair<AtomicString, DOMWindow*> WindowPerformance::SanitizedAttribution(
248 ExecutionContext* task_context,
249 bool has_multiple_contexts,
250 LocalFrame* observer_frame) {
251 DCHECK(IsMainThread());
252 if (has_multiple_contexts) {
253 // Unable to attribute, multiple script execution contents were involved.
254 DEFINE_STATIC_LOCAL(const AtomicString, kAmbiguousAttribution,
255 ("multiple-contexts"));
256 return std::make_pair(kAmbiguousAttribution, nullptr);
257 }
258
259 LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(task_context);
260 if (!window || !window->GetFrame()) {
261 // Unable to attribute as no script was involved.
262 DEFINE_STATIC_LOCAL(const AtomicString, kUnknownAttribution, ("unknown"));
263 return std::make_pair(kUnknownAttribution, nullptr);
264 }
265
266 // Exactly one culprit location, attribute based on origin boundary.
267 Frame* culprit_frame = window->GetFrame();
268 DCHECK(culprit_frame);
269 if (CanAccessOrigin(observer_frame, culprit_frame)) {
270 // From accessible frames or same origin, return culprit location URL.
271 return std::make_pair(SameOriginAttribution(observer_frame, culprit_frame),
272 culprit_frame->DomWindow());
273 }
274 // For cross-origin, if the culprit is the descendant or ancestor of
275 // observer then indicate the *closest* cross-origin frame between
276 // the observer and the culprit, in the corresponding direction.
277 if (culprit_frame->Tree().IsDescendantOf(observer_frame)) {
278 // If the culprit is a descendant of the observer, then walk up the tree
279 // from culprit to observer, and report the *last* cross-origin (from
280 // observer) frame. If no intermediate cross-origin frame is found, then
281 // report the culprit directly.
282 Frame* last_cross_origin_frame = culprit_frame;
283 for (Frame* frame = culprit_frame; frame != observer_frame;
284 frame = frame->Tree().Parent()) {
285 if (!CanAccessOrigin(observer_frame, frame)) {
286 last_cross_origin_frame = frame;
287 }
288 }
289 DEFINE_STATIC_LOCAL(const AtomicString, kCrossOriginDescendantAttribution,
290 ("cross-origin-descendant"));
291 return std::make_pair(kCrossOriginDescendantAttribution,
292 last_cross_origin_frame->DomWindow());
293 }
294 if (observer_frame->Tree().IsDescendantOf(culprit_frame)) {
295 DEFINE_STATIC_LOCAL(const AtomicString, kCrossOriginAncestorAttribution,
296 ("cross-origin-ancestor"));
297 return std::make_pair(kCrossOriginAncestorAttribution, nullptr);
298 }
299 DEFINE_STATIC_LOCAL(const AtomicString, kCrossOriginAttribution,
300 ("cross-origin-unreachable"));
301 return std::make_pair(kCrossOriginAttribution, nullptr);
302 }
303
ReportLongTask(base::TimeTicks start_time,base::TimeTicks end_time,ExecutionContext * task_context,bool has_multiple_contexts)304 void WindowPerformance::ReportLongTask(base::TimeTicks start_time,
305 base::TimeTicks end_time,
306 ExecutionContext* task_context,
307 bool has_multiple_contexts) {
308 if (!DomWindow())
309 return;
310 std::pair<AtomicString, DOMWindow*> attribution =
311 WindowPerformance::SanitizedAttribution(
312 task_context, has_multiple_contexts, DomWindow()->GetFrame());
313 DOMWindow* culprit_dom_window = attribution.second;
314 if (!culprit_dom_window || !culprit_dom_window->GetFrame() ||
315 !culprit_dom_window->GetFrame()->DeprecatedLocalOwner()) {
316 AddLongTaskTiming(start_time, end_time, attribution.first, "window",
317 g_empty_string, g_empty_string, g_empty_string);
318 } else {
319 HTMLFrameOwnerElement* frame_owner =
320 culprit_dom_window->GetFrame()->DeprecatedLocalOwner();
321 AddLongTaskTiming(start_time, end_time, attribution.first,
322 GetFrameOwnerType(frame_owner), GetFrameSrc(frame_owner),
323 GetFrameAttribute(frame_owner, html_names::kIdAttr),
324 GetFrameAttribute(frame_owner, html_names::kNameAttr));
325 }
326 }
327
RegisterEventTiming(const AtomicString & event_type,base::TimeTicks start_time,base::TimeTicks processing_start,base::TimeTicks processing_end,bool cancelable,Node * target)328 void WindowPerformance::RegisterEventTiming(const AtomicString& event_type,
329 base::TimeTicks start_time,
330 base::TimeTicks processing_start,
331 base::TimeTicks processing_end,
332 bool cancelable,
333 Node* target) {
334 // |start_time| could be null in some tests that inject input.
335 DCHECK(!processing_start.is_null());
336 DCHECK(!processing_end.is_null());
337 DCHECK_GE(processing_end, processing_start);
338 if (!DomWindow())
339 return;
340
341 if (!event_counts_)
342 event_counts_ = MakeGarbageCollected<EventCounts>();
343 event_counts_->Add(event_type);
344 PerformanceEventTiming* entry = PerformanceEventTiming::Create(
345 event_type, MonotonicTimeToDOMHighResTimeStamp(start_time),
346 MonotonicTimeToDOMHighResTimeStamp(processing_start),
347 MonotonicTimeToDOMHighResTimeStamp(processing_end), cancelable, target);
348 // Add |entry| to the end of the queue along with the frame index at which is
349 // is being queued to know when to queue a swap promise for it.
350 event_timings_.push_back(entry);
351 event_frames_.push_back(frame_index_);
352 bool should_queue_swap_promise = false;
353 // If there are no pending swap promises, we should queue one. This ensures
354 // that |event_timings_| are processed even if the Blink lifecycle does not
355 // occur due to no DOM updates.
356 if (pending_swap_promise_count_ == 0u) {
357 should_queue_swap_promise = true;
358 } else {
359 // There are pending swap promises, so only queue one if the event
360 // corresponds to a later frame than the one of the latest queued swap
361 // promise.
362 should_queue_swap_promise = frame_index_ > last_registered_frame_index_;
363 }
364 if (should_queue_swap_promise) {
365 DomWindow()->GetFrame()->GetChromeClient().NotifySwapTime(
366 *DomWindow()->GetFrame(),
367 CrossThreadBindOnce(&WindowPerformance::ReportEventTimings,
368 WrapCrossThreadWeakPersistent(this), frame_index_));
369 last_registered_frame_index_ = frame_index_;
370 ++pending_swap_promise_count_;
371 }
372 }
373
ReportEventTimings(uint64_t frame_index,WebSwapResult result,base::TimeTicks timestamp)374 void WindowPerformance::ReportEventTimings(uint64_t frame_index,
375 WebSwapResult result,
376 base::TimeTicks timestamp) {
377 DCHECK(pending_swap_promise_count_);
378 --pending_swap_promise_count_;
379 // |event_timings_| and |event_frames_| should always have the same size.
380 DCHECK(event_timings_.size() == event_frames_.size());
381 if (event_timings_.IsEmpty())
382 return;
383 bool event_timing_enabled =
384 RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext());
385 DOMHighResTimeStamp end_time = MonotonicTimeToDOMHighResTimeStamp(timestamp);
386 while (!event_timings_.IsEmpty()) {
387 PerformanceEventTiming* entry = event_timings_.front();
388 uint64_t entry_frame_index = event_frames_.front();
389 // If the entry was queued at a frame index that is larger than
390 // |frame_index|, then we've reached the end of the entries that we can
391 // process during this callback.
392 if (entry_frame_index > frame_index)
393 break;
394
395 event_timings_.pop_front();
396 event_frames_.pop_front();
397
398 int duration_in_ms = std::round((end_time - entry->startTime()) / 8) * 8;
399 entry->SetDuration(duration_in_ms);
400 if (!first_input_timing_) {
401 if (entry->name() == "pointerdown") {
402 first_pointer_down_event_timing_ =
403 PerformanceEventTiming::CreateFirstInputTiming(entry);
404 } else if (entry->name() == "pointerup") {
405 DispatchFirstInputTiming(first_pointer_down_event_timing_);
406 } else if (entry->name() == "click" || entry->name() == "keydown" ||
407 entry->name() == "mousedown") {
408 DispatchFirstInputTiming(
409 PerformanceEventTiming::CreateFirstInputTiming(entry));
410 }
411 }
412 if (!event_timing_enabled)
413 continue;
414
415 if (HasObserverFor(PerformanceEntry::kEvent)) {
416 UseCounter::Count(GetExecutionContext(),
417 WebFeature::kEventTimingExplicitlyRequested);
418 NotifyObserversOfEntry(*entry);
419 }
420
421 // Only buffer really slow events to keep memory usage low.
422 // TODO(npm): is 104 a reasonable buffering threshold or should it be
423 // relaxed?
424 if (duration_in_ms >= PerformanceObserver::kDefaultDurationThreshold &&
425 !IsEventTimingBufferFull()) {
426 AddEventTimingBuffer(*entry);
427 }
428 }
429 }
430
AddElementTiming(const AtomicString & name,const String & url,const FloatRect & rect,base::TimeTicks start_time,base::TimeTicks load_time,const AtomicString & identifier,const IntSize & intrinsic_size,const AtomicString & id,Element * element)431 void WindowPerformance::AddElementTiming(const AtomicString& name,
432 const String& url,
433 const FloatRect& rect,
434 base::TimeTicks start_time,
435 base::TimeTicks load_time,
436 const AtomicString& identifier,
437 const IntSize& intrinsic_size,
438 const AtomicString& id,
439 Element* element) {
440 PerformanceElementTiming* entry = PerformanceElementTiming::Create(
441 name, url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
442 MonotonicTimeToDOMHighResTimeStamp(load_time), identifier,
443 intrinsic_size.Width(), intrinsic_size.Height(), id, element);
444 if (HasObserverFor(PerformanceEntry::kElement))
445 NotifyObserversOfEntry(*entry);
446 if (!IsElementTimingBufferFull())
447 AddElementTimingBuffer(*entry);
448 }
449
DispatchFirstInputTiming(PerformanceEventTiming * entry)450 void WindowPerformance::DispatchFirstInputTiming(
451 PerformanceEventTiming* entry) {
452 if (!entry)
453 return;
454 DCHECK_EQ("first-input", entry->entryType());
455 if (HasObserverFor(PerformanceEntry::kFirstInput)) {
456 UseCounter::Count(GetExecutionContext(),
457 WebFeature::kEventTimingExplicitlyRequested);
458 UseCounter::Count(GetExecutionContext(),
459 WebFeature::kEventTimingFirstInputExplicitlyRequested);
460 NotifyObserversOfEntry(*entry);
461 }
462
463 DCHECK(!first_input_timing_);
464 first_input_timing_ = entry;
465 }
466
AddLayoutShiftEntry(LayoutShift * entry)467 void WindowPerformance::AddLayoutShiftEntry(LayoutShift* entry) {
468 if (HasObserverFor(PerformanceEntry::kLayoutShift))
469 NotifyObserversOfEntry(*entry);
470 AddLayoutShiftBuffer(*entry);
471 }
472
AddVisibilityStateEntry(bool is_visible,base::TimeTicks timestamp)473 void WindowPerformance::AddVisibilityStateEntry(bool is_visible,
474 base::TimeTicks timestamp) {
475 DCHECK(RuntimeEnabledFeatures::VisibilityStateEntryEnabled());
476 VisibilityStateEntry* entry = MakeGarbageCollected<VisibilityStateEntry>(
477 PageHiddenStateString(!is_visible),
478 MonotonicTimeToDOMHighResTimeStamp(timestamp));
479 if (HasObserverFor(PerformanceEntry::kVisibilityState))
480 NotifyObserversOfEntry(*entry);
481
482 if (visibility_state_buffer_.size() < kDefaultVisibilityStateEntrySize)
483 visibility_state_buffer_.push_back(entry);
484 }
485
PageVisibilityChanged()486 void WindowPerformance::PageVisibilityChanged() {
487 if (!RuntimeEnabledFeatures::VisibilityStateEntryEnabled())
488 return;
489
490 AddVisibilityStateEntry(GetPage()->IsPageVisible(), base::TimeTicks::Now());
491 }
492
eventCounts()493 EventCounts* WindowPerformance::eventCounts() {
494 DCHECK(RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext()));
495 if (!event_counts_)
496 event_counts_ = MakeGarbageCollected<EventCounts>();
497 return event_counts_;
498 }
499
OnLargestContentfulPaintUpdated(base::TimeTicks paint_time,uint64_t paint_size,base::TimeTicks load_time,const AtomicString & id,const String & url,Element * element)500 void WindowPerformance::OnLargestContentfulPaintUpdated(
501 base::TimeTicks paint_time,
502 uint64_t paint_size,
503 base::TimeTicks load_time,
504 const AtomicString& id,
505 const String& url,
506 Element* element) {
507 base::TimeDelta render_timestamp = MonotonicTimeToTimeDelta(paint_time);
508 base::TimeDelta load_timestamp = MonotonicTimeToTimeDelta(load_time);
509 base::TimeDelta start_timestamp =
510 render_timestamp.is_zero() ? load_timestamp : render_timestamp;
511 auto* entry = MakeGarbageCollected<LargestContentfulPaint>(
512 start_timestamp.InMillisecondsF(), render_timestamp, paint_size,
513 load_timestamp, id, url, element);
514 if (HasObserverFor(PerformanceEntry::kLargestContentfulPaint))
515 NotifyObserversOfEntry(*entry);
516 AddLargestContentfulPaint(entry);
517 }
518
OnPaintFinished()519 void WindowPerformance::OnPaintFinished() {
520 ++frame_index_;
521 }
522
523 } // namespace blink
524