<html>
    <head>
      <base href="https://bugs.webkit.org/" />
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - Wheel event callback removing the window causes crash in WebCore."
   href="https://bugs.webkit.org/show_bug.cgi?id=150871">150871</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>Wheel event callback removing the window causes crash in WebCore.
          </td>
        </tr>

        <tr>
          <th>Classification</th>
          <td>Unclassified
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>WebKit
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>WebKit Nightly Build
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>Macintosh
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Mac OS X 10.10
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>Normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P2
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>WebCore JavaScript
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>webkit-unassigned&#64;lists.webkit.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>alexanderomara&#64;gmail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>In WebKit, if a window is removed while it is triggering a wheel event, it will crash on a bad memory access (example code and live demo below).

The crash is triggered in the WebCore/page/FrameView.cpp, in FrameView::wheelEvent, on what I believe is the delegatesScrolling() code.

However, the fault appears to lie with the function calling it.

WebCore/page/mac/EventHandlerMac.mm:
...
bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent&amp; wheelEvent, ContainerNode* scrollableContainer, ScrollableArea* scrollableArea)
{
    // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
    ASSERT(m_frame.view());
...

The only check against it being null appears to be an ASSERT call, which is not graceful and removed in release builds.

Compare this against a similar funtion:

WebCore/page/EventHandler.cpp
...
bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent&amp; event, ContainerNode*, ScrollableArea*)
{
    // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
    FrameView* view = m_frame.view();

    bool didHandleEvent = view ? view-&gt;wheelEvent(event) : false;
    m_isHandlingWheelEvent = false;
    return didHandleEvent;
}
...

This has a check against a null pointer, before calling the wheelEvent.


Currently, this can be seen affecting both the latest Nightly WebKit browser and Apple's Safari, as well as anything that links against the same libraries, such as those using WebView.

Additionally, I ran a test with pywebview, which I believe is using a legacy API for these libraries, and it crashes in WebCore/page/mac/EventHandlerMac.mm, on the EventHandler::platformCompletePlatformWidgetWheelEvent function.



Here is a live demo for how to trigger the crash:

<a href="https://cdn.rawgit.com/AlexanderOMara/49e10fcd48b0ab680c56/raw/5c131abd9a11a8f5b1a4534c8ceba2d23f515781/index.html">https://cdn.rawgit.com/AlexanderOMara/49e10fcd48b0ab680c56/raw/5c131abd9a11a8f5b1a4534c8ceba2d23f515781/index.html</a>

And the code from the link:

&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;utf-8&quot; /&gt;
        &lt;title&gt;WebKit Wheel Event Remove Window Crash&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;script type=&quot;text/javascript&quot;&gt;
/*&lt;!--*/
(function(window) {'use strict';

var iframe = window.document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.left = iframe.style.top = '5%';
iframe.style.width = iframe.style.height = '90%';
window.document.body.appendChild(iframe);

function iframeReady() {
    iframe.contentWindow.document.body.innerHTML = '&lt;h1&gt;Wheel here to crash.&lt;/h1&gt;';
    iframe.contentWindow.addEventListener('wheel', function() {
        // Removing the window during event firing causes crash.
        window.document.body.removeChild(iframe);
    });
}

var pollInterval;
var pollReady = function() {
    if (iframe.contentWindow.document.body) {
        window.clearInterval(pollInterval);
        iframeReady();
    }
};
pollInterval = window.setInterval(pollReady, 100);

})(window);
/*--&gt;*/
        &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are the assignee for the bug.</li>
      </ul>
    </body>
</html>