<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180214] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180214">180214</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2015-02-17 01:57:42 -0800 (Tue, 17 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[GTK] GObject DOM bindings object are cached forever
https://bugs.webkit.org/show_bug.cgi?id=141558

Reviewed by Sergio Villar Senin.

Source/WebCore:

Rework the DOMObjectCache to avoid having to manually clear the
objects when the frame is destroyed, using a FrameDestructionObserver
instead. When a new object associated to a Frame is added to the
cache, a FrameDestructionObserver is created for the frame if
needed, and the DOM object is tracked by the observer too. When
the frame is detached from the page all its objects are cleared,
and if the aren't additional references, the object is finalized
and removed from the cache normally.
This patch also simplifies and modernizes the code to make it
easier to read an maintain.

* bindings/gobject/DOMObjectCache.cpp:
(WebKit::DOMObjectCacheData::DOMObjectCacheData): Add constructor
to initialize its members and simplify the callers.
(WebKit::DOMObjectCacheData::clearObject): Remove the references
added by the cache, ensuring the GObject is not finalized until
the method returns.
(WebKit::DOMObjectCacheData::refObject): Adds a reference owned by
the cache.
(WebKit::domObjectCacheFrameObservers): Map a frame to a FrameDestructionObserver.
(WebKit::getOrCreateDOMObjectCacheFrameObserver): Create a new
FrameDestructionObserver for the given Frame or return the
existing one.
(WebKit::domObjects): Map wrapped object to wrapper object.
(WebKit::DOMObjectCache::forget): Remove the wrapped object from
the cache.
(WebKit::DOMObjectCache::get): Return the wrapped object if it is
in the cache, increasing the cache references.
(WebKit::DOMObjectCache::put): Add the wrapper object to the cache
for the given wrapped object. If it's a Node and has a frame add
the node to the FrameDestructionObserver corresponding to the frame.
(WebKit::getFrameFromHandle): Deleted.
(WebKit::weakRefNotify): Deleted.
(WebKit::DOMObjectCache::clearByFrame): Deleted.
(WebKit::DOMObjectCache::~DOMObjectCache): Deleted.
* bindings/gobject/DOMObjectCache.h:

Tools:

Add checks for all DOM objects to ensure they are not leaked. Also
add a dedicated test for the DOM Object Cache.

* TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp:
(WebKitDOMNodeFilterTest::testTreeWalker):
(WebKitDOMNodeFilterTest::testNodeIterator):
* TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp:
(WebKitDOMNodeTest::testHierarchyNavigation):
(WebKitDOMNodeTest::testInsertion):
(WebKitDOMNodeTest::testTagNames):
(WebKitDOMNodeTest::testDOMCache):
(registerTests):
* TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp:
(WebKitDOMXPathNSResolverTest::evaluateFooChildTextAndCheckResult):
(WebKitDOMXPathNSResolverTest::testXPathNSResolverNative):
(WebKitDOMXPathNSResolverTest::testXPathNSResolverCustom):
* TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp:
(testWebKitDOMObjectCache):
(beforeAll):
* TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp:
(documentLoadedCallback):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsgobjectDOMObjectCachecpp">trunk/Source/WebCore/bindings/gobject/DOMObjectCache.cpp</a></li>
<li><a href="#trunkSourceWebCorebindingsgobjectDOMObjectCacheh">trunk/Source/WebCore/bindings/gobject/DOMObjectCache.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2GtkDOMNodeFilterTestcpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2GtkDOMNodeTestcpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2GtkDOMXPathNSResolverTestcpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2GtkTestDOMNodecpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2GtkWebExtensionTestcpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Source/WebCore/ChangeLog        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2015-02-17  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [GTK] GObject DOM bindings object are cached forever
+        https://bugs.webkit.org/show_bug.cgi?id=141558
+
+        Reviewed by Sergio Villar Senin.
+
+        Rework the DOMObjectCache to avoid having to manually clear the
+        objects when the frame is destroyed, using a FrameDestructionObserver
+        instead. When a new object associated to a Frame is added to the
+        cache, a FrameDestructionObserver is created for the frame if
+        needed, and the DOM object is tracked by the observer too. When
+        the frame is detached from the page all its objects are cleared,
+        and if the aren't additional references, the object is finalized
+        and removed from the cache normally.
+        This patch also simplifies and modernizes the code to make it
+        easier to read an maintain.
+
+        * bindings/gobject/DOMObjectCache.cpp:
+        (WebKit::DOMObjectCacheData::DOMObjectCacheData): Add constructor
+        to initialize its members and simplify the callers.
+        (WebKit::DOMObjectCacheData::clearObject): Remove the references
+        added by the cache, ensuring the GObject is not finalized until
+        the method returns.
+        (WebKit::DOMObjectCacheData::refObject): Adds a reference owned by
+        the cache.
+        (WebKit::domObjectCacheFrameObservers): Map a frame to a FrameDestructionObserver.
+        (WebKit::getOrCreateDOMObjectCacheFrameObserver): Create a new
+        FrameDestructionObserver for the given Frame or return the
+        existing one.
+        (WebKit::domObjects): Map wrapped object to wrapper object.
+        (WebKit::DOMObjectCache::forget): Remove the wrapped object from
+        the cache.
+        (WebKit::DOMObjectCache::get): Return the wrapped object if it is
+        in the cache, increasing the cache references.
+        (WebKit::DOMObjectCache::put): Add the wrapper object to the cache
+        for the given wrapped object. If it's a Node and has a frame add
+        the node to the FrameDestructionObserver corresponding to the frame.
+        (WebKit::getFrameFromHandle): Deleted.
+        (WebKit::weakRefNotify): Deleted.
+        (WebKit::DOMObjectCache::clearByFrame): Deleted.
+        (WebKit::DOMObjectCache::~DOMObjectCache): Deleted.
+        * bindings/gobject/DOMObjectCache.h:
+
</ins><span class="cx"> 2015-02-17  ChangSeok Oh  &lt;changseok.oh@collabora.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION(r180050): It broke the !ENABLE(CSS_GRID_LAYOUT) build
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsgobjectDOMObjectCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/gobject/DOMObjectCache.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/gobject/DOMObjectCache.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Source/WebCore/bindings/gobject/DOMObjectCache.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- *  Copyright (C) 2010 Igalia S.L.
</del><ins>+ *  Copyright (C) 2010, 2015 Igalia S.L.
</ins><span class="cx">  *
</span><span class="cx">  *  This library is free software; you can redistribute it and/or
</span><span class="cx">  *  modify it under the terms of the GNU Lesser General Public
</span><span class="lines">@@ -20,141 +20,163 @@
</span><span class="cx"> #include &quot;DOMObjectCache.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Document.h&quot;
</span><ins>+#include &quot;FrameDestructionObserver.h&quot;
</ins><span class="cx"> #include &quot;Node.h&quot;
</span><span class="cx"> #include &lt;glib-object.h&gt;
</span><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><ins>+#include &lt;wtf/NeverDestroyed.h&gt;
+#include &lt;wtf/Vector.h&gt;
+#include &lt;wtf/gobject/GRefPtr.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><del>-typedef struct {
</del><ins>+struct DOMObjectCacheData {
+    DOMObjectCacheData(GObject* wrapper)
+        : object(wrapper)
+        , cacheReferences(1)
+    {
+    }
+
+    void clearObject()
+    {
+        ASSERT(object);
+        ASSERT(cacheReferences &gt;= 1);
+
+        GRefPtr&lt;GObject&gt; protect(object);
+        do {
+            g_object_unref(object);
+        } while (--cacheReferences);
+        object = nullptr;
+    }
+
+    void* refObject()
+    {
+        ASSERT(object);
+
+        cacheReferences++;
+        return g_object_ref(object);
+    }
+
</ins><span class="cx">     GObject* object;
</span><del>-    WebCore::Frame* frame;
-    guint timesReturned;
-} DOMObjectCacheData;
</del><ins>+    unsigned cacheReferences;
+};
</ins><span class="cx"> 
</span><del>-typedef HashMap&lt;void*, DOMObjectCacheData*&gt; DOMObjectMap;
</del><ins>+class DOMObjectCacheFrameObserver;
+typedef HashMap&lt;WebCore::Frame*, std::unique_ptr&lt;DOMObjectCacheFrameObserver&gt;&gt; DOMObjectCacheFrameObserverMap;
</ins><span class="cx"> 
</span><del>-static DOMObjectMap&amp; domObjects()
</del><ins>+static DOMObjectCacheFrameObserverMap&amp; domObjectCacheFrameObservers()
</ins><span class="cx"> {
</span><del>-    static DOMObjectMap staticDOMObjects;
-    return staticDOMObjects;
</del><ins>+    static NeverDestroyed&lt;DOMObjectCacheFrameObserverMap&gt; map;
+    return map;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static WebCore::Frame* getFrameFromHandle(void* objectHandle)
</del><ins>+static DOMObjectCacheFrameObserver&amp; getOrCreateDOMObjectCacheFrameObserver(WebCore::Frame&amp; frame)
</ins><span class="cx"> {
</span><del>-    WebCore::Node* node = static_cast&lt;WebCore::Node*&gt;(objectHandle);
-    if (!node-&gt;inDocument())
-        return 0;
-    return node-&gt;document().frame();
-}
</del><ins>+    auto observerPtr = domObjectCacheFrameObservers().get(&amp;frame);
+    if (observerPtr)
+        return *observerPtr;
</ins><span class="cx"> 
</span><del>-void DOMObjectCache::forget(void* objectHandle)
-{
-    DOMObjectCacheData* cacheData = domObjects().get(objectHandle);
-    ASSERT(cacheData);
-    g_slice_free(DOMObjectCacheData, cacheData);
-    domObjects().take(objectHandle);
</del><ins>+    std::unique_ptr&lt;DOMObjectCacheFrameObserver&gt; observer = std::make_unique&lt;DOMObjectCacheFrameObserver&gt;(frame);
+    observerPtr = observer.get();
+    domObjectCacheFrameObservers().set(&amp;frame, WTF::move(observer));
+    return *observerPtr;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void weakRefNotify(gpointer data, GObject*)
-{
-    gboolean* objectDead = static_cast&lt;gboolean*&gt;(data);
-    *objectDead = TRUE;
-}
</del><ins>+class DOMObjectCacheFrameObserver final: public WebCore::FrameDestructionObserver {
+public:
+    DOMObjectCacheFrameObserver(WebCore::Frame&amp; frame)
+        : FrameDestructionObserver(&amp;frame)
+    {
+    }
</ins><span class="cx"> 
</span><del>-void DOMObjectCache::clearByFrame(WebCore::Frame* frame)
-{
-    Vector&lt;DOMObjectCacheData*&gt; toUnref;
</del><ins>+    ~DOMObjectCacheFrameObserver()
+    {
+        ASSERT(m_objects.isEmpty());
+    }
</ins><span class="cx"> 
</span><del>-    // Unreffing the objects removes them from the cache in their
-    // finalize method, so just save them to do that while we are not
-    // iterating the cache itself.
-    DOMObjectMap::iterator end = domObjects().end();
-    for (DOMObjectMap::iterator iter = domObjects().begin(); iter != end; ++iter) {
-        DOMObjectCacheData* data = iter-&gt;value;
-        ASSERT(data);
-        if ((!frame || data-&gt;frame == frame) &amp;&amp; data-&gt;timesReturned)
-            toUnref.append(data);
</del><ins>+    void addObjectCacheData(DOMObjectCacheData&amp; data)
+    {
+        ASSERT(!m_objects.contains(&amp;data));
+
+        m_objects.append(&amp;data);
+        g_object_weak_ref(data.object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    Vector&lt;DOMObjectCacheData*&gt;::iterator last = toUnref.end();
-    for (Vector&lt;DOMObjectCacheData*&gt;::iterator it = toUnref.begin(); it != last; ++it) {
-        DOMObjectCacheData* data = *it;
-        // We can't really know what the user has done with the DOM
-        // objects, so in case any of the external references to them
-        // were unreffed (but not all, otherwise the object would be
-        // dead and out of the cache) we'll add a weak ref before we
-        // start to get rid of the cache's own references; if the
-        // object dies in the middle of the process, we'll just stop.
-        gboolean objectDead = FALSE;
-        g_object_weak_ref(data-&gt;object, weakRefNotify, &amp;objectDead);
-        // We need to check objectDead first, otherwise the cache data
-        // might be garbage already.
-        while (!objectDead &amp;&amp; data-&gt;timesReturned &gt; 0) {
-            // If this is the last unref we are going to do,
-            // disconnect the weak ref. We cannot do it afterwards
-            // because the object might be dead at that point.
-            if (data-&gt;timesReturned == 1) {
-                g_object_weak_unref(data-&gt;object, weakRefNotify, &amp;objectDead);
-                // At this point, the next time the DOMObject is
-                // unref'ed it will be finalized,
-                // DOMObject::finalize() will call
-                // DOMObjectCache::forget(), which will free 'data'.
-                // Toggling 'objectDead' here will ensure we don't
-                // dereference an invalid pointer in the next
-                // iteration.
-                objectDead = TRUE;
-            }
-            data-&gt;timesReturned--;
-            g_object_unref(data-&gt;object);
</del><ins>+private:
+    static void objectFinalizedCallback(gpointer userData, GObject* finalizedObject)
+    {
+        DOMObjectCacheFrameObserver* observer = static_cast&lt;DOMObjectCacheFrameObserver*&gt;(userData);
+        observer-&gt;m_objects.removeFirstMatching([finalizedObject](DOMObjectCacheData* data) {
+            return data-&gt;object == finalizedObject;
+        });
+    }
+
+    void clear()
+    {
+        if (m_objects.isEmpty())
+            return;
+
+        auto objects = WTF::move(m_objects);
+        for (auto* data : objects) {
+            g_object_weak_unref(data-&gt;object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
+            data-&gt;clearObject();
</ins><span class="cx">         }
</span><span class="cx">     }
</span><ins>+
+    virtual void willDetachPage() override
+    {
+        clear();
+    }
+
+    virtual void frameDestroyed() override
+    {
+        clear();
+        WebCore::Frame* frame = m_frame;
+        FrameDestructionObserver::frameDestroyed();
+        domObjectCacheFrameObservers().remove(frame);
+    }
+
+    Vector&lt;DOMObjectCacheData*, 8&gt; m_objects;
+};
+
+typedef HashMap&lt;void*, std::unique_ptr&lt;DOMObjectCacheData&gt;&gt; DOMObjectMap;
+
+static DOMObjectMap&amp; domObjects()
+{
+    static NeverDestroyed&lt;DOMObjectMap&gt; staticDOMObjects;
+    return staticDOMObjects;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-DOMObjectCache::~DOMObjectCache()
</del><ins>+void DOMObjectCache::forget(void* objectHandle)
</ins><span class="cx"> {
</span><del>-    clearByFrame();
</del><ins>+    ASSERT(domObjects().contains(objectHandle));
+    domObjects().remove(objectHandle);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void* DOMObjectCache::get(void* objectHandle)
</span><span class="cx"> {
</span><span class="cx">     DOMObjectCacheData* data = domObjects().get(objectHandle);
</span><del>-    if (!data)
-        return 0;
-
-    // We want to add one ref each time a wrapper is returned, so that
-    // the user can manually unref them if he chooses to.
-    ASSERT(data-&gt;object);
-    data-&gt;timesReturned++;
-    return g_object_ref(data-&gt;object);
</del><ins>+    return data ? data-&gt;refObject() : nullptr;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void* DOMObjectCache::put(void* objectHandle, void* wrapper)
</del><ins>+void DOMObjectCache::put(void* objectHandle, void* wrapper)
</ins><span class="cx"> {
</span><del>-    if (domObjects().get(objectHandle))
-        return wrapper;
-
-    DOMObjectCacheData* data = g_slice_new(DOMObjectCacheData);
-    data-&gt;object = static_cast&lt;GObject*&gt;(wrapper);
-    data-&gt;frame = 0;
-    data-&gt;timesReturned = 1;
-
-    domObjects().set(objectHandle, data);
-    return wrapper;
</del><ins>+    if (domObjects().contains(objectHandle))
+        return;
+    domObjects().set(objectHandle, std::make_unique&lt;DOMObjectCacheData&gt;(G_OBJECT(wrapper)));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void* DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper)
</del><ins>+void DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper)
</ins><span class="cx"> {
</span><del>-    // call the ::put version that takes void* to do the basic cache
-    // insertion work
-    put(static_cast&lt;void*&gt;(objectHandle), wrapper);
</del><ins>+    if (domObjects().contains(objectHandle))
+        return;
</ins><span class="cx"> 
</span><del>-    DOMObjectCacheData* data = domObjects().get(objectHandle);
-    ASSERT(data);
</del><ins>+    std::unique_ptr&lt;DOMObjectCacheData&gt; data = std::make_unique&lt;DOMObjectCacheData&gt;(G_OBJECT(wrapper));
+    auto dataPtr = data.get();
+    domObjects().set(objectHandle, WTF::move(data));
</ins><span class="cx"> 
</span><del>-    data-&gt;frame = getFrameFromHandle(objectHandle);
-
-    return wrapper;
</del><ins>+    if (WebCore::Frame* frame = objectHandle-&gt;document().frame())
+        getOrCreateDOMObjectCacheFrameObserver(*frame).addObjectCacheData(*dataPtr);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsgobjectDOMObjectCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/gobject/DOMObjectCache.h (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/gobject/DOMObjectCache.h        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Source/WebCore/bindings/gobject/DOMObjectCache.h        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -21,18 +21,15 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> class Node;
</span><del>-class Frame;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> class DOMObjectCache {
</span><span class="cx"> public:
</span><span class="cx">     static void* get(void* objectHandle);
</span><del>-    static void* put(void* objectHandle, void* wrapper);
-    static void* put(WebCore::Node* objectHandle, void* wrapper);
-    static void clearByFrame(WebCore::Frame* frame = 0);
</del><ins>+    static void put(void* objectHandle, void* wrapper);
+    static void put(WebCore::Node* objectHandle, void* wrapper);
</ins><span class="cx">     static void forget(void* objectHandle);
</span><del>-    ~DOMObjectCache();
</del><span class="cx"> };
</span><span class="cx"> } // namespace WebKit
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/ChangeLog        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -1,3 +1,32 @@
</span><ins>+2015-02-17  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [GTK] GObject DOM bindings object are cached forever
+        https://bugs.webkit.org/show_bug.cgi?id=141558
+
+        Reviewed by Sergio Villar Senin.
+
+        Add checks for all DOM objects to ensure they are not leaked. Also
+        add a dedicated test for the DOM Object Cache.
+
+        * TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp:
+        (WebKitDOMNodeFilterTest::testTreeWalker):
+        (WebKitDOMNodeFilterTest::testNodeIterator):
+        * TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp:
+        (WebKitDOMNodeTest::testHierarchyNavigation):
+        (WebKitDOMNodeTest::testInsertion):
+        (WebKitDOMNodeTest::testTagNames):
+        (WebKitDOMNodeTest::testDOMCache):
+        (registerTests):
+        * TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp:
+        (WebKitDOMXPathNSResolverTest::evaluateFooChildTextAndCheckResult):
+        (WebKitDOMXPathNSResolverTest::testXPathNSResolverNative):
+        (WebKitDOMXPathNSResolverTest::testXPathNSResolverCustom):
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp:
+        (testWebKitDOMObjectCache):
+        (beforeAll):
+        * TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp:
+        (documentLoadedCallback):
+
</ins><span class="cx"> 2015-02-16  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] WebKitFrame objects are never released
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2GtkDOMNodeFilterTestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -66,17 +66,21 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><span class="cx">         WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, &quot;root&quot;);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE(root));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root));
</ins><span class="cx"> 
</span><span class="cx">         // No filter.
</span><del>-        WebKitDOMTreeWalker* walker = webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker));
-        g_assert(!webkit_dom_tree_walker_get_filter(walker));
</del><ins>+        GRefPtr&lt;WebKitDOMTreeWalker&gt; walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get()));
+        g_assert(!webkit_dom_tree_walker_get_filter(walker.get()));
</ins><span class="cx"> 
</span><span class="cx">         unsigned i = 0;
</span><del>-        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker), ++i) {
</del><ins>+        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedNodesAll));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]);
</span><span class="lines">@@ -85,12 +89,14 @@
</span><span class="cx"> 
</span><span class="cx">         // Input elements filter.
</span><span class="cx">         GRefPtr&lt;WebKitDOMNodeFilter&gt; filter = adoptGRef(static_cast&lt;WebKitDOMNodeFilter*&gt;(g_object_new(webkit_node_filter_get_type(), nullptr)));
</span><del>-        walker = webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker));
-        g_assert(webkit_dom_tree_walker_get_filter(walker) == filter.get());
</del><ins>+        walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(filter.get()));
+        g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get());
</ins><span class="cx"> 
</span><span class="cx">         i = 0;
</span><del>-        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker), ++i) {
</del><ins>+        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedNodesNoInput));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]);
</span><span class="lines">@@ -98,12 +104,14 @@
</span><span class="cx">         g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput));
</span><span class="cx"> 
</span><span class="cx">         // Show only elements, reusing the input filter.
</span><del>-        walker = webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker));
-        g_assert(webkit_dom_tree_walker_get_filter(walker) == filter.get());
</del><ins>+        walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get()));
+        g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get());
</ins><span class="cx"> 
</span><span class="cx">         i = 0;
</span><del>-        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker), ++i) {
</del><ins>+        for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedElementsNoInput));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]);
</span><span class="lines">@@ -117,17 +125,21 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><span class="cx">         WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, &quot;root&quot;);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE(root));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root));
</ins><span class="cx"> 
</span><span class="cx">         // No filter.
</span><del>-        WebKitDOMNodeIterator* iter = webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter));
-        g_assert(!webkit_dom_node_iterator_get_filter(iter));
</del><ins>+        GRefPtr&lt;WebKitDOMNodeIterator&gt; iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+        g_assert(!webkit_dom_node_iterator_get_filter(iter.get()));
</ins><span class="cx"> 
</span><span class="cx">         unsigned i = 0;
</span><del>-        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter, nullptr)) {
</del><ins>+        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedNodesAll));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]);
</span><span class="lines">@@ -137,12 +149,14 @@
</span><span class="cx"> 
</span><span class="cx">         // Input elements filter.
</span><span class="cx">         GRefPtr&lt;WebKitDOMNodeFilter&gt; filter = adoptGRef(static_cast&lt;WebKitDOMNodeFilter*&gt;(g_object_new(webkit_node_filter_get_type(), nullptr)));
</span><del>-        iter = webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter));
-        g_assert(webkit_dom_node_iterator_get_filter(iter) == filter.get());
</del><ins>+        iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+        g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get());
</ins><span class="cx"> 
</span><span class="cx">         i = 0;
</span><del>-        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter, nullptr)) {
</del><ins>+        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedNodesNoInput));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]);
</span><span class="lines">@@ -151,12 +165,14 @@
</span><span class="cx">         g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput));
</span><span class="cx"> 
</span><span class="cx">         // Show only elements, reusing the input filter.
</span><del>-        iter = webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr);
-        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter));
-        g_assert(webkit_dom_node_iterator_get_filter(iter) == filter.get());
</del><ins>+        iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+        g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get());
</ins><span class="cx"> 
</span><span class="cx">         i = 0;
</span><del>-        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter, nullptr)) {
</del><ins>+        while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             g_assert_cmpuint(i, &lt;, G_N_ELEMENTS(expectedElementsNoInput));
</span><span class="cx">             GUniquePtr&lt;char&gt; nodeName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]);
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2GtkDOMNodeTestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -33,21 +33,26 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><span class="cx">         WebKitDOMHTMLHeadElement* head = webkit_dom_document_get_head(document);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(head));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(head));
</ins><span class="cx"> 
</span><span class="cx">         // Title, head's child.
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(head)));
</span><span class="cx">         GRefPtr&lt;WebKitDOMNodeList&gt; list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(head)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
</span><span class="cx">         WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_TITLE_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx"> 
</span><span class="cx">         // Body, Head sibling.
</span><span class="cx">         node = webkit_dom_node_get_next_sibling(WEBKIT_DOM_NODE(head));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_BODY_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         WebKitDOMHTMLBodyElement* body = WEBKIT_DOM_HTML_BODY_ELEMENT(node);
</span><span class="cx"> 
</span><span class="cx">         // There is no third sibling
</span><span class="lines">@@ -56,10 +61,13 @@
</span><span class="cx">         // Body's previous sibling is Head.
</span><span class="cx">         node = webkit_dom_node_get_previous_sibling(WEBKIT_DOM_NODE(body));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx"> 
</span><span class="cx">         // Body has 3 children.
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><ins>+        g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         unsigned long length = webkit_dom_node_list_get_length(list.get());
</span><span class="cx">         g_assert_cmpint(length, ==, 3);
</span><span class="cx"> 
</span><span class="lines">@@ -67,6 +75,7 @@
</span><span class="cx">         for (unsigned long i = 0; i &lt; length; i++) {
</span><span class="cx">             node = webkit_dom_node_list_item(list.get(), i);
</span><span class="cx">             g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(node));
</span><ins>+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         // Go backwards
</span><span class="lines">@@ -81,9 +90,11 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><span class="cx">         WebKitDOMHTMLElement* body = webkit_dom_document_get_body(document);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(body));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(body));
</ins><span class="cx"> 
</span><span class="cx">         // Body shouldn't have any children at this point.
</span><span class="cx">         g_assert(!webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="lines">@@ -94,60 +105,74 @@
</span><span class="cx">         // Insert one P element.
</span><span class="cx">         WebKitDOMElement* p = webkit_dom_document_create_element(document, &quot;P&quot;, 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
</ins><span class="cx">         webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), 0);
</span><span class="cx"> 
</span><span class="cx">         // Now it should have one, the same that we inserted.
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         GRefPtr&lt;WebKitDOMNodeList&gt; list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
</span><span class="cx">         WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
</span><span class="cx"> 
</span><span class="cx">         // Replace the P tag with a DIV tag.
</span><span class="cx">         WebKitDOMElement* div = webkit_dom_document_create_element(document, &quot;DIV&quot;, 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
</ins><span class="cx">         webkit_dom_node_replace_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p), 0);
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
</span><span class="cx">         node = webkit_dom_node_list_item(list.get(), 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
</span><span class="cx"> 
</span><span class="cx">         // Now remove the tag.
</span><span class="cx">         webkit_dom_node_remove_child(WEBKIT_DOM_NODE(body), node, 0);
</span><span class="cx">         list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 0);
</span><span class="cx"> 
</span><span class="cx">         // Test insert before. If refChild is null, insert newChild as last element of parent.
</span><span class="cx">         div = webkit_dom_document_create_element(document, &quot;DIV&quot;, 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
</ins><span class="cx">         webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), 0, 0);
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
</span><span class="cx">         node = webkit_dom_node_list_item(list.get(), 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
</span><span class="cx"> 
</span><span class="cx">         // Now insert a 'p' before 'div'.
</span><span class="cx">         p = webkit_dom_document_create_element(document, &quot;P&quot;, 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
</ins><span class="cx">         webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), WEBKIT_DOM_NODE(div), 0);
</span><span class="cx">         g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
</ins><span class="cx">         g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 2);
</span><span class="cx">         node = webkit_dom_node_list_item(list.get(), 0);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
</span><span class="cx">         node = webkit_dom_node_list_item(list.get(), 1);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">         g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
</span><span class="cx"> 
</span><span class="cx">         return true;
</span><span class="lines">@@ -159,13 +184,17 @@
</span><span class="cx"> 
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><del>-        WebKitDOMNodeList* list = webkit_dom_document_get_elements_by_tag_name(document, &quot;*&quot;);
-        gulong nodeCount = webkit_dom_node_list_get_length(list);
</del><ins>+        GRefPtr&lt;WebKitDOMNodeList&gt; list = adoptGRef(webkit_dom_document_get_elements_by_tag_name(document, &quot;*&quot;));
+        g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+        gulong nodeCount = webkit_dom_node_list_get_length(list.get());
</ins><span class="cx">         g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames));
</span><span class="cx">         for (unsigned i = 0; i &lt; nodeCount; i++) {
</span><del>-            WebKitDOMNode* node = webkit_dom_node_list_item(list, i);
</del><ins>+            WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), i);
</ins><span class="cx">             g_assert(WEBKIT_DOM_IS_NODE(node));
</span><ins>+            assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
</ins><span class="cx">             GUniquePtr&lt;char&gt; tagName(webkit_dom_node_get_node_name(node));
</span><span class="cx">             g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]);
</span><span class="cx">         }
</span><span class="lines">@@ -173,6 +202,42 @@
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool testDOMCache(WebKitWebPage* page)
+    {
+        WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+        g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+        // DOM objects already in the document should be automatically handled by the cache.
+        WebKitDOMElement* div = webkit_dom_document_get_element_by_id(document, &quot;container&quot;);
+        g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
+
+        // Get the same elment twice should return the same pointer.
+        g_assert(div == webkit_dom_document_get_element_by_id(document, &quot;container&quot;));
+
+        // A new DOM object created that is derived from Node should be automatically handled by the cache.
+        WebKitDOMElement* p = webkit_dom_document_create_element(document, &quot;P&quot;, nullptr);
+        g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
+
+        // A new DOM object created that isn't derived from Node should be manually handled.
+        GRefPtr&lt;WebKitDOMNodeIterator&gt; iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+        g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+
+        // We can also manually handle a DOM object handled by the cache.
+        GRefPtr&lt;WebKitDOMElement&gt; p2 = adoptGRef(webkit_dom_document_create_element(document, &quot;P&quot;, nullptr));
+        g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p2.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p2.get()));
+
+        // DOM objects removed from the document are also correctly handled by the cache.
+        WebKitDOMElement* a = webkit_dom_document_create_element(document, &quot;A&quot;, nullptr);
+        g_assert(WEBKIT_DOM_IS_ELEMENT(a));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(a));
+        webkit_dom_node_remove_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(a), nullptr);
+    }
+
</ins><span class="cx">     bool runTest(const char* testName, WebKitWebPage* page) override
</span><span class="cx">     {
</span><span class="cx">         if (!strcmp(testName, &quot;hierarchy-navigation&quot;))
</span><span class="lines">@@ -181,6 +246,8 @@
</span><span class="cx">             return testInsertion(page);
</span><span class="cx">         if (!strcmp(testName, &quot;tag-names&quot;))
</span><span class="cx">             return testTagNames(page);
</span><ins>+        if (!strcmp(testName, &quot;dom-cache&quot;))
+            return testDOMCache(page);
</ins><span class="cx"> 
</span><span class="cx">         g_assert_not_reached();
</span><span class="cx">         return false;
</span><span class="lines">@@ -192,6 +259,7 @@
</span><span class="cx">     REGISTER_TEST(WebKitDOMNodeTest, &quot;WebKitDOMNode/hierarchy-navigation&quot;);
</span><span class="cx">     REGISTER_TEST(WebKitDOMNodeTest, &quot;WebKitDOMNode/insertion&quot;);
</span><span class="cx">     REGISTER_TEST(WebKitDOMNodeTest, &quot;WebKitDOMNode/tag-names&quot;);
</span><ins>+    REGISTER_TEST(WebKitDOMNodeTest, &quot;WebKitDOMNode/dom-cache&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2GtkDOMXPathNSResolverTestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -64,12 +64,15 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMElement* documentElement = webkit_dom_document_get_document_element(document);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_ELEMENT(documentElement));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(documentElement));
</ins><span class="cx"> 
</span><del>-        WebKitDOMXPathResult* result = webkit_dom_document_evaluate(document, &quot;foo:child/text()&quot;, WEBKIT_DOM_NODE(documentElement), resolver, WEBKIT_DOM_XPATH_RESULT_ORDERED_NODE_ITERATOR_TYPE, nullptr, nullptr);
-        g_assert(WEBKIT_DOM_IS_XPATH_RESULT(result));
</del><ins>+        GRefPtr&lt;WebKitDOMXPathResult&gt; result = adoptGRef(webkit_dom_document_evaluate(document, &quot;foo:child/text()&quot;, WEBKIT_DOM_NODE(documentElement), resolver, WEBKIT_DOM_XPATH_RESULT_ORDERED_NODE_ITERATOR_TYPE, nullptr, nullptr));
+        g_assert(WEBKIT_DOM_IS_XPATH_RESULT(result.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(result.get()));
</ins><span class="cx"> 
</span><del>-        WebKitDOMNode* nodeResult = webkit_dom_xpath_result_iterate_next(result, nullptr);
</del><ins>+        WebKitDOMNode* nodeResult = webkit_dom_xpath_result_iterate_next(result.get(), nullptr);
</ins><span class="cx">         g_assert(WEBKIT_DOM_IS_NODE(nodeResult));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(nodeResult));
</ins><span class="cx"> 
</span><span class="cx">         GUniquePtr&lt;char&gt; nodeValue(webkit_dom_node_get_node_value(nodeResult));
</span><span class="cx">         g_assert_cmpstr(nodeValue.get(), ==, &quot;SUCCESS&quot;);
</span><span class="lines">@@ -79,10 +82,12 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><del>-        WebKitDOMXPathNSResolver* resolver = webkit_dom_document_create_ns_resolver(document, WEBKIT_DOM_NODE(webkit_dom_document_get_document_element(document)));
-        g_assert(WEBKIT_DOM_IS_XPATH_NS_RESOLVER(resolver));
-        evaluateFooChildTextAndCheckResult(document, resolver);
</del><ins>+        GRefPtr&lt;WebKitDOMXPathNSResolver&gt; resolver = adoptGRef(webkit_dom_document_create_ns_resolver(document, WEBKIT_DOM_NODE(webkit_dom_document_get_document_element(document))));
+        g_assert(WEBKIT_DOM_IS_XPATH_NS_RESOLVER(resolver.get()));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get()));
+        evaluateFooChildTextAndCheckResult(document, resolver.get());
</ins><span class="cx"> 
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="lines">@@ -91,8 +96,10 @@
</span><span class="cx">     {
</span><span class="cx">         WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
</span><span class="cx">         g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
</ins><span class="cx"> 
</span><span class="cx">         GRefPtr&lt;WebKitDOMXPathNSResolver&gt; resolver = adoptGRef(WEBKIT_DOM_XPATH_NS_RESOLVER(g_object_new(webkit_xpath_ns_resolver_get_type(), nullptr)));
</span><ins>+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get()));
</ins><span class="cx">         evaluateFooChildTextAndCheckResult(document, resolver.get());
</span><span class="cx"> 
</span><span class="cx">         return true;
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2GtkTestDOMNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -59,11 +59,22 @@
</span><span class="cx">     g_assert(test-&gt;runWebProcessTest(&quot;WebKitDOMNode&quot;, &quot;tag-names&quot;));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static void testWebKitDOMObjectCache(WebViewTest* test, gconstpointer)
+{
+    static const char* testHTML = &quot;&lt;html&gt;&lt;body&gt;&lt;div id='container'&gt;&lt;p&gt;DOM Cache test&lt;/p&gt;&lt;a id='link href='#'&gt;link&lt;/a&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;&quot;;
+    test-&gt;loadHtml(testHTML, nullptr);
+    test-&gt;waitUntilLoadFinished();
+
+    g_assert(test-&gt;runWebProcessTest(&quot;WebKitDOMNode&quot;, &quot;dom-cache&quot;));
+}
+
+
</ins><span class="cx"> void beforeAll()
</span><span class="cx"> {
</span><span class="cx">     WebViewTest::add(&quot;WebKitDOMNode&quot;, &quot;hierarchy-navigation&quot;, testWebKitDOMNodeHierarchyNavigation);
</span><span class="cx">     WebViewTest::add(&quot;WebKitDOMNode&quot;, &quot;insertion&quot;, testWebKitDOMNodeInsertion);
</span><span class="cx">     WebViewTest::add(&quot;WebKitDOMNode&quot;, &quot;tag-names&quot;, testWebKitDOMNodeTagNames);
</span><ins>+    WebViewTest::add(&quot;WebKitDOMNode&quot;, &quot;dom-cache&quot;, testWebKitDOMObjectCache);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void afterAll()
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2GtkWebExtensionTestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp (180213 => 180214)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp        2015-02-17 09:42:13 UTC (rev 180213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp        2015-02-17 09:57:42 UTC (rev 180214)
</span><span class="lines">@@ -102,14 +102,14 @@
</span><span class="cx"> {
</span><span class="cx">     // FIXME: Too much code just to send a message, we need convenient custom API for this.
</span><span class="cx">     WebKitDOMDocument* document = webkit_web_page_get_dom_document(webPage);
</span><del>-    WebKitDOMDOMWindow* window = webkit_dom_document_get_default_view(document);
-    if (WebKitDOMWebKitNamespace* webkit = webkit_dom_dom_window_get_webkit_namespace(window)) {
</del><ins>+    GRefPtr&lt;WebKitDOMDOMWindow&gt; window = adoptGRef(webkit_dom_document_get_default_view(document));
+    if (WebKitDOMWebKitNamespace* webkit = webkit_dom_dom_window_get_webkit_namespace(window.get())) {
</ins><span class="cx">         WebKitDOMUserMessageHandlersNamespace* messageHandlers = webkit_dom_webkit_namespace_get_message_handlers(webkit);
</span><span class="cx">         if (WebKitDOMUserMessageHandler* handler = webkit_dom_user_message_handlers_namespace_get_handler(messageHandlers, &quot;dom&quot;))
</span><span class="cx">             webkit_dom_user_message_handler_post_message(handler, &quot;DocumentLoaded&quot;);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    webkit_dom_dom_window_webkit_message_handlers_post_message(window, &quot;dom-convenience&quot;, &quot;DocumentLoaded&quot;);
</del><ins>+    webkit_dom_dom_window_webkit_message_handlers_post_message(window.get(), &quot;dom-convenience&quot;, &quot;DocumentLoaded&quot;);
</ins><span class="cx"> 
</span><span class="cx">     gpointer data = g_object_get_data(G_OBJECT(extension), &quot;dbus-connection&quot;);
</span><span class="cx">     if (data)
</span></span></pre>
</div>
</div>

</body>
</html>