<rdar://problem/
3977973> pages on ebay leak referenced JavaScript objects -- over time browsing becomes super-slow
I fixed this by removing all event listeners for a document, it's
children, and any disconnected nodes that used to be in the
document at document detach time. Mozilla temporarily disables
event listeners on such nodes, but re-enables them if you
re-parant a node into a new document. However, in WebCore, you
can't re-parent a node into another document, so there is no
observable change in behavior.
We have to do this to break the possible reference cycles between
event listeners and the dom nodes they are attached to (e.g. via
scope chain, as in this case).
* khtml/xml/dom_docimpl.cpp:
(DocumentImpl::detach):
(DocumentImpl::removeAllEventListenersFromAllNodesx):
(DocumentImpl::registerDisconnectedNodeWithEventListeners):
(DocumentImpl::unregisterDisconnectedNodeWithEventListeners):
(DocumentImpl::removeAllDisconnectedNodeEventListeners):
* khtml/xml/dom_docimpl.h:
* khtml/xml/dom_nodeimpl.cpp:
(NodeImpl::~NodeImpl):
(NodeImpl::addEventListener):
(NodeImpl::removeEventListener):
(NodeImpl::removeAllEventListeners):
(NodeImpl::removeHTMLEventListener):
(NodeImpl::insertedIntoDocument):
(NodeImpl::removedFromDocument):
* khtml/xml/dom_nodeimpl.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8557
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2005-02-08 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by Darin.
+
+ <rdar://problem/3977973> pages on ebay leak referenced JavaScript objects -- over time browsing becomes super-slow
+
+ I fixed this by removing all event listeners for a document, it's
+ children, and any disconnected nodes that used to be in the
+ document at document detach time. Mozilla temporarily disables
+ event listeners on such nodes, but re-enables them if you
+ re-parant a node into a new document. However, in WebCore, you
+ can't re-parent a node into another document, so there is no
+ observable change in behavior.
+
+ We have to do this to break the possible reference cycles between
+ event listeners and the dom nodes they are attached to (e.g. via
+ scope chain, as in this case).
+
+ * khtml/xml/dom_docimpl.cpp:
+ (DocumentImpl::detach):
+ (DocumentImpl::removeAllEventListenersFromAllNodesx):
+ (DocumentImpl::registerDisconnectedNodeWithEventListeners):
+ (DocumentImpl::unregisterDisconnectedNodeWithEventListeners):
+ (DocumentImpl::removeAllDisconnectedNodeEventListeners):
+ * khtml/xml/dom_docimpl.h:
+ * khtml/xml/dom_nodeimpl.cpp:
+ (NodeImpl::~NodeImpl):
+ (NodeImpl::addEventListener):
+ (NodeImpl::removeEventListener):
+ (NodeImpl::removeAllEventListeners):
+ (NodeImpl::removeHTMLEventListener):
+ (NodeImpl::insertedIntoDocument):
+ (NodeImpl::removedFromDocument):
+ * khtml/xml/dom_nodeimpl.h:
+
2005-02-09 Chris Blumenberg <cblu@apple.com>
Fixed: <rdar://problem/3999213> Sometimes 2 Windows Media Player plugin instances are loaded
m_imageLoadEventDispatchSoonList.clear();
m_imageLoadEventDispatchingList.clear();
+ removeAllEventListenersFromAllNodes();
+
NodeBaseImpl::detach();
if ( render )
}
}
+void DocumentImpl::removeAllEventListenersFromAllNodes()
+{
+ m_windowEventListeners.clear();
+ removeAllDisconnectedNodeEventListeners();
+ for (NodeImpl *n = this; n; n = n->traverseNextNode()) {
+ n->removeAllEventListeners();
+ }
+}
+
+void DocumentImpl::registerDisconnectedNodeWithEventListeners(NodeImpl *node)
+{
+ m_disconnectedNodesWithEventListeners.insert(node, node);
+}
+
+void DocumentImpl::unregisterDisconnectedNodeWithEventListeners(NodeImpl *node)
+{
+ m_disconnectedNodesWithEventListeners.remove(node);
+}
+
+void DocumentImpl::removeAllDisconnectedNodeEventListeners()
+{
+ for (QPtrDictIterator<NodeImpl> iter(m_disconnectedNodesWithEventListeners);
+ iter.current();
+ ++iter) {
+ iter.current()->removeAllEventListeners();
+ }
+}
+
#if APPLE_CHANGES
KWQAccObjectCache* DocumentImpl::getAccObjectCache()
{
DOMString m_policyBaseURL;
+ QPtrDict<NodeImpl> m_disconnectedNodesWithEventListeners;
+
#if APPLE_CHANGES
public:
KWQSignal m_finishedParsing;
const QValueList<khtml::DashboardRegionValue> & dashboardRegions() const;
void setDashboardRegions (const QValueList<khtml::DashboardRegionValue>& regions);
+ void removeAllEventListenersFromAllNodes();
+
+ void registerDisconnectedNodeWithEventListeners(NodeImpl *node);
+ void unregisterDisconnectedNodeWithEventListeners(NodeImpl *node);
+
private:
+ void removeAllDisconnectedNodeEventListeners();
+
JSEditor *jsEditor();
JSEditor *m_jsEditor;
{
if (m_render)
detach();
+ if (m_regdListeners && !m_regdListeners->isEmpty() && getDocument() && !inDocument())
+ getDocument()->unregisterDisconnectedNodeWithEventListeners(this);
delete m_regdListeners;
delete m_nodeLists;
if (document)
void NodeImpl::addEventListener(int id, EventListener *listener, const bool useCapture)
{
+ if (getDocument() && !getDocument()->attached())
+ return;
+
switch (id) {
case EventImpl::DOMSUBTREEMODIFIED_EVENT:
getDocument()->addListenerType(DocumentImpl::DOMSUBTREEMODIFIED_LISTENER);
// spec says that "duplicate instances are discarded" in this case.
removeEventListener(id,listener,useCapture);
+ // adding the first one
+ if (m_regdListeners->isEmpty() && getDocument() && !inDocument())
+ getDocument()->registerDisconnectedNodeWithEventListeners(this);
+
m_regdListeners->append(rl);
listener->deref();
}
for (; it.current(); ++it)
if (*(it.current()) == rl) {
m_regdListeners->removeRef(it.current());
+ // removed last
+ if (m_regdListeners->isEmpty() && getDocument() && !inDocument())
+ getDocument()->unregisterDisconnectedNodeWithEventListeners(this);
return;
}
}
+void NodeImpl::removeAllEventListeners()
+{
+ delete m_regdListeners;
+ m_regdListeners = 0;
+}
+
void NodeImpl::removeHTMLEventListener(int id)
{
if (!m_regdListeners) // nothing to remove
if (it.current()->id == id &&
it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
m_regdListeners->removeRef(it.current());
+ // removed last
+ if (m_regdListeners->isEmpty() && getDocument() && !inDocument())
+ getDocument()->unregisterDisconnectedNodeWithEventListeners(this);
return;
}
}
void NodeImpl::insertedIntoDocument()
{
+ if (m_regdListeners && !m_regdListeners->isEmpty() && getDocument())
+ getDocument()->unregisterDisconnectedNodeWithEventListeners(this);
+
setInDocument(true);
}
void NodeImpl::removedFromDocument()
{
+ if (m_regdListeners && !m_regdListeners->isEmpty() && getDocument())
+ getDocument()->registerDisconnectedNodeWithEventListeners(this);
+
setInDocument(false);
}
void removeHTMLEventListener(int id);
void setHTMLEventListener(int id, EventListener *listener);
EventListener *getHTMLEventListener(int id);
+ void removeAllEventListeners();
bool dispatchEvent(EventImpl *evt, int &exceptioncode, bool tempEvent = false);
bool dispatchGenericEvent( EventImpl *evt, int &exceptioncode);