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

Reply via email to