Skip to content
  • jberlin@webkit.org's avatar
    Crash using the new WKBundleDOMWindowExtensions APIs. · a4facf2c
    jberlin@webkit.org authored
    https://bugs.webkit.org/show_bug.cgi?id=85888
    
    Reviewed by Brady Eidson.
    
    Source/WebCore:
    
    WKBundlePageWillDestroyGlobalObjectForDOMWindowExtensionCallback was only being invoked when
    the WKPage was destroyed, and then only for the child frames. In addition, the
    DOMWindowExtension was holding onto a destroyed DOMWindow and attempting to unregister from
    when the WK2 wrapper object was attempting to destroy the DOMWindowExtension.
    
    The underlying issue here was that the DOMWindowProperties were getting disconnectFrame
    and willDetachPage called on them at the wrong times.
    
    Rename DOMWindowProperty::disconnectFrame and reconnectFrame to disconnectFrameForPageCache
    and reconnectFrameFromPageCache for clarity.
    
    Only invoke DOMWindowProperty::disconnectFrameForPageCache when the frame is going into the
    page cache.
    
    In the cases where the DOMWindow is getting destroyed, the frame is being destroyed, or the
    DOMWindow is getting cleared because the frame is being navigated, invoke
    DOMWindowProperty::willDestroyGlobalObjectInFrame instead of disconnectFrame.
    
    Invoke DOMWindowProperty::willDetachGlobalObjectFromFrame when a document is being detached
    because the frame has been detached (e.g. fast/storage/storage-detached-iframe.html) and
    won't be immediately destroyed.
    
    Invoke DOMWindowProperty::willDestroyGlobalObjectInCachedFrame when a cached frame is
    being destroyed.
    
    New WK2 API Test: DOMWindowExtensionNoCache.
    
    * Modules/indexeddb/DOMWindowIndexedDatabase.cpp:
    (WebCore::DOMWindowIndexedDatabase::disconnectFrameForPageCache):
    Updated for disconnectFrame rename.
    (WebCore::DOMWindowIndexedDatabase::reconnectFrameFromPageCache):
    Updated for reconnectFrame rename.
    (WebCore::DOMWindowIndexedDatabase::willDestroyGlobalObjectInCachedFrame):
    Get rid of the suspended IDBFactory.
    (WebCore::DOMWindowIndexedDatabase::willDestroyGlobalObjectInFrame):
    Get rid of the IDBFactory.
    (WebCore::DOMWindowIndexedDatabase::willDetachGlobalObjectFromFrame):
    Ditto.
    * Modules/indexeddb/DOMWindowIndexedDatabase.h:
    
    * dom/Document.cpp:
    (WebCore::Document::prepareForDestruction):
    Tell the DOMWindow before detaching the Document.
    * dom/Document.h:
    
    * history/CachedFrame.cpp:
    (WebCore::CachedFrame::destroy):
    Tell the DOMWindow.
    
    * loader/FrameLoader.cpp:
    (WebCore::FrameLoader::clear):
    Use Document::prepareForDestruction so that the DOMWindow is told about the main frame
    navigation before detaching the Document.
    
    * loader/appcache/DOMApplicationCache.cpp:
    (WebCore::DOMApplicationCache::disconnectFrameForPageCache):
    Updated for the disconnectFrame rename.
    (WebCore::DOMApplicationCache::reconnectFrameFromPageCache):
    Updated for the reconnectFrame rename.
    (WebCore::DOMApplicationCache::willDestroyGlobalObjectInFrame):
    Cover the cases formerly covered by disconnectFrame (which was sometimes being called when
    called when the frame was destroyed).
    * loader/appcache/DOMApplicationCache.h:
    
    * notifications/DOMWindowNotifications.cpp:
    (WebCore::DOMWindowNotifications::disconnectFrameForPageCache):
    Updated for the disconnectFrame rename.
    (WebCore::DOMWindowNotifications::reconnectFrameFromPageCache):
    Updated for the reconnectFrame rename.
    (WebCore::DOMWindowNotifications::willDestroyGlobalObjectInCachedFrame):
    Get rid of the suspended notification center.
    (WebCore::DOMWindowNotifications::willDestroyGlobalObjectInFrame):
    Get rid of the notification center.
    (WebCore::DOMWindowNotifications::willDetachGlobalObjectFromFrame):
    Do not allow use of the notification center by detached frames.
    * notifications/DOMWindowNotifications.h:
    
    * page/DOMWindow.cpp:
    (WebCore::DOMWindow::clearDOMWindowProperties):
    Do not call disconnectDOMWindowProperties. It is now the responsibility of the callers to
    tell the DOMWindowProperties the correct cause of being cleared.
    (WebCore::DOMWindow::~DOMWindow):
    Make sure the DOMWindowProperties still know that the DOMWindow is going away.
    (WebCore::DOMWindow::frameDestroyed):
    Invoke willDestroyGlobalObjectInFrame on the DOMWindowProperties.
    (WebCore::DOMWindow::willDetachPage):
    It is no longer necessary to tell the DOMWindowProperties anything here.
    (WebCore::DOMWindow::willDestroyCachedFrame):
    Tell the DOMWindowProperties.
    (WebCore::DOMWindow::willDestroyDocumentInFrame):
    Ditto.
    (WebCore::DOMWindow::willDetachDocumentFromFrame):
    Ditto.
    (WebCore::DOMWindow::clear):
    Ditto.
    (WebCore::DOMWindow::disconnectDOMWindowProperties):
    Updated for the disconnectFrame rename.
    (WebCore::DOMWindow::reconnectDOMWindowProperties):
    Ditto.
    * page/DOMWindow.h:
    
    * page/DOMWindowExtension.cpp:
    (WebCore::DOMWindowExtension::DOMWindowExtension):
    Move the responsibility for tracking the disconnected DOMWindow to DOMWindowProperty, since
    DOMWindowProperty will need it to unregister the property when a cached frame is destroyed.
    (WebCore::DOMWindowExtension::disconnectFrameForPageCache):
    Remove the code to check for disconnectFrame being called twice - it is now only called when
    a frame goes into the page cache.
    Let the DOMWindowProperty keep track of the disconnected DOMWindow.
    (WebCore::DOMWindowExtension::reconnectFrameFromPageCache):
    Let the DOMWindowProperty keep track of the disconnected DOMWindow.
    (WebCore::DOMWindowExtension::willDestroyGlobalObjectInCachedFrame):
    Dispatch the willDestroyGlobalObjectForDOMWindowExtension callback.
    (WebCore::DOMWindowExtension::willDestroyGlobalObjectInFrame):
    Ditto, but only if the callback hasn't already been sent because the frame has been detached.
    (WebCore::DOMWindowExtension::willDetachGlobalObjectFromFrame):
    Send the callback because nothing interesting can be done in the frame once it has been
    detached.
    * page/DOMWindowExtension.h:
    
    * page/DOMWindowProperty.cpp:
    (WebCore::DOMWindowProperty::DOMWindowProperty):
    Keep track of the disconnected DOMWindow so it can be used to unregister the property when a
    cached frame is destroyed.
    (WebCore::DOMWindowProperty::~DOMWindowProperty):
    Also unregister the property when a DOMWindowProperty for a cached frame is destroyed.
    (WebCore::DOMWindowProperty::disconnectFrameForPageCache):
    Keep track of the disconnected DOMWindow.
    (WebCore::DOMWindowProperty::reconnectFrameFromPageCache):
    Ditto.
    (WebCore::DOMWindowProperty::willDestroyGlobalObjectInCachedFrame):
    Unregister the property from the disconnected DOMWindow.
    (WebCore::DOMWindowProperty::willDestroyGlobalObjectInFrame):
    Unregister the property from the DOMWindow and stop keeping track of the frame.
    (WebCore::DOMWindowProperty::willDetachGlobalObjectFromFrame):
    Do not set m_frame to 0 because detached frames still have access to the DOMWindow, even if
    they can't do anything meaningful with it.
    * page/DOMWindowProperty.h:
    
    * page/Frame.cpp:
    (WebCore::Frame::setView):
    Tell the DOMWindow that the Document is being detached so it can tell the
    DOMWindowProperties.
    
    * page/PointerLock.cpp:
    (WebCore::PointerLock::disconnectFrameForPageCache):
    Updated for disconnectFrame rename.
    (WebCore::PointerLock::willDestroyGlobalObjectInFrame):
    Cover the cases formerly covered by disconnectFrame (which was sometimes being called when
    called when the frame was destroyed).
    * page/PointerLock.h:
    
    Tools:
    
    Cached frames can live slightly longer than the page, but most clients unregister themselves
    and do other cleanup in the willDestroyPage callback, making them miss the
    willDestroyGlobalObjectForDOMWindowExtension callbacks.
    
    The calls to willDestroyGlobalObjectForDOMWindowExtension in the DOMWindowExtensionBasic
    test were all being invoked underneath WebPage::close. This is unrealistic. Update that test
    to destroy the BundleDOMWindowExtensions in response to the willDestroyPage callback.
    
    Add a test to verify that willDestroyGlobalObjectForDOMWindowExtension is being called for
    pages that don't go into the page cache.
    
    * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
    Add DOMWindowExtensionNoCache.cpp, DOMWindowExtensionNoCache_Bundle.cpp, simple-unload.html
    and simple-iframe-unload.html
    
    * TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic.cpp:
    (TestWebKitAPI):
    Remove the expected messages for willDestroyGlobalObjectForDOMWindowExtension.
    (TestWebKitAPI::didReceiveMessageFromInjectedBundle):
    Do not bother to keep track of the live extension count - all of them are expected to be
    live until the test completes.
    (TestWebKitAPI::TEST):
    Fix the calls to EXPECT to pass the expected value first, and use EXPECT_WK_STREQ so that
    message failures will be clearer.
    * TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp:
    (TestWebKitAPI::DOMWindowExtensionBasic::willDestroyPage):
    Clean up the BundleDOMWindowExtensions.
    (TestWebKitAPI::DOMWindowExtensionBasic::willDestroyGlobalObjectForDOMWindowExtension):
    Add an ASSERT_NOT_REACHED.
    
    * TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache.cpp: Added.
    (TestWebKitAPI::didReceiveMessageFromInjectedBundle):
    Keep track of the messages received so they can be checked at the end of the test.
    (TestWebKitAPI::TEST):
    Navigate to uncacheable pages and back.
    * TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp: Copied from Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp.
    (DOMWindowExtensionNoCache):
    (TestWebKitAPI::DOMWindowExtensionNoCache::DOMWindowExtensionNoCache):
    Set up all the states for each BundleDOMWindowExtension.
    (TestWebKitAPI::DOMWindowExtensionNoCache::frameLoadFinished):
    Tell the UI Process about the states of the BundleDOMWindowExtensions.
    (TestWebKitAPI::DOMWindowExtensionNoCache::sendExtensionStateMessage):
    (TestWebKitAPI::DOMWindowExtensionNoCache::initialize):
    (TestWebKitAPI::DOMWindowExtensionNoCache::didCreatePage):
    (TestWebKitAPI::DOMWindowExtensionNoCache::willDestroyPage):
    Remvoe the remaining BundleDOMWindowExtensions, send the updated state, and finish the test.
    (TestWebKitAPI::DOMWindowExtensionNoCache::updateExtensionStateRecord):
    (TestWebKitAPI::DOMWindowExtensionNoCache::sendBundleMessage):
    (TestWebKitAPI::DOMWindowExtensionNoCache::globalObjectIsAvailableForFrame):
    (TestWebKitAPI::DOMWindowExtensionNoCache::willDisconnectDOMWindowExtensionFromGlobalObject):
    ASSERT that these pages not going into the page cache are not getting disconnected to go into
    the page cache.
    (TestWebKitAPI::DOMWindowExtensionNoCache::didReconnectDOMWindowExtensionToGlobalObject):
    Ditto about getting reconnected when coming out of the page cache.
    (TestWebKitAPI::DOMWindowExtensionNoCache::willDestroyGlobalObjectForDOMWindowExtension):
    Tell the UI Process, update the state, and get rid of the BundleDOMWindowExtension.
    (TestWebKitAPI::didFinishLoadForFrameCallback):
    (TestWebKitAPI::globalObjectIsAvailableForFrameCallback):
    (TestWebKitAPI::willDisconnectDOMWindowExtensionFromGlobalObjectCallback):
    (TestWebKitAPI::didReconnectDOMWindowExtensionToGlobalObjectCallback):
    (TestWebKitAPI::willDestroyGlobalObjectForDOMWindowExtensionCallback):
    
    * TestWebKitAPI/Tests/WebKit2/simple-iframe-unload.html: Added.
    * TestWebKitAPI/Tests/WebKit2/simple-unload.html: Added.
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@116595 268f45cc-cd09-0410-ab3c-d52691b4dbfc
    a4facf2c