Bug 1722322 - Implement LargestContentfulPaint r=emilio
Spec: https://w3c.github.io/largest-contentful-paint Differential Revision: https://phabricator.services.mozilla.com/D151079
This commit is contained in:
@@ -647,7 +647,7 @@ IntersectionInput DOMIntersectionObserver::ComputeInput(
|
|||||||
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
|
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
|
||||||
// (steps 2.1 - 2.5)
|
// (steps 2.1 - 2.5)
|
||||||
IntersectionOutput DOMIntersectionObserver::Intersect(
|
IntersectionOutput DOMIntersectionObserver::Intersect(
|
||||||
const IntersectionInput& aInput, Element& aTarget,
|
const IntersectionInput& aInput, const Element& aTarget,
|
||||||
IsContentVisibilityObserver aIsContentVisibilityObserver) {
|
IsContentVisibilityObserver aIsContentVisibilityObserver) {
|
||||||
const bool isSimilarOrigin = SimilarOrigin(aTarget, aInput.mRootNode) ==
|
const bool isSimilarOrigin = SimilarOrigin(aTarget, aInput.mRootNode) ==
|
||||||
BrowsingContextOrigin::Similar;
|
BrowsingContextOrigin::Similar;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ class DOMIntersectionObserver final : public nsISupports,
|
|||||||
|
|
||||||
enum class IsContentVisibilityObserver : bool { No, Yes };
|
enum class IsContentVisibilityObserver : bool { No, Yes };
|
||||||
static IntersectionOutput Intersect(
|
static IntersectionOutput Intersect(
|
||||||
const IntersectionInput&, Element&,
|
const IntersectionInput&, const Element&,
|
||||||
IsContentVisibilityObserver = IsContentVisibilityObserver::No);
|
IsContentVisibilityObserver = IsContentVisibilityObserver::No);
|
||||||
// Intersects with a given rect, already relative to the root frame.
|
// Intersects with a given rect, already relative to the root frame.
|
||||||
static IntersectionOutput Intersect(const IntersectionInput&, const nsRect&);
|
static IntersectionOutput Intersect(const IntersectionInput&, const nsRect&);
|
||||||
@@ -168,6 +168,9 @@ class DOMIntersectionObserver final : public nsISupports,
|
|||||||
static already_AddRefed<DOMIntersectionObserver>
|
static already_AddRefed<DOMIntersectionObserver>
|
||||||
CreateContentVisibilityObserver(Document&);
|
CreateContentVisibilityObserver(Document&);
|
||||||
|
|
||||||
|
static Maybe<nsRect> EdgeInclusiveIntersection(const nsRect& aRect,
|
||||||
|
const nsRect& aOtherRect);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Connect();
|
void Connect();
|
||||||
void QueueIntersectionObserverEntry(Element* aTarget,
|
void QueueIntersectionObserverEntry(Element* aTarget,
|
||||||
|
|||||||
@@ -2539,6 +2539,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
|
|||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentIdentifiersForLCP)
|
||||||
|
|
||||||
// Traverse all our nsCOMArrays.
|
// Traverse all our nsCOMArrays.
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
|
||||||
@@ -2664,6 +2665,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
|
|||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentIdentifiersForLCP);
|
||||||
|
|
||||||
if (tmp->mDocGroup && tmp->mDocGroup->GetBrowsingContextGroup()) {
|
if (tmp->mDocGroup && tmp->mDocGroup->GetBrowsingContextGroup()) {
|
||||||
tmp->mDocGroup->GetBrowsingContextGroup()->RemoveDocument(tmp,
|
tmp->mDocGroup->GetBrowsingContextGroup()->RemoveDocument(tmp,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
#include "mozilla/dom/RadioGroupContainer.h"
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "mozilla/dom/TreeOrderedArray.h"
|
#include "mozilla/dom/TreeOrderedArray.h"
|
||||||
#include "mozilla/dom/ViewportMetaData.h"
|
#include "mozilla/dom/ViewportMetaData.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "mozilla/glean/GleanMetrics.h"
|
#include "mozilla/glean/GleanMetrics.h"
|
||||||
#include "nsAtom.h"
|
#include "nsAtom.h"
|
||||||
#include "nsCOMArray.h"
|
#include "nsCOMArray.h"
|
||||||
@@ -2428,6 +2429,10 @@ class Document : public nsINode,
|
|||||||
|
|
||||||
LinkedList<MediaQueryList>& MediaQueryLists() { return mDOMMediaQueryLists; }
|
LinkedList<MediaQueryList>& MediaQueryLists() { return mDOMMediaQueryLists; }
|
||||||
|
|
||||||
|
nsTHashtable<LCPEntryHashEntry>& ContentIdentifiersForLCP() {
|
||||||
|
return mContentIdentifiersForLCP;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the compatibility mode for this document
|
* Get the compatibility mode for this document
|
||||||
*/
|
*/
|
||||||
@@ -5043,6 +5048,11 @@ class Document : public nsINode,
|
|||||||
// Our live MediaQueryLists
|
// Our live MediaQueryLists
|
||||||
LinkedList<MediaQueryList> mDOMMediaQueryLists;
|
LinkedList<MediaQueryList> mDOMMediaQueryLists;
|
||||||
|
|
||||||
|
// A hashset to keep track of which {element, imgRequestProxy}
|
||||||
|
// combination has been processed to avoid considering the same
|
||||||
|
// element twice for LargestContentfulPaint.
|
||||||
|
nsTHashtable<LCPEntryHashEntry> mContentIdentifiersForLCP;
|
||||||
|
|
||||||
// Array of observers
|
// Array of observers
|
||||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||||
|
|
||||||
|
|||||||
@@ -181,8 +181,13 @@ enum : uint32_t {
|
|||||||
// element or has a HTML datalist element ancestor.
|
// element or has a HTML datalist element ancestor.
|
||||||
ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR = ELEMENT_FLAG_BIT(4),
|
ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR = ELEMENT_FLAG_BIT(4),
|
||||||
|
|
||||||
|
// If this flag is set on an element, that means this element
|
||||||
|
// has been considered by our LargestContentfulPaint algorithm and
|
||||||
|
// it's not going to be considered again.
|
||||||
|
ELEMENT_PROCESSED_BY_LCP_FOR_TEXT = ELEMENT_FLAG_BIT(5),
|
||||||
|
|
||||||
// Remaining bits are for subclasses
|
// Remaining bits are for subclasses
|
||||||
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 5
|
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 6
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef ELEMENT_FLAG_BIT
|
#undef ELEMENT_FLAG_BIT
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
#include "mozilla/dom/ScriptSettings.h"
|
#include "mozilla/dom/ScriptSettings.h"
|
||||||
#include "mozilla/intl/LocaleService.h"
|
#include "mozilla/intl/LocaleService.h"
|
||||||
#include "mozilla/intl/Locale.h"
|
#include "mozilla/intl/Locale.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
||||||
#include "mozilla/widget/TextRecognition.h"
|
#include "mozilla/widget/TextRecognition.h"
|
||||||
|
|
||||||
@@ -254,9 +255,11 @@ void nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest,
|
|||||||
FireEvent(u"error"_ns);
|
FireEvent(u"error"_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
SVGObserverUtils::InvalidateDirectRenderingObservers(
|
Element* element = AsContent()->AsElement();
|
||||||
AsContent()->AsElement());
|
SVGObserverUtils::InvalidateDirectRenderingObservers(element);
|
||||||
MaybeResolveDecodePromises();
|
MaybeResolveDecodePromises();
|
||||||
|
LargestContentfulPaint::MaybeProcessImageForElementTiming(mCurrentRequest,
|
||||||
|
element);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsImageLoadingContent::OnUnlockedDraw() {
|
void nsImageLoadingContent::OnUnlockedDraw() {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include "mozilla/dom/NotifyPaintEvent.h"
|
#include "mozilla/dom/NotifyPaintEvent.h"
|
||||||
#include "mozilla/dom/PageTransitionEvent.h"
|
#include "mozilla/dom/PageTransitionEvent.h"
|
||||||
#include "mozilla/dom/PerformanceEventTiming.h"
|
#include "mozilla/dom/PerformanceEventTiming.h"
|
||||||
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
#include "mozilla/dom/PointerEvent.h"
|
#include "mozilla/dom/PointerEvent.h"
|
||||||
#include "mozilla/dom/RootedDictionary.h"
|
#include "mozilla/dom/RootedDictionary.h"
|
||||||
#include "mozilla/dom/ScrollAreaEvent.h"
|
#include "mozilla/dom/ScrollAreaEvent.h"
|
||||||
@@ -838,6 +839,14 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
|
|||||||
if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
|
if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
|
||||||
eventTimingEntry =
|
eventTimingEntry =
|
||||||
PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
|
PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
|
||||||
|
|
||||||
|
if (aEvent->IsTrusted() && aEvent->mMessage == eScroll) {
|
||||||
|
if (auto* perf = aPresContext->GetPerformanceMainThread()) {
|
||||||
|
if (!perf->HasDispatchedScrollEvent()) {
|
||||||
|
perf->SetHasDispatchedScrollEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retargeted = false;
|
bool retargeted = false;
|
||||||
|
|||||||
@@ -5,15 +5,30 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsLayoutUtils.h"
|
||||||
#include "nsRFPService.h"
|
#include "nsRFPService.h"
|
||||||
#include "Performance.h"
|
#include "Performance.h"
|
||||||
|
#include "imgRequest.h"
|
||||||
#include "PerformanceMainThread.h"
|
#include "PerformanceMainThread.h"
|
||||||
#include "LargestContentfulPaint.h"
|
#include "LargestContentfulPaint.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/DOMIntersectionObserver.h"
|
||||||
|
#include "mozilla/dom/Document.h"
|
||||||
|
#include "mozilla/dom/Element.h"
|
||||||
|
|
||||||
|
#include "mozilla/PresShell.h"
|
||||||
|
#include "mozilla/Logging.h"
|
||||||
|
#include "mozilla/nsVideoFrame.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
static LazyLogModule gLCPLogging("LargestContentfulPaint");
|
||||||
|
|
||||||
|
#define LOG(...) MOZ_LOG(gLCPLogging, LogLevel::Debug, (__VA_ARGS__))
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(LargestContentfulPaint, PerformanceEntry,
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(LargestContentfulPaint, PerformanceEntry,
|
||||||
mPerformance, mURI, mElement)
|
mPerformance, mURI, mElement,
|
||||||
|
mLCPImageEntryKey)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LargestContentfulPaint)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LargestContentfulPaint)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
|
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
|
||||||
@@ -21,19 +36,34 @@ NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
|
|||||||
NS_IMPL_ADDREF_INHERITED(LargestContentfulPaint, PerformanceEntry)
|
NS_IMPL_ADDREF_INHERITED(LargestContentfulPaint, PerformanceEntry)
|
||||||
NS_IMPL_RELEASE_INHERITED(LargestContentfulPaint, PerformanceEntry)
|
NS_IMPL_RELEASE_INHERITED(LargestContentfulPaint, PerformanceEntry)
|
||||||
|
|
||||||
LargestContentfulPaint::LargestContentfulPaint(Performance* aPerformance,
|
static double GetAreaInDoublePixelsFromAppUnits(const nsSize& aSize) {
|
||||||
DOMHighResTimeStamp aRenderTime,
|
return NSAppUnitsToDoublePixels(aSize.Width(), AppUnitsPerCSSPixel()) *
|
||||||
DOMHighResTimeStamp aLoadTime,
|
NSAppUnitsToDoublePixels(aSize.Height(), AppUnitsPerCSSPixel());
|
||||||
unsigned long aSize,
|
}
|
||||||
nsIURI* aURI, Element* aElement)
|
|
||||||
|
static double GetAreaInDoublePixelsFromAppUnits(const nsRect& aRect) {
|
||||||
|
return NSAppUnitsToDoublePixels(aRect.Width(), AppUnitsPerCSSPixel()) *
|
||||||
|
NSAppUnitsToDoublePixels(aRect.Height(), AppUnitsPerCSSPixel());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImagePendingRendering::ImagePendingRendering(
|
||||||
|
const LCPImageEntryKey& aLCPImageEntryKey, DOMHighResTimeStamp aLoadTime)
|
||||||
|
: mLCPImageEntryKey(aLCPImageEntryKey), mLoadTime(aLoadTime) {}
|
||||||
|
|
||||||
|
LargestContentfulPaint::LargestContentfulPaint(
|
||||||
|
PerformanceMainThread* aPerformance, const DOMHighResTimeStamp aRenderTime,
|
||||||
|
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
|
||||||
|
nsIURI* aURI, Element* aElement,
|
||||||
|
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey)
|
||||||
: PerformanceEntry(aPerformance->GetParentObject(), u""_ns,
|
: PerformanceEntry(aPerformance->GetParentObject(), u""_ns,
|
||||||
kLargestContentfulPaintName),
|
kLargestContentfulPaintName),
|
||||||
mPerformance(static_cast<PerformanceMainThread*>(aPerformance)),
|
mPerformance(aPerformance),
|
||||||
mRenderTime(aRenderTime),
|
mRenderTime(aRenderTime),
|
||||||
mLoadTime(aLoadTime),
|
mLoadTime(aLoadTime),
|
||||||
mSize(aSize),
|
mSize(aSize),
|
||||||
mURI(aURI),
|
mURI(aURI),
|
||||||
mElement(aElement) {
|
mElement(aElement),
|
||||||
|
mLCPImageEntryKey(aLCPImageEntryKey) {
|
||||||
MOZ_ASSERT(mPerformance);
|
MOZ_ASSERT(mPerformance);
|
||||||
MOZ_ASSERT(mElement);
|
MOZ_ASSERT(mElement);
|
||||||
// The element could be a pseudo-element
|
// The element could be a pseudo-element
|
||||||
@@ -58,6 +88,187 @@ Element* LargestContentfulPaint::GetElement() const {
|
|||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LargestContentfulPaint::BufferEntryIfNeeded() {
|
||||||
|
mPerformance->BufferLargestContentfulPaintEntryIfNeeded(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static*/
|
||||||
|
bool LCPHelpers::IsQualifiedImageRequest(imgRequest* aRequest,
|
||||||
|
Element* aContainingElement) {
|
||||||
|
MOZ_ASSERT(aContainingElement);
|
||||||
|
if (!aRequest) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aRequest->IsChrome()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aContainingElement->ChromeOnlyAccess()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception: this is a poster image of video element
|
||||||
|
if (nsIContent* parent = aContainingElement->GetParent()) {
|
||||||
|
nsVideoFrame* videoFrame = do_QueryFrame(parent->GetPrimaryFrame());
|
||||||
|
if (videoFrame && videoFrame->GetPosterImage() == aContainingElement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception: CSS generated images
|
||||||
|
if (aContainingElement->IsInNativeAnonymousSubtree()) {
|
||||||
|
if (nsINode* rootParentOrHost =
|
||||||
|
aContainingElement
|
||||||
|
->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
|
||||||
|
if (!rootParentOrHost->ChromeOnlyAccess()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void LargestContentfulPaint::MaybeProcessImageForElementTiming(
|
||||||
|
imgRequestProxy* aRequest, Element* aElement) {
|
||||||
|
if (!StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(aRequest);
|
||||||
|
imgRequest* request = aRequest->GetOwner();
|
||||||
|
if (!LCPHelpers::IsQualifiedImageRequest(request, aElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Document* document = aElement->GetComposedDoc();
|
||||||
|
if (!document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPresContext* pc =
|
||||||
|
aElement->GetPresContext(Element::PresContextFor::eForComposedDoc);
|
||||||
|
if (!pc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceMainThread* performance = pc->GetPerformanceMainThread();
|
||||||
|
if (!performance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MOZ_UNLIKELY(MOZ_LOG_TEST(gLCPLogging, LogLevel::Debug))) {
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
aRequest->GetURI(getter_AddRefs(uri));
|
||||||
|
LOG("MaybeProcessImageForElementTiming, Element=%p, URI=%s, "
|
||||||
|
"performance=%p ",
|
||||||
|
aElement, uri ? uri->GetSpecOrDefault().get() : "", performance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LCPImageEntryKey entryKey = LCPImageEntryKey(aElement, aRequest);
|
||||||
|
if (!document->ContentIdentifiersForLCP().EnsureInserted(entryKey)) {
|
||||||
|
LOG(" The content identifier existed for element=%p and request=%p, "
|
||||||
|
"return.",
|
||||||
|
aElement, aRequest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
uint32_t status = imgIRequest::STATUS_NONE;
|
||||||
|
aRequest->GetImageStatus(&status);
|
||||||
|
MOZ_ASSERT(status & imgIRequest::STATUS_LOAD_COMPLETE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Here we are exposing the load time of the image which could be
|
||||||
|
// a privacy concern. The spec talks about it at
|
||||||
|
// https://wicg.github.io/element-timing/#sec-security
|
||||||
|
// TLDR: The similar metric can be obtained by ResourceTiming
|
||||||
|
// API and onload handlers already, so this is not exposing anything
|
||||||
|
// new.
|
||||||
|
DOMHighResTimeStamp nowTime =
|
||||||
|
performance->TimeStampToDOMHighResForRendering(TimeStamp::Now());
|
||||||
|
|
||||||
|
if (!request->IsData() && !request->ShouldReportRenderTimeForLCP()) {
|
||||||
|
// https://wicg.github.io/element-timing/#report-image-element-timing
|
||||||
|
LOG(" Added a pending image rendering (TAO FAILED)");
|
||||||
|
// For TAO failed requests, the renderTime is exposed as 0 for
|
||||||
|
// security reasons.
|
||||||
|
LCPHelpers::CreateLCPEntryForImage(performance, aElement, aRequest, nowTime,
|
||||||
|
0 /* aRenderTime */, entryKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, add the triple (element, imageRequest, now) to root’s images
|
||||||
|
// pending rendering.
|
||||||
|
//
|
||||||
|
// At this point, the loadTime of the image is known, but
|
||||||
|
// the renderTime is unknown, so it's added to ImagesPendingRendering
|
||||||
|
// as a placeholder, and the corresponding LCP entry will be created
|
||||||
|
// when the renderTime is known.
|
||||||
|
LOG(" Added a pending image rendering (TAO PASSED)");
|
||||||
|
performance->AddImagesPendingRendering(
|
||||||
|
ImagePendingRendering{entryKey, nowTime});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LCPHelpers::CanFinalizeLCPEntry(const nsIFrame* aFrame) {
|
||||||
|
if (!StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aFrame) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPresContext* presContext = aFrame->PresContext();
|
||||||
|
return !presContext->HasStoppedGeneratingLCP() &&
|
||||||
|
presContext->GetPerformanceMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCPHelpers::FinalizeLCPEntryForImage(
|
||||||
|
Element* aContainingBlock, imgRequestProxy* aImgRequestProxy,
|
||||||
|
const nsRect& aTargetRectRelativeToSelf) {
|
||||||
|
LOG("FinalizeLCPEntryForImage element=%p", aContainingBlock);
|
||||||
|
if (!aImgRequestProxy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsQualifiedImageRequest(aImgRequestProxy->GetOwner(),
|
||||||
|
aContainingBlock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIFrame* frame = aContainingBlock->GetPrimaryFrame();
|
||||||
|
|
||||||
|
if (!CanFinalizeLCPEntry(frame)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceMainThread* performance =
|
||||||
|
frame->PresContext()->GetPerformanceMainThread();
|
||||||
|
MOZ_ASSERT(performance);
|
||||||
|
|
||||||
|
RefPtr<LargestContentfulPaint> entry =
|
||||||
|
performance->GetImageLCPEntry(aContainingBlock, aImgRequestProxy);
|
||||||
|
if (!entry) {
|
||||||
|
LOG(" No Image Entry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry->UpdateSize(aContainingBlock, aTargetRectRelativeToSelf, performance,
|
||||||
|
true);
|
||||||
|
// If area is less than or equal to document’s largest contentful paint size,
|
||||||
|
// return.
|
||||||
|
if (!performance->UpdateLargestContentfulPaintSize(entry->Size())) {
|
||||||
|
LOG(
|
||||||
|
|
||||||
|
" This paint(%lu) is not greater than the largest paint (%lf)that "
|
||||||
|
"we've "
|
||||||
|
"reported so far, return",
|
||||||
|
entry->Size(), performance->GetLargestContentfulPaintSize());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->QueueEntry();
|
||||||
|
}
|
||||||
|
|
||||||
DOMHighResTimeStamp LargestContentfulPaint::RenderTime() const {
|
DOMHighResTimeStamp LargestContentfulPaint::RenderTime() const {
|
||||||
return nsRFPService::ReduceTimePrecisionAsMSecs(
|
return nsRFPService::ReduceTimePrecisionAsMSecs(
|
||||||
mRenderTime, mPerformance->GetRandomTimelineSeed(),
|
mRenderTime, mPerformance->GetRandomTimelineSeed(),
|
||||||
@@ -77,9 +288,228 @@ DOMHighResTimeStamp LargestContentfulPaint::StartTime() const {
|
|||||||
mPerformance->GetRTPCallerType());
|
mPerformance->GetRTPCallerType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
Element* LargestContentfulPaint::GetContainingBlockForTextFrame(
|
||||||
|
const nsTextFrame* aTextFrame) {
|
||||||
|
nsIFrame* containingFrame = aTextFrame->GetContainingBlock();
|
||||||
|
MOZ_ASSERT(containingFrame);
|
||||||
|
return Element::FromNodeOrNull(containingFrame->GetContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LargestContentfulPaint::QueueEntry() {
|
||||||
|
LOG("QueueEntry entry=%p", this);
|
||||||
|
mPerformance->QueueLargestContentfulPaintEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
void LargestContentfulPaint::GetUrl(nsAString& aUrl) {
|
void LargestContentfulPaint::GetUrl(nsAString& aUrl) {
|
||||||
if (mURI) {
|
if (mURI) {
|
||||||
CopyUTF8toUTF16(mURI->GetSpecOrDefault(), aUrl);
|
CopyUTF8toUTF16(mURI->GetSpecOrDefault(), aUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LargestContentfulPaint::UpdateSize(
|
||||||
|
const Element* aContainingBlock, const nsRect& aTargetRectRelativeToSelf,
|
||||||
|
const PerformanceMainThread* aPerformance, bool aIsImage) {
|
||||||
|
nsIFrame* frame = aContainingBlock->GetPrimaryFrame();
|
||||||
|
MOZ_ASSERT(frame);
|
||||||
|
|
||||||
|
nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
|
||||||
|
if (!rootFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame->Style()->IsInOpacityZeroSubtree()) {
|
||||||
|
LOG(" Opacity:0 return");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following size computation is based on a pending pull request
|
||||||
|
// https://github.com/w3c/largest-contentful-paint/pull/99
|
||||||
|
|
||||||
|
// Let visibleDimensions be concreteDimensions, adjusted for positioning
|
||||||
|
// by object-position or background-position and element’s content box.
|
||||||
|
const nsRect& visibleDimensions = aTargetRectRelativeToSelf;
|
||||||
|
|
||||||
|
// Let clientContentRect be the smallest DOMRectReadOnly containing
|
||||||
|
// visibleDimensions with element’s transforms applied.
|
||||||
|
nsRect clientContentRect = nsLayoutUtils::TransformFrameRectToAncestor(
|
||||||
|
frame, visibleDimensions, rootFrame);
|
||||||
|
|
||||||
|
// Let intersectionRect be the value returned by the intersection rect
|
||||||
|
// algorithm using element as the target and viewport as the root.
|
||||||
|
// (From https://wicg.github.io/element-timing/#sec-report-image-element)
|
||||||
|
IntersectionInput input = DOMIntersectionObserver::ComputeInput(
|
||||||
|
*frame->PresContext()->Document(), rootFrame->GetContent(), nullptr);
|
||||||
|
const IntersectionOutput output =
|
||||||
|
DOMIntersectionObserver::Intersect(input, *aContainingBlock);
|
||||||
|
|
||||||
|
Maybe<nsRect> intersectionRect = output.mIntersectionRect;
|
||||||
|
|
||||||
|
if (intersectionRect.isNothing()) {
|
||||||
|
LOG(" The intersectionRect is nothing for Element=%p. return.",
|
||||||
|
aContainingBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let intersectingClientContentRect be the intersection of clientContentRect
|
||||||
|
// with intersectionRect.
|
||||||
|
Maybe<nsRect> intersectionWithContentRect =
|
||||||
|
clientContentRect.EdgeInclusiveIntersection(intersectionRect.value());
|
||||||
|
|
||||||
|
if (intersectionWithContentRect.isNothing()) {
|
||||||
|
LOG(" The intersectionWithContentRect is nothing for Element=%p. return.",
|
||||||
|
aContainingBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRect renderedRect = intersectionWithContentRect.value();
|
||||||
|
|
||||||
|
double area = GetAreaInDoublePixelsFromAppUnits(renderedRect);
|
||||||
|
|
||||||
|
double viewport = GetAreaInDoublePixelsFromAppUnits(input.mRootRect);
|
||||||
|
|
||||||
|
LOG(" Viewport = %f, RenderRect = %f.", viewport, area);
|
||||||
|
// We don't want to report things that take the entire viewport.
|
||||||
|
if (area >= viewport) {
|
||||||
|
LOG(" The renderedRect is at least same as the area of the "
|
||||||
|
"viewport for Element=%p, return.",
|
||||||
|
aContainingBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<nsSize> intrinsicSize = frame->GetIntrinsicSize().ToSize();
|
||||||
|
const bool hasIntrinsicSize = intrinsicSize && !intrinsicSize->IsEmpty();
|
||||||
|
|
||||||
|
if (aIsImage && hasIntrinsicSize) {
|
||||||
|
// Let (naturalWidth, naturalHeight) be imageRequest’s natural dimension.
|
||||||
|
// Let naturalArea be naturalWidth * naturalHeight.
|
||||||
|
double naturalArea =
|
||||||
|
GetAreaInDoublePixelsFromAppUnits(intrinsicSize.value());
|
||||||
|
|
||||||
|
LOG(" naturalArea = %f", naturalArea);
|
||||||
|
|
||||||
|
// Let boundingClientArea be clientContentRect’s width * clientContentRect’s
|
||||||
|
// height.
|
||||||
|
double boundingClientArea =
|
||||||
|
NSAppUnitsToDoublePixels(clientContentRect.Width(),
|
||||||
|
AppUnitsPerCSSPixel()) *
|
||||||
|
NSAppUnitsToDoublePixels(clientContentRect.Height(),
|
||||||
|
AppUnitsPerCSSPixel());
|
||||||
|
LOG(" boundingClientArea = %f", boundingClientArea);
|
||||||
|
|
||||||
|
// Let scaleFactor be boundingClientArea / naturalArea.
|
||||||
|
double scaleFactor = boundingClientArea / naturalArea;
|
||||||
|
LOG(" scaleFactor = %f", scaleFactor);
|
||||||
|
|
||||||
|
// If scaleFactor is greater than 1, then divide area by scaleFactor.
|
||||||
|
if (scaleFactor > 1) {
|
||||||
|
LOG(" area before sacled doown %f", area);
|
||||||
|
area = area / scaleFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(!mSize);
|
||||||
|
mSize = area;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCPTextFrameHelper::MaybeUnionTextFrame(
|
||||||
|
nsTextFrame* aTextFrame, const nsRect& aRelativeToSelfRect) {
|
||||||
|
if (!StaticPrefs::dom_enable_largest_contentful_paint() ||
|
||||||
|
aTextFrame->PresContext()->HasStoppedGeneratingLCP()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element* containingBlock =
|
||||||
|
LargestContentfulPaint::GetContainingBlockForTextFrame(aTextFrame);
|
||||||
|
if (!containingBlock ||
|
||||||
|
// If element is contained in doc’s set of elements with rendered text,
|
||||||
|
// continue
|
||||||
|
containingBlock->HasFlag(ELEMENT_PROCESSED_BY_LCP_FOR_TEXT) ||
|
||||||
|
containingBlock->ChromeOnlyAccess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(containingBlock->GetPrimaryFrame());
|
||||||
|
|
||||||
|
PerformanceMainThread* perf =
|
||||||
|
aTextFrame->PresContext()->GetPerformanceMainThread();
|
||||||
|
if (!perf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& unionRect = perf->GetTextFrameUnions().LookupOrInsert(containingBlock);
|
||||||
|
unionRect = unionRect.Union(aRelativeToSelfRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCPHelpers::CreateLCPEntryForImage(
|
||||||
|
PerformanceMainThread* aPerformance, Element* aElement,
|
||||||
|
imgRequestProxy* aRequestProxy, const DOMHighResTimeStamp aLoadTime,
|
||||||
|
const DOMHighResTimeStamp aRenderTime,
|
||||||
|
const LCPImageEntryKey& aImageEntryKey) {
|
||||||
|
MOZ_ASSERT(StaticPrefs::dom_enable_largest_contentful_paint());
|
||||||
|
MOZ_ASSERT(aRequestProxy);
|
||||||
|
if (MOZ_UNLIKELY(MOZ_LOG_TEST(gLCPLogging, LogLevel::Debug))) {
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
aRequestProxy->GetURI(getter_AddRefs(uri));
|
||||||
|
LOG("CreateLCPEntryForImage "
|
||||||
|
"Element=%p, aRequestProxy=%p, URI=%s loadTime=%f, "
|
||||||
|
"aRenderTime=%f\n",
|
||||||
|
aElement, aRequestProxy, uri->GetSpecOrDefault().get(), aLoadTime,
|
||||||
|
aRenderTime);
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(aPerformance);
|
||||||
|
MOZ_ASSERT_IF(aRenderTime < aLoadTime, aRenderTime == 0);
|
||||||
|
if (aPerformance->HasDispatchedInputEvent() ||
|
||||||
|
aPerformance->HasDispatchedScrollEvent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let url be the empty string.
|
||||||
|
// If imageRequest is not null, set url to be imageRequest’s request URL.
|
||||||
|
nsCOMPtr<nsIURI> requestURI;
|
||||||
|
aRequestProxy->GetURI(getter_AddRefs(requestURI));
|
||||||
|
|
||||||
|
// At this point, we have all the information about the entry
|
||||||
|
// except the size.
|
||||||
|
RefPtr<LargestContentfulPaint> entry =
|
||||||
|
new LargestContentfulPaint(aPerformance, aRenderTime, aLoadTime, 0,
|
||||||
|
requestURI, aElement, Some(aImageEntryKey));
|
||||||
|
|
||||||
|
LOG(" Upsert a LargestContentfulPaint entry=%p to LCPEntryMap.",
|
||||||
|
entry.get());
|
||||||
|
aPerformance->StoreImageLCPEntry(aElement, aRequestProxy, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCPHelpers::FinalizeLCPEntryForText(
|
||||||
|
PerformanceMainThread* aPerformance, const DOMHighResTimeStamp aRenderTime,
|
||||||
|
Element* aContainingBlock, const nsRect& aTargetRectRelativeToSelf,
|
||||||
|
const nsPresContext* aPresContext) {
|
||||||
|
MOZ_ASSERT(aPerformance);
|
||||||
|
LOG("FinalizeLCPEntryForText element=%p", aContainingBlock);
|
||||||
|
|
||||||
|
if (!aContainingBlock->GetPrimaryFrame()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(CanFinalizeLCPEntry(aContainingBlock->GetPrimaryFrame()));
|
||||||
|
MOZ_ASSERT(!aContainingBlock->HasFlag(ELEMENT_PROCESSED_BY_LCP_FOR_TEXT));
|
||||||
|
MOZ_ASSERT(!aContainingBlock->ChromeOnlyAccess());
|
||||||
|
|
||||||
|
aContainingBlock->SetFlags(ELEMENT_PROCESSED_BY_LCP_FOR_TEXT);
|
||||||
|
|
||||||
|
RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
|
||||||
|
aPerformance, aRenderTime, 0, 0, nullptr, aContainingBlock, Nothing());
|
||||||
|
|
||||||
|
entry->UpdateSize(aContainingBlock, aTargetRectRelativeToSelf, aPerformance,
|
||||||
|
false);
|
||||||
|
// If area is less than or equal to document’s largest contentful paint size,
|
||||||
|
// return.
|
||||||
|
if (!aPerformance->UpdateLargestContentfulPaintSize(entry->Size())) {
|
||||||
|
LOG(" This paint(%lu) is not greater than the largest paint (%lf)that "
|
||||||
|
"we've "
|
||||||
|
"reported so far, return",
|
||||||
|
entry->Size(), aPerformance->GetLargestContentfulPaintSize());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry->QueueEntry();
|
||||||
|
}
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
#define mozilla_dom_LargestContentfulPaint_h___
|
#define mozilla_dom_LargestContentfulPaint_h___
|
||||||
|
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
|
#include "mozilla/dom/Element.h"
|
||||||
#include "mozilla/dom/PerformanceEntry.h"
|
#include "mozilla/dom/PerformanceEntry.h"
|
||||||
#include "mozilla/dom/PerformanceLargestContentfulPaintBinding.h"
|
#include "mozilla/dom/PerformanceLargestContentfulPaintBinding.h"
|
||||||
#include "nsIWeakReferenceUtils.h"
|
|
||||||
|
#include "imgRequestProxy.h"
|
||||||
|
|
||||||
class nsTextFrame;
|
class nsTextFrame;
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
@@ -21,6 +23,143 @@ static constexpr nsLiteralString kLargestContentfulPaintName =
|
|||||||
class Performance;
|
class Performance;
|
||||||
class PerformanceMainThread;
|
class PerformanceMainThread;
|
||||||
|
|
||||||
|
struct LCPImageEntryKey {
|
||||||
|
LCPImageEntryKey(Element* aElement, imgRequestProxy* aImgRequestProxy)
|
||||||
|
: mElement(aElement), mImageRequestProxy(aImgRequestProxy) {
|
||||||
|
MOZ_ASSERT(aElement);
|
||||||
|
MOZ_ASSERT(aImgRequestProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
LCPImageEntryKey(const LCPImageEntryKey& aLCPImageEntryKey) {
|
||||||
|
mElement = aLCPImageEntryKey.mElement;
|
||||||
|
mImageRequestProxy = aLCPImageEntryKey.mImageRequestProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const LCPImageEntryKey& aOther) const {
|
||||||
|
return mElement == aOther.mElement &&
|
||||||
|
mImageRequestProxy == aOther.mImageRequestProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Equals(const Element* aElement,
|
||||||
|
const imgRequestProxy* aImgRequestProxy) const {
|
||||||
|
return mElement == mElement && mImageRequestProxy == mImageRequestProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Element> mElement;
|
||||||
|
RefPtr<imgRequestProxy> mImageRequestProxy;
|
||||||
|
|
||||||
|
~LCPImageEntryKey() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionUnlink(LCPImageEntryKey& aField) {
|
||||||
|
aField.mElement = nullptr;
|
||||||
|
aField.mImageRequestProxy = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionTraverse(
|
||||||
|
nsCycleCollectionTraversalCallback& aCallback, LCPImageEntryKey& aField,
|
||||||
|
const char* aName, uint32_t aFlags = 0) {
|
||||||
|
ImplCycleCollectionTraverse(aCallback, aField.mElement,
|
||||||
|
"LCPImageEntryKey.mElement", aFlags);
|
||||||
|
ImplCycleCollectionTraverse(aCallback, aField.mImageRequestProxy,
|
||||||
|
"LCPImageEntryKey.mImageRequestProxy", aFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LCPTextFrameHelper final {
|
||||||
|
static void MaybeUnionTextFrame(nsTextFrame* aTextFrame,
|
||||||
|
const nsRect& aRelativeToSelfRect);
|
||||||
|
static void HasContainingElementBeenProcessed();
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImagePendingRendering final {
|
||||||
|
public:
|
||||||
|
ImagePendingRendering(const LCPImageEntryKey& aLCPImageEntryKey,
|
||||||
|
DOMHighResTimeStamp aLoadTime);
|
||||||
|
|
||||||
|
Element* GetElement() const { return mLCPImageEntryKey.mElement; }
|
||||||
|
|
||||||
|
imgRequestProxy* GetImgRequestProxy() const {
|
||||||
|
return mLCPImageEntryKey.mImageRequestProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
LCPImageEntryKey mLCPImageEntryKey;
|
||||||
|
DOMHighResTimeStamp mLoadTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionTraverse(
|
||||||
|
nsCycleCollectionTraversalCallback& aCallback,
|
||||||
|
ImagePendingRendering& aField, const char* aName, uint32_t aFlags = 0) {
|
||||||
|
ImplCycleCollectionTraverse(aCallback, aField.mLCPImageEntryKey,
|
||||||
|
"ImagePendingRendering.mImageEntryKey",
|
||||||
|
aCallback.Flags());
|
||||||
|
}
|
||||||
|
|
||||||
|
class LCPEntryHashEntry : public PLDHashEntryHdr {
|
||||||
|
public:
|
||||||
|
typedef const LCPImageEntryKey& KeyType;
|
||||||
|
typedef const LCPImageEntryKey* KeyTypePointer;
|
||||||
|
|
||||||
|
explicit LCPEntryHashEntry(KeyTypePointer aKey) : mKey(*aKey) {}
|
||||||
|
LCPEntryHashEntry(LCPEntryHashEntry&&) = default;
|
||||||
|
|
||||||
|
~LCPEntryHashEntry() = default;
|
||||||
|
|
||||||
|
bool KeyEquals(KeyTypePointer aKey) const { return mKey == *aKey; }
|
||||||
|
|
||||||
|
KeyType GetKey() const { return mKey; }
|
||||||
|
|
||||||
|
static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
|
||||||
|
|
||||||
|
static PLDHashNumber HashKey(KeyTypePointer aKey) {
|
||||||
|
if (!aKey) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mozilla::HashGeneric(
|
||||||
|
reinterpret_cast<uintptr_t>(aKey->mElement.get()),
|
||||||
|
reinterpret_cast<uintptr_t>(aKey->mImageRequestProxy.get()));
|
||||||
|
}
|
||||||
|
enum { ALLOW_MEMMOVE = true };
|
||||||
|
|
||||||
|
LCPImageEntryKey mKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionTraverse(
|
||||||
|
nsCycleCollectionTraversalCallback& aCallback, LCPEntryHashEntry& aField,
|
||||||
|
const char* aName, uint32_t aFlags = 0) {
|
||||||
|
ImplCycleCollectionTraverse(aCallback, aField.mKey, "LCPEntryHashEntry.mKey",
|
||||||
|
aFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LCPHelpers final {
|
||||||
|
public:
|
||||||
|
// Creates the LCP Entry for images with all information except the size of
|
||||||
|
// the element. The size of the image is unknown at the moment. The entry is
|
||||||
|
// not going to be queued in this function.
|
||||||
|
static void CreateLCPEntryForImage(
|
||||||
|
PerformanceMainThread* aPerformance, Element* aElement,
|
||||||
|
imgRequestProxy* aRequestProxy, const DOMHighResTimeStamp aLoadTime,
|
||||||
|
const DOMHighResTimeStamp aRenderTime,
|
||||||
|
const LCPImageEntryKey& aContentIdentifier);
|
||||||
|
|
||||||
|
// Called when the size of the image is known.
|
||||||
|
static void FinalizeLCPEntryForImage(Element* aContainingBlock,
|
||||||
|
imgRequestProxy* aImgRequestProxy,
|
||||||
|
const nsRect& aTargetRectRelativeToSelf);
|
||||||
|
|
||||||
|
static void FinalizeLCPEntryForText(PerformanceMainThread* aPerformance,
|
||||||
|
const DOMHighResTimeStamp aRenderTime,
|
||||||
|
Element* aContainingBlock,
|
||||||
|
const nsRect& aTargetRectRelativeToSelf,
|
||||||
|
const nsPresContext* aPresContext);
|
||||||
|
|
||||||
|
static bool IsQualifiedImageRequest(imgRequest* aRequest,
|
||||||
|
Element* aContainingElement);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool CanFinalizeLCPEntry(const nsIFrame* aFrame);
|
||||||
|
};
|
||||||
|
|
||||||
// https://w3c.github.io/largest-contentful-paint/
|
// https://w3c.github.io/largest-contentful-paint/
|
||||||
class LargestContentfulPaint final : public PerformanceEntry {
|
class LargestContentfulPaint final : public PerformanceEntry {
|
||||||
public:
|
public:
|
||||||
@@ -29,10 +168,12 @@ class LargestContentfulPaint final : public PerformanceEntry {
|
|||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LargestContentfulPaint,
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LargestContentfulPaint,
|
||||||
PerformanceEntry)
|
PerformanceEntry)
|
||||||
|
|
||||||
LargestContentfulPaint(Performance* aPerformance,
|
LargestContentfulPaint(
|
||||||
DOMHighResTimeStamp aRenderTime,
|
PerformanceMainThread* aPerformance,
|
||||||
DOMHighResTimeStamp aLoadTime, unsigned long aSize,
|
const DOMHighResTimeStamp aRenderTime,
|
||||||
nsIURI* aURI, Element* aElement);
|
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
|
||||||
|
nsIURI* aURI, Element* aElement,
|
||||||
|
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey);
|
||||||
|
|
||||||
JSObject* WrapObject(JSContext* aCx,
|
JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> aGivenProto) override;
|
JS::Handle<JSObject*> aGivenProto) override;
|
||||||
@@ -51,6 +192,23 @@ class LargestContentfulPaint final : public PerformanceEntry {
|
|||||||
|
|
||||||
Element* GetElement() const;
|
Element* GetElement() const;
|
||||||
|
|
||||||
|
static Element* GetContainingBlockForTextFrame(const nsTextFrame* aTextFrame);
|
||||||
|
|
||||||
|
void UpdateSize(const Element* aContainingBlock,
|
||||||
|
const nsRect& aTargetRectRelativeToSelf,
|
||||||
|
const PerformanceMainThread* aPerformance, bool aIsImage);
|
||||||
|
|
||||||
|
void BufferEntryIfNeeded() override;
|
||||||
|
|
||||||
|
static void MaybeProcessImageForElementTiming(imgRequestProxy* aRequest,
|
||||||
|
Element* aElement);
|
||||||
|
|
||||||
|
void QueueEntry();
|
||||||
|
|
||||||
|
const Maybe<LCPImageEntryKey>& GetLCPImageEntryKey() const {
|
||||||
|
return mLCPImageEntryKey;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~LargestContentfulPaint() = default;
|
~LargestContentfulPaint() = default;
|
||||||
|
|
||||||
@@ -63,6 +221,8 @@ class LargestContentfulPaint final : public PerformanceEntry {
|
|||||||
|
|
||||||
RefPtr<Element> mElement;
|
RefPtr<Element> mElement;
|
||||||
RefPtr<nsAtom> mId;
|
RefPtr<nsAtom> mId;
|
||||||
|
|
||||||
|
Maybe<LCPImageEntryKey> mLCPImageEntryKey;
|
||||||
};
|
};
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||||
#include "mozilla/HoldDropJSObjects.h"
|
#include "mozilla/HoldDropJSObjects.h"
|
||||||
#include "PerformanceEventTiming.h"
|
#include "PerformanceEventTiming.h"
|
||||||
|
#include "LargestContentfulPaint.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/Event.h"
|
#include "mozilla/dom/Event.h"
|
||||||
#include "mozilla/dom/EventCounts.h"
|
#include "mozilla/dom/EventCounts.h"
|
||||||
@@ -25,9 +26,13 @@
|
|||||||
#include "nsIChannel.h"
|
#include "nsIChannel.h"
|
||||||
#include "nsIHttpChannel.h"
|
#include "nsIHttpChannel.h"
|
||||||
#include "nsIDocShell.h"
|
#include "nsIDocShell.h"
|
||||||
|
#include "nsTextFrame.h"
|
||||||
|
#include "nsContainerFrame.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
extern mozilla::LazyLogModule gLCPLogging;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec) {
|
void GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec) {
|
||||||
@@ -59,19 +64,24 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
|
|||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
|
||||||
Performance)
|
Performance)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry, mFCPTiming,
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(
|
||||||
mEventTimingEntries, mFirstInputEvent,
|
mTiming, mNavigation, mDocEntry, mFCPTiming, mEventTimingEntries,
|
||||||
mPendingPointerDown,
|
mLargestContentfulPaintEntries, mFirstInputEvent, mPendingPointerDown,
|
||||||
mPendingEventTimingEntries, mEventCounts)
|
mPendingEventTimingEntries, mEventCounts)
|
||||||
|
tmp->mImageLCPEntryMap.Clear();
|
||||||
|
tmp->mTextFrameUnions.Clear();
|
||||||
|
tmp->mImagesPendingRendering.Clear();
|
||||||
mozilla::DropJSObjects(tmp);
|
mozilla::DropJSObjects(tmp);
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
|
||||||
Performance)
|
Performance)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming, mNavigation, mDocEntry, mFCPTiming,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
|
||||||
mEventTimingEntries, mFirstInputEvent,
|
mTiming, mNavigation, mDocEntry, mFCPTiming, mEventTimingEntries,
|
||||||
mPendingPointerDown,
|
mLargestContentfulPaintEntries, mFirstInputEvent, mPendingPointerDown,
|
||||||
mPendingEventTimingEntries, mEventCounts)
|
mPendingEventTimingEntries, mEventCounts, mImagesPendingRendering,
|
||||||
|
mImageLCPEntryMap, mTextFrameUnions)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
|
||||||
@@ -241,6 +251,15 @@ void PerformanceMainThread::BufferEventTimingEntryIfNeeded(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::BufferLargestContentfulPaintEntryIfNeeded(
|
||||||
|
LargestContentfulPaint* aEntry) {
|
||||||
|
MOZ_ASSERT(StaticPrefs::dom_enable_largest_contentful_paint());
|
||||||
|
if (mLargestContentfulPaintEntries.Length() <
|
||||||
|
kMaxLargestContentfulPaintBufferSize) {
|
||||||
|
mLargestContentfulPaintEntries.AppendElement(aEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PerformanceMainThread::DispatchPendingEventTimingEntries() {
|
void PerformanceMainThread::DispatchPendingEventTimingEntries() {
|
||||||
DOMHighResTimeStamp renderingTime = NowUnclamped();
|
DOMHighResTimeStamp renderingTime = NowUnclamped();
|
||||||
|
|
||||||
@@ -267,7 +286,7 @@ void PerformanceMainThread::DispatchPendingEventTimingEntries() {
|
|||||||
MOZ_ASSERT(!mFirstInputEvent);
|
MOZ_ASSERT(!mFirstInputEvent);
|
||||||
mFirstInputEvent = mPendingPointerDown.forget();
|
mFirstInputEvent = mPendingPointerDown.forget();
|
||||||
QueueEntry(mFirstInputEvent);
|
QueueEntry(mFirstInputEvent);
|
||||||
mHasDispatchedInputEvent = true;
|
SetHasDispatchedInputEvent();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -277,7 +296,7 @@ void PerformanceMainThread::DispatchPendingEventTimingEntries() {
|
|||||||
mFirstInputEvent = entry->Clone();
|
mFirstInputEvent = entry->Clone();
|
||||||
mFirstInputEvent->SetEntryType(u"first-input"_ns);
|
mFirstInputEvent->SetEntryType(u"first-input"_ns);
|
||||||
QueueEntry(mFirstInputEvent);
|
QueueEntry(mFirstInputEvent);
|
||||||
mHasDispatchedInputEvent = true;
|
SetHasDispatchedInputEvent();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -449,6 +468,12 @@ void PerformanceMainThread::QueueNavigationTimingEntry() {
|
|||||||
QueueEntry(mDocEntry);
|
QueueEntry(mDocEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::QueueLargestContentfulPaintEntry(
|
||||||
|
LargestContentfulPaint* aEntry) {
|
||||||
|
MOZ_ASSERT(StaticPrefs::dom_enable_largest_contentful_paint());
|
||||||
|
QueueEntry(aEntry);
|
||||||
|
}
|
||||||
|
|
||||||
EventCounts* PerformanceMainThread::EventCounts() {
|
EventCounts* PerformanceMainThread::EventCounts() {
|
||||||
MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
|
MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
|
||||||
return mEventCounts;
|
return mEventCounts;
|
||||||
@@ -501,6 +526,14 @@ void PerformanceMainThread::GetEntriesByTypeForObserver(
|
|||||||
aRetval.AppendElements(mEventTimingEntries);
|
aRetval.AppendElements(mEventTimingEntries);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
if (aEntryType.EqualsLiteral("largest-contentful-paint")) {
|
||||||
|
aRetval.AppendElements(mLargestContentfulPaintEntries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GetEntriesByType(aEntryType, aRetval);
|
return GetEntriesByType(aEntryType, aRetval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,4 +595,129 @@ size_t PerformanceMainThread::SizeOfEventEntries(
|
|||||||
}
|
}
|
||||||
return eventEntries;
|
return eventEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::ProcessElementTiming() {
|
||||||
|
if (!StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bool shouldLCPDataEmpty =
|
||||||
|
HasDispatchedInputEvent() || HasDispatchedScrollEvent();
|
||||||
|
MOZ_ASSERT_IF(shouldLCPDataEmpty,
|
||||||
|
mTextFrameUnions.IsEmpty() && mImageLCPEntryMap.IsEmpty());
|
||||||
|
|
||||||
|
if (shouldLCPDataEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPresContext* presContext = GetPresShell()->GetPresContext();
|
||||||
|
MOZ_ASSERT(presContext);
|
||||||
|
|
||||||
|
// After https://github.com/w3c/largest-contentful-paint/issues/104 is
|
||||||
|
// resolved, LargestContentfulPaint and FirstContentfulPaint should
|
||||||
|
// be using the same timestamp, which should be the same timestamp
|
||||||
|
// as to what https://w3c.github.io/paint-timing/#mark-paint-timing step 2
|
||||||
|
// defines.
|
||||||
|
// TODO(sefeng): Check the timestamp after this issue is resolved.
|
||||||
|
DOMHighResTimeStamp rawNowTime =
|
||||||
|
TimeStampToDOMHighResForRendering(presContext->GetMarkPaintTimingStart());
|
||||||
|
|
||||||
|
MOZ_ASSERT(GetOwnerGlobal());
|
||||||
|
Document* document = GetOwnerGlobal()->GetAsInnerWindow()->GetExtantDoc();
|
||||||
|
if (!document ||
|
||||||
|
!nsContentUtils::GetInProcessSubtreeRootDocument(document)->IsActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<ImagePendingRendering> imagesPendingRendering =
|
||||||
|
std::move(mImagesPendingRendering);
|
||||||
|
for (const auto& imagePendingRendering : imagesPendingRendering) {
|
||||||
|
RefPtr<Element> element = imagePendingRendering.GetElement();
|
||||||
|
if (!element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(imagePendingRendering.mLoadTime < rawNowTime);
|
||||||
|
if (imgRequestProxy* requestProxy =
|
||||||
|
imagePendingRendering.GetImgRequestProxy()) {
|
||||||
|
LCPHelpers::CreateLCPEntryForImage(
|
||||||
|
this, element, requestProxy, imagePendingRendering.mLoadTime,
|
||||||
|
rawNowTime, imagePendingRendering.mLCPImageEntryKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(mImagesPendingRendering.IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::FinalizeLCPEntriesForText() {
|
||||||
|
nsPresContext* presContext = GetPresShell()->GetPresContext();
|
||||||
|
MOZ_ASSERT(presContext);
|
||||||
|
|
||||||
|
DOMHighResTimeStamp renderTime =
|
||||||
|
TimeStampToDOMHighResForRendering(presContext->GetMarkPaintTimingStart());
|
||||||
|
|
||||||
|
bool canFinalize = StaticPrefs::dom_enable_largest_contentful_paint() &&
|
||||||
|
!presContext->HasStoppedGeneratingLCP();
|
||||||
|
if (canFinalize) {
|
||||||
|
for (const auto& textFrameUnion : GetTextFrameUnions()) {
|
||||||
|
LCPHelpers::FinalizeLCPEntryForText(
|
||||||
|
this, renderTime, textFrameUnion.GetKey(), textFrameUnion.GetData(),
|
||||||
|
presContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearTextFrameUnions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::StoreImageLCPEntry(
|
||||||
|
Element* aElement, imgRequestProxy* aImgRequestProxy,
|
||||||
|
LargestContentfulPaint* aEntry) {
|
||||||
|
mImageLCPEntryMap.InsertOrUpdate({aElement, aImgRequestProxy}, aEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<LargestContentfulPaint>
|
||||||
|
PerformanceMainThread::GetImageLCPEntry(Element* aElement,
|
||||||
|
imgRequestProxy* aImgRequestProxy) {
|
||||||
|
Maybe<RefPtr<LargestContentfulPaint>> entry =
|
||||||
|
mImageLCPEntryMap.Extract({aElement, aImgRequestProxy});
|
||||||
|
if (entry.isNothing()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Document* doc = aElement->GetComposedDoc();
|
||||||
|
MOZ_ASSERT(doc, "Element should be connected when it's painted");
|
||||||
|
|
||||||
|
const Maybe<const LCPImageEntryKey>& contentIdentifier =
|
||||||
|
entry.value()->GetLCPImageEntryKey();
|
||||||
|
if (contentIdentifier.isSome()) {
|
||||||
|
doc->ContentIdentifiersForLCP().EnsureRemoved(contentIdentifier.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.value().forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerformanceMainThread::UpdateLargestContentfulPaintSize(double aSize) {
|
||||||
|
if (aSize > mLargestContentfulPaintSize) {
|
||||||
|
mLargestContentfulPaintSize = aSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::SetHasDispatchedScrollEvent() {
|
||||||
|
mHasDispatchedScrollEvent = true;
|
||||||
|
ClearGeneratedTempDataForLCP();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::SetHasDispatchedInputEvent() {
|
||||||
|
mHasDispatchedInputEvent = true;
|
||||||
|
mImageLCPEntryMap.Clear();
|
||||||
|
ClearGeneratedTempDataForLCP();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceMainThread::ClearTextFrameUnions() { mTextFrameUnions.Clear(); }
|
||||||
|
|
||||||
|
void PerformanceMainThread::ClearGeneratedTempDataForLCP() {
|
||||||
|
ClearTextFrameUnions();
|
||||||
|
mImageLCPEntryMap.Clear();
|
||||||
|
}
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -9,12 +9,19 @@
|
|||||||
|
|
||||||
#include "Performance.h"
|
#include "Performance.h"
|
||||||
#include "PerformanceStorage.h"
|
#include "PerformanceStorage.h"
|
||||||
|
#include "LargestContentfulPaint.h"
|
||||||
|
#include "nsTextFrame.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
class PerformanceNavigationTiming;
|
class PerformanceNavigationTiming;
|
||||||
class PerformanceEventTiming;
|
class PerformanceEventTiming;
|
||||||
|
|
||||||
|
using ImageLCPEntryMap =
|
||||||
|
nsTHashMap<LCPEntryHashEntry, RefPtr<LargestContentfulPaint>>;
|
||||||
|
|
||||||
|
using TextFrameUnions = nsTHashMap<nsRefPtrHashKey<Element>, nsRect>;
|
||||||
|
|
||||||
class PerformanceMainThread final : public Performance,
|
class PerformanceMainThread final : public Performance,
|
||||||
public PerformanceStorage {
|
public PerformanceStorage {
|
||||||
public:
|
public:
|
||||||
@@ -45,11 +52,14 @@ class PerformanceMainThread final : public Performance,
|
|||||||
const nsAString& aInitiatorType,
|
const nsAString& aInitiatorType,
|
||||||
const nsAString& aEntryName);
|
const nsAString& aEntryName);
|
||||||
virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) override;
|
virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) override;
|
||||||
|
bool HadFCPTimingEntry() const { return mFCPTiming; }
|
||||||
|
|
||||||
void InsertEventTimingEntry(PerformanceEventTiming*) override;
|
void InsertEventTimingEntry(PerformanceEventTiming*) override;
|
||||||
void BufferEventTimingEntryIfNeeded(PerformanceEventTiming*) override;
|
void BufferEventTimingEntryIfNeeded(PerformanceEventTiming*) override;
|
||||||
void DispatchPendingEventTimingEntries() override;
|
void DispatchPendingEventTimingEntries() override;
|
||||||
|
|
||||||
|
void BufferLargestContentfulPaintEntryIfNeeded(LargestContentfulPaint*);
|
||||||
|
|
||||||
TimeStamp CreationTimeStamp() const override;
|
TimeStamp CreationTimeStamp() const override;
|
||||||
|
|
||||||
DOMHighResTimeStamp CreationTime() const override;
|
DOMHighResTimeStamp CreationTime() const override;
|
||||||
@@ -87,6 +97,7 @@ class PerformanceMainThread final : public Performance,
|
|||||||
|
|
||||||
void UpdateNavigationTimingEntry() override;
|
void UpdateNavigationTimingEntry() override;
|
||||||
void QueueNavigationTimingEntry() override;
|
void QueueNavigationTimingEntry() override;
|
||||||
|
void QueueLargestContentfulPaintEntry(LargestContentfulPaint* aEntry);
|
||||||
|
|
||||||
size_t SizeOfEventEntries(mozilla::MallocSizeOf aMallocSizeOf) const override;
|
size_t SizeOfEventEntries(mozilla::MallocSizeOf aMallocSizeOf) const override;
|
||||||
|
|
||||||
@@ -94,10 +105,40 @@ class PerformanceMainThread final : public Performance,
|
|||||||
static constexpr uint32_t kDefaultEventTimingDurationThreshold = 104;
|
static constexpr uint32_t kDefaultEventTimingDurationThreshold = 104;
|
||||||
static constexpr double kDefaultEventTimingMinDuration = 16.0;
|
static constexpr double kDefaultEventTimingMinDuration = 16.0;
|
||||||
|
|
||||||
|
static constexpr uint32_t kMaxLargestContentfulPaintBufferSize = 150;
|
||||||
|
|
||||||
class EventCounts* EventCounts() override;
|
class EventCounts* EventCounts() override;
|
||||||
|
|
||||||
bool IsGlobalObjectWindow() const override { return true; };
|
bool IsGlobalObjectWindow() const override { return true; };
|
||||||
|
|
||||||
|
bool HasDispatchedInputEvent() const { return mHasDispatchedInputEvent; }
|
||||||
|
|
||||||
|
void SetHasDispatchedScrollEvent();
|
||||||
|
bool HasDispatchedScrollEvent() const { return mHasDispatchedScrollEvent; }
|
||||||
|
|
||||||
|
void ProcessElementTiming();
|
||||||
|
|
||||||
|
void AddImagesPendingRendering(ImagePendingRendering aImagePendingRendering) {
|
||||||
|
mImagesPendingRendering.AppendElement(aImagePendingRendering);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreImageLCPEntry(Element* aElement, imgRequestProxy* aImgRequestProxy,
|
||||||
|
LargestContentfulPaint* aEntry);
|
||||||
|
|
||||||
|
already_AddRefed<LargestContentfulPaint> GetImageLCPEntry(
|
||||||
|
Element* aElement, imgRequestProxy* aImgRequestProxy);
|
||||||
|
|
||||||
|
bool UpdateLargestContentfulPaintSize(double aSize);
|
||||||
|
double GetLargestContentfulPaintSize() const {
|
||||||
|
return mLargestContentfulPaintSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTHashMap<nsRefPtrHashKey<Element>, nsRect>& GetTextFrameUnions() {
|
||||||
|
return mTextFrameUnions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeLCPEntriesForText();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~PerformanceMainThread();
|
~PerformanceMainThread();
|
||||||
|
|
||||||
@@ -119,23 +160,76 @@ class PerformanceMainThread final : public Performance,
|
|||||||
JS::Heap<JSObject*> mMozMemory;
|
JS::Heap<JSObject*> mMozMemory;
|
||||||
|
|
||||||
nsTArray<RefPtr<PerformanceEventTiming>> mEventTimingEntries;
|
nsTArray<RefPtr<PerformanceEventTiming>> mEventTimingEntries;
|
||||||
|
nsTArray<RefPtr<LargestContentfulPaint>> mLargestContentfulPaintEntries;
|
||||||
|
|
||||||
AutoCleanLinkedList<RefPtr<PerformanceEventTiming>>
|
AutoCleanLinkedList<RefPtr<PerformanceEventTiming>>
|
||||||
mPendingEventTimingEntries;
|
mPendingEventTimingEntries;
|
||||||
bool mHasDispatchedInputEvent = false;
|
bool mHasDispatchedInputEvent = false;
|
||||||
|
bool mHasDispatchedScrollEvent = false;
|
||||||
|
|
||||||
RefPtr<PerformanceEventTiming> mFirstInputEvent;
|
RefPtr<PerformanceEventTiming> mFirstInputEvent;
|
||||||
RefPtr<PerformanceEventTiming> mPendingPointerDown;
|
RefPtr<PerformanceEventTiming> mPendingPointerDown;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void ClearTextFrameUnions();
|
||||||
|
void ClearGeneratedTempDataForLCP();
|
||||||
|
|
||||||
|
void SetHasDispatchedInputEvent();
|
||||||
|
|
||||||
bool mHasQueuedRefreshdriverObserver = false;
|
bool mHasQueuedRefreshdriverObserver = false;
|
||||||
|
|
||||||
RefPtr<class EventCounts> mEventCounts;
|
RefPtr<class EventCounts> mEventCounts;
|
||||||
void IncEventCount(const nsAtom* aType);
|
void IncEventCount(const nsAtom* aType);
|
||||||
|
|
||||||
PresShell* GetPresShell();
|
PresShell* GetPresShell();
|
||||||
|
|
||||||
|
nsTArray<ImagePendingRendering> mImagesPendingRendering;
|
||||||
|
|
||||||
|
// The key is the pair of the element initiates the image loading
|
||||||
|
// and the imgRequestProxy of the image, and the value is
|
||||||
|
// the LCP entry for this image. When the image is
|
||||||
|
// completely loaded, we add it to mImageLCPEntryMap.
|
||||||
|
// Later, when the image is painted, we get the LCP entry from it
|
||||||
|
// to update the size and queue the entry if needed.
|
||||||
|
//
|
||||||
|
// When the initiating element is disconnected from the document,
|
||||||
|
// we keep the orphan entry because if the same memory address is
|
||||||
|
// reused by a different LCP candidate, it'll update
|
||||||
|
// mImageLCPEntryMap precedes before it tries to get the LCP entry.
|
||||||
|
ImageLCPEntryMap mImageLCPEntryMap;
|
||||||
|
|
||||||
|
// Keeps track of the rendered size of the largest contentful paint that
|
||||||
|
// we have processed so far.
|
||||||
|
double mLargestContentfulPaintSize = 0.0;
|
||||||
|
|
||||||
|
// When a text frame is painted, its area (relative to the
|
||||||
|
// containing block) is unioned with other text frames that
|
||||||
|
// belong to the same containing block.
|
||||||
|
// mTextFrameUnions's key is the containing block, and
|
||||||
|
// the value is the unioned area.
|
||||||
|
TextFrameUnions mTextFrameUnions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionTraverse(
|
||||||
|
nsCycleCollectionTraversalCallback& aCallback, ImageLCPEntryMap& aField,
|
||||||
|
const char* aName, uint32_t aFlags = 0) {
|
||||||
|
for (auto& entry : aField) {
|
||||||
|
ImplCycleCollectionTraverse(aCallback, entry.mKey, "ImageLCPEntryMap.mKey", aCallback.Flags());
|
||||||
|
RefPtr<LargestContentfulPaint>* lcpEntry = entry.GetModifiableData();
|
||||||
|
ImplCycleCollectionTraverse(aCallback, *lcpEntry, "ImageLCPEntryMap.mData", aCallback.Flags());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImplCycleCollectionTraverse(
|
||||||
|
nsCycleCollectionTraversalCallback& aCallback, TextFrameUnions& aField,
|
||||||
|
const char* aName, uint32_t aFlags = 0) {
|
||||||
|
for (auto& entry : aField) {
|
||||||
|
ImplCycleCollectionTraverse(
|
||||||
|
aCallback, entry, "TextFrameUnions's key (nsRefPtrHashKey<Element>)",
|
||||||
|
aFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
||||||
#endif // mozilla_dom_PerformanceMainThread_h
|
#endif // mozilla_dom_PerformanceMainThread_h
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "PerformanceEntry.h"
|
#include "PerformanceEntry.h"
|
||||||
|
#include "LargestContentfulPaint.h"
|
||||||
#include "PerformanceObserverEntryList.h"
|
#include "PerformanceObserverEntryList.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
@@ -215,6 +216,12 @@ void PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
if (entryTypes.Contains(kLargestContentfulPaintName) &&
|
||||||
|
!validEntryTypes.Contains(kLargestContentfulPaintName)) {
|
||||||
|
validEntryTypes.AppendElement(kLargestContentfulPaintName);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const nsLiteralString& name : kValidTypeNames) {
|
for (const nsLiteralString& name : kValidTypeNames) {
|
||||||
if (entryTypes.Contains(name) && !validEntryTypes.Contains(name)) {
|
if (entryTypes.Contains(name) && !validEntryTypes.Contains(name)) {
|
||||||
validEntryTypes.AppendElement(name);
|
validEntryTypes.AppendElement(name);
|
||||||
@@ -275,6 +282,12 @@ void PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
if (type == kLargestContentfulPaintName) {
|
||||||
|
typeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!typeValid) {
|
if (!typeValid) {
|
||||||
ReportUnsupportedTypesErrorToConsole(
|
ReportUnsupportedTypesErrorToConsole(
|
||||||
NS_IsMainThread(), UnsupportedEntryTypesIgnoredMsgId, type);
|
NS_IsMainThread(), UnsupportedEntryTypesIgnoredMsgId, type);
|
||||||
@@ -328,6 +341,10 @@ void PerformanceObserver::GetSupportedEntryTypes(
|
|||||||
validTypes.AppendElement(name);
|
validTypes.AppendElement(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
validTypes.AppendElement(u"largest-contentful-paint"_ns);
|
||||||
|
}
|
||||||
for (const nsLiteralString& name : kValidTypeNames) {
|
for (const nsLiteralString& name : kValidTypeNames) {
|
||||||
validTypes.AppendElement(name);
|
validTypes.AppendElement(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -776,6 +776,8 @@ let interfaceNamesInGlobalScope = [
|
|||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
{ name: "KeyframeEffect", insecureContext: true },
|
{ name: "KeyframeEffect", insecureContext: true },
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
|
{ name: "LargestContentfulPaint", insecureContext: true, nightly: true },
|
||||||
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
{ name: "Location", insecureContext: true },
|
{ name: "Location", insecureContext: true },
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
"Lock",
|
"Lock",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ imgRequest::imgRequest(imgLoader* aLoader, const ImageCacheKey& aCacheKey)
|
|||||||
mImageAvailable(false),
|
mImageAvailable(false),
|
||||||
mIsDeniedCrossSiteCORSRequest(false),
|
mIsDeniedCrossSiteCORSRequest(false),
|
||||||
mIsCrossSiteNoCORSRequest(false),
|
mIsCrossSiteNoCORSRequest(false),
|
||||||
|
mShouldReportRenderTimeForLCP(false),
|
||||||
mMutex("imgRequest"),
|
mMutex("imgRequest"),
|
||||||
mProgressTracker(new ProgressTracker()),
|
mProgressTracker(new ProgressTracker()),
|
||||||
mIsMultiPartChannel(false),
|
mIsMultiPartChannel(false),
|
||||||
@@ -641,6 +642,7 @@ imgRequest::OnStartRequest(nsIRequest* aRequest) {
|
|||||||
mIsCrossSiteNoCORSRequest = loadInfo->GetTainting() == LoadTainting::Opaque;
|
mIsCrossSiteNoCORSRequest = loadInfo->GetTainting() == LoadTainting::Opaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateShouldReportRenderTimeForLCP();
|
||||||
// Figure out if we're multipart.
|
// Figure out if we're multipart.
|
||||||
nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
|
nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
|
||||||
{
|
{
|
||||||
@@ -1232,3 +1234,13 @@ imgRequest::OnRedirectVerifyCallback(nsresult result) {
|
|||||||
mRedirectCallback = nullptr;
|
mRedirectCallback = nullptr;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void imgRequest::UpdateShouldReportRenderTimeForLCP() {
|
||||||
|
if (mTimedChannel) {
|
||||||
|
bool allRedirectPassTAO = false;
|
||||||
|
mTimedChannel->GetAllRedirectsPassTimingAllowCheck(&allRedirectPassTAO);
|
||||||
|
mShouldReportRenderTimeForLCP =
|
||||||
|
mTimedChannel->TimingAllowCheck(mTriggeringPrincipal) &&
|
||||||
|
allRedirectPassTAO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -202,6 +202,10 @@ class imgRequest final : public nsIThreadRetargetableStreamListener,
|
|||||||
|
|
||||||
bool IsCrossSiteNoCORSRequest() const { return mIsCrossSiteNoCORSRequest; }
|
bool IsCrossSiteNoCORSRequest() const { return mIsCrossSiteNoCORSRequest; }
|
||||||
|
|
||||||
|
bool ShouldReportRenderTimeForLCP() const {
|
||||||
|
return mShouldReportRenderTimeForLCP;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class FinishPreparingForNewPartRunnable;
|
friend class FinishPreparingForNewPartRunnable;
|
||||||
|
|
||||||
@@ -209,6 +213,8 @@ class imgRequest final : public nsIThreadRetargetableStreamListener,
|
|||||||
|
|
||||||
void FinishPreparingForNewPart(const NewPartResult& aResult);
|
void FinishPreparingForNewPart(const NewPartResult& aResult);
|
||||||
|
|
||||||
|
void UpdateShouldReportRenderTimeForLCP();
|
||||||
|
|
||||||
void Cancel(nsresult aStatus);
|
void Cancel(nsresult aStatus);
|
||||||
|
|
||||||
// Update the cache entry size based on the image container.
|
// Update the cache entry size based on the image container.
|
||||||
@@ -276,6 +282,8 @@ class imgRequest final : public nsIThreadRetargetableStreamListener,
|
|||||||
bool mIsDeniedCrossSiteCORSRequest;
|
bool mIsDeniedCrossSiteCORSRequest;
|
||||||
bool mIsCrossSiteNoCORSRequest;
|
bool mIsCrossSiteNoCORSRequest;
|
||||||
|
|
||||||
|
bool mShouldReportRenderTimeForLCP;
|
||||||
|
|
||||||
mutable mozilla::Mutex mMutex;
|
mutable mozilla::Mutex mMutex;
|
||||||
|
|
||||||
// Member variables protected by mMutex. Note that *all* flags in our bitfield
|
// Member variables protected by mMutex. Note that *all* flags in our bitfield
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ class imgRequestProxy : public mozilla::PreloaderBase,
|
|||||||
bool mHadListener : 1;
|
bool mHadListener : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline nsISupports* ToSupports(imgRequestProxy* p) {
|
||||||
|
return NS_ISUPPORTS_CAST(imgIRequest*, p);
|
||||||
|
}
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(imgRequestProxy, NS_IMGREQUESTPROXY_CID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(imgRequestProxy, NS_IMGREQUESTPROXY_CID)
|
||||||
|
|
||||||
// Used for static image proxies for which no requests are available, so
|
// Used for static image proxies for which no requests are available, so
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
#include "mozilla/dom/FontFaceSet.h"
|
#include "mozilla/dom/FontFaceSet.h"
|
||||||
#include "mozilla/dom/ElementBinding.h"
|
#include "mozilla/dom/ElementBinding.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/AutoRestore.h"
|
#include "mozilla/AutoRestore.h"
|
||||||
@@ -11839,6 +11841,13 @@ void PresShell::EndPaint() {
|
|||||||
}
|
}
|
||||||
return CallState::Continue;
|
return CallState::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (nsPresContext* presContext = GetPresContext()) {
|
||||||
|
if (PerformanceMainThread* perf =
|
||||||
|
presContext->GetPerformanceMainThread()) {
|
||||||
|
perf->FinalizeLCPEntriesForText();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ struct RangePaintInfo;
|
|||||||
class ReflowCountMgr;
|
class ReflowCountMgr;
|
||||||
#endif
|
#endif
|
||||||
class WeakFrame;
|
class WeakFrame;
|
||||||
|
class nsTextFrame;
|
||||||
class ZoomConstraintsClient;
|
class ZoomConstraintsClient;
|
||||||
|
|
||||||
struct nsCallbackEventRequest;
|
struct nsCallbackEventRequest;
|
||||||
@@ -113,6 +114,7 @@ class Element;
|
|||||||
class Event;
|
class Event;
|
||||||
class HTMLSlotElement;
|
class HTMLSlotElement;
|
||||||
class Selection;
|
class Selection;
|
||||||
|
class PerformanceMainThread;
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
#include "mozilla/TimelineManager.h"
|
#include "mozilla/TimelineManager.h"
|
||||||
#include "mozilla/dom/Performance.h"
|
#include "mozilla/dom/Performance.h"
|
||||||
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
#include "mozilla/dom/PerformanceTiming.h"
|
#include "mozilla/dom/PerformanceTiming.h"
|
||||||
#include "mozilla/dom/PerformancePaintTiming.h"
|
#include "mozilla/dom/PerformancePaintTiming.h"
|
||||||
#include "mozilla/layers/APZThreadUtils.h"
|
#include "mozilla/layers/APZThreadUtils.h"
|
||||||
@@ -2781,8 +2782,16 @@ void nsPresContext::NotifyNonBlankPaint() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nsPresContext::HasStoppedGeneratingLCP() const {
|
||||||
|
if (auto* perf = GetPerformanceMainThread()) {
|
||||||
|
return perf->HasDispatchedInputEvent() || perf->HasDispatchedScrollEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void nsPresContext::NotifyContentfulPaint() {
|
void nsPresContext::NotifyContentfulPaint() {
|
||||||
if (mHadFirstContentfulPaint) {
|
if (mHadFirstContentfulPaint && HasStoppedGeneratingLCP()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nsRootPresContext* rootPresContext = GetRootPresContext();
|
nsRootPresContext* rootPresContext = GetRootPresContext();
|
||||||
@@ -2804,19 +2813,24 @@ void nsPresContext::NotifyContentfulPaint() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mHadFirstContentfulPaint = true;
|
|
||||||
mFirstContentfulPaintTransactionId =
|
if (!mHadFirstContentfulPaint) {
|
||||||
Some(rootPresContext->mRefreshDriver->LastTransactionId().Next());
|
mHadFirstContentfulPaint = true;
|
||||||
if (nsPIDOMWindowInner* innerWindow = mDocument->GetInnerWindow()) {
|
mFirstContentfulPaintTransactionId =
|
||||||
if (Performance* perf = innerWindow->GetPerformance()) {
|
Some(rootPresContext->mRefreshDriver->LastTransactionId().Next());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* perf = GetPerformanceMainThread()) {
|
||||||
|
mMarkPaintTimingStart = TimeStamp::Now();
|
||||||
|
MOZ_ASSERT(rootPresContext->RefreshDriver()->IsInRefresh(),
|
||||||
|
"We should only notify contentful paint during refresh "
|
||||||
|
"driver ticks");
|
||||||
|
if (!perf->HadFCPTimingEntry()) {
|
||||||
TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh(
|
TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh(
|
||||||
/* aEnsureTimerStarted */ false);
|
/* aEnsureTimerStarted */ false);
|
||||||
MOZ_ASSERT(!nowTime.IsNull(),
|
MOZ_ASSERT(!nowTime.IsNull(),
|
||||||
"Most recent refresh timestamp should exist since we are in "
|
"Most recent refresh timestamp should exist since we are in "
|
||||||
"a refresh driver tick");
|
"a refresh driver tick");
|
||||||
MOZ_ASSERT(rootPresContext->RefreshDriver()->IsInRefresh(),
|
|
||||||
"We should only notify contentful paint during refresh "
|
|
||||||
"driver ticks");
|
|
||||||
RefPtr<PerformancePaintTiming> paintTiming = new PerformancePaintTiming(
|
RefPtr<PerformancePaintTiming> paintTiming = new PerformancePaintTiming(
|
||||||
perf, u"first-contentful-paint"_ns, nowTime);
|
perf, u"first-contentful-paint"_ns, nowTime);
|
||||||
perf->SetFCPTimingEntry(paintTiming);
|
perf->SetFCPTimingEntry(paintTiming);
|
||||||
@@ -2833,12 +2847,15 @@ void nsPresContext::NotifyContentfulPaint() {
|
|||||||
nsContentUtils::TruncatedURLForDisplay(docURI).get());
|
nsContentUtils::TruncatedURLForDisplay(docURI).get());
|
||||||
PROFILER_MARKER_TEXT(
|
PROFILER_MARKER_TEXT(
|
||||||
"FirstContentfulPaint", DOM,
|
"FirstContentfulPaint", DOM,
|
||||||
MarkerOptions(MarkerTiming::Interval(navigationStart, nowTime),
|
MarkerOptions(
|
||||||
MarkerInnerWindowId(innerWindow->WindowID())),
|
MarkerTiming::Interval(navigationStart, nowTime),
|
||||||
|
MarkerInnerWindowId(mDocument->GetInnerWindow()->WindowID())),
|
||||||
marker);
|
marker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf->ProcessElementTiming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3050,6 +3067,16 @@ void nsPresContext::SetSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
|
|||||||
RestyleHint::RecascadeSubtree());
|
RestyleHint::RecascadeSubtree());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PerformanceMainThread* nsPresContext::GetPerformanceMainThread() const {
|
||||||
|
if (nsPIDOMWindowInner* innerWindow = mDocument->GetInnerWindow()) {
|
||||||
|
if (auto* perf = static_cast<PerformanceMainThread*>(
|
||||||
|
innerWindow->GetPerformance())) {
|
||||||
|
return perf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
void nsPresContext::ValidatePresShellAndDocumentReleation() const {
|
void nsPresContext::ValidatePresShellAndDocumentReleation() const {
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class LayerManager;
|
|||||||
namespace dom {
|
namespace dom {
|
||||||
class Document;
|
class Document;
|
||||||
class Element;
|
class Element;
|
||||||
|
class PerformanceMainThread;
|
||||||
enum class PrefersColorSchemeOverride : uint8_t;
|
enum class PrefersColorSchemeOverride : uint8_t;
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
@@ -214,6 +215,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
|
|||||||
|
|
||||||
void DocumentCharSetChanged(NotNull<const Encoding*> aCharSet);
|
void DocumentCharSetChanged(NotNull<const Encoding*> aCharSet);
|
||||||
|
|
||||||
|
mozilla::dom::PerformanceMainThread* GetPerformanceMainThread() const;
|
||||||
/**
|
/**
|
||||||
* Returns the parent prescontext for this one. Returns null if this is a
|
* Returns the parent prescontext for this one. Returns null if this is a
|
||||||
* root.
|
* root.
|
||||||
@@ -1040,6 +1042,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
|
|||||||
|
|
||||||
bool HadNonBlankPaint() const { return mHadNonBlankPaint; }
|
bool HadNonBlankPaint() const { return mHadNonBlankPaint; }
|
||||||
bool HadFirstContentfulPaint() const { return mHadFirstContentfulPaint; }
|
bool HadFirstContentfulPaint() const { return mHadFirstContentfulPaint; }
|
||||||
|
bool HasStoppedGeneratingLCP() const;
|
||||||
void NotifyNonBlankPaint();
|
void NotifyNonBlankPaint();
|
||||||
void NotifyContentfulPaint();
|
void NotifyContentfulPaint();
|
||||||
void NotifyPaintStatusReset();
|
void NotifyPaintStatusReset();
|
||||||
@@ -1122,6 +1125,10 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::TimeStamp GetMarkPaintTimingStart() const {
|
||||||
|
return mMarkPaintTimingStart;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// May be called multiple times (unlink, destructor)
|
// May be called multiple times (unlink, destructor)
|
||||||
void Destroy();
|
void Destroy();
|
||||||
@@ -1245,6 +1252,9 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
|
|||||||
|
|
||||||
mozilla::TimeStamp mReflowStartTime;
|
mozilla::TimeStamp mReflowStartTime;
|
||||||
|
|
||||||
|
// Defined in https://w3c.github.io/paint-timing/#mark-paint-timing step 2.
|
||||||
|
mozilla::TimeStamp mMarkPaintTimingStart;
|
||||||
|
|
||||||
Maybe<TransactionId> mFirstContentfulPaintTransactionId;
|
Maybe<TransactionId> mFirstContentfulPaintTransactionId;
|
||||||
|
|
||||||
mozilla::UniquePtr<mozilla::MediaFeatureChange>
|
mozilla::UniquePtr<mozilla::MediaFeatureChange>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "nsLayoutUtils.h"
|
#include "nsLayoutUtils.h"
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
|
#include "imgRequest.h"
|
||||||
#include "nsComponentManagerUtils.h"
|
#include "nsComponentManagerUtils.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
#include "nsIXULRuntime.h"
|
#include "nsIXULRuntime.h"
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsTextFrame.h"
|
||||||
#include "mozilla/PendingAnimationTracker.h"
|
#include "mozilla/PendingAnimationTracker.h"
|
||||||
#include "mozilla/PendingFullscreenEvent.h"
|
#include "mozilla/PendingFullscreenEvent.h"
|
||||||
#include "mozilla/dom/PerformanceMainThread.h"
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
@@ -71,6 +73,7 @@
|
|||||||
#include "mozilla/dom/Selection.h"
|
#include "mozilla/dom/Selection.h"
|
||||||
#include "mozilla/dom/VsyncMainChild.h"
|
#include "mozilla/dom/VsyncMainChild.h"
|
||||||
#include "mozilla/dom/WindowBinding.h"
|
#include "mozilla/dom/WindowBinding.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||||
#include "mozilla/RestyleManager.h"
|
#include "mozilla/RestyleManager.h"
|
||||||
#include "mozilla/TaskController.h"
|
#include "mozilla/TaskController.h"
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ EXPORTS.mozilla += [
|
|||||||
"CSSAlignUtils.h",
|
"CSSAlignUtils.h",
|
||||||
"CSSOrderAwareFrameIterator.h",
|
"CSSOrderAwareFrameIterator.h",
|
||||||
"LayoutMessageUtils.h",
|
"LayoutMessageUtils.h",
|
||||||
|
"nsVideoFrame.h",
|
||||||
"PrintedSheetFrame.h",
|
"PrintedSheetFrame.h",
|
||||||
"ReflowInput.h",
|
"ReflowInput.h",
|
||||||
"ReflowOutput.h",
|
"ReflowOutput.h",
|
||||||
|
|||||||
@@ -5582,6 +5582,8 @@ class MOZ_HEAP_CLASS WeakFrame {
|
|||||||
nsIFrame* operator->() { return mFrame; }
|
nsIFrame* operator->() { return mFrame; }
|
||||||
operator nsIFrame*() { return mFrame; }
|
operator nsIFrame*() { return mFrame; }
|
||||||
|
|
||||||
|
bool operator==(nsIFrame* const aOther) const { return mFrame == aOther; }
|
||||||
|
|
||||||
void Clear(mozilla::PresShell* aPresShell);
|
void Clear(mozilla::PresShell* aPresShell);
|
||||||
|
|
||||||
bool IsAlive() const { return !!mFrame; }
|
bool IsAlive() const { return !!mFrame; }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "mozilla/dom/HTMLImageElement.h"
|
#include "mozilla/dom/HTMLImageElement.h"
|
||||||
#include "mozilla/dom/ReferrerInfo.h"
|
#include "mozilla/dom/ReferrerInfo.h"
|
||||||
#include "mozilla/dom/ResponsiveImageSelector.h"
|
#include "mozilla/dom/ResponsiveImageSelector.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "mozilla/image/WebRenderImageProvider.h"
|
#include "mozilla/image/WebRenderImageProvider.h"
|
||||||
#include "mozilla/layers/RenderRootStateManager.h"
|
#include "mozilla/layers/RenderRootStateManager.h"
|
||||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||||
@@ -1115,6 +1116,8 @@ void nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
|
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
|
||||||
|
LargestContentfulPaint::MaybeProcessImageForElementTiming(
|
||||||
|
static_cast<imgRequestProxy*>(aRequest), GetContent()->AsElement());
|
||||||
uint32_t imgStatus;
|
uint32_t imgStatus;
|
||||||
aRequest->GetImageStatus(&imgStatus);
|
aRequest->GetImageStatus(&imgStatus);
|
||||||
nsresult status =
|
nsresult status =
|
||||||
@@ -2365,6 +2368,13 @@ bool nsDisplayImage::CreateWebRenderCommands(
|
|||||||
mImage->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext,
|
mImage->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext,
|
||||||
region, flags, getter_AddRefs(provider));
|
region, flags, getter_AddRefs(provider));
|
||||||
|
|
||||||
|
if (nsCOMPtr<imgIRequest> currentRequest = frame->GetCurrentRequest()) {
|
||||||
|
LCPHelpers::FinalizeLCPEntryForImage(
|
||||||
|
frame->GetContent()->AsElement(),
|
||||||
|
static_cast<imgRequestProxy*>(currentRequest.get()),
|
||||||
|
GetDestRect() - ToReferenceFrame());
|
||||||
|
}
|
||||||
|
|
||||||
// While we got a container, it may not contain a fully decoded surface. If
|
// While we got a container, it may not contain a fully decoded surface. If
|
||||||
// that is the case, and we have an image we were previously displaying which
|
// that is the case, and we have an image we were previously displaying which
|
||||||
// has a fully decoded surface, then we should prefer the previous image.
|
// has a fully decoded surface, then we should prefer the previous image.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "mozilla/IntegerRange.h"
|
#include "mozilla/IntegerRange.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
|
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsBlockFrame.h"
|
#include "nsBlockFrame.h"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "mozilla/dom/ServiceWorkerRegistration.h"
|
#include "mozilla/dom/ServiceWorkerRegistration.h"
|
||||||
#include "mozilla/dom/SVGElement.h"
|
#include "mozilla/dom/SVGElement.h"
|
||||||
#include "mozilla/dom/TouchEvent.h"
|
#include "mozilla/dom/TouchEvent.h"
|
||||||
|
#include "mozilla/dom/PerformanceMainThread.h"
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
#include "mozilla/PresShell.h"
|
#include "mozilla/PresShell.h"
|
||||||
#include "mozilla/ShapeUtils.h"
|
#include "mozilla/ShapeUtils.h"
|
||||||
@@ -1220,7 +1221,7 @@ void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
nsRootPresContext* rootPresContext = pc->GetRootPresContext();
|
nsRootPresContext* rootPresContext = pc->GetRootPresContext();
|
||||||
if (!pc->HadFirstContentfulPaint() && rootPresContext) {
|
if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
|
||||||
if (!CurrentPresShellState()->mIsBackgroundOnly) {
|
if (!CurrentPresShellState()->mIsBackgroundOnly) {
|
||||||
if (pc->HasEverBuiltInvisibleText() ||
|
if (pc->HasEverBuiltInvisibleText() ||
|
||||||
DisplayListIsContentful(this, aPaintedContents)) {
|
DisplayListIsContentful(this, aPaintedContents)) {
|
||||||
@@ -3429,6 +3430,17 @@ bool nsDisplayBackgroundImage::CreateWebRenderCommands(
|
|||||||
if (result == ImgDrawResult::NOT_SUPPORTED) {
|
if (result == ImgDrawResult::NOT_SUPPORTED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nsIContent* content = StyleFrame()->GetContent()) {
|
||||||
|
if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
|
||||||
|
->mImage.mLayers[mLayer]
|
||||||
|
.mImage.GetImageRequest()) {
|
||||||
|
// LCP don't consider gradient backgrounds.
|
||||||
|
LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
|
||||||
|
mBounds - ToReferenceFrame());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7483,6 +7495,10 @@ void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
|
|||||||
// the WebRender fallback painting path, and we don't want to issue
|
// the WebRender fallback painting path, and we don't want to issue
|
||||||
// recorded commands that are dependent on the visible/building rect.
|
// recorded commands that are dependent on the visible/building rect.
|
||||||
RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
|
RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
|
||||||
|
|
||||||
|
auto* textFrame = static_cast<nsTextFrame*>(mFrame);
|
||||||
|
LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
|
||||||
|
mBounds - ToReferenceFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsDisplayText::CreateWebRenderCommands(
|
bool nsDisplayText::CreateWebRenderCommands(
|
||||||
@@ -7568,6 +7584,8 @@ bool nsDisplayText::CreateWebRenderCommands(
|
|||||||
gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
|
gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
|
||||||
this, bounds, deviceOffset);
|
this, bounds, deviceOffset);
|
||||||
|
|
||||||
|
LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
|
||||||
|
|
||||||
aBuilder.StartGroup(this);
|
aBuilder.StartGroup(this);
|
||||||
|
|
||||||
RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
|
RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/DocumentInlines.h"
|
#include "mozilla/dom/DocumentInlines.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "mozilla/dom/ImageTracker.h"
|
#include "mozilla/dom/ImageTracker.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsIReflowCallback.h"
|
#include "nsIReflowCallback.h"
|
||||||
@@ -818,10 +819,16 @@ void ImageLoader::OnLoadComplete(imgIRequest* aRequest) {
|
|||||||
// may happen during other network events.
|
// may happen during other network events.
|
||||||
UnblockOnloadIfNeeded(fwf);
|
UnblockOnloadIfNeeded(fwf);
|
||||||
}
|
}
|
||||||
if (fwf.mFrame->StyleVisibility()->IsVisible()) {
|
nsIFrame* frame = fwf.mFrame;
|
||||||
fwf.mFrame->SchedulePaint();
|
if (frame->StyleVisibility()->IsVisible()) {
|
||||||
|
frame->SchedulePaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::dom_enable_largest_contentful_paint()) {
|
||||||
|
LargestContentfulPaint::MaybeProcessImageForElementTiming(
|
||||||
|
static_cast<imgRequestProxy*>(aRequest),
|
||||||
|
frame->GetContent()->AsElement());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla::css
|
} // namespace mozilla::css
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "mozilla/SVGUtils.h"
|
#include "mozilla/SVGUtils.h"
|
||||||
#include "mozilla/dom/MutationEventBinding.h"
|
#include "mozilla/dom/MutationEventBinding.h"
|
||||||
#include "mozilla/dom/SVGImageElement.h"
|
#include "mozilla/dom/SVGImageElement.h"
|
||||||
|
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||||
#include "nsIReflowCallback.h"
|
#include "nsIReflowCallback.h"
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
@@ -391,6 +392,12 @@ void SVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
|
|||||||
LayoutDeviceSize devPxSize(width, height);
|
LayoutDeviceSize devPxSize(width, height);
|
||||||
nsRect destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(
|
nsRect destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(
|
||||||
devPxSize, appUnitsPerDevPx));
|
devPxSize, appUnitsPerDevPx));
|
||||||
|
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
|
||||||
|
if (currentRequest) {
|
||||||
|
LCPHelpers::FinalizeLCPEntryForImage(
|
||||||
|
GetContent()->AsElement(),
|
||||||
|
static_cast<imgRequestProxy*>(currentRequest.get()), destRect);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
|
// Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
|
||||||
// That method needs our image to have a fixed native width & height,
|
// That method needs our image to have a fixed native width & height,
|
||||||
@@ -467,14 +474,7 @@ bool SVGImageFrame::CreateWebRenderCommands(
|
|||||||
|
|
||||||
// try to setup the image
|
// try to setup the image
|
||||||
if (!mImageContainer) {
|
if (!mImageContainer) {
|
||||||
nsCOMPtr<imgIRequest> currentRequest;
|
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
|
||||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
|
||||||
do_QueryInterface(GetContent());
|
|
||||||
if (imageLoader) {
|
|
||||||
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
|
||||||
getter_AddRefs(currentRequest));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRequest) {
|
if (currentRequest) {
|
||||||
currentRequest->GetImage(getter_AddRefs(mImageContainer));
|
currentRequest->GetImage(getter_AddRefs(mImageContainer));
|
||||||
}
|
}
|
||||||
@@ -633,6 +633,14 @@ bool SVGImageFrame::CreateWebRenderCommands(
|
|||||||
mImageContainer, this, destRect, clipRect, aSc, flags, svgContext,
|
mImageContainer, this, destRect, clipRect, aSc, flags, svgContext,
|
||||||
region);
|
region);
|
||||||
|
|
||||||
|
if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
|
||||||
|
LCPHelpers::FinalizeLCPEntryForImage(
|
||||||
|
GetContent()->AsElement(),
|
||||||
|
static_cast<imgRequestProxy*>(currentRequest.get()),
|
||||||
|
LayoutDeviceRect::ToAppUnits(destRect, appUnitsPerDevPx) -
|
||||||
|
toReferenceFrame);
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<image::WebRenderImageProvider> provider;
|
RefPtr<image::WebRenderImageProvider> provider;
|
||||||
ImgDrawResult drawResult = mImageContainer->GetImageProvider(
|
ImgDrawResult drawResult = mImageContainer->GetImageProvider(
|
||||||
aManager->LayerManager(), decodeSize, svgContext, region, flags,
|
aManager->LayerManager(), decodeSize, svgContext, region, flags,
|
||||||
@@ -783,6 +791,17 @@ bool SVGImageFrame::ReflowFinished() {
|
|||||||
|
|
||||||
void SVGImageFrame::ReflowCallbackCanceled() { mReflowCallbackPosted = false; }
|
void SVGImageFrame::ReflowCallbackCanceled() { mReflowCallbackPosted = false; }
|
||||||
|
|
||||||
|
already_AddRefed<imgIRequest> SVGImageFrame::GetCurrentRequest() const {
|
||||||
|
nsCOMPtr<imgIRequest> request;
|
||||||
|
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||||
|
do_QueryInterface(GetContent());
|
||||||
|
if (imageLoader) {
|
||||||
|
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||||
|
getter_AddRefs(request));
|
||||||
|
}
|
||||||
|
return request.forget();
|
||||||
|
}
|
||||||
|
|
||||||
bool SVGImageFrame::IgnoreHitTest() const {
|
bool SVGImageFrame::IgnoreHitTest() const {
|
||||||
switch (Style()->PointerEvents()) {
|
switch (Style()->PointerEvents()) {
|
||||||
case StylePointerEvents::None:
|
case StylePointerEvents::None:
|
||||||
|
|||||||
@@ -118,6 +118,8 @@ class SVGImageFrame final : public nsIFrame,
|
|||||||
private:
|
private:
|
||||||
bool IgnoreHitTest() const;
|
bool IgnoreHitTest() const;
|
||||||
|
|
||||||
|
already_AddRefed<imgIRequest> GetCurrentRequest() const;
|
||||||
|
|
||||||
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
|
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
|
||||||
int32_t aNativeHeight);
|
int32_t aNativeHeight);
|
||||||
gfx::Matrix GetVectorImageTransform();
|
gfx::Matrix GetVectorImageTransform();
|
||||||
|
|||||||
@@ -2392,7 +2392,7 @@
|
|||||||
# Whether the LargestContentfulPaint API will be gathered and returned by performance observer*
|
# Whether the LargestContentfulPaint API will be gathered and returned by performance observer*
|
||||||
- name: dom.enable_largest_contentful_paint
|
- name: dom.enable_largest_contentful_paint
|
||||||
type: RelaxedAtomicBool
|
type: RelaxedAtomicBool
|
||||||
value: false
|
value: @IS_NIGHTLY_BUILD@
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
# Whether performance.GetEntries* will contain an entry for the active document
|
# Whether performance.GetEntries* will contain an entry for the active document
|
||||||
|
|||||||
Reference in New Issue
Block a user