/* -*- 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 "nsContainerFrame.h" #include "nsIScrollableFrame.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" #include "nsRefreshDriver.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/ServoBindings.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "Units.h" namespace mozilla::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) if (tmp->mCallback.is>()) { ImplCycleCollectionUnlink( tmp->mCallback.as>()); } 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) if (tmp->mCallback.is>()) { ImplCycleCollectionTraverse( cb, tmp->mCallback.as>(), "mCallback", 0); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END DOMIntersectionObserver::DOMIntersectionObserver( already_AddRefed&& aOwner, dom::IntersectionCallback& aCb) : mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(RefPtr(&aCb)) {} already_AddRefed DOMIntersectionObserver::Constructor( const GlobalObject& aGlobal, dom::IntersectionCallback& aCb, ErrorResult& aRv) { return Constructor(aGlobal, aCb, IntersectionObserverInit(), aRv); } already_AddRefed DOMIntersectionObserver::Constructor( const GlobalObject& aGlobal, dom::IntersectionCallback& aCb, const IntersectionObserverInit& aOptions, ErrorResult& aRv) { nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); if (!window) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr observer = new DOMIntersectionObserver(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()); observer->mRoot = aOptions.mRoot.Value().GetAsDocument(); } } if (!observer->SetRootMargin(aOptions.mRootMargin)) { aRv.ThrowSyntaxError("rootMargin must be specified in pixels or percent."); return nullptr; } if (aOptions.mThreshold.IsDoubleSequence()) { const Sequence& thresholds = aOptions.mThreshold.GetAsDoubleSequence(); observer->mThresholds.SetCapacity(thresholds.Length()); for (const auto& thresh : thresholds) { if (thresh < 0.0 || thresh > 1.0) { aRv.ThrowRangeError(); return nullptr; } observer->mThresholds.AppendElement(thresh); } observer->mThresholds.Sort(); if (observer->mThresholds.IsEmpty()) { observer->mThresholds.AppendElement(0.0); } } else { double thresh = aOptions.mThreshold.GetAsDouble(); if (thresh < 0.0 || thresh > 1.0) { aRv.ThrowRangeError(); return nullptr; } observer->mThresholds.AppendElement(thresh); } return observer.forget(); } static void LazyLoadCallback( const Sequence>& aEntries) { for (const auto& entry : aEntries) { Element* target = entry->Target(); if (entry->IsIntersecting()) { if (auto* image = HTMLImageElement::FromNode(target)) { image->StopLazyLoading(HTMLImageElement::StartLoading::Yes); } else if (auto* iframe = HTMLIFrameElement::FromNode(target)) { iframe->StopLazyLoading(); } else { MOZ_ASSERT_UNREACHABLE( "Only and