Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: bd130fc5376dc79334604292566f3073f46dc5a4
https://github.com/WebKit/WebKit/commit/bd130fc5376dc79334604292566f3073f46dc5a4
Author: Kiet Ho <[email protected]>
Date: 2026-07-15 (Wed, 15 Jul 2026)
Changed paths:
M
LayoutTests/imported/w3c/web-platform-tests/resize-observer/observe-015-expected.txt
M
LayoutTests/imported/w3c/web-platform-tests/resize-observer/observe-018-expected.txt
A
LayoutTests/resize-observer/resize-observer-observe-multiple-then-gc-expected.txt
A LayoutTests/resize-observer/resize-observer-observe-multiple-then-gc.html
M Source/WebCore/dom/Element.cpp
M Source/WebCore/page/ResizeObserver.cpp
M Source/WebCore/page/ResizeObserver.h
Log Message:
-----------
[resize-observer] Crash when observing an element multiple times
rdar://181487491
https://bugs.webkit.org/show_bug.cgi?id=319009
Reviewed by Ryosuke Niwa.
In ResizeObserver, an observation in m_observations should exist in
m_observationMap
and vice versa. But this invariant is broken if an element is observed twice
with
different box options:
void ResizeObserver::observeInternal(Element& target, const
ResizeObserverBoxOptions boxOptions)
{
ASSERT(!m_JSOrNativeCallback.valueless_by_exception());
auto addResult = m_observationMap.ensure(target, [&]() {
return ResizeObservation::create(target, boxOptions);
}); <-- (a)
Ref observation = addResult.iterator->value;
if (!addResult.isNewEntry) {
// The spec suggests unconditionally unobserving here, but that causes
a test failure:
// https://github.com/web-platform-tests/wpt/issues/30708
if (observation->observedBox() == boxOptions)
return;
unobserve(target); <-- (b)
}
// (c)
auto& observerData = target.ensureResizeObserverData();
observerData.observers.append(*this);
m_observations.add(WTF::move(observation));
[...]
}
(a) adds the element to m_observationMap if one doesn't exist, (b) unobserves
if the element is already being observed but the new observation requests
different box options, and (c) adds the element to m_observations.
If an element is observed twice like this:
observer.observe(input);
observer.observe(input, { box: "border-box" });
On the second observation: (a) does not add anything to m_observationMap
because it's already observed. (b) unobserve() removes the element from both
m_observationMap and m_observations, and (c) adds it to m_observations, but
crucially, it does not add it back to m_observationMap. This breaks the
invariant between m_observation and m_observationMap.
Fix this by first testing if the element is observed first. If it is,
unobserve it before adding the element to m_observationMap and m_observation.
Additionally, harden ResizeObserverData::observers by making it a WeakHashSet,
so iterating through it would skip already freed observers.
Test: imported/w3c/web-platform-tests/resize-observer/observe-015.html
imported/w3c/web-platform-tests/resize-observer/observe-018.html
resize-observer/resize-observer-observe-multiple-then-gc.html
*
LayoutTests/imported/w3c/web-platform-tests/resize-observer/observe-015-expected.txt:
*
LayoutTests/imported/w3c/web-platform-tests/resize-observer/observe-018-expected.txt:
- Test progression.
*
LayoutTests/resize-observer/resize-observer-observe-multiple-then-gc-expected.txt:
Added.
* LayoutTests/resize-observer/resize-observer-observe-multiple-then-gc.html:
Added.
- Added test. The crash is during when an observed element is freed, so it
needs to call the internal GC method and can't be upstreamed to WPT.
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::disconnectFromResizeObserversSlow):
* Source/WebCore/page/ResizeObserver.cpp:
(WebCore::ResizeObserver::observeInternal):
(WebCore::ResizeObserver::removeTarget):
* Source/WebCore/page/ResizeObserver.h:
Canonical link: https://commits.webkit.org/317224@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications