Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 6ede33e742f29f884116edc86a0cc2a2bcacd366
https://github.com/WebKit/WebKit/commit/6ede33e742f29f884116edc86a0cc2a2bcacd366
Author: Wenson Hsieh <[email protected]>
Date: 2026-02-21 (Sat, 21 Feb 2026)
Changed paths:
M Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
M Source/WebKit/UIProcess/ios/WKScrollView.h
M Source/WebKit/UIProcess/ios/WKScrollView.mm
M Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm
Log Message:
-----------
REGRESSION (306807@main): Unrecognized selector -scrollViewDidScroll: under
-[WKScrollViewDelegateForwarder forwardInvocation:]
https://bugs.webkit.org/show_bug.cgi?id=308402
rdar://170105626
Reviewed by Abrar Rahman Protyasha and Richard Robinson.
After the safer CPP changes in 306807@main turned `_internalDelegate` into a
`WeakObjCPtr`, it's
possible to end up in a scenario where `WKScrollViewDelegateForwarder` receives
an unrecognized
selector that was intended for `WKWebView`. This happens when:
1. The scroll view get an external delegate from the WebKit client (which
internally creates a
`WKScrollViewDelegateForwarder` that forwards scroll view delegate calls to
either the web view
or the WebKit client's delegate). When calling into `-[UIScrollView
setDelegate:]`, UIKit also
caches the results of `-respondsToSelector:` on the delegate forwarder for
all scroll view
delegate methods.
2. The web view is later deallocated (`-[WKWebView dealloc]`). Before calling
into
`-setInternalDelegate:`, however, `_internalDelegate` in `WKScrollView` is
already set to `nil`
due to `WeakObjCPtr` semantics, which causes `-[WKScrollView
setInternalDelegate:]` to bail
early and **not** update the real delegate (which remains
`WKScrollViewDelegateForwarder`).
3. Later on, if the scroll view is used in any way that causes one of the
scroll view delegate
methods that `WKWebView` responds to (per the cached results in [1]), UIKit
ends up trying to
invoke that selector on `WKScrollViewDelegateForwarder`, which has since
been detached from its
web view. The end result is an unrecognized selector and an ObjC exception.
This is essentially a TOCTOU between [1] (where UIKit checks that the delegate
forwarder responds to
selectors like `scrollViewDidScroll:`, etc.) and [3] (where UIKit goes to
invoke the selector), in
the case where the internal delegate (web view) is destroyed in the interim.
To fix this, we add a second method to forcibly invalidate the
`_internalDelegate` and call into
`-[UIScrollView setDelegate:]` to update the cached `respondsToSelector:` flags.
Test: WKScrollViewTests.DoNotCrashIfScrollViewOutlivesWebView
* Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView dealloc]):
* Source/WebKit/UIProcess/ios/WKScrollView.h:
* Source/WebKit/UIProcess/ios/WKScrollView.mm:
(-[WKScrollView _invalidateInternalDelegate]):
See above for more details.
* Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm:
(TestWebKitAPI::TEST(WKScrollViewTests, DoNotCrashIfScrollViewOutlivesWebView)):
Canonical link: https://commits.webkit.org/307999@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications