Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/quota/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 50 kB image not shown  

SSL LargestContentfulPaint.cpp   Sprache: unbekannt

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/dom/Element.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsRFPService.h"
#include "Performance.h"
#include "imgRequest.h"
#include "PerformanceMainThread.h"
#include "LargestContentfulPaint.h"

#include "mozilla/dom/BrowsingContext.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 {

static LazyLogModule gLCPLogging("LargestContentfulPaint");

#define LOG(...) MOZ_LOG(gLCPLogging, LogLevel::Debug, (__VA_ARGS__))

NS_IMPL_CYCLE_COLLECTION_INHERITED(LargestContentfulPaint, PerformanceEntry,
                                   mPerformance, mURI, mElement)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LargestContentfulPaint)
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)

NS_IMPL_ADDREF_INHERITED(LargestContentfulPaint, PerformanceEntry)
NS_IMPL_RELEASE_INHERITED(LargestContentfulPaint, PerformanceEntry)

static double GetAreaInDoublePixelsFromAppUnits(const nsSize& aSize) {
  return NSAppUnitsToDoublePixels(aSize.Width(), AppUnitsPerCSSPixel()) *
         NSAppUnitsToDoublePixels(aSize.Height(), AppUnitsPerCSSPixel());
}

static double GetAreaInDoublePixelsFromAppUnits(const nsRect& aRect) {
  return NSAppUnitsToDoublePixels(aRect.Width(), AppUnitsPerCSSPixel()) *
         NSAppUnitsToDoublePixels(aRect.Height(), AppUnitsPerCSSPixel());
}

static DOMHighResTimeStamp GetReducedTimePrecisionDOMHighRes(
    Performance* aPerformance, const TimeStamp& aRawTimeStamp) {
  MOZ_ASSERT(aPerformance);
  DOMHighResTimeStamp rawValue =
      aPerformance->GetDOMTiming()->TimeStampToDOMHighRes(aRawTimeStamp);
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed(),
      aPerformance->GetRTPCallerType());
}

LargestContentfulPaint::LargestContentfulPaint(
    PerformanceMainThread* aPerformance, const TimeStamp& aRenderTime,
    const Maybe<TimeStamp>& aLoadTime, const unsigned long aSize, nsIURI* aURI,
    Element* aElement, bool aShouldExposeRenderTime)
    : PerformanceEntry(aPerformance->GetParentObject(), u""_ns,
                       kLargestContentfulPaintName),
      mPerformance(aPerformance),
      mRenderTime(aRenderTime),
      mLoadTime(aLoadTime),
      mShouldExposeRenderTime(aShouldExposeRenderTime),
      mSize(aSize),
      mURI(aURI) {
  MOZ_ASSERT(mPerformance);
  MOZ_ASSERT(aElement);
  // The element could be a pseudo-element
  if (aElement->ChromeOnlyAccess()) {
    mElement = do_GetWeakReference(Element::FromNodeOrNull(
        aElement->FindFirstNonChromeOnlyAccessContent()));
  } else {
    mElement = do_GetWeakReference(aElement);
  }

  if (const Element* element = GetElement()) {
    mId = element->GetID();
  }
}

JSObject* LargestContentfulPaint::WrapObject(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return LargestContentfulPaint_Binding::Wrap(aCx, this, aGivenProto);
}

Element* LargestContentfulPaint::GetElement() const {
  nsCOMPtr<Element> element = do_QueryReferent(mElement);
  return element ? nsContentUtils::GetAnElementForTiming(
                       element, element->GetComposedDoc(), 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);
  }

  aElement->SetFlags(ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP);

  nsTArray<WeakPtr<PreloaderBase>>& imageRequestProxiesForElement =
      document->ContentIdentifiersForLCP().LookupOrInsert(aElement);

  if (imageRequestProxiesForElement.Contains(aRequest)) {
    LOG("  The content identifier existed for element=%p and request=%p, "
        "return.",
        aElement, aRequest);
    return;
  }

  imageRequestProxiesForElement.AppendElement(aRequest);

#ifdef DEBUG
  uint32_t status = imgIRequest::STATUS_NONE;
  aRequest->GetImageStatus(&status);
  MOZ_ASSERT(status & imgIRequest::STATUS_LOAD_COMPLETE);
#endif

  // 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.
  // 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.
  LOG("  Added a pending image rendering");
  performance->AddImagesPendingRendering(
      ImagePendingRendering{aElement, aRequest, TimeStamp::Now()});
}

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 image=%p", aContainingBlock,
      aImgRequestProxy);
  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);

  if (performance->HasDispatchedInputEvent() ||
      performance->HasDispatchedScrollEvent()) {
    return;
  }

  if (!performance->IsPendingLCPCandidate(aContainingBlock, aImgRequestProxy)) {
    return;
  }

  imgRequestProxy::LCPTimings& lcpTimings = aImgRequestProxy->GetLCPTimings();
  if (!lcpTimings.AreSet()) {
    return;
  }

  imgRequest* request = aImgRequestProxy->GetOwner();
  MOZ_ASSERT(request);

  nsCOMPtr<nsIURI> requestURI;
  aImgRequestProxy->GetURI(getter_AddRefs(requestURI));

  const bool taoPassed =
      request->ShouldReportRenderTimeForLCP() || request->IsData();

  RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
      performance, lcpTimings.mRenderTime.ref(), lcpTimings.mLoadTime, 0,
      requestURI, aContainingBlock, taoPassed);

  entry->UpdateSize(aContainingBlock, aTargetRectRelativeToSelf, performance,
                    true);

  // Resets the LCPTiming so that unless this (element, image) pair goes
  // through PerformanceMainThread::ProcessElementTiming again, they
  // won't generate new LCP entries.
  lcpTimings.Reset();

  // 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 {
  if (!mShouldExposeRenderTime) {
    return 0;
  }
  return GetReducedTimePrecisionDOMHighRes(mPerformance, mRenderTime);
}

DOMHighResTimeStamp LargestContentfulPaint::LoadTime() const {
  if (mLoadTime.isNothing()) {
    return 0;
  }

  return GetReducedTimePrecisionDOMHighRes(mPerformance, mLoadTime.ref());
}

DOMHighResTimeStamp LargestContentfulPaint::StartTime() const {
  if (mShouldExposeRenderTime) {
    return GetReducedTimePrecisionDOMHighRes(mPerformance, mRenderTime);
  }

  if (mLoadTime.isNothing()) {
    return 0;
  }

  return GetReducedTimePrecisionDOMHighRes(mPerformance, mLoadTime.ref());
}

/* 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);

  ReportLCPToNavigationTimings();
}

void LargestContentfulPaint::GetUrl(nsAString& aUrl) {
  if (mURI) {
    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::FinalizeLCPEntryForText(
    PerformanceMainThread* aPerformance, const TimeStamp& 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, Nothing(), 0, nullptr, aContainingBlock, true);

  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();
}

void LargestContentfulPaint::ReportLCPToNavigationTimings() {
  nsCOMPtr<Element> element = do_QueryReferent(mElement);
  if (!element) {
    return;
  }

  const Document* document = element->OwnerDoc();

  MOZ_ASSERT(document);

  nsDOMNavigationTiming* timing = document->GetNavigationTiming();

  if (MOZ_UNLIKELY(!timing)) {
    return;
  }

  if (document->IsResourceDoc()) {
    return;
  }

  if (BrowsingContext* browsingContext = document->GetBrowsingContext()) {
    if (browsingContext->GetEmbeddedInContentDocument()) {
      return;
    }
  }

  if (!document->IsTopLevelContentDocument()) {
    return;
  }
  timing->NotifyLargestContentfulRenderForRootContentDocument(
      GetReducedTimePrecisionDOMHighRes(mPerformance, mRenderTime));
}
}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=92 H=100 G=95

[zur Elbe Produktseite wechseln0.13QuellennavigatorsAnalyse erneut starten2026-06-08]