summaryrefslogtreecommitdiff
path: root/Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp')
-rw-r--r--Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp b/Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp
new file mode 100644
index 000000000..0e790f9c8
--- /dev/null
+++ b/Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010, 2015 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "DOMObjectCache.h"
+
+#include <WebCore/DOMWindowProperty.h>
+#include <WebCore/Document.h>
+#include <WebCore/Frame.h>
+#include <WebCore/FrameDestructionObserver.h>
+#include <WebCore/Node.h>
+#include <glib-object.h>
+#include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RunLoop.h>
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WebKit {
+
+struct DOMObjectCacheData {
+ DOMObjectCacheData(GObject* wrapper)
+ : object(wrapper)
+ , cacheReferences(1)
+ {
+ }
+
+ void clearObject()
+ {
+ ASSERT(object);
+ ASSERT(cacheReferences >= 1);
+ ASSERT(object->ref_count >= 1);
+
+ // Make sure we don't unref more than the references the object actually has. It can happen that user
+ // unreffed a reference owned by the cache.
+ cacheReferences = std::min(static_cast<unsigned>(object->ref_count), cacheReferences);
+ GRefPtr<GObject> protect(object);
+ do {
+ g_object_unref(object);
+ } while (--cacheReferences);
+ object = nullptr;
+ }
+
+ void* refObject()
+ {
+ ASSERT(object);
+
+ cacheReferences++;
+ return g_object_ref(object);
+ }
+
+ GObject* object;
+ unsigned cacheReferences;
+};
+
+class DOMObjectCacheFrameObserver;
+typedef HashMap<WebCore::Frame*, std::unique_ptr<DOMObjectCacheFrameObserver>> DOMObjectCacheFrameObserverMap;
+
+static DOMObjectCacheFrameObserverMap& domObjectCacheFrameObservers()
+{
+ static NeverDestroyed<DOMObjectCacheFrameObserverMap> map;
+ return map;
+}
+
+static DOMObjectCacheFrameObserver& getOrCreateDOMObjectCacheFrameObserver(WebCore::Frame& frame)
+{
+ DOMObjectCacheFrameObserverMap::AddResult result = domObjectCacheFrameObservers().add(&frame, nullptr);
+ if (result.isNewEntry)
+ result.iterator->value = std::make_unique<DOMObjectCacheFrameObserver>(frame);
+ return *result.iterator->value;
+}
+
+class DOMObjectCacheFrameObserver final: public WebCore::FrameDestructionObserver {
+public:
+ DOMObjectCacheFrameObserver(WebCore::Frame& frame)
+ : FrameDestructionObserver(&frame)
+ {
+ }
+
+ ~DOMObjectCacheFrameObserver()
+ {
+ ASSERT(m_objects.isEmpty());
+ }
+
+ void addObjectCacheData(DOMObjectCacheData& data)
+ {
+ ASSERT(!m_objects.contains(&data));
+
+ WebCore::DOMWindow* domWindow = m_frame->document()->domWindow();
+ if (domWindow && (!m_domWindowObserver || m_domWindowObserver->domWindow() != domWindow)) {
+ // New DOMWindow, clear the cache and create a new DOMWindowObserver.
+ clear();
+ m_domWindowObserver = std::make_unique<DOMWindowObserver>(*m_frame, *this, domWindow);
+ }
+
+ m_objects.append(&data);
+ g_object_weak_ref(data.object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
+ }
+
+private:
+ class DOMWindowObserver final: public WebCore::DOMWindowProperty {
+ WTF_MAKE_FAST_ALLOCATED;
+ public:
+ DOMWindowObserver(WebCore::Frame& frame, DOMObjectCacheFrameObserver& frameObserver, WebCore::DOMWindow* window)
+ : DOMWindowProperty(&frame)
+ , m_frameObserver(frameObserver)
+ , m_domWindow(window)
+ {
+ ASSERT(m_domWindow);
+ }
+
+ virtual ~DOMWindowObserver()
+ {
+ }
+
+ WebCore::DOMWindow* domWindow() const { return m_domWindow; }
+
+ private:
+ void willDetachGlobalObjectFromFrame() override
+ {
+ // Clear the DOMWindowProperty first, and then notify the Frame observer.
+ DOMWindowProperty::willDetachGlobalObjectFromFrame();
+ m_frameObserver.willDetachGlobalObjectFromFrame();
+ }
+
+ DOMObjectCacheFrameObserver& m_frameObserver;
+ WebCore::DOMWindow* m_domWindow;
+ };
+
+ static void objectFinalizedCallback(gpointer userData, GObject* finalizedObject)
+ {
+ DOMObjectCacheFrameObserver* observer = static_cast<DOMObjectCacheFrameObserver*>(userData);
+ observer->m_objects.removeFirstMatching([finalizedObject](DOMObjectCacheData* data) {
+ return data->object == finalizedObject;
+ });
+ }
+
+ void clear()
+ {
+ if (m_objects.isEmpty())
+ return;
+
+ auto objects = WTFMove(m_objects);
+
+ // Deleting of DOM wrappers might end up deleting the wrapped core object which could cause some problems
+ // for example if a Document is deleted during the frame destruction, so we remove the weak references now
+ // and delete the objects on next run loop iteration. See https://bugs.webkit.org/show_bug.cgi?id=151700.
+ for (auto* data : objects)
+ g_object_weak_unref(data->object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
+
+ RunLoop::main().dispatch([objects] {
+ for (auto* data : objects)
+ data->clearObject();
+ });
+ }
+
+ void willDetachPage() override
+ {
+ clear();
+ }
+
+ void frameDestroyed() override
+ {
+ clear();
+ WebCore::Frame* frame = m_frame;
+ FrameDestructionObserver::frameDestroyed();
+ domObjectCacheFrameObservers().remove(frame);
+ }
+
+ void willDetachGlobalObjectFromFrame()
+ {
+ clear();
+ m_domWindowObserver = nullptr;
+ }
+
+ Vector<DOMObjectCacheData*, 8> m_objects;
+ std::unique_ptr<DOMWindowObserver> m_domWindowObserver;
+};
+
+typedef HashMap<void*, std::unique_ptr<DOMObjectCacheData>> DOMObjectMap;
+
+static DOMObjectMap& domObjects()
+{
+ static NeverDestroyed<DOMObjectMap> staticDOMObjects;
+ return staticDOMObjects;
+}
+
+void DOMObjectCache::forget(void* objectHandle)
+{
+ ASSERT(domObjects().contains(objectHandle));
+ domObjects().remove(objectHandle);
+}
+
+void* DOMObjectCache::get(void* objectHandle)
+{
+ DOMObjectCacheData* data = domObjects().get(objectHandle);
+ return data ? data->refObject() : nullptr;
+}
+
+void DOMObjectCache::put(void* objectHandle, void* wrapper)
+{
+ DOMObjectMap::AddResult result = domObjects().add(objectHandle, nullptr);
+ if (result.isNewEntry)
+ result.iterator->value = std::make_unique<DOMObjectCacheData>(G_OBJECT(wrapper));
+}
+
+void DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper)
+{
+ DOMObjectMap::AddResult result = domObjects().add(objectHandle, nullptr);
+ if (!result.isNewEntry)
+ return;
+
+ result.iterator->value = std::make_unique<DOMObjectCacheData>(G_OBJECT(wrapper));
+ if (WebCore::Frame* frame = objectHandle->document().frame())
+ getOrCreateDOMObjectCacheFrameObserver(*frame).addObjectCacheData(*result.iterator->value);
+}
+
+}