/* -*- 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"DOMIntersectionObserver.h"#include"nsCSSPropertyID.h"#include"nsIFrame.h"#include"nsContentUtils.h"#include"nsLayoutUtils.h"#include"mozilla/PresShell.h"#include"mozilla/StaticPrefs_dom.h"#include"mozilla/ServoBindings.h"#include"mozilla/StaticPrefs_dom.h"#include"mozilla/dom/BrowserChild.h"#include"mozilla/dom/BrowsingContext.h"#include"mozilla/dom/DocumentInlines.h"#include"mozilla/dom/HTMLImageElement.h"#include"Units.h"namespacemozilla{namespacedom{NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry)NS_WRAPPERCACHE_INTERFACE_MAP_ENTRYNS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_ENDNS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserverEntry)NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserverEntry)NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMIntersectionObserverEntry,mOwner,mRootBounds,mBoundingClientRect,mIntersectionRect,mTarget)NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserver)NS_WRAPPERCACHE_INTERFACE_MAP_ENTRYNS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_ENTRY(DOMIntersectionObserver)NS_INTERFACE_MAP_ENDNS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTION_CLASS(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPERNS_IMPL_CYCLE_COLLECTION_TRACE_ENDNS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPERtmp->Disconnect();NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)if(tmp->mCallback.is<RefPtr<dom::IntersectionCallback>>()){ImplCycleCollectionUnlink(tmp->mCallback.as<RefPtr<dom::IntersectionCallback>>());}NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)if(tmp->mCallback.is<RefPtr<dom::IntersectionCallback>>()){ImplCycleCollectionTraverse(cb,tmp->mCallback.as<RefPtr<dom::IntersectionCallback>>(),"mCallback",0);}NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDalready_AddRefed<DOMIntersectionObserver>DOMIntersectionObserver::Constructor(constGlobalObject&aGlobal,dom::IntersectionCallback&aCb,ErrorResult&aRv){returnConstructor(aGlobal,aCb,IntersectionObserverInit(),aRv);}already_AddRefed<DOMIntersectionObserver>DOMIntersectionObserver::Constructor(constGlobalObject&aGlobal,dom::IntersectionCallback&aCb,constIntersectionObserverInit&aOptions,ErrorResult&aRv){nsCOMPtr<nsPIDOMWindowInner>window=do_QueryInterface(aGlobal.GetAsSupports());if(!window){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}RefPtr<DOMIntersectionObserver>observer=newDOMIntersectionObserver(window.forget(),aCb);if(!aOptions.mRoot.IsNull()){if(aOptions.mRoot.Value().IsElement()){observer->mRoot=aOptions.mRoot.Value().GetAsElement();}else{MOZ_ASSERT(aOptions.mRoot.Value().IsDocument());if(!StaticPrefs::dom_IntersectionObserverExplicitDocumentRoot_enabled()){aRv.ThrowTypeError<dom::MSG_DOES_NOT_IMPLEMENT_INTERFACE>("'root' member of IntersectionObserverInit","Element");returnnullptr;}observer->mRoot=aOptions.mRoot.Value().GetAsDocument();}}if(!observer->SetRootMargin(aOptions.mRootMargin)){aRv.ThrowSyntaxError("rootMargin must be specified in pixels or percent.");returnnullptr;}if(aOptions.mThreshold.IsDoubleSequence()){constSequence<double>&thresholds=aOptions.mThreshold.GetAsDoubleSequence();observer->mThresholds.SetCapacity(thresholds.Length());for(constauto&thresh:thresholds){if(thresh<0.0||thresh>1.0){aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();returnnullptr;}observer->mThresholds.AppendElement(thresh);}observer->mThresholds.Sort();}else{doublethresh=aOptions.mThreshold.GetAsDouble();if(thresh<0.0||thresh>1.0){aRv.ThrowRangeError<dom::MSG_THRESHOLD_RANGE_ERROR>();returnnullptr;}observer->mThresholds.AppendElement(thresh);}returnobserver.forget();}staticvoidLazyLoadCallback(constSequence<OwningNonNull<DOMIntersectionObserverEntry>>&aEntries){for(constauto&entry:aEntries){MOZ_ASSERT(entry->Target()->IsHTMLElement(nsGkAtoms::img));if(entry->IsIntersecting()){static_cast<HTMLImageElement*>(entry->Target())->StopLazyLoadingAndStartLoadIfNeeded();}}}staticLengthPercentagePrefMargin(floataValue,boolaIsPercentage){returnaIsPercentage?LengthPercentage::FromPercentage(aValue/100.0f):LengthPercentage::FromPixels(aValue);}DOMIntersectionObserver::DOMIntersectionObserver(Document&aDocument,NativeCallbackaCallback):mOwner(aDocument.GetInnerWindow()),mDocument(&aDocument),mCallback(aCallback),mConnected(false){}already_AddRefed<DOMIntersectionObserver>DOMIntersectionObserver::CreateLazyLoadObserver(Document&aDocument){RefPtr<DOMIntersectionObserver>observer=newDOMIntersectionObserver(aDocument,LazyLoadCallback);observer->mThresholds.AppendElement(std::numeric_limits<double>::min());#define SET_MARGIN(side_, side_lower_) \ observer->mRootMargin.Get(eSide##side_) = PrefMargin( \ StaticPrefs::dom_image_lazy_loading_root_margin_##side_lower_(), \ StaticPrefs:: \ dom_image_lazy_loading_root_margin_##side_lower_##_percentage());SET_MARGIN(Top,top);SET_MARGIN(Right,right);SET_MARGIN(Bottom,bottom);SET_MARGIN(Left,left);#undef SET_MARGINreturnobserver.forget();}boolDOMIntersectionObserver::SetRootMargin(constnsAString&aString){returnServo_IntersectionObserverRootMargin_Parse(&aString,&mRootMargin);}voidDOMIntersectionObserver::GetRootMargin(DOMString&aRetVal){nsString&retVal=aRetVal;Servo_IntersectionObserverRootMargin_ToString(&mRootMargin,&retVal);}voidDOMIntersectionObserver::GetThresholds(nsTArray<double>&aRetVal){aRetVal=mThresholds;}voidDOMIntersectionObserver::Observe(Element&aTarget){if(mObservationTargets.Contains(&aTarget)){return;}aTarget.RegisterIntersectionObserver(this);mObservationTargets.AppendElement(&aTarget);Connect();if(mDocument){if(nsPresContext*pc=mDocument->GetPresContext()){pc->RefreshDriver()->EnsureIntersectionObservationsUpdateHappens();}}}voidDOMIntersectionObserver::Unobserve(Element&aTarget){if(!mObservationTargets.Contains(&aTarget)){return;}if(mObservationTargets.Length()==1){Disconnect();return;}mObservationTargets.RemoveElement(&aTarget);aTarget.UnregisterIntersectionObserver(this);}voidDOMIntersectionObserver::UnlinkTarget(Element&aTarget){mObservationTargets.RemoveElement(&aTarget);if(mObservationTargets.Length()==0){Disconnect();}}voidDOMIntersectionObserver::Connect(){if(mConnected){return;}mConnected=true;if(mDocument){mDocument->AddIntersectionObserver(this);}}voidDOMIntersectionObserver::Disconnect(){if(!mConnected){return;}mConnected=false;for(size_ti=0;i<mObservationTargets.Length();++i){Element*target=mObservationTargets.ElementAt(i);target->UnregisterIntersectionObserver(this);}mObservationTargets.Clear();if(mDocument){mDocument->RemoveIntersectionObserver(this);}}voidDOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>&aRetVal){aRetVal.SwapElements(mQueuedEntries);mQueuedEntries.Clear();}staticMaybe<nsRect>EdgeInclusiveIntersection(constnsRect&aRect,constnsRect&aOtherRect){nscoordleft=std::max(aRect.x,aOtherRect.x);nscoordtop=std::max(aRect.y,aOtherRect.y);nscoordright=std::min(aRect.XMost(),aOtherRect.XMost());nscoordbottom=std::min(aRect.YMost(),aOtherRect.YMost());if(left>right||top>bottom){returnNothing();}returnSome(nsRect(left,top,right-left,bottom-top));}enumclassBrowsingContextOrigin{Similar,Different,Unknown};// FIXME(emilio): The whole concept of "units of related similar-origin browsing// contexts" is gone, but this is still in the spec, see// https://github.com/w3c/IntersectionObserver/issues/161staticBrowsingContextOriginSimilarOrigin(constElement&aTarget,constnsINode*aRoot){if(!aRoot){returnBrowsingContextOrigin::Unknown;}nsIPrincipal*principal1=aTarget.NodePrincipal();nsIPrincipal*principal2=aRoot->NodePrincipal();if(principal1==principal2){returnBrowsingContextOrigin::Similar;}nsAutoCStringbaseDomain1;nsAutoCStringbaseDomain2;if(NS_FAILED(principal1->GetBaseDomain(baseDomain1))||NS_FAILED(principal2->GetBaseDomain(baseDomain2))){returnBrowsingContextOrigin::Different;}returnbaseDomain1==baseDomain2?BrowsingContextOrigin::Similar:BrowsingContextOrigin::Different;}// NOTE: This returns nullptr if |aDocument| is in a cross process.staticDocument*GetTopLevelDocument(constDocument&aDocument){BrowsingContext*browsingContext=aDocument.GetBrowsingContext();if(!browsingContext){returnnullptr;}nsPIDOMWindowOuter*topWindow=browsingContext->Top()->GetDOMWindow();if(!topWindow){// If we don't have a DOMWindow, We are not in same origin.returnnullptr;}returntopWindow->GetExtantDoc();}// https://w3c.github.io/IntersectionObserver/#compute-the-intersection//// TODO(emilio): Proof of this being equivalent to the spec welcome, seems// reasonably close.//// Also, it's unclear to me why the spec talks about browsing context while// discarding observations of targets of different documents.//// Both aRootBounds and the return value are relative to// nsLayoutUtils::GetContainingBlockForClientRect(aRoot).//// In case of out-of-process document, aRemoteDocumentVisibleRect is a rectangle// in the out-of-process document's coordinate system.staticMaybe<nsRect>ComputeTheIntersection(nsIFrame*aTarget,nsIFrame*aRoot,constnsRect&aRootBounds,constMaybe<nsRect>&aRemoteDocumentVisibleRect){nsIFrame*target=aTarget;// 1. Let intersectionRect be the result of running the// getBoundingClientRect() algorithm on the target.//// FIXME(emilio, mstange): Spec uses `getBoundingClientRect()` (which is the// union of all continuations), but this code doesn't handle continuations.//// `intersectionRect` is kept relative to `target` during the loop.Maybe<nsRect>intersectionRect=Some(target->GetRectRelativeToSelf());// 2. Let container be the containing block of the target.// (We go through the parent chain and only look at scroll frames)//// FIXME(emilio): Spec uses containing blocks, we use scroll frames, but we// only apply overflow-clipping, not clip-path, so it's ~fine. We do need to// apply clip-path.//// 3. While container is not the intersection root:nsIFrame*containerFrame=nsLayoutUtils::GetCrossDocParentFrame(target);while(containerFrame&&containerFrame!=aRoot){// FIXME(emilio): What about other scroll frames that inherit from// nsHTMLScrollFrame but have a different type, like nsListControlFrame?// This looks bogus in that case, but different bug.if(containerFrame->IsScrollFrame()){nsIScrollableFrame*scrollFrame=do_QueryFrame(containerFrame);//nsRectsubFrameRect=scrollFrame->GetScrollPortRect();// 3.1 Map intersectionRect to the coordinate space of container.nsRectintersectionRectRelativeToContainer=nsLayoutUtils::TransformFrameRectToAncestor(target,intersectionRect.value(),containerFrame);// 3.2 If container has overflow clipping or a css clip-path property,// update intersectionRect by applying container's clip.//// TODO: Apply clip-path.//// 3.3 is handled, looks like, by this same clipping, given the root// scroll-frame cannot escape the viewport, probably?//intersectionRect=EdgeInclusiveIntersection(intersectionRectRelativeToContainer,subFrameRect);if(!intersectionRect){returnNothing();}target=containerFrame;}containerFrame=nsLayoutUtils::GetCrossDocParentFrame(containerFrame);}MOZ_ASSERT(intersectionRect);// 4. Map intersectionRect to the coordinate space of the intersection root.nsRectintersectionRectRelativeToRoot=nsLayoutUtils::TransformFrameRectToAncestor(target,intersectionRect.value(),nsLayoutUtils::GetContainingBlockForClientRect(aRoot));// 5.Update intersectionRect by intersecting it with the root intersection// rectangle.intersectionRect=EdgeInclusiveIntersection(intersectionRectRelativeToRoot,aRootBounds);if(intersectionRect.isNothing()){returnNothing();}// 6. Map intersectionRect to the coordinate space of the viewport of the// Document containing the target.//// FIXME(emilio): I think this may not be correct if the root is explicit// and in the same document, since then the rectangle may not be relative to// the viewport already (but it's in the same document).nsRectrect=intersectionRect.value();if(aTarget->PresContext()!=aRoot->PresContext()){if(nsIFrame*rootScrollFrame=aTarget->PresShell()->GetRootScrollFrame()){nsLayoutUtils::TransformRect(aRoot,rootScrollFrame,rect);}}// In out-of-process iframes we need to take an intersection with the remote// document visble rect which was already clipped by ancestor document's// viewports.if(aRemoteDocumentVisibleRect){MOZ_ASSERT(aRoot->PresContext()->IsRootContentDocumentInProcess()&&!aRoot->PresContext()->IsRootContentDocumentCrossProcess());intersectionRect=EdgeInclusiveIntersection(rect,*aRemoteDocumentVisibleRect);if(intersectionRect.isNothing()){returnNothing();}rect=intersectionRect.value();}returnSome(rect);}structOopIframeMetrics{nsIFrame*mInProcessRootFrame=nullptr;nsRectmInProcessRootRect;nsRectmRemoteDocumentVisibleRect;};staticMaybe<OopIframeMetrics>GetOopIframeMetrics(Document&aDocument){Document*rootDoc=nsContentUtils::GetRootDocument(&aDocument);MOZ_ASSERT(rootDoc&&!rootDoc->IsTopLevelContentDocument());PresShell*rootPresShell=rootDoc->GetPresShell();if(!rootPresShell||rootPresShell->IsDestroying()){returnNothing();}nsIFrame*inProcessRootFrame=rootPresShell->GetRootFrame();if(!inProcessRootFrame){returnNothing();}BrowserChild*browserChild=BrowserChild::GetFrom(rootDoc->GetDocShell());if(!browserChild){returnNothing();}MOZ_DIAGNOSTIC_ASSERT(!browserChild->IsTopLevel());nsRectinProcessRootRect;if(nsIScrollableFrame*scrollFrame=rootPresShell->GetRootScrollFrameAsScrollable()){inProcessRootRect=scrollFrame->GetScrollPortRect();}Maybe<LayoutDeviceRect>remoteDocumentVisibleRect=browserChild->GetTopLevelViewportVisibleRectInSelfCoords();if(!remoteDocumentVisibleRect){returnNothing();}returnSome(OopIframeMetrics{inProcessRootFrame,inProcessRootRect,LayoutDeviceRect::ToAppUnits(*remoteDocumentVisibleRect,rootPresShell->GetPresContext()->AppUnitsPerDevPixel()),});}// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo// (step 2)voidDOMIntersectionObserver::Update(Document*aDocument,DOMHighResTimeStamptime){// 1 - Let rootBounds be observer's root intersection rectangle.// ... but since the intersection rectangle depends on the target, we defer// the inflation until later.// NOTE: |rootRect| and |rootFrame| will be root in the same process. In// out-of-process iframes, they are NOT root ones of the top level content// document.nsRectrootRect;nsIFrame*rootFrame=nullptr;nsINode*root=mRoot;Maybe<nsRect>remoteDocumentVisibleRect;if(mRoot&&mRoot->IsElement()){if((rootFrame=mRoot->AsElement()->GetPrimaryFrame())){nsRectrootRectRelativeToRootFrame;if(rootFrame->IsScrollFrame()){// rootRectRelativeToRootFrame should be the content rect of rootFrame,// not including the scrollbars.nsIScrollableFrame*scrollFrame=do_QueryFrame(rootFrame);rootRectRelativeToRootFrame=scrollFrame->GetScrollPortRect();}else{// rootRectRelativeToRootFrame should be the border rect of rootFrame.rootRectRelativeToRootFrame=rootFrame->GetRectRelativeToSelf();}nsIFrame*containingBlock=nsLayoutUtils::GetContainingBlockForClientRect(rootFrame);rootRect=nsLayoutUtils::TransformFrameRectToAncestor(rootFrame,rootRectRelativeToRootFrame,containingBlock);}}else{MOZ_ASSERT(!mRoot||mRoot->IsDocument());Document*rootDocument=mRoot?mRoot->AsDocument():GetTopLevelDocument(*aDocument);if(rootDocument){if(PresShell*presShell=rootDocument->GetPresShell()){rootFrame=presShell->GetRootScrollFrame();if(rootFrame){root=rootFrame->GetContent()->AsElement();nsIScrollableFrame*scrollFrame=do_QueryFrame(rootFrame);rootRect=scrollFrame->GetScrollPortRect();}}}elseif(Maybe<OopIframeMetrics>metrics=GetOopIframeMetrics(*aDocument)){// `implicit root` case in an out-of-process iframe.rootFrame=metrics->mInProcessRootFrame;rootRect=metrics->mInProcessRootRect;remoteDocumentVisibleRect=Some(metrics->mRemoteDocumentVisibleRect);}}nsMarginrootMargin;// This root margin is NOT applied in `implicit root`// case, e.g. in out-of-process iframes.for(constautoside:mozilla::AllPhysicalSides()){nscoordbasis=side==eSideTop||side==eSideBottom?rootRect.Height():rootRect.Width();rootMargin.Side(side)=mRootMargin.Get(side).Resolve(basis,NSToCoordRoundWithClamp);}// 2. For each target in observer’s internal [[ObservationTargets]] slot,// processed in the same order that observe() was called on each target:for(Element*target:mObservationTargets){nsIFrame*targetFrame=target->GetPrimaryFrame();// 2.2. If the intersection root is not the implicit root, and target is not// in the same Document as the intersection root, skip further processing// for target.if(mRoot&&mRoot->OwnerDoc()!=target->OwnerDoc()){continue;}nsRectrootBounds;if(rootFrame&&targetFrame){// FIXME(emilio): Why only if there are frames?rootBounds=rootRect;}BrowsingContextOriginorigin=SimilarOrigin(*target,root);MOZ_ASSERT_IF(remoteDocumentVisibleRect,origin!=BrowsingContextOrigin::Similar);if(origin==BrowsingContextOrigin::Similar){rootBounds.Inflate(rootMargin);}Maybe<nsRect>intersectionRect;nsRecttargetRect;if(targetFrame&&rootFrame){// 2.1. If the intersection root is not the implicit root and target is// not a descendant of the intersection root in the containing block// chain, skip further processing for target.if(mRoot&&!nsLayoutUtils::IsProperAncestorFrameCrossDoc(rootFrame,targetFrame)){continue;}// 2.3. Let targetRect be a DOMRectReadOnly obtained by running the// getBoundingClientRect() algorithm on target.targetRect=nsLayoutUtils::GetAllInFlowRectsUnion(targetFrame,nsLayoutUtils::GetContainingBlockForClientRect(targetFrame),nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);// 2.4. Let intersectionRect be the result of running the compute the// intersection algorithm on target.intersectionRect=ComputeTheIntersection(targetFrame,rootFrame,rootBounds,remoteDocumentVisibleRect);}// 2.5. Let targetArea be targetRect’s area.int64_ttargetArea=(int64_t)targetRect.Width()*(int64_t)targetRect.Height();// 2.6. Let intersectionArea be intersectionRect’s area.int64_tintersectionArea=!intersectionRect?0:(int64_t)intersectionRect->Width()*(int64_t)intersectionRect->Height();// 2.7. Let isIntersecting be true if targetRect and rootBounds intersect or// are edge-adjacent, even if the intersection has zero area (because// rootBounds or targetRect have zero area); otherwise, let isIntersecting// be false.constboolisIntersecting=intersectionRect.isSome();// 2.8. If targetArea is non-zero, let intersectionRatio be intersectionArea// divided by targetArea. Otherwise, let intersectionRatio be 1 if// isIntersecting is true, or 0 if isIntersecting is false.doubleintersectionRatio;if(targetArea>0.0){intersectionRatio=std::min((double)intersectionArea/(double)targetArea,1.0);}else{intersectionRatio=isIntersecting?1.0:0.0;}// 2.9 Let thresholdIndex be the index of the first entry in// observer.thresholds whose value is greater than intersectionRatio, or the// length of observer.thresholds if intersectionRatio is greater than or// equal to the last entry in observer.thresholds.int32_tthresholdIndex=-1;// FIXME(emilio): Why the isIntersecting check?if(isIntersecting){thresholdIndex=mThresholds.IndexOfFirstElementGt(intersectionRatio);if(thresholdIndex==0){// Per the spec, we should leave threshold at 0 and distinguish between// "less than all thresholds and intersecting" and "not intersecting"// (queuing observer entries as both cases come to pass). However,// neither Chrome nor the WPT tests expect this behavior, so treat these// two cases as one.//// FIXME(emilio): Looks like a good candidate for a spec issue.thresholdIndex=-1;}}// Steps 2.10 - 2.15.if(target->UpdateIntersectionObservation(this,thresholdIndex)){QueueIntersectionObserverEntry(target,time,origin==BrowsingContextOrigin::Similar?Some(rootBounds):Nothing(),targetRect,intersectionRect,intersectionRatio);}}}voidDOMIntersectionObserver::QueueIntersectionObserverEntry(Element*aTarget,DOMHighResTimeStamptime,constMaybe<nsRect>&aRootRect,constnsRect&aTargetRect,constMaybe<nsRect>&aIntersectionRect,doubleaIntersectionRatio){RefPtr<DOMRect>rootBounds;if(aRootRect.isSome()){rootBounds=newDOMRect(this);rootBounds->SetLayoutRect(aRootRect.value());}RefPtr<DOMRect>boundingClientRect=newDOMRect(this);boundingClientRect->SetLayoutRect(aTargetRect);RefPtr<DOMRect>intersectionRect=newDOMRect(this);if(aIntersectionRect.isSome()){intersectionRect->SetLayoutRect(aIntersectionRect.value());}RefPtr<DOMIntersectionObserverEntry>entry=newDOMIntersectionObserverEntry(this,time,rootBounds.forget(),boundingClientRect.forget(),intersectionRect.forget(),aIntersectionRect.isSome(),aTarget,aIntersectionRatio);mQueuedEntries.AppendElement(entry.forget());}voidDOMIntersectionObserver::Notify(){if(!mQueuedEntries.Length()){return;}Sequence<OwningNonNull<DOMIntersectionObserverEntry>>entries;if(entries.SetCapacity(mQueuedEntries.Length(),mozilla::fallible)){for(size_ti=0;i<mQueuedEntries.Length();++i){RefPtr<DOMIntersectionObserverEntry>next=mQueuedEntries[i];*entries.AppendElement(mozilla::fallible)=next;}}mQueuedEntries.Clear();if(mCallback.is<RefPtr<dom::IntersectionCallback>>()){RefPtr<dom::IntersectionCallback>callback(mCallback.as<RefPtr<dom::IntersectionCallback>>());callback->Call(this,entries,*this);}else{mCallback.as<NativeCallback>()(entries);}}}// namespace dom}// namespace mozilla