Skip to content

Consolidate lazy-load observers#59807

Open
credod wants to merge 1 commit intoWebKit:mainfrom
Squarespace:eng/Consolidate-lazy-load-observers
Open

Consolidate lazy-load observers#59807
credod wants to merge 1 commit intoWebKit:mainfrom
Squarespace:eng/Consolidate-lazy-load-observers

Conversation

@credod
Copy link

@credod credod commented Mar 3, 2026

https://bugs.webkit.org/show_bug.cgi?id=309078

Reviewed by NOBODY (OOPS!).

Consolidates LazyLoadImageObserver, LazyLoadFrameObserver, and LazyLoadModelObserver to a single LazyLoadElementObserver.

* Source/WebCore/Modules/model-element/HTMLModelElement.cpp: (WebCore::HTMLModelElement::~HTMLModelElement):
(WebCore::HTMLModelElement::stop):
(WebCore::HTMLModelElement::insertedIntoAncestor): (WebCore::HTMLModelElement::removedFromAncestor):
(WebCore::HTMLModelElement::lazyLoadIntersectionCallbackInvoked):
* Source/WebCore/Modules/model-element/HTMLModelElement.h:
* Source/WebCore/Modules/model-element/LazyLoadModelObserver.cpp: Removed.
* Source/WebCore/Modules/model-element/LazyLoadModelObserver.h: Removed.
* Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations:
* Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/dom/Document.cpp: (WebCore::Document::lazyLoadElementObserver):
(WebCore::Document::lazyLoadImageObserver): Deleted. (WebCore::Document::lazyLoadModelObserver): Deleted.
* Source/WebCore/dom/Document.h:
* Source/WebCore/html/HTMLIFrameElement.cpp: (WebCore::HTMLIFrameElement::attributeChanged):
(WebCore::HTMLIFrameElement::referrerPolicy const): (WebCore::HTMLIFrameElement::shouldLoadFrameLazily): (WebCore::HTMLIFrameElement::isLazyLoadObserverActive const): (WebCore::HTMLIFrameElement::loadDeferredFrame):
(WebCore::HTMLIFrameElement::lazyLoadIntersectionCallbackInvoked): (WebCore::HTMLIFrameElement::lazyLoadFrameObserver): Deleted.
* Source/WebCore/html/HTMLIFrameElement.h:
* Source/WebCore/html/HTMLImageElement.cpp: (WebCore::HTMLImageElement::lazyLoadIntersectionCallbackInvoked):
* Source/WebCore/html/HTMLImageElement.h:
* Source/WebCore/html/LazyLoadElementObserver.cpp: Renamed from Source/WebCore/html/LazyLoadImageObserver.cpp. (WebCore::LazyLoadElementObserver::observe):
(WebCore::LazyLoadElementObserver::unobserve):
(WebCore::LazyLoadElementObserver::intersectionObserver): (WebCore::LazyLoadElementObserver::isObserved const):
* Source/WebCore/html/LazyLoadElementObserver.h: Renamed from Source/WebCore/html/LazyLoadImageObserver.h.
* Source/WebCore/html/LazyLoadFrameObserver.cpp: Removed.
* Source/WebCore/html/LazyLoadFrameObserver.h: Removed.
* Source/WebCore/html/Origin.cpp:
* Source/WebCore/html/shadow/ProgressShadowElement.cpp:
* Source/WebCore/loader/ImageLoader.cpp: (WebCore::ImageLoader::didUpdateCachedImage):
(WebCore::ImageLoader::notifyFinished):
(WebCore::ImageLoader::resetLazyImageLoading):

583d2f0

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win
✅ 🧪 bindings ✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ✅ 🧪 win-tests
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🛠 gtk3-libwebrtc
✅ 🧪 api-ios ✅ 🧪 mac-wk1 ✅ 🛠 gtk
✅ 🛠 ios-safer-cpp ✅ 🧪 mac-wk2 ✅ 🧪 gtk-wk2
✅ 🛠 vision ✅ 🧪 mac-AS-debug-wk2 ✅ 🧪 api-gtk
✅ 🛠 vision-sim ✅ 🧪 mac-wk2-stress 🛠 playstation
✅ 🧪 vision-wk2 ✅ 🧪 mac-intel-wk2
✅ 🛠 tv ✅ 🛠 mac-safer-cpp
✅ 🛠 tv-sim
✅ 🛠 watch
✅ 🛠 watch-sim

if (!m_observer) {
auto callback = LazyImageLoadIntersectionObserverCallback::create(document);
auto callback = LazyLoadIntersectionObserverCallback::create(document);
static NeverDestroyed<const String> lazyLoadingScrollMarginFallback(MAKE_STATIC_STRING_IMPL("100%"));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing to call out here is that the lazy loading behavior for <iframe> will change. LazyLoadFrameObserver didn't previously specify custom scrollMargin, meaning that <iframe> elements would only "intersect" when they entered the viewport. Here, we are setting this value to be 100%, which is what was done for LazyLoadImageObserver and LazyLoadModelObserver.

The HTML spec suggests that we should be reusing the lazy load observer instance across <img> and <iframe>:

For img and iframe elements that will lazy load, these steps are run from the lazy load intersection observer's callback or when their lazy loading attribute is set to the Eager state. This causes the element to continue loading.

Therefore, this change would ensure that Webkit is following this standard outlined by the spec but I'm curious to know what people think about this behavioral change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems OK, but I'd like the behavior change to be confirmed by a new test (or a newly passing test). Are there any WPT for iframe lazy loading that use scroll-margin?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WPT tests typically seem to allow generous scroll margins so that browsers can implement lazy-loading to their liking. I did run existing WPT tests against this patch and seems like this implementation matches existing behavior, and additionally gets us passing a few more tests actually:

<iframe> loading tests in Safari <iframe> loading tests in MiniBrowser (with changes from this patch)
image image

if (!m_observer) {
auto callback = LazyImageLoadIntersectionObserverCallback::create(document);
auto callback = LazyLoadIntersectionObserverCallback::create(document);
static NeverDestroyed<const String> lazyLoadingScrollMarginFallback(MAKE_STATIC_STRING_IMPL("100%"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems OK, but I'd like the behavior change to be confirmed by a new test (or a newly passing test). Are there any WPT for iframe lazy loading that use scroll-margin?

element->document().lazyLoadImageObserver().unobserve(*element, element->document());
}
if (RefPtr element = dynamicDowncast<HTMLImageElement>(entry->target()))
element->lazyLoadIntersectionCallbackInvoked(entry->isIntersecting());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Element could have a virtual lazyLoadIntersectionCallbackInvoked, so you don't need to dynamicDowncast here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could certainly clean things up here, but it kind of feels like overkill for Element to be aware of this, since it only impacts a few specific elements for now. However, I'm happy to make this change if we think this is a better approach!

element->document().lazyLoadImageObserver().unobserve(*element, element->document());
}
if (RefPtr element = dynamicDowncast<HTMLImageElement>(entry->target()))
element->lazyLoadIntersectionCallbackInvoked(entry->isIntersecting());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Element could have a virtual lazyLoadIntersectionCallbackInvoked, so you don't need to dynamicDowncast here

@webkit-ews-buildbot
Copy link
Collaborator

macOS Safer C++ Build #83729 (95e1fb2)

❌ Found 2 failing files with 8 issues. Please address these issues before landing. See WebKit Guidelines for Safer C++ Programming.
(cc @rniwa)

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Mar 3, 2026
@webkit-ews-buildbot
Copy link
Collaborator

iOS Safer C++ Build #2019 (95e1fb2)

❌ Found 3 failing files with 9 issues. Please address these issues before landing. See WebKit Guidelines for Safer C++ Programming.
(cc @rniwa)

@credod credod force-pushed the eng/Consolidate-lazy-load-observers branch from 95e1fb2 to d7bb612 Compare March 3, 2026 22:17
@webkit-ews-buildbot
Copy link
Collaborator

macOS Safer C++ Build #83792 (d7bb612)

⚠️ Found 1 fixed file! Please update expectations in Source/[Project]/SaferCPPExpectations by running the following command and update your pull request:

  • Tools/Scripts/update-safer-cpp-expectations -p WebCore --UncheckedCallArgsChecker html/LazyLoadElementObserver.cpp --platform macOS

@webkit-ews-buildbot
Copy link
Collaborator

iOS Safer C++ Build #2082 (d7bb612)

❌ Found 1 failing file with 1 issue. Please address these issues before landing. See WebKit Guidelines for Safer C++ Programming.
(cc @rniwa)

⚠️ Found 1 fixed file! Please update expectations in Source/[Project]/SaferCPPExpectations by running the following command and update your pull request:

  • Tools/Scripts/update-safer-cpp-expectations -p WebCore --UncheckedCallArgsChecker html/LazyLoadElementObserver.cpp --platform iOS

@credod credod force-pushed the eng/Consolidate-lazy-load-observers branch from d7bb612 to aa6db59 Compare March 4, 2026 16:29
@weinig
Copy link
Contributor

weinig commented Mar 4, 2026

Thanks for doing the cleanup!

@webkit-ews-buildbot
Copy link
Collaborator

macOS Safer C++ Build #83945 (aa6db59)

⚠️ Found 1 fixed file! Please update expectations in Source/[Project]/SaferCPPExpectations by running the following command and update your pull request:

  • Tools/Scripts/update-safer-cpp-expectations -p WebCore --NoDeleteChecker Modules/webtransport/WebTransportDatagramsWritable.cpp --platform macOS

https://bugs.webkit.org/show_bug.cgi?id=309078

Reviewed by NOBODY (OOPS!).

Consolidates LazyLoadImageObserver, LazyLoadFrameObserver, and LazyLoadModelObserver to a single LazyLoadElementObserver.

* Source/WebCore/Modules/model-element/HTMLModelElement.cpp:
(WebCore::HTMLModelElement::~HTMLModelElement):
(WebCore::HTMLModelElement::stop):
(WebCore::HTMLModelElement::insertedIntoAncestor):
(WebCore::HTMLModelElement::removedFromAncestor):
(WebCore::HTMLModelElement::lazyLoadIntersectionCallbackInvoked):
(WebCore::HTMLModelElement::viewportIntersectionChanged): Deleted.
* Source/WebCore/Modules/model-element/HTMLModelElement.h:
* Source/WebCore/Modules/model-element/LazyLoadModelObserver.cpp: Removed.
* Source/WebCore/Modules/model-element/LazyLoadModelObserver.h: Removed.
* Source/WebCore/Modules/webtransport/WebTransportDatagramsWritable.h:
* Source/WebCore/SaferCPPExpectations/NoDeleteCheckerExpectations:
* Source/WebCore/SaferCPPExpectations/UncheckedCallArgsCheckerExpectations:
* Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/dom/Document.cpp:
(WebCore::Document::lazyLoadElementObserver):
(WebCore::Document::lazyLoadImageObserver): Deleted.
(WebCore::Document::lazyLoadModelObserver): Deleted.
* Source/WebCore/dom/Document.h:
* Source/WebCore/html/HTMLIFrameElement.cpp:
(WebCore::HTMLIFrameElement::attributeChanged):
(WebCore::HTMLIFrameElement::referrerPolicy const):
(WebCore::HTMLIFrameElement::shouldLoadFrameLazily):
(WebCore::HTMLIFrameElement::isLazyLoadObserverActive const):
(WebCore::HTMLIFrameElement::loadDeferredFrame):
(WebCore::HTMLIFrameElement::lazyLoadIntersectionCallbackInvoked):
(WebCore::HTMLIFrameElement::lazyLoadFrameObserver): Deleted.
* Source/WebCore/html/HTMLIFrameElement.h:
* Source/WebCore/html/HTMLImageElement.cpp:
(WebCore::HTMLImageElement::lazyLoadIntersectionCallbackInvoked):
* Source/WebCore/html/HTMLImageElement.h:
* Source/WebCore/html/LazyLoadElementObserver.cpp: Renamed from Source/WebCore/html/LazyLoadImageObserver.cpp.
(WebCore::LazyLoadElementObserver::observe):
(WebCore::LazyLoadElementObserver::unobserve):
(WebCore::LazyLoadElementObserver::intersectionObserver):
(WebCore::LazyLoadElementObserver::isObserved const):
* Source/WebCore/html/LazyLoadElementObserver.h: Renamed from Source/WebCore/html/LazyLoadImageObserver.h.
* Source/WebCore/html/LazyLoadFrameObserver.cpp: Removed.
* Source/WebCore/html/LazyLoadFrameObserver.h: Removed.
* Source/WebCore/html/Origin.cpp:
* Source/WebCore/html/shadow/ProgressShadowElement.cpp:
* Source/WebCore/loader/ImageLoader.cpp:
(WebCore::ImageLoader::didUpdateCachedImage):
(WebCore::ImageLoader::notifyFinished):
(WebCore::ImageLoader::resetLazyImageLoading):
@credod credod force-pushed the eng/Consolidate-lazy-load-observers branch from aa6db59 to 583d2f0 Compare March 4, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merging-blocked Applied to prevent a change from being merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants