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

Reply via email to