forked from mirrors/gecko-dev
		
	 8101ae2ea8
			
		
	
	
		8101ae2ea8
		
	
	
	
	
		
			
			Also for the intersection observer root margin, since it was easier to fix it up and clean it up than not doing it. This is the first big step to get rid of nscoord. It duplicates a bit of logic in nsLayoutUtils since for now max/min-width/height are still represented with nsStyleCoord, but I think I prefer to land this incrementally. I didn't add helpers for the physical accessors of the style rect sides that nsStyleSides has (top/bottom/left/right) since I think we generally should encourage the logical versions, but let me know if you want me to do that. Differential Revision: https://phabricator.services.mozilla.com/D17739
		
			
				
	
	
		
			467 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/ServoBindings.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_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_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
|   NS_INTERFACE_MAP_ENTRY(DOMIntersectionObserver)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_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_WRAPPER
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 | |
|   tmp->Disconnect();
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
 | |
|     const mozilla::dom::GlobalObject& aGlobal,
 | |
|     mozilla::dom::IntersectionCallback& aCb, mozilla::ErrorResult& aRv) {
 | |
|   return Constructor(aGlobal, aCb, IntersectionObserverInit(), aRv);
 | |
| }
 | |
| 
 | |
| already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
 | |
|     const mozilla::dom::GlobalObject& aGlobal,
 | |
|     mozilla::dom::IntersectionCallback& aCb,
 | |
|     const mozilla::dom::IntersectionObserverInit& aOptions,
 | |
|     mozilla::ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsPIDOMWindowInner> window =
 | |
|       do_QueryInterface(aGlobal.GetAsSupports());
 | |
|   if (!window) {
 | |
|     aRv.Throw(NS_ERROR_FAILURE);
 | |
|     return nullptr;
 | |
|   }
 | |
|   RefPtr<DOMIntersectionObserver> observer =
 | |
|       new DOMIntersectionObserver(window.forget(), aCb);
 | |
| 
 | |
|   observer->mRoot = aOptions.mRoot;
 | |
| 
 | |
|   if (!observer->SetRootMargin(aOptions.mRootMargin)) {
 | |
|     aRv.ThrowDOMException(
 | |
|         NS_ERROR_DOM_SYNTAX_ERR,
 | |
|         NS_LITERAL_CSTRING(
 | |
|             "rootMargin must be specified in pixels or percent."));
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (aOptions.mThreshold.IsDoubleSequence()) {
 | |
|     const mozilla::dom::Sequence<double>& thresholds =
 | |
|         aOptions.mThreshold.GetAsDoubleSequence();
 | |
|     observer->mThresholds.SetCapacity(thresholds.Length());
 | |
|     for (const auto& thresh : thresholds) {
 | |
|       if (thresh < 0.0 || thresh > 1.0) {
 | |
|         aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
 | |
|         return nullptr;
 | |
|       }
 | |
|       observer->mThresholds.AppendElement(thresh);
 | |
|     }
 | |
|     observer->mThresholds.Sort();
 | |
|   } else {
 | |
|     double thresh = aOptions.mThreshold.GetAsDouble();
 | |
|     if (thresh < 0.0 || thresh > 1.0) {
 | |
|       aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
 | |
|       return nullptr;
 | |
|     }
 | |
|     observer->mThresholds.AppendElement(thresh);
 | |
|   }
 | |
| 
 | |
|   return observer.forget();
 | |
| }
 | |
| 
 | |
| bool DOMIntersectionObserver::SetRootMargin(const nsAString& aString) {
 | |
|   return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin);
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::GetRootMargin(mozilla::dom::DOMString& aRetVal) {
 | |
|   nsString& retVal = aRetVal;
 | |
|   Servo_IntersectionObserverRootMargin_ToString(&mRootMargin, &retVal);
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::GetThresholds(nsTArray<double>& aRetVal) {
 | |
|   aRetVal = mThresholds;
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::Observe(Element& aTarget) {
 | |
|   if (mObservationTargets.Contains(&aTarget)) {
 | |
|     return;
 | |
|   }
 | |
|   aTarget.RegisterIntersectionObserver(this);
 | |
|   mObservationTargets.AppendElement(&aTarget);
 | |
|   Connect();
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::Unobserve(Element& aTarget) {
 | |
|   if (!mObservationTargets.Contains(&aTarget)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mObservationTargets.Length() == 1) {
 | |
|     Disconnect();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mObservationTargets.RemoveElement(&aTarget);
 | |
|   aTarget.UnregisterIntersectionObserver(this);
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::UnlinkTarget(Element& aTarget) {
 | |
|   mObservationTargets.RemoveElement(&aTarget);
 | |
|   if (mObservationTargets.Length() == 0) {
 | |
|     Disconnect();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::Connect() {
 | |
|   if (mConnected) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mConnected = true;
 | |
|   if (mDocument) {
 | |
|     mDocument->AddIntersectionObserver(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::Disconnect() {
 | |
|   if (!mConnected) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mConnected = false;
 | |
|   for (size_t i = 0; i < mObservationTargets.Length(); ++i) {
 | |
|     Element* target = mObservationTargets.ElementAt(i);
 | |
|     target->UnregisterIntersectionObserver(this);
 | |
|   }
 | |
|   mObservationTargets.Clear();
 | |
|   if (mDocument) {
 | |
|     mDocument->RemoveIntersectionObserver(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::TakeRecords(
 | |
|     nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal) {
 | |
|   aRetVal.SwapElements(mQueuedEntries);
 | |
|   mQueuedEntries.Clear();
 | |
| }
 | |
| 
 | |
| static bool CheckSimilarOrigin(nsINode* aNode1, nsINode* aNode2) {
 | |
|   nsIPrincipal* principal1 = aNode1->NodePrincipal();
 | |
|   nsIPrincipal* principal2 = aNode2->NodePrincipal();
 | |
|   nsAutoCString baseDomain1;
 | |
|   nsAutoCString baseDomain2;
 | |
| 
 | |
|   nsresult rv = principal1->GetBaseDomain(baseDomain1);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return principal1 == principal2;
 | |
|   }
 | |
| 
 | |
|   rv = principal2->GetBaseDomain(baseDomain2);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return principal1 == principal2;
 | |
|   }
 | |
| 
 | |
|   return baseDomain1 == baseDomain2;
 | |
| }
 | |
| 
 | |
| static Maybe<nsRect> EdgeInclusiveIntersection(const nsRect& aRect,
 | |
|                                                const nsRect& aOtherRect) {
 | |
|   nscoord left = std::max(aRect.x, aOtherRect.x);
 | |
|   nscoord top = std::max(aRect.y, aOtherRect.y);
 | |
|   nscoord right = std::min(aRect.XMost(), aOtherRect.XMost());
 | |
|   nscoord bottom = std::min(aRect.YMost(), aOtherRect.YMost());
 | |
|   if (left > right || top > bottom) {
 | |
|     return Nothing();
 | |
|   }
 | |
|   return Some(nsRect(left, top, right - left, bottom - top));
 | |
| }
 | |
| 
 | |
| enum class BrowsingContextInfo {
 | |
|   SimilarOriginBrowsingContext,
 | |
|   DifferentOriginBrowsingContext,
 | |
|   UnknownBrowsingContext
 | |
| };
 | |
| 
 | |
| void DOMIntersectionObserver::Update(Document* aDocument,
 | |
|                                      DOMHighResTimeStamp time) {
 | |
|   Element* root = nullptr;
 | |
|   nsIFrame* rootFrame = nullptr;
 | |
|   nsRect rootRect;
 | |
| 
 | |
|   if (mRoot) {
 | |
|     root = mRoot;
 | |
|     rootFrame = root->GetPrimaryFrame();
 | |
|     if (rootFrame) {
 | |
|       nsRect rootRectRelativeToRootFrame;
 | |
|       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 {
 | |
|     nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell();
 | |
|     if (presShell) {
 | |
|       rootFrame = presShell->GetRootScrollFrame();
 | |
|       if (rootFrame) {
 | |
|         nsPresContext* presContext = rootFrame->PresContext();
 | |
|         while (!presContext->IsRootContentDocument()) {
 | |
|           presContext = presContext->GetParentPresContext();
 | |
|           if (!presContext) {
 | |
|             break;
 | |
|           }
 | |
|           nsIFrame* rootScrollFrame =
 | |
|               presContext->PresShell()->GetRootScrollFrame();
 | |
|           if (rootScrollFrame) {
 | |
|             rootFrame = rootScrollFrame;
 | |
|           } else {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         root = rootFrame->GetContent()->AsElement();
 | |
|         nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
 | |
|         // If we end up with a null root frame for some reason, we'll proceed
 | |
|         // with an empty root intersection rect.
 | |
|         if (scrollFrame) {
 | |
|           rootRect = scrollFrame->GetScrollPortRect();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsMargin rootMargin;
 | |
|   NS_FOR_CSS_SIDES(side) {
 | |
|     nscoord basis = side == eSideTop || side == eSideBottom ? rootRect.Height()
 | |
|                                                             : rootRect.Width();
 | |
|     rootMargin.Side(side) =
 | |
|         nsLayoutUtils::ComputeCBDependentValue(basis, mRootMargin.Get(side));
 | |
|   }
 | |
| 
 | |
|   for (size_t i = 0; i < mObservationTargets.Length(); ++i) {
 | |
|     Element* target = mObservationTargets.ElementAt(i);
 | |
|     nsIFrame* targetFrame = target->GetPrimaryFrame();
 | |
|     nsRect targetRect;
 | |
|     Maybe<nsRect> intersectionRect;
 | |
|     bool isSameDoc = root && root->GetComposedDoc() == target->GetComposedDoc();
 | |
| 
 | |
|     if (rootFrame && targetFrame) {
 | |
|       // If mRoot is set we are testing intersection with a container element
 | |
|       // instead of the implicit root.
 | |
|       if (mRoot) {
 | |
|         // Skip further processing of this target if it is not in the same
 | |
|         // Document as the intersection root, e.g. if root is an element of
 | |
|         // the main document and target an element from an embedded iframe.
 | |
|         if (!isSameDoc) {
 | |
|           continue;
 | |
|         }
 | |
|         // Skip further processing of this target if is not a descendant of the
 | |
|         // intersection root in the containing block chain. E.g. this would be
 | |
|         // the case if the target is in a position:absolute element whose
 | |
|         // containing block is an ancestor of root.
 | |
|         if (!nsLayoutUtils::IsAncestorFrameCrossDoc(rootFrame, targetFrame)) {
 | |
|           continue;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       targetRect = nsLayoutUtils::GetAllInFlowRectsUnion(
 | |
|           targetFrame,
 | |
|           nsLayoutUtils::GetContainingBlockForClientRect(targetFrame),
 | |
|           nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
 | |
|       intersectionRect = Some(targetFrame->GetRectRelativeToSelf());
 | |
| 
 | |
|       nsIFrame* containerFrame =
 | |
|           nsLayoutUtils::GetCrossDocParentFrame(targetFrame);
 | |
|       while (containerFrame && containerFrame != rootFrame) {
 | |
|         if (containerFrame->IsScrollFrame()) {
 | |
|           nsIScrollableFrame* scrollFrame = do_QueryFrame(containerFrame);
 | |
|           nsRect subFrameRect = scrollFrame->GetScrollPortRect();
 | |
|           nsRect intersectionRectRelativeToContainer =
 | |
|               nsLayoutUtils::TransformFrameRectToAncestor(
 | |
|                   targetFrame, intersectionRect.value(), containerFrame);
 | |
|           intersectionRect = EdgeInclusiveIntersection(
 | |
|               intersectionRectRelativeToContainer, subFrameRect);
 | |
|           if (!intersectionRect) {
 | |
|             break;
 | |
|           }
 | |
|           targetFrame = containerFrame;
 | |
|         }
 | |
| 
 | |
|         // TODO: Apply clip-path.
 | |
| 
 | |
|         containerFrame = nsLayoutUtils::GetCrossDocParentFrame(containerFrame);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     nsRect rootIntersectionRect;
 | |
|     BrowsingContextInfo isInSimilarOriginBrowsingContext =
 | |
|         BrowsingContextInfo::UnknownBrowsingContext;
 | |
| 
 | |
|     if (rootFrame && targetFrame) {
 | |
|       rootIntersectionRect = rootRect;
 | |
|     }
 | |
| 
 | |
|     if (root && target) {
 | |
|       isInSimilarOriginBrowsingContext =
 | |
|           CheckSimilarOrigin(root, target)
 | |
|               ? BrowsingContextInfo::SimilarOriginBrowsingContext
 | |
|               : BrowsingContextInfo::DifferentOriginBrowsingContext;
 | |
|     }
 | |
| 
 | |
|     if (isInSimilarOriginBrowsingContext ==
 | |
|         BrowsingContextInfo::SimilarOriginBrowsingContext) {
 | |
|       rootIntersectionRect.Inflate(rootMargin);
 | |
|     }
 | |
| 
 | |
|     if (intersectionRect.isSome()) {
 | |
|       nsRect intersectionRectRelativeToRoot =
 | |
|           nsLayoutUtils::TransformFrameRectToAncestor(
 | |
|               targetFrame, intersectionRect.value(),
 | |
|               nsLayoutUtils::GetContainingBlockForClientRect(rootFrame));
 | |
|       intersectionRect = EdgeInclusiveIntersection(
 | |
|           intersectionRectRelativeToRoot, rootIntersectionRect);
 | |
|       if (intersectionRect.isSome() && !isSameDoc) {
 | |
|         nsRect rect = intersectionRect.value();
 | |
|         nsPresContext* presContext = targetFrame->PresContext();
 | |
|         nsIFrame* rootScrollFrame =
 | |
|             presContext->PresShell()->GetRootScrollFrame();
 | |
|         if (rootScrollFrame) {
 | |
|           nsLayoutUtils::TransformRect(rootFrame, rootScrollFrame, rect);
 | |
|         }
 | |
|         intersectionRect = Some(rect);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     int64_t targetArea =
 | |
|         (int64_t)targetRect.Width() * (int64_t)targetRect.Height();
 | |
|     int64_t intersectionArea = !intersectionRect
 | |
|                                    ? 0
 | |
|                                    : (int64_t)intersectionRect->Width() *
 | |
|                                          (int64_t)intersectionRect->Height();
 | |
| 
 | |
|     double intersectionRatio;
 | |
|     if (targetArea > 0.0) {
 | |
|       intersectionRatio =
 | |
|           std::min((double)intersectionArea / (double)targetArea, 1.0);
 | |
|     } else {
 | |
|       intersectionRatio = intersectionRect.isSome() ? 1.0 : 0.0;
 | |
|     }
 | |
| 
 | |
|     int32_t threshold = -1;
 | |
|     if (intersectionRect.isSome()) {
 | |
|       // Spec: "Let thresholdIndex be the index of the first entry in
 | |
|       // observer.thresholds whose value is greater than intersectionRatio."
 | |
|       threshold = mThresholds.IndexOfFirstElementGt(intersectionRatio);
 | |
|       if (threshold == 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.
 | |
|         threshold = -1;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (target->UpdateIntersectionObservation(this, threshold)) {
 | |
|       QueueIntersectionObserverEntry(
 | |
|           target, time,
 | |
|           isInSimilarOriginBrowsingContext ==
 | |
|                   BrowsingContextInfo::DifferentOriginBrowsingContext
 | |
|               ? Nothing()
 | |
|               : Some(rootIntersectionRect),
 | |
|           targetRect, intersectionRect, intersectionRatio);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::QueueIntersectionObserverEntry(
 | |
|     Element* aTarget, DOMHighResTimeStamp time, const Maybe<nsRect>& aRootRect,
 | |
|     const nsRect& aTargetRect, const Maybe<nsRect>& aIntersectionRect,
 | |
|     double aIntersectionRatio) {
 | |
|   RefPtr<DOMRect> rootBounds;
 | |
|   if (aRootRect.isSome()) {
 | |
|     rootBounds = new DOMRect(this);
 | |
|     rootBounds->SetLayoutRect(aRootRect.value());
 | |
|   }
 | |
|   RefPtr<DOMRect> boundingClientRect = new DOMRect(this);
 | |
|   boundingClientRect->SetLayoutRect(aTargetRect);
 | |
|   RefPtr<DOMRect> intersectionRect = new DOMRect(this);
 | |
|   if (aIntersectionRect.isSome()) {
 | |
|     intersectionRect->SetLayoutRect(aIntersectionRect.value());
 | |
|   }
 | |
|   RefPtr<DOMIntersectionObserverEntry> entry = new DOMIntersectionObserverEntry(
 | |
|       this, time, rootBounds.forget(), boundingClientRect.forget(),
 | |
|       intersectionRect.forget(), aIntersectionRect.isSome(), aTarget,
 | |
|       aIntersectionRatio);
 | |
|   mQueuedEntries.AppendElement(entry.forget());
 | |
| }
 | |
| 
 | |
| void DOMIntersectionObserver::Notify() {
 | |
|   if (!mQueuedEntries.Length()) {
 | |
|     return;
 | |
|   }
 | |
|   mozilla::dom::Sequence<mozilla::OwningNonNull<DOMIntersectionObserverEntry>>
 | |
|       entries;
 | |
|   if (entries.SetCapacity(mQueuedEntries.Length(), mozilla::fallible)) {
 | |
|     for (size_t i = 0; i < mQueuedEntries.Length(); ++i) {
 | |
|       RefPtr<DOMIntersectionObserverEntry> next = mQueuedEntries[i];
 | |
|       *entries.AppendElement(mozilla::fallible) = next;
 | |
|     }
 | |
|   }
 | |
|   mQueuedEntries.Clear();
 | |
|   mCallback->Call(this, entries, *this);
 | |
| }
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 |