1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PerformanceMainThread.h"
8 #include "PerformanceNavigation.h"
9 #include "PerformancePaintTiming.h"
12 #include "js/PropertyAndElement.h" // JS_DefineProperty
13 #include "mozilla/HoldDropJSObjects.h"
14 #include "PerformanceEventTiming.h"
15 #include "LargestContentfulPaint.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/Event.h"
18 #include "mozilla/dom/EventCounts.h"
19 #include "mozilla/dom/PerformanceEventTimingBinding.h"
20 #include "mozilla/dom/PerformanceNavigationTiming.h"
21 #include "mozilla/dom/PerformanceResourceTiming.h"
22 #include "mozilla/dom/PerformanceTiming.h"
23 #include "mozilla/StaticPrefs_dom.h"
24 #include "mozilla/StaticPrefs_privacy.h"
25 #include "mozilla/PresShell.h"
26 #include "nsIChannel.h"
27 #include "nsIHttpChannel.h"
28 #include "nsIDocShell.h"
29 #include "nsTextFrame.h"
30 #include "nsContainerFrame.h"
32 namespace mozilla::dom
{
34 extern mozilla::LazyLogModule gLCPLogging
;
38 void GetURLSpecFromChannel(nsITimedChannel
* aChannel
, nsAString
& aSpec
) {
39 aSpec
.AssignLiteral("document");
41 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aChannel
);
47 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
48 if (NS_WARN_IF(NS_FAILED(rv
)) || !uri
) {
53 rv
= uri
->GetSpec(spec
);
54 if (NS_WARN_IF(NS_FAILED(rv
))) {
58 CopyUTF8toUTF16(spec
, aSpec
);
63 NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread
)
65 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread
,
67 NS_IMPL_CYCLE_COLLECTION_UNLINK(
68 mTiming
, mNavigation
, mDocEntry
, mFCPTiming
, mEventTimingEntries
,
69 mLargestContentfulPaintEntries
, mFirstInputEvent
, mPendingPointerDown
,
70 mPendingEventTimingEntries
, mEventCounts
)
71 tmp
->mImageLCPEntryMap
.Clear();
72 tmp
->mTextFrameUnions
.Clear();
73 mozilla::DropJSObjects(tmp
);
74 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread
,
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
79 mTiming
, mNavigation
, mDocEntry
, mFCPTiming
, mEventTimingEntries
,
80 mLargestContentfulPaintEntries
, mFirstInputEvent
, mPendingPointerDown
,
81 mPendingEventTimingEntries
, mEventCounts
, mImageLCPEntryMap
,
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
86 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread
,
88 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory
)
89 NS_IMPL_CYCLE_COLLECTION_TRACE_END
91 NS_IMPL_ADDREF_INHERITED(PerformanceMainThread
, Performance
)
92 NS_IMPL_RELEASE_INHERITED(PerformanceMainThread
, Performance
)
94 // QueryInterface implementation for PerformanceMainThread
95 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread
)
96 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
97 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, EventTarget
)
98 NS_INTERFACE_MAP_END_INHERITING(Performance
)
100 PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner
* aWindow
,
101 nsDOMNavigationTiming
* aDOMTiming
,
102 nsITimedChannel
* aChannel
)
103 : Performance(aWindow
->AsGlobal()),
104 mDOMTiming(aDOMTiming
),
106 MOZ_ASSERT(aWindow
, "Parent window object should be provided");
107 if (StaticPrefs::dom_enable_event_timing()) {
108 mEventCounts
= new class EventCounts(GetParentObject());
110 CreateNavigationTimingEntry();
112 if (StaticPrefs::dom_enable_largest_contentful_paint()) {
113 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
114 MarkerInnerWindowId innerWindowID
=
115 owner
? MarkerInnerWindowId(owner
->WindowID())
116 : MarkerInnerWindowId::NoId();
117 // There might be multiple LCP entries and we only care about the latest one
118 // which is also the biggest value. That's why we need to record these
119 // markers in two different places:
120 // - During the Document unload, so we can record the closed pages.
121 // - During the profile capture, so we can record the open pages.
122 // We are capturing the second one here.
123 // Our static analysis doesn't allow capturing ref-counted pointers in
124 // lambdas, so we need to hide it in a uintptr_t. This is safe because this
125 // lambda will be destroyed in ~PerformanceMainThread().
126 uintptr_t self
= reinterpret_cast<uintptr_t>(this);
127 profiler_add_state_change_callback(
128 // Using the "Pausing" state as "GeneratingProfile" profile happens too
129 // late; we can not record markers if the profiler is already paused.
130 ProfilingState::Pausing
,
131 [self
, innerWindowID
](ProfilingState aProfilingState
) {
132 const PerformanceMainThread
* selfPtr
=
133 reinterpret_cast<const PerformanceMainThread
*>(self
);
135 selfPtr
->GetDOMTiming()->MaybeAddLCPProfilerMarker(innerWindowID
);
141 PerformanceMainThread::~PerformanceMainThread() {
142 profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));
143 mozilla::DropJSObjects(this);
146 void PerformanceMainThread::GetMozMemory(JSContext
* aCx
,
147 JS::MutableHandle
<JSObject
*> aObj
) {
149 JS::Rooted
<JSObject
*> mozMemoryObj(aCx
, JS_NewPlainObject(aCx
));
150 JS::Rooted
<JSObject
*> gcMemoryObj(aCx
, js::gc::NewMemoryInfoObject(aCx
));
151 if (!mozMemoryObj
|| !gcMemoryObj
) {
152 MOZ_CRASH("out of memory creating performance.mozMemory");
154 if (!JS_DefineProperty(aCx
, mozMemoryObj
, "gc", gcMemoryObj
,
156 MOZ_CRASH("out of memory creating performance.mozMemory");
158 mMozMemory
= mozMemoryObj
;
159 mozilla::HoldJSObjects(this);
162 aObj
.set(mMozMemory
);
165 PerformanceTiming
* PerformanceMainThread::Timing() {
167 // For navigation timing, the third argument (an nsIHttpChannel) is null
168 // since the cross-domain redirect were already checked. The last
169 // argument (zero time) for performance.timing is the navigation start
171 mTiming
= new PerformanceTiming(this, mChannel
, nullptr,
172 mDOMTiming
->GetNavigationStart());
178 void PerformanceMainThread::DispatchBufferFullEvent() {
179 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
180 // it bubbles, and it isn't cancelable
181 event
->InitEvent(u
"resourcetimingbufferfull"_ns
, true, false);
182 event
->SetTrusted(true);
183 DispatchEvent(*event
);
186 PerformanceNavigation
* PerformanceMainThread::Navigation() {
188 mNavigation
= new PerformanceNavigation(this);
195 * An entry should be added only after the resource is loaded.
196 * This method is not thread safe and can only be called on the main thread.
198 void PerformanceMainThread::AddEntry(nsIHttpChannel
* channel
,
199 nsITimedChannel
* timedChannel
) {
200 MOZ_ASSERT(NS_IsMainThread());
202 nsAutoString initiatorType
;
203 nsAutoString entryName
;
205 UniquePtr
<PerformanceTimingData
> performanceTimingData(
206 PerformanceTimingData::Create(timedChannel
, channel
, 0, initiatorType
,
208 if (!performanceTimingData
) {
211 AddRawEntry(std::move(performanceTimingData
), initiatorType
, entryName
);
214 void PerformanceMainThread::AddEntry(const nsString
& entryName
,
215 const nsString
& initiatorType
,
216 UniquePtr
<PerformanceTimingData
>&& aData
) {
217 AddRawEntry(std::move(aData
), initiatorType
, entryName
);
220 void PerformanceMainThread::AddRawEntry(UniquePtr
<PerformanceTimingData
> aData
,
221 const nsAString
& aInitiatorType
,
222 const nsAString
& aEntryName
) {
223 // The PerformanceResourceTiming object will use the PerformanceTimingData
224 // object to get all the required timings.
226 MakeRefPtr
<PerformanceResourceTiming
>(std::move(aData
), this, aEntryName
);
227 entry
->SetInitiatorType(aInitiatorType
);
228 InsertResourceEntry(entry
);
231 void PerformanceMainThread::SetFCPTimingEntry(PerformancePaintTiming
* aEntry
) {
239 void PerformanceMainThread::InsertEventTimingEntry(
240 PerformanceEventTiming
* aEventEntry
) {
241 mPendingEventTimingEntries
.insertBack(aEventEntry
);
243 if (mHasQueuedRefreshdriverObserver
) {
247 PresShell
* presShell
= GetPresShell();
252 nsPresContext
* presContext
= presShell
->GetPresContext();
257 // Using PostRefreshObserver is fine because we don't
258 // run any JS between the `mark paint timing` step and the
259 // `pending Event Timing entries` step. So mixing the order
261 mHasQueuedRefreshdriverObserver
= true;
262 presContext
->RegisterManagedPostRefreshObserver(
263 new ManagedPostRefreshObserver(
264 presContext
, [performance
= RefPtr
<PerformanceMainThread
>(this)](
267 // XXX Should we do this even if canceled?
268 performance
->DispatchPendingEventTimingEntries();
270 performance
->mHasQueuedRefreshdriverObserver
= false;
271 return ManagedPostRefreshObserver::Unregister::Yes
;
275 void PerformanceMainThread::BufferEventTimingEntryIfNeeded(
276 PerformanceEventTiming
* aEventEntry
) {
277 if (mEventTimingEntries
.Length() < kDefaultEventTimingBufferSize
) {
278 mEventTimingEntries
.AppendElement(aEventEntry
);
282 void PerformanceMainThread::BufferLargestContentfulPaintEntryIfNeeded(
283 LargestContentfulPaint
* aEntry
) {
284 MOZ_ASSERT(StaticPrefs::dom_enable_largest_contentful_paint());
285 if (mLargestContentfulPaintEntries
.Length() <
286 kMaxLargestContentfulPaintBufferSize
) {
287 mLargestContentfulPaintEntries
.AppendElement(aEntry
);
291 void PerformanceMainThread::DispatchPendingEventTimingEntries() {
292 DOMHighResTimeStamp renderingTime
= NowUnclamped();
294 while (!mPendingEventTimingEntries
.isEmpty()) {
295 RefPtr
<PerformanceEventTiming
> entry
=
296 mPendingEventTimingEntries
.popFirst();
298 entry
->SetDuration(renderingTime
- entry
->RawStartTime());
299 IncEventCount(entry
->GetName());
301 if (entry
->RawDuration() >= kDefaultEventTimingMinDuration
) {
305 if (!mHasDispatchedInputEvent
) {
306 switch (entry
->GetMessage()) {
308 mPendingPointerDown
= entry
->Clone();
309 mPendingPointerDown
->SetEntryType(u
"first-input"_ns
);
313 if (mPendingPointerDown
) {
314 MOZ_ASSERT(!mFirstInputEvent
);
315 mFirstInputEvent
= mPendingPointerDown
.forget();
316 QueueEntry(mFirstInputEvent
);
317 SetHasDispatchedInputEvent();
324 mFirstInputEvent
= entry
->Clone();
325 mFirstInputEvent
->SetEntryType(u
"first-input"_ns
);
326 QueueEntry(mFirstInputEvent
);
327 SetHasDispatchedInputEvent();
337 DOMHighResTimeStamp
PerformanceMainThread::GetPerformanceTimingFromString(
338 const nsAString
& aProperty
) {
339 // ::Measure expects the values returned from this function to be passed
340 // through ReduceTimePrecision already.
341 if (!IsPerformanceTimingAttribute(aProperty
)) {
344 // Values from Timing() are already reduced
345 if (aProperty
.EqualsLiteral("redirectStart")) {
346 return Timing()->RedirectStart();
348 if (aProperty
.EqualsLiteral("redirectEnd")) {
349 return Timing()->RedirectEnd();
351 if (aProperty
.EqualsLiteral("fetchStart")) {
352 return Timing()->FetchStart();
354 if (aProperty
.EqualsLiteral("domainLookupStart")) {
355 return Timing()->DomainLookupStart();
357 if (aProperty
.EqualsLiteral("domainLookupEnd")) {
358 return Timing()->DomainLookupEnd();
360 if (aProperty
.EqualsLiteral("connectStart")) {
361 return Timing()->ConnectStart();
363 if (aProperty
.EqualsLiteral("secureConnectionStart")) {
364 return Timing()->SecureConnectionStart();
366 if (aProperty
.EqualsLiteral("connectEnd")) {
367 return Timing()->ConnectEnd();
369 if (aProperty
.EqualsLiteral("requestStart")) {
370 return Timing()->RequestStart();
372 if (aProperty
.EqualsLiteral("responseStart")) {
373 return Timing()->ResponseStart();
375 if (aProperty
.EqualsLiteral("responseEnd")) {
376 return Timing()->ResponseEnd();
378 // Values from GetDOMTiming() are not.
379 DOMHighResTimeStamp retValue
;
380 if (aProperty
.EqualsLiteral("navigationStart")) {
381 // DOMHighResTimeStamp is in relation to navigationStart, so this will be
383 retValue
= GetDOMTiming()->GetNavigationStart();
384 } else if (aProperty
.EqualsLiteral("unloadEventStart")) {
385 retValue
= GetDOMTiming()->GetUnloadEventStart();
386 } else if (aProperty
.EqualsLiteral("unloadEventEnd")) {
387 retValue
= GetDOMTiming()->GetUnloadEventEnd();
388 } else if (aProperty
.EqualsLiteral("domLoading")) {
389 retValue
= GetDOMTiming()->GetDomLoading();
390 } else if (aProperty
.EqualsLiteral("domInteractive")) {
391 retValue
= GetDOMTiming()->GetDomInteractive();
392 } else if (aProperty
.EqualsLiteral("domContentLoadedEventStart")) {
393 retValue
= GetDOMTiming()->GetDomContentLoadedEventStart();
394 } else if (aProperty
.EqualsLiteral("domContentLoadedEventEnd")) {
395 retValue
= GetDOMTiming()->GetDomContentLoadedEventEnd();
396 } else if (aProperty
.EqualsLiteral("domComplete")) {
397 retValue
= GetDOMTiming()->GetDomComplete();
398 } else if (aProperty
.EqualsLiteral("loadEventStart")) {
399 retValue
= GetDOMTiming()->GetLoadEventStart();
400 } else if (aProperty
.EqualsLiteral("loadEventEnd")) {
401 retValue
= GetDOMTiming()->GetLoadEventEnd();
404 "IsPerformanceTimingAttribute and GetPerformanceTimingFromString are "
408 return nsRFPService::ReduceTimePrecisionAsMSecs(
409 retValue
, GetRandomTimelineSeed(), mRTPCallerType
);
412 void PerformanceMainThread::InsertUserEntry(PerformanceEntry
* aEntry
) {
413 MOZ_ASSERT(NS_IsMainThread());
416 double markCreationEpoch
= 0;
418 if (StaticPrefs::dom_performance_enable_user_timing_logging() ||
419 StaticPrefs::dom_performance_enable_notify_performance_timing()) {
420 nsresult rv
= NS_ERROR_FAILURE
;
421 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
422 if (owner
&& owner
->GetDocumentURI()) {
423 rv
= owner
->GetDocumentURI()->GetHost(uri
);
427 // If we have no URI, just put in "none".
428 uri
.AssignLiteral("none");
431 // PR_Now() returns a signed 64-bit integer. Since it represents a
432 // timestamp, only ~32-bits will represent the value which should safely fit
434 markCreationEpoch
= static_cast<double>(PR_Now() / PR_USEC_PER_MSEC
);
436 if (StaticPrefs::dom_performance_enable_user_timing_logging()) {
437 Performance::LogEntry(aEntry
, uri
);
441 if (StaticPrefs::dom_performance_enable_notify_performance_timing()) {
442 TimingNotification(aEntry
, uri
, markCreationEpoch
);
445 Performance::InsertUserEntry(aEntry
);
448 TimeStamp
PerformanceMainThread::CreationTimeStamp() const {
449 return GetDOMTiming()->GetNavigationStartTimeStamp();
452 DOMHighResTimeStamp
PerformanceMainThread::CreationTime() const {
453 return GetDOMTiming()->GetNavigationStart();
456 void PerformanceMainThread::CreateNavigationTimingEntry() {
457 MOZ_ASSERT(!mDocEntry
, "mDocEntry should be null.");
459 if (!StaticPrefs::dom_enable_performance_navigation_timing()) {
464 GetURLSpecFromChannel(mChannel
, name
);
466 UniquePtr
<PerformanceTimingData
> timing(
467 new PerformanceTimingData(mChannel
, nullptr, 0));
469 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
471 timing
->SetPropertiesFromHttpChannel(httpChannel
, mChannel
);
474 mDocEntry
= new PerformanceNavigationTiming(std::move(timing
), this, name
);
477 void PerformanceMainThread::UpdateNavigationTimingEntry() {
482 // Let's update some values.
483 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
485 mDocEntry
->UpdatePropertiesFromHttpChannel(httpChannel
, mChannel
);
489 void PerformanceMainThread::QueueNavigationTimingEntry() {
494 UpdateNavigationTimingEntry();
496 QueueEntry(mDocEntry
);
499 void PerformanceMainThread::QueueLargestContentfulPaintEntry(
500 LargestContentfulPaint
* aEntry
) {
501 MOZ_ASSERT(StaticPrefs::dom_enable_largest_contentful_paint());
505 EventCounts
* PerformanceMainThread::EventCounts() {
506 MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
510 void PerformanceMainThread::GetEntries(
511 nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
512 aRetval
= mResourceEntries
.Clone();
513 aRetval
.AppendElements(mUserEntries
);
516 aRetval
.AppendElement(mDocEntry
);
520 aRetval
.AppendElement(mFCPTiming
);
522 aRetval
.Sort(PerformanceEntryComparator());
525 void PerformanceMainThread::GetEntriesByType(
526 const nsAString
& aEntryType
, nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
527 RefPtr
<nsAtom
> type
= NS_Atomize(aEntryType
);
528 if (type
== nsGkAtoms::navigation
) {
532 aRetval
.AppendElement(mDocEntry
);
537 if (type
== nsGkAtoms::paint
) {
539 aRetval
.AppendElement(mFCPTiming
);
544 if (type
== nsGkAtoms::firstInput
&& mFirstInputEvent
) {
545 aRetval
.AppendElement(mFirstInputEvent
);
549 Performance::GetEntriesByType(aEntryType
, aRetval
);
551 void PerformanceMainThread::GetEntriesByTypeForObserver(
552 const nsAString
& aEntryType
, nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
553 if (aEntryType
.EqualsLiteral("event")) {
554 aRetval
.AppendElements(mEventTimingEntries
);
558 if (StaticPrefs::dom_enable_largest_contentful_paint()) {
559 if (aEntryType
.EqualsLiteral("largest-contentful-paint")) {
560 aRetval
.AppendElements(mLargestContentfulPaintEntries
);
565 return GetEntriesByType(aEntryType
, aRetval
);
568 void PerformanceMainThread::GetEntriesByName(
569 const nsAString
& aName
, const Optional
<nsAString
>& aEntryType
,
570 nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
571 Performance::GetEntriesByName(aName
, aEntryType
, aRetval
);
573 if (mFCPTiming
&& mFCPTiming
->GetName()->Equals(aName
) &&
574 (!aEntryType
.WasPassed() ||
575 mFCPTiming
->GetEntryType()->Equals(aEntryType
.Value()))) {
576 aRetval
.AppendElement(mFCPTiming
);
580 // The navigation entry is the first one. If it exists and the name matches,
581 // let put it in front.
582 if (mDocEntry
&& mDocEntry
->GetName()->Equals(aName
)) {
583 aRetval
.InsertElementAt(0, mDocEntry
);
588 mozilla::PresShell
* PerformanceMainThread::GetPresShell() {
589 nsIGlobalObject
* ownerGlobal
= GetOwnerGlobal();
593 if (Document
* doc
= ownerGlobal
->GetAsInnerWindow()->GetExtantDoc()) {
594 return doc
->GetPresShell();
599 void PerformanceMainThread::IncEventCount(const nsAtom
* aType
) {
600 MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
602 // This occurs when the pref was false when the performance
603 // object was first created, and became true later. It's
604 // okay to return early because eventCounts is not exposed.
610 uint64_t count
= EventCounts_Binding::MaplikeHelpers::Get(
611 mEventCounts
, nsDependentAtomString(aType
), rv
);
612 MOZ_ASSERT(!rv
.Failed());
613 EventCounts_Binding::MaplikeHelpers::Set(
614 mEventCounts
, nsDependentAtomString(aType
), ++count
, rv
);
615 MOZ_ASSERT(!rv
.Failed());
618 size_t PerformanceMainThread::SizeOfEventEntries(
619 mozilla::MallocSizeOf aMallocSizeOf
) const {
620 size_t eventEntries
= 0;
621 for (const PerformanceEventTiming
* entry
: mEventTimingEntries
) {
622 eventEntries
+= entry
->SizeOfIncludingThis(aMallocSizeOf
);
627 void PerformanceMainThread::ProcessElementTiming() {
628 if (!StaticPrefs::dom_enable_largest_contentful_paint()) {
631 const bool shouldLCPDataEmpty
=
632 HasDispatchedInputEvent() || HasDispatchedScrollEvent();
633 MOZ_ASSERT_IF(shouldLCPDataEmpty
,
634 mTextFrameUnions
.IsEmpty() && mImageLCPEntryMap
.IsEmpty());
636 if (shouldLCPDataEmpty
) {
640 nsPresContext
* presContext
= GetPresShell()->GetPresContext();
641 MOZ_ASSERT(presContext
);
643 // After https://github.com/w3c/largest-contentful-paint/issues/104 is
644 // resolved, LargestContentfulPaint and FirstContentfulPaint should
645 // be using the same timestamp, which should be the same timestamp
646 // as to what https://w3c.github.io/paint-timing/#mark-paint-timing step 2
648 // TODO(sefeng): Check the timestamp after this issue is resolved.
649 TimeStamp rawNowTime
= presContext
->GetMarkPaintTimingStart();
651 MOZ_ASSERT(GetOwnerGlobal());
652 Document
* document
= GetOwnerGlobal()->GetAsInnerWindow()->GetExtantDoc();
654 !nsContentUtils::GetInProcessSubtreeRootDocument(document
)->IsActive()) {
658 nsTArray
<ImagePendingRendering
> imagesPendingRendering
=
659 std::move(mImagesPendingRendering
);
660 for (const auto& imagePendingRendering
: imagesPendingRendering
) {
661 RefPtr
<Element
> element
= imagePendingRendering
.GetElement();
666 MOZ_ASSERT(imagePendingRendering
.mLoadTime
<= rawNowTime
);
667 if (imgRequestProxy
* requestProxy
=
668 imagePendingRendering
.GetImgRequestProxy()) {
669 LCPHelpers::CreateLCPEntryForImage(
670 this, element
, requestProxy
, imagePendingRendering
.mLoadTime
,
671 rawNowTime
, imagePendingRendering
.mLCPImageEntryKey
);
675 MOZ_ASSERT(mImagesPendingRendering
.IsEmpty());
678 void PerformanceMainThread::FinalizeLCPEntriesForText() {
679 nsPresContext
* presContext
= GetPresShell()->GetPresContext();
680 MOZ_ASSERT(presContext
);
682 bool canFinalize
= StaticPrefs::dom_enable_largest_contentful_paint() &&
683 !presContext
->HasStoppedGeneratingLCP();
684 nsTHashMap
<nsRefPtrHashKey
<Element
>, nsRect
> textFrameUnion
=
685 std::move(GetTextFrameUnions());
687 for (const auto& textFrameUnion
: textFrameUnion
) {
688 LCPHelpers::FinalizeLCPEntryForText(
689 this, presContext
->GetMarkPaintTimingStart(), textFrameUnion
.GetKey(),
690 textFrameUnion
.GetData(), presContext
);
693 MOZ_ASSERT(GetTextFrameUnions().IsEmpty());
696 void PerformanceMainThread::StoreImageLCPEntry(
697 Element
* aElement
, imgRequestProxy
* aImgRequestProxy
,
698 LargestContentfulPaint
* aEntry
) {
699 mImageLCPEntryMap
.InsertOrUpdate({aElement
, aImgRequestProxy
}, aEntry
);
702 already_AddRefed
<LargestContentfulPaint
>
703 PerformanceMainThread::GetImageLCPEntry(Element
* aElement
,
704 imgRequestProxy
* aImgRequestProxy
) {
705 Maybe
<RefPtr
<LargestContentfulPaint
>> entry
=
706 mImageLCPEntryMap
.Extract({aElement
, aImgRequestProxy
});
707 if (entry
.isNothing()) {
711 Document
* doc
= aElement
->GetComposedDoc();
712 MOZ_ASSERT(doc
, "Element should be connected when it's painted");
714 Maybe
<LCPImageEntryKey
>& contentIdentifier
=
715 entry
.value()->GetLCPImageEntryKey();
716 if (contentIdentifier
.isSome()) {
717 doc
->ContentIdentifiersForLCP().EnsureRemoved(contentIdentifier
.value());
718 contentIdentifier
.reset();
721 return entry
.value().forget();
724 bool PerformanceMainThread::UpdateLargestContentfulPaintSize(double aSize
) {
725 if (aSize
> mLargestContentfulPaintSize
) {
726 mLargestContentfulPaintSize
= aSize
;
732 void PerformanceMainThread::SetHasDispatchedScrollEvent() {
733 mHasDispatchedScrollEvent
= true;
734 ClearGeneratedTempDataForLCP();
737 void PerformanceMainThread::SetHasDispatchedInputEvent() {
738 mHasDispatchedInputEvent
= true;
739 ClearGeneratedTempDataForLCP();
742 void PerformanceMainThread::ClearGeneratedTempDataForLCP() {
743 mTextFrameUnions
.Clear();
744 mImageLCPEntryMap
.Clear();
745 mImagesPendingRendering
.Clear();
747 nsIGlobalObject
* ownerGlobal
= GetOwnerGlobal();
752 if (Document
* document
= ownerGlobal
->GetAsInnerWindow()->GetExtantDoc()) {
753 document
->ContentIdentifiersForLCP().Clear();
756 } // namespace mozilla::dom