Title: [199181] trunk
Revision
199181
Author
[email protected]
Date
2016-04-07 14:15:34 -0700 (Thu, 07 Apr 2016)

Log Message

Wheel event callback removing the window causes crash in WebCore.
https://bugs.webkit.org/show_bug.cgi?id=150871
<rdar://problem/23418283>

Reviewed by Simon Fraser.

Source/WebCore:

Null check the FrameView before using it, since the iframe may have been removed
from its parent document inside the event handler.
        
The new test triggered a cross-load side-effect, where wheel event filtering wasn't
reset between page loads. Fix by calling clearLatchedState() in EventHandler::clear(),
which resets the filtering.

Since the Frame destructor invokes EventHandler::clear, which invokes MainFrame methods,
we run the risk of attempting to dereference destroyed MainFrame elements of the current
Frame object. Instead, clear the EventHandler in the MainFrame destructor.

Finally, confirm that the mainFrame member is not being destroyed in the handful of
places that might attempt to access the mainFrame during object destruction (essentially
cleanup methods).

Test: fast/events/wheel-event-destroys-frame.html

* loader/FrameLoader.cpp:
(WebCore::FrameLoader::clear): Protect against accessing mainFrame content during destruction.
* page/EventHandler.cpp:
(WebCore::EventHandler::clear): Call 'clearLatchedState' instead of endFilteringDeltas.
(WebCore::EventHandler::clearLatchedState): Null-check the filter before calling it.
* page/Frame.cpp:
(WebCore::Frame::~Frame): Do not call 'setView' in the destructor for a MainFrame.
(WebCore::Frame::setView): Check for a null event handler before invoking it.
(WebCore::Frame::setMainFrameWasDestroyed): Added. Mark that the MainFrame
member of the Frame is being destroyed (if the current Frame is a MainFrame) and clear
the EventHandler member so that it doesn't attempt to access mainFrame content.
(WebCore::Frame::mainFrame): When accessing the mainFrame member, assert that the
mainFrame is not being destroyed.
* page/MainFrame.cpp:
(WebCore::MainFrame::~MainFrame): Set the m_recentWheelEventDeltaFilter to nullptr to
prevent attempts to access it during object destruction. Call the new 'setMainFrameWasDestroyed'
method to reset eventHandler and mark the MainFrame as being in the process of destruction.
* page/WheelEventDeltaFilter.cpp:
(WebCore::WheelEventDeltaFilter::filteredDelta): Add logging.
* page/mac/EventHandlerMac.mm:
(WebCore::EventHandler::platformCompleteWheelEvent): Add null check.
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::scrollTo): Add logging.

LayoutTests:

* fast/events/wheel-event-destroys-frame-expected.txt: Added.
* fast/events/wheel-event-destroys-frame.html: Added.
* platform/ios-simulator/TestExpectations: Skip wheel-event test on iOS.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (199180 => 199181)


--- trunk/LayoutTests/ChangeLog	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/LayoutTests/ChangeLog	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,3 +1,15 @@
+2016-04-07  Brent Fulgham  <[email protected]>
+
+        Wheel event callback removing the window causes crash in WebCore.
+        https://bugs.webkit.org/show_bug.cgi?id=150871
+        <rdar://problem/23418283>
+
+        Reviewed by Simon Fraser.
+
+        * fast/events/wheel-event-destroys-frame-expected.txt: Added.
+        * fast/events/wheel-event-destroys-frame.html: Added.
+        * platform/ios-simulator/TestExpectations: Skip wheel-event test on iOS.
+
 2016-04-07  Saam barati  <[email protected]>
 
         Initial implementation of annex b.3.3 behavior was incorrect

Added: trunk/LayoutTests/fast/events/wheel-event-destroys-frame-expected.txt (0 => 199181)


--- trunk/LayoutTests/fast/events/wheel-event-destroys-frame-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/wheel-event-destroys-frame-expected.txt	2016-04-07 21:15:34 UTC (rev 199181)
@@ -0,0 +1,3 @@
+This test should not crash
+
+

Added: trunk/LayoutTests/fast/events/wheel-event-destroys-frame.html (0 => 199181)


--- trunk/LayoutTests/fast/events/wheel-event-destroys-frame.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/wheel-event-destroys-frame.html	2016-04-07 21:15:34 UTC (rev 199181)
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script>
+        if (window.testRunner) {
+            testRunner.waitUntilDone();
+            testRunner.dumpAsText();
+        }
+
+        function frameLoaded(iframe)
+        {
+            iframe.contentWindow.addEventListener('wheel', function() {
+                // Removing the window during event firing causes crash.
+                window.document.body.removeChild(iframe);
+                window.setTimeout(function() {
+                    if (window.testRunner)
+                        testRunner.notifyDone();
+                }, 0);
+            });
+
+            if (!window.eventSender)
+                return;
+
+            var iframeTarget = document.getElementById('iframe');
+            var iframeBounds = iframeTarget.getBoundingClientRect();
+
+            eventSender.mouseMoveTo(iframeBounds.left + 10, iframeBounds.top + 10);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'began', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none');
+        }
+    </script>
+</head>
+<body>
+    <p>This test should not crash</p>
+    <iframe id="iframe" _onload_="frameLoaded(this)" src="" here</body>"></iframe>
+</body>
+</html>

Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (199180 => 199181)


--- trunk/LayoutTests/platform/ios-simulator/TestExpectations	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations	2016-04-07 21:15:34 UTC (rev 199181)
@@ -57,6 +57,7 @@
 # No mousewheel events on iOS
 fast/scrolling/iframe-scrollable-after-back.html [ Skip ]
 fast/scrolling/overflow-scrollable-after-back.html [ Skip ]
+fast/events/wheel-event-destroys-frame.html [ Skip ]
 
 # Not supported on iOS
 batterystatus

Modified: trunk/Source/WebCore/ChangeLog (199180 => 199181)


--- trunk/Source/WebCore/ChangeLog	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/ChangeLog	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,3 +1,52 @@
+2016-04-07  Brent Fulgham  <[email protected]>
+
+        Wheel event callback removing the window causes crash in WebCore.
+        https://bugs.webkit.org/show_bug.cgi?id=150871
+        <rdar://problem/23418283>
+
+        Reviewed by Simon Fraser.
+
+        Null check the FrameView before using it, since the iframe may have been removed
+        from its parent document inside the event handler.
+        
+        The new test triggered a cross-load side-effect, where wheel event filtering wasn't
+        reset between page loads. Fix by calling clearLatchedState() in EventHandler::clear(),
+        which resets the filtering.
+
+        Since the Frame destructor invokes EventHandler::clear, which invokes MainFrame methods,
+        we run the risk of attempting to dereference destroyed MainFrame elements of the current
+        Frame object. Instead, clear the EventHandler in the MainFrame destructor.
+
+        Finally, confirm that the mainFrame member is not being destroyed in the handful of
+        places that might attempt to access the mainFrame during object destruction (essentially
+        cleanup methods).
+
+        Test: fast/events/wheel-event-destroys-frame.html
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::clear): Protect against accessing mainFrame content during destruction.
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::clear): Call 'clearLatchedState' instead of endFilteringDeltas.
+        (WebCore::EventHandler::clearLatchedState): Null-check the filter before calling it.
+        * page/Frame.cpp:
+        (WebCore::Frame::~Frame): Do not call 'setView' in the destructor for a MainFrame.
+        (WebCore::Frame::setView): Check for a null event handler before invoking it.
+        (WebCore::Frame::setMainFrameWasDestroyed): Added. Mark that the MainFrame
+        member of the Frame is being destroyed (if the current Frame is a MainFrame) and clear
+        the EventHandler member so that it doesn't attempt to access mainFrame content.
+        (WebCore::Frame::mainFrame): When accessing the mainFrame member, assert that the
+        mainFrame is not being destroyed.
+        * page/MainFrame.cpp:
+        (WebCore::MainFrame::~MainFrame): Set the m_recentWheelEventDeltaFilter to nullptr to
+        prevent attempts to access it during object destruction. Call the new 'setMainFrameWasDestroyed'
+        method to reset eventHandler and mark the MainFrame as being in the process of destruction.
+        * page/WheelEventDeltaFilter.cpp:
+        (WebCore::WheelEventDeltaFilter::filteredDelta): Add logging.
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::EventHandler::platformCompleteWheelEvent): Add null check.
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::scrollTo): Add logging.
+
 2016-04-05  Ada Chan  <[email protected]>
 
         Rename TextTrackRepresentationiOS to TextTrackRepresentationCocoa and enable on Mac

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (199180 => 199181)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -604,7 +604,11 @@
     }
 
     m_frame.selection().prepareForDestruction();
-    m_frame.eventHandler().clear();
+
+    // We may call this code during object destruction, so need to make sure eventHandler is present.
+    if (auto eventHandler = m_frame.eventHandlerPtr())
+        eventHandler->clear();
+
     if (clearFrameView && m_frame.view())
         m_frame.view()->clear();
 

Modified: trunk/Source/WebCore/page/EventHandler.cpp (199180 => 199181)


--- trunk/Source/WebCore/page/EventHandler.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Alexey Proskuryakov ([email protected])
  * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
  *
@@ -455,9 +455,7 @@
     m_mousePressed = false;
     m_capturesDragging = false;
     m_capturingMouseEventsElement = nullptr;
-#if PLATFORM(MAC)
-    m_frame.mainFrame().resetLatchingState();
-#endif
+    clearLatchedState();
 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
     m_originatingTouchPointTargets.clear();
     m_originatingTouchPointDocument = nullptr;
@@ -2693,7 +2691,8 @@
 #if PLATFORM(MAC)
     m_frame.mainFrame().resetLatchingState();
 #endif
-    m_frame.mainFrame().wheelEventDeltaFilter()->endFilteringDeltas();
+    if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter())
+        filter->endFilteringDeltas();
 }
 
 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent)

Modified: trunk/Source/WebCore/page/Frame.cpp (199180 => 199181)


--- trunk/Source/WebCore/page/Frame.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/Frame.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -5,7 +5,7 @@
  *                     2000 Simon Hausmann <[email protected]>
  *                     2000 Stefan Schimanski <[email protected]>
  *                     2001 George Staikos <[email protected]>
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2005 Alexey Proskuryakov <[email protected]>
  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2008 Eric Seidel <[email protected]>
@@ -160,7 +160,6 @@
     , m_script(std::make_unique<ScriptController>(*this))
     , m_editor(std::make_unique<Editor>(*this))
     , m_selection(std::make_unique<FrameSelection>(this))
-    , m_eventHandler(std::make_unique<EventHandler>(*this))
     , m_animationController(std::make_unique<AnimationController>(*this))
 #if PLATFORM(IOS)
     , m_overflowAutoScrollTimer(*this, &Frame::overflowAutoScrollTimerFired)
@@ -169,6 +168,7 @@
     , m_pageZoomFactor(parentPageZoomFactor(this))
     , m_textZoomFactor(parentTextZoomFactor(this))
     , m_activeDOMObjectsAndAnimationsSuspendedCount(0)
+    , m_eventHandler(std::make_unique<EventHandler>(*this))
 {
     AtomicString::init();
     HTMLNames::init();
@@ -251,7 +251,9 @@
     if (m_view)
         m_view->unscheduleRelayout();
     
-    eventHandler().clear();
+    // This may be called during destruction, so need to do a null check.
+    if (m_eventHandler)
+        m_eventHandler->clear();
 
     m_view = WTFMove(view);
 

Modified: trunk/Source/WebCore/page/Frame.h (199180 => 199181)


--- trunk/Source/WebCore/page/Frame.h	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/Frame.h	2016-04-07 21:15:34 UTC (rev 199181)
@@ -5,7 +5,7 @@
  *                     2000-2001 Simon Hausmann <[email protected]>
  *                     2000-2001 Dirk Mueller <[email protected]>
  *                     2000 Stefan Schimanski <[email protected]>
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2008 Eric Seidel <[email protected]>
  *
@@ -151,6 +151,7 @@
 
         Editor& editor() const;
         EventHandler& eventHandler() const;
+        EventHandler* eventHandlerPtr() const;
         FrameLoader& loader() const;
         NavigationScheduler& navigationScheduler() const;
         FrameSelection& selection() const;
@@ -278,6 +279,7 @@
 
     protected:
         Frame(Page&, HTMLFrameOwnerElement*, FrameLoaderClient&);
+        void setMainFrameWasDestroyed();
 
     private:
         HashSet<FrameDestructionObserver*> m_destructionObservers;
@@ -296,7 +298,6 @@
         const std::unique_ptr<ScriptController> m_script;
         const std::unique_ptr<Editor> m_editor;
         const std::unique_ptr<FrameSelection> m_selection;
-        const std::unique_ptr<EventHandler> m_eventHandler;
         const std::unique_ptr<AnimationController> m_animationController;
 
 #if ENABLE(DATA_DETECTION)
@@ -326,6 +327,10 @@
         float m_textZoomFactor;
 
         int m_activeDOMObjectsAndAnimationsSuspendedCount;
+        bool m_mainFrameWasDestroyed { false };
+
+    protected:
+        std::unique_ptr<EventHandler> m_eventHandler;
     };
 
     inline void Frame::init()
@@ -398,11 +403,22 @@
         return *m_eventHandler;
     }
 
+    inline EventHandler* Frame::eventHandlerPtr() const
+    {
+        return m_eventHandler.get();
+    }
+
     inline MainFrame& Frame::mainFrame() const
     {
+        ASSERT_WITH_SECURITY_IMPLICATION(!m_mainFrameWasDestroyed);
         return m_mainFrame;
     }
 
+    inline void Frame::setMainFrameWasDestroyed()
+    {
+        m_mainFrameWasDestroyed = false;
+    }
+
 } // namespace WebCore
 
 #endif // Frame_h

Modified: trunk/Source/WebCore/page/MainFrame.cpp (199180 => 199181)


--- trunk/Source/WebCore/page/MainFrame.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/MainFrame.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -66,6 +66,11 @@
 {
     if (m_diagnosticLoggingClient)
         m_diagnosticLoggingClient->mainFrameDestroyed();
+
+    m_recentWheelEventDeltaFilter = nullptr;
+    m_eventHandler = nullptr;
+
+    setMainFrameWasDestroyed();
 }
 
 Ref<MainFrame> MainFrame::create(Page& page, PageConfiguration& configuration)

Modified: trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp (199180 => 199181)


--- trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,6 +31,8 @@
 #endif
 
 #include "FloatSize.h"
+#include "Logging.h"
+#include "TextStream.h"
 
 namespace WebCore {
     
@@ -58,6 +60,7 @@
 
 FloatSize WheelEventDeltaFilter::filteredDelta() const
 {
+    LOG_WITH_STREAM(Scrolling, stream << "BasicWheelEventDeltaFilter::filteredDelta returning " << m_currentFilteredDelta);
     return m_currentFilteredDelta;
 }
 

Modified: trunk/Source/WebCore/page/mac/EventHandlerMac.mm (199180 => 199181)


--- trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2008, 2009, 2014-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -1049,9 +1049,10 @@
 
 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& wheelEvent, ContainerNode* scrollableContainer, ScrollableArea* scrollableArea)
 {
+    FrameView* view = m_frame.view();
     // 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());
-    FrameView* view = m_frame.view();
+    if (!view)
+        return false;
 
     ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
     if (wheelEvent.useLatchedEventElement() && !latchingIsLockedToAncestorOfThisFrame(m_frame) && latchingState && latchingState->scrollableContainer()) {

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (199180 => 199181)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2016-04-07 21:08:40 UTC (rev 199180)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2016-04-07 21:15:34 UTC (rev 199181)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
  *
  * Portions are Copyright (C) 1998 Netscape Communications Corporation.
  *
@@ -2346,6 +2346,8 @@
     if (!box)
         return;
 
+    LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position);
+
     ScrollPosition newPosition = position;
     if (!box->isHTMLMarquee()) {
         // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to