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