commit 9ded204d1de955c52f2af8b99773bb875b2962de parent 5e022982db6bb7dac68a0c8ec056fedce56869a6 Author: Emilio Cobos Álvarez <emilio@crisal.io> Date: Sat, 3 Jan 2026 12:23:24 +0000 Bug 1864794 - Use scrollMargin for image lazy loading. r=keithamus Differential Revision: https://phabricator.services.mozilla.com/D277782 Diffstat:
19 files changed, 41 insertions(+), 66 deletions(-)
diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp @@ -210,13 +210,12 @@ static LengthPercentage PrefMargin(float aValue, bool aIsPercentage) { : LengthPercentage::FromPixels(aValue); } -IntersectionObserverMargin DOMIntersectionObserver::LazyLoadingRootMargin() { +static IntersectionObserverMargin LazyLoadingMargin() { IntersectionObserverMargin margin; -#define SET_MARGIN(side_, side_lower_) \ - margin.Get(eSide##side_) = PrefMargin( \ - StaticPrefs::dom_image_lazy_loading_root_margin_##side_lower_(), \ - StaticPrefs:: \ - dom_image_lazy_loading_root_margin_##side_lower_##_percentage()); +#define SET_MARGIN(side_, side_lower_) \ + margin.Get(eSide##side_) = PrefMargin( \ + StaticPrefs::dom_lazy_loading_margin_##side_lower_(), \ + StaticPrefs::dom_lazy_loading_margin_##side_lower_##_percentage()); SET_MARGIN(Top, top); SET_MARGIN(Right, right); SET_MARGIN(Bottom, bottom); @@ -236,7 +235,10 @@ DOMIntersectionObserver::CreateLazyLoadObserver(Document& aDocument) { RefPtr<DOMIntersectionObserver> observer = new DOMIntersectionObserver(aDocument, LazyLoadCallback); observer->mThresholds.AppendElement(0.0f); - observer->mRootMargin = LazyLoadingRootMargin(); + auto* margin = StaticPrefs::dom_lazy_loading_margin_is_scroll() + ? &observer->mScrollMargin + : &observer->mRootMargin; + *margin = LazyLoadingMargin(); return observer.forget(); } @@ -659,6 +661,16 @@ static Maybe<OopIframeMetrics> GetOopIframeMetrics( }); } +IntersectionInput DOMIntersectionObserver::ComputeInputForIframeThrottling( + const Document& aEmbedderDocument) { + auto margin = LazyLoadingMargin(); + // TODO: Consider not using exactly the same parameters as lazy-load? + const bool useScroll = StaticPrefs::dom_lazy_loading_margin_is_scroll(); + return ComputeInput(aEmbedderDocument, /* aRoot = */ nullptr, + /* aRootMargin = */ useScroll ? nullptr : &margin, + /* aScrollMargin = */ useScroll ? &margin : nullptr); +} + // https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo // step 2.1 IntersectionInput DOMIntersectionObserver::ComputeInput( diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h @@ -155,8 +155,7 @@ class DOMIntersectionObserver final : public nsISupports, void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal); - static StyleRect<LengthPercentage> LazyLoadingRootMargin(); - + static IntersectionInput ComputeInputForIframeThrottling(const Document&); static IntersectionInput ComputeInput( const Document& aDocument, const nsINode* aRoot, const StyleRect<LengthPercentage>* aRootMargin, diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp @@ -7646,10 +7646,8 @@ bool Document::ShouldThrottleFrameRequests() const { // document that previously wasn't visible scrolls into view. This is // acceptable / unlikely to be human-perceivable, though we could improve on // it if needed by adding an intersection margin or something of that sort. - auto margin = DOMIntersectionObserver::LazyLoadingRootMargin(); - const IntersectionInput input = DOMIntersectionObserver::ComputeInput( - *el->OwnerDoc(), /* aRoot = */ nullptr, &margin, - /* aScrollMargin = */ nullptr); + const IntersectionInput input = + DOMIntersectionObserver::ComputeInputForIframeThrottling(*el->OwnerDoc()); const IntersectionOutput output = DOMIntersectionObserver::Intersect( input, *el, DOMIntersectionObserver::BoxToUse::Content); return !output.Intersects(); @@ -17944,9 +17942,8 @@ static void UpdateEffectsOnBrowsingContext(BrowsingContext* aBc, } void Document::UpdateRemoteFrameEffects(bool aIncludeInactive) { - auto margin = DOMIntersectionObserver::LazyLoadingRootMargin(); - const IntersectionInput input = DOMIntersectionObserver::ComputeInput( - *this, /* aRoot = */ nullptr, &margin, /* aScrollMargin = */ nullptr); + const IntersectionInput input = + DOMIntersectionObserver::ComputeInputForIframeThrottling(*this); if (auto* wc = GetWindowContext()) { for (const RefPtr<BrowsingContext>& child : wc->Children()) { UpdateEffectsOnBrowsingContext(child, input, aIncludeInactive); diff --git a/dom/base/test/test_bug1730284.html b/dom/base/test/test_bug1730284.html @@ -31,7 +31,7 @@ overflow: auto; } .scroller-padding { - height: 500px; + height: 800px; } </style> <iframe class="visible"></iframe> diff --git a/layout/generic/test/frame_visibility_in_iframe.html b/layout/generic/test/frame_visibility_in_iframe.html @@ -7,8 +7,8 @@ <body> <div style="width: 100%; height: calc(100vh - 100px);"></div> <div style="height: 100px; overflow: scroll;"> - <!-- 2x height of the scroll port to make this test pass with/without Fission --> - <div style="width: 100%; height: 200px;"></div> + <!-- Must be larger than the IntersectionObserver margin used in DOMIntersectionObserver::ComputeInputForIframeThrottling --> + <div style="width: 100%; height: 800px;"></div> <iframe id="iframe" style="height: 100px; border: none;"></iframe> </div> <script> diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -3160,46 +3160,52 @@ # The root margin for image lazy loading, defined as four (value, percentage) # pairs. -- name: dom.image-lazy-loading.root-margin.top +- name: dom.lazy-loading.margin.top type: float value: 600 mirror: always -- name: dom.image-lazy-loading.root-margin.top.percentage +- name: dom.lazy-loading.margin.top.percentage type: bool value: false mirror: always -- name: dom.image-lazy-loading.root-margin.bottom +- name: dom.lazy-loading.margin.bottom type: float value: 600 mirror: always -- name: dom.image-lazy-loading.root-margin.bottom.percentage +- name: dom.lazy-loading.margin.bottom.percentage type: bool value: false mirror: always -- name: dom.image-lazy-loading.root-margin.left +- name: dom.lazy-loading.margin.left type: float value: 600 mirror: always -- name: dom.image-lazy-loading.root-margin.left.percentage +- name: dom.lazy-loading.margin.left.percentage type: bool value: false mirror: always -- name: dom.image-lazy-loading.root-margin.right +- name: dom.lazy-loading.margin.right type: float value: 600 mirror: always -- name: dom.image-lazy-loading.root-margin.right.percentage +- name: dom.lazy-loading.margin.right.percentage type: bool value: false mirror: always +# Whether we use scrollMargin or rootMargin +- name: dom.lazy-loading.margin.is-scroll + type: bool + value: true + mirror: always + # Enable indexedDB in private browsing mode with encryption - name: dom.indexedDB.privateBrowsing.enabled type: RelaxedAtomicBool diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-horizontal.html] - [Test that lazy-loaded iframes load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-nested-2.html] - [Test that lazy-loaded iframes load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-nested-3.html] - [Test that lazy-loaded iframes load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-nested-4.html] - [Test that lazy-loaded iframes load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-nested-5.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html.ini @@ -1,3 +0,0 @@ -[iframe-loading-lazy-in-scroller-nested.html] - [Test that lazy-loaded iframes load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-horizontal.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-nested-2.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-nested-3.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-nested-4.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-nested-5.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller-nested.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html.ini @@ -1,3 +0,0 @@ -[image-loading-lazy-in-scroller.html] - [Test that lazy-loaded images load when near viewport.] - expected: FAIL