Commit a4facf2c authored by jberlin@webkit.org's avatar jberlin@webkit.org

Crash using the new WKBundleDOMWindowExtensions APIs.

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
parent 5d8b357b
2012-05-09 Jessie Berlin <jberlin@apple.com>
Crash using the new WKBundleDOMWindowExtensions APIs.
https://bugs.webkit.org/show_bug.cgi?id=85888
Reviewed by Brady Eidson.
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:
2012-05-09 Ian Vollick <vollick@chromium.org>
[chromium] Ensure animations get ticked at least once when added.
......@@ -58,18 +58,36 @@ DOMWindowIndexedDatabase* DOMWindowIndexedDatabase::from(DOMWindow* window)
return supplement;
}
void DOMWindowIndexedDatabase::disconnectFrame()
void DOMWindowIndexedDatabase::disconnectFrameForPageCache()
{
m_suspendedIDBFactory = m_idbFactory.release();
DOMWindowProperty::disconnectFrame();
DOMWindowProperty::disconnectFrameForPageCache();
}
void DOMWindowIndexedDatabase::reconnectFrame(Frame* frame)
void DOMWindowIndexedDatabase::reconnectFrameFromPageCache(Frame* frame)
{
DOMWindowProperty::reconnectFrame(frame);
DOMWindowProperty::reconnectFrameFromPageCache(frame);
m_idbFactory = m_suspendedIDBFactory.release();
}
void DOMWindowIndexedDatabase::willDestroyGlobalObjectInCachedFrame()
{
m_suspendedIDBFactory = nullptr;
DOMWindowProperty::willDestroyGlobalObjectInCachedFrame();
}
void DOMWindowIndexedDatabase::willDestroyGlobalObjectInFrame()
{
m_idbFactory = nullptr;
DOMWindowProperty::willDestroyGlobalObjectInFrame();
}
void DOMWindowIndexedDatabase::willDetachGlobalObjectFromFrame()
{
m_idbFactory = nullptr;
DOMWindowProperty::willDetachGlobalObjectFromFrame();
}
IDBFactory* DOMWindowIndexedDatabase::webkitIndexedDB(DOMWindow* window)
{
return from(window)->webkitIndexedDB();
......
......@@ -43,8 +43,11 @@ public:
static IDBFactory* webkitIndexedDB(DOMWindow*);
virtual void disconnectFrame() OVERRIDE;
virtual void reconnectFrame(Frame*) OVERRIDE;
virtual void disconnectFrameForPageCache() OVERRIDE;
virtual void reconnectFrameFromPageCache(Frame*) OVERRIDE;
virtual void willDestroyGlobalObjectInCachedFrame() OVERRIDE;
virtual void willDestroyGlobalObjectInFrame() OVERRIDE;
virtual void willDetachGlobalObjectFromFrame() OVERRIDE;
private:
explicit DOMWindowIndexedDatabase(DOMWindow*);
......
......@@ -2070,6 +2070,13 @@ void Document::detach()
m_renderArena.clear();
}
void Document::prepareForDestruction()
{
if (DOMWindow* window = this->domWindow())
window->willDetachDocumentFromFrame();
detach();
}
void Document::removeAllEventListeners()
{
EventTarget::removeAllEventListeners();
......
......@@ -578,6 +578,7 @@ public:
virtual void attach();
virtual void detach();
void prepareForDestruction();
// Override ScriptExecutionContext methods to do additional work
virtual void suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension) OVERRIDE;
......
......@@ -250,6 +250,8 @@ void CachedFrame::destroy()
ASSERT(m_view);
ASSERT(m_document->frame() == m_view->frame());
m_domWindow->willDestroyCachedFrame();
if (!m_isMainFrame) {
m_view->frame()->detachFromPage();
m_view->frame()->loader()->detachViewsAndDocumentLoader();
......
......@@ -516,7 +516,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo
m_frame->document()->stopActiveDOMObjects();
if (m_frame->document()->attached()) {
m_frame->document()->willRemove();
m_frame->document()->detach();
m_frame->document()->prepareForDestruction();
m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document());
}
......
......@@ -47,22 +47,27 @@ DOMApplicationCache::DOMApplicationCache(Frame* frame)
cacheHost->setDOMApplicationCache(this);
}
void DOMApplicationCache::disconnectFrame()
void DOMApplicationCache::disconnectFrameForPageCache()
{
ApplicationCacheHost* cacheHost = applicationCacheHost();
if (cacheHost)
if (ApplicationCacheHost* cacheHost = applicationCacheHost())
cacheHost->setDOMApplicationCache(0);
DOMWindowProperty::disconnectFrame();
DOMWindowProperty::disconnectFrameForPageCache();
}
void DOMApplicationCache::reconnectFrame(Frame* frame)
void DOMApplicationCache::reconnectFrameFromPageCache(Frame* frame)
{
DOMWindowProperty::reconnectFrame(frame);
ApplicationCacheHost* cacheHost = applicationCacheHost();
if (cacheHost)
DOMWindowProperty::reconnectFrameFromPageCache(frame);
if (ApplicationCacheHost* cacheHost = applicationCacheHost())
cacheHost->setDOMApplicationCache(this);
}
void DOMApplicationCache::willDestroyGlobalObjectInFrame()
{
if (ApplicationCacheHost* cacheHost = applicationCacheHost())
cacheHost->setDOMApplicationCache(0);
DOMWindowProperty::willDestroyGlobalObjectInFrame();
}
ApplicationCacheHost* DOMApplicationCache::applicationCacheHost() const
{
if (!m_frame || !m_frame->loader()->documentLoader())
......
......@@ -47,8 +47,9 @@ public:
static PassRefPtr<DOMApplicationCache> create(Frame* frame) { return adoptRef(new DOMApplicationCache(frame)); }
~DOMApplicationCache() { ASSERT(!m_frame); }
virtual void disconnectFrame() OVERRIDE;
virtual void reconnectFrame(Frame*) OVERRIDE;
virtual void disconnectFrameForPageCache() OVERRIDE;
virtual void reconnectFrameFromPageCache(Frame*) OVERRIDE;
virtual void willDestroyGlobalObjectInFrame() OVERRIDE;
unsigned short status() const;
void update(ExceptionCode&);
......
......@@ -63,18 +63,36 @@ NotificationCenter* DOMWindowNotifications::webkitNotifications(DOMWindow* windo
return DOMWindowNotifications::from(window)->webkitNotifications();
}
void DOMWindowNotifications::disconnectFrame()
void DOMWindowNotifications::disconnectFrameForPageCache()
{
m_suspendedNotificationCenter = m_notificationCenter.release();
DOMWindowProperty::disconnectFrame();
DOMWindowProperty::disconnectFrameForPageCache();
}
void DOMWindowNotifications::reconnectFrame(Frame* frame)
void DOMWindowNotifications::reconnectFrameFromPageCache(Frame* frame)
{
DOMWindowProperty::reconnectFrame(frame);
DOMWindowProperty::reconnectFrameFromPageCache(frame);
m_notificationCenter = m_suspendedNotificationCenter.release();
}
void DOMWindowNotifications::willDestroyGlobalObjectInCachedFrame()
{
m_suspendedNotificationCenter = nullptr;
DOMWindowProperty::willDestroyGlobalObjectInCachedFrame();
}
void DOMWindowNotifications::willDestroyGlobalObjectInFrame()
{
m_notificationCenter = nullptr;
DOMWindowProperty::willDestroyGlobalObjectInFrame();
}
void DOMWindowNotifications::willDetachGlobalObjectFromFrame()
{
m_notificationCenter = nullptr;
DOMWindowProperty::willDetachGlobalObjectFromFrame();
}
NotificationCenter* DOMWindowNotifications::webkitNotifications()
{
if (!m_window->isCurrentlyDisplayedInFrame())
......
......@@ -45,8 +45,11 @@ public:
static NotificationCenter* webkitNotifications(DOMWindow*);
static DOMWindowNotifications* from(DOMWindow*);
virtual void disconnectFrame() OVERRIDE;
virtual void reconnectFrame(Frame*) OVERRIDE;
virtual void disconnectFrameForPageCache() OVERRIDE;
virtual void reconnectFrameFromPageCache(Frame*) OVERRIDE;
virtual void willDestroyGlobalObjectInCachedFrame() OVERRIDE;
virtual void willDestroyGlobalObjectInFrame() OVERRIDE;
virtual void willDetachGlobalObjectFromFrame() OVERRIDE;
private:
explicit DOMWindowNotifications(DOMWindow*);
......
......@@ -427,6 +427,11 @@ DOMWindow::~DOMWindow()
}
#endif
if (m_suspendedForPageCache)
willDestroyCachedFrame();
else
willDestroyDocumentInFrame();
// As the ASSERTs above indicate, this clear should only be necesary if this DOMWindow is suspended for the page cache.
// But we don't want to risk any of these objects hanging around after we've been destroyed.
clearDOMWindowProperties();
......@@ -467,6 +472,7 @@ Page* DOMWindow::page()
void DOMWindow::frameDestroyed()
{
willDestroyDocumentInFrame();
FrameDestructionObserver::frameDestroyed();
clearDOMWindowProperties();
}
......@@ -474,11 +480,36 @@ void DOMWindow::frameDestroyed()
void DOMWindow::willDetachPage()
{
InspectorInstrumentation::frameWindowDiscarded(m_frame, this);
}
void DOMWindow::willDestroyCachedFrame()
{
// It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may
// unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInCachedFrame.
Vector<DOMWindowProperty*> properties;
copyToVector(m_properties, properties);
for (size_t i = 0; i < properties.size(); ++i)
properties[i]->willDetachPage();
properties[i]->willDestroyGlobalObjectInCachedFrame();
}
void DOMWindow::willDestroyDocumentInFrame()
{
// It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may
// unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInFrame.
Vector<DOMWindowProperty*> properties;
copyToVector(m_properties, properties);
for (size_t i = 0; i < properties.size(); ++i)
properties[i]->willDestroyGlobalObjectInFrame();
}
void DOMWindow::willDetachDocumentFromFrame()
{
// It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may
// unregister themselves from the DOMWindow as a result of the call to willDetachGlobalObjectFromFrame.
Vector<DOMWindowProperty*> properties;
copyToVector(m_properties, properties);
for (size_t i = 0; i < properties.size(); ++i)
properties[i]->willDetachGlobalObjectFromFrame();
}
void DOMWindow::registerProperty(DOMWindowProperty* property)
......@@ -499,6 +530,7 @@ void DOMWindow::clear()
if (m_suspendedForPageCache)
return;
willDestroyDocumentInFrame();
clearDOMWindowProperties();
}
......@@ -516,24 +548,27 @@ void DOMWindow::resumeFromPageCache()
void DOMWindow::disconnectDOMWindowProperties()
{
// It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may
// unregister themselves from the DOMWindow as a result of the call to disconnectFrameForPageCache.
Vector<DOMWindowProperty*> properties;
copyToVector(m_properties, properties);
for (size_t i = 0; i < properties.size(); ++i)
properties[i]->disconnectFrame();
properties[i]->disconnectFrameForPageCache();
}
void DOMWindow::reconnectDOMWindowProperties()
{
ASSERT(m_suspendedForPageCache);
// It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may
// unregister themselves from the DOMWindow as a result of the call to reconnectFromPageCache.
Vector<DOMWindowProperty*> properties;
copyToVector(m_properties, properties);
for (size_t i = 0; i < properties.size(); ++i)
properties[i]->reconnectFrame(m_frame);
properties[i]->reconnectFrameFromPageCache(m_frame);
}
void DOMWindow::clearDOMWindowProperties()
{
disconnectDOMWindowProperties();
m_properties.clear();
m_screen = 0;
......
......@@ -390,6 +390,9 @@ namespace WebCore {
// by the document that is currently active in m_frame.
bool isCurrentlyDisplayedInFrame() const;
void willDetachDocumentFromFrame();
void willDestroyCachedFrame();
private:
explicit DOMWindow(Frame*);
......@@ -411,6 +414,7 @@ namespace WebCore {
void clearDOMWindowProperties();
void disconnectDOMWindowProperties();
void reconnectDOMWindowProperties();
void willDestroyDocumentInFrame();
RefPtr<SecurityOrigin> m_securityOrigin;
KURL m_url;
......
......@@ -36,90 +36,82 @@ namespace WebCore {
DOMWindowExtension::DOMWindowExtension(Frame* frame, DOMWrapperWorld* world)
: DOMWindowProperty(frame)
, m_world(world)
, m_disconnectedDOMWindow(0)
, m_wasDetached(false)
{
ASSERT(this->frame());
ASSERT(m_world);
}
DOMWindowExtension::~DOMWindowExtension()
void DOMWindowExtension::disconnectFrameForPageCache()
{
// DOMWindowExtension lifetime isn't tied directly to the DOMWindow itself so it is important that it unregister
// itself from any DOMWindow it is associated with when destroyed.
// This might happen if the DOMWindowExtension is destroyed while its DOMWindow is in a CachedPage.
if (m_disconnectedDOMWindow)
m_disconnectedDOMWindow->unregisterProperty(this);
}
void DOMWindowExtension::disconnectFrame()
{
// The DOMWindow destructor calls disconnectFrame on all its DOMWindowProperties, even if it
// did that already when entering the page cache.
if (m_disconnectedFrame) {
ASSERT(!frame());
return;
}
// Calling out to the client might result in this DOMWindowExtension being destroyed
// while there is still work to do.
RefPtr<DOMWindowExtension> protector = this;
// DOMWindowProperties are disconnected from frames after they are detached.
// DOMWindowExtensions only want to stay prepared for client callbacks if they've never been detached.
if (!m_wasDetached) {
Frame* frame = this->frame();
frame->loader()->client()->dispatchWillDisconnectDOMWindowExtensionFromGlobalObject(this);
Frame* frame = this->frame();
frame->loader()->client()->dispatchWillDisconnectDOMWindowExtensionFromGlobalObject(this);
m_disconnectedFrame = frame;
m_disconnectedDOMWindow = frame->domWindow();
}
m_disconnectedFrame = frame;
DOMWindowProperty::disconnectFrame();
DOMWindowProperty::disconnectFrameForPageCache();
}
void DOMWindowExtension::reconnectFrame(Frame* frame)
void DOMWindowExtension::reconnectFrameFromPageCache(Frame* frame)
{
// DOMWindowProperties should never reconnect to a frame after they've been detached from the page.
ASSERT(!m_wasDetached);
ASSERT(m_disconnectedFrame == frame);
DOMWindowProperty::reconnectFrame(frame);
DOMWindowProperty::reconnectFrameFromPageCache(frame);
m_disconnectedFrame = 0;
m_disconnectedDOMWindow = 0;
this->frame()->loader()->client()->dispatchDidReconnectDOMWindowExtensionToGlobalObject(this);
}
void DOMWindowExtension::willDetachPage()
void DOMWindowExtension::willDestroyGlobalObjectInCachedFrame()
{
// willDetachPage might be called multiple times but we only want to react once.
if (m_wasDetached)
return;
ASSERT(m_disconnectedFrame);
// Calling out to the client might result in this DOMWindowExtension being destroyed
// while there is still work to do.
RefPtr<DOMWindowExtension> protector = this;
Frame* frame = m_disconnectedFrame.get();
if (!frame)
frame = this->frame();
ASSERT(frame);
m_disconnectedFrame->loader()->client()->dispatchWillDestroyGlobalObjectForDOMWindowExtension(this);
m_disconnectedFrame = 0;
// DOMWindowExtension lifetime isn't tied directly to the DOMWindow itself so it is important that it unregister
// itself from any DOMWindow it is associated with when detached.
// This might be the disconnected DOMWindow if the DOMWindow is in a CachedPage that is pruned.
DOMWindow* associatedDOMWindow = m_disconnectedDOMWindow ? m_disconnectedDOMWindow : frame->domWindow();
associatedDOMWindow->unregisterProperty(this);
m_disconnectedDOMWindow = 0;
frame->loader()->client()->dispatchWillDestroyGlobalObjectForDOMWindowExtension(this);
DOMWindowProperty::willDestroyGlobalObjectInCachedFrame();
}
m_disconnectedFrame = 0;
void DOMWindowExtension::willDestroyGlobalObjectInFrame()
{
ASSERT(!m_disconnectedFrame);
// Calling out to the client might result in this DOMWindowExtension being destroyed
// while there is still work to do.
RefPtr<DOMWindowExtension> protector = this;
if (!m_wasDetached) {
Frame* frame = this->frame();
ASSERT(frame);
frame->loader()->client()->dispatchWillDestroyGlobalObjectForDOMWindowExtension(this);
}
DOMWindowProperty::willDestroyGlobalObjectInFrame();
}
void DOMWindowExtension::willDetachGlobalObjectFromFrame()
{
ASSERT(!m_disconnectedFrame);
ASSERT(!m_wasDetached);
// Calling out to the client might result in this DOMWindowExtension being destroyed
// while there is still work to do.
RefPtr<DOMWindowExtension> protector = this;
Frame* frame = this->frame();
ASSERT(frame);
frame->loader()->client()->dispatchWillDestroyGlobalObjectForDOMWindowExtension(this);
DOMWindowProperty::willDetachPage();
m_wasDetached = true;
DOMWindowProperty::willDetachGlobalObjectFromFrame();
}
} // namespace WebCore
......@@ -33,7 +33,6 @@
namespace WebCore {
class DOMWindow;
class DOMWindowExtension;
class DOMWrapperWorld;
class Frame;
......@@ -45,11 +44,11 @@ public:
return adoptRef(new DOMWindowExtension(frame, world));
}
~DOMWindowExtension();
virtual void disconnectFrame() OVERRIDE;
virtual void reconnectFrame(Frame*) OVERRIDE;
virtual void willDetachPage() OVERRIDE;
virtual void disconnectFrameForPageCache() OVERRIDE;
virtual void reconnectFrameFromPageCache(Frame*) OVERRIDE;
virtual void willDestroyGlobalObjectInCachedFrame() OVERRIDE;
virtual void willDestroyGlobalObjectInFrame() OVERRIDE;
virtual void willDetachGlobalObjectFromFrame() OVERRIDE;
DOMWrapperWorld* world() const { return m_world.get(); }
......@@ -58,7 +57,6 @@ private:
RefPtr<DOMWrapperWorld> m_world;
RefPtr<Frame> m_disconnectedFrame;
DOMWindow* m_disconnectedDOMWindow;
bool m_wasDetached;
};
......
......@@ -33,6 +33,7 @@ namespace WebCore {
DOMWindowProperty::DOMWindowProperty(Frame* frame)
: m_frame(frame)
, m_disconnectedDOMWindow(0)
{
if (m_frame)
m_frame->domWindow()->registerProperty(this);
......@@ -40,25 +41,53 @@ DOMWindowProperty::DOMWindowProperty(Frame* frame)
DOMWindowProperty::~DOMWindowProperty()
{
if (m_frame)
if (m_frame) {
ASSERT(!m_disconnectedDOMWindow);
m_frame->domWindow()->unregisterProperty(this);
} else if (m_disconnectedDOMWindow)