diff options
Diffstat (limited to 'Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp')
-rw-r--r-- | Source/WebKit2/WebProcess/InjectedBundle/API/gtk/DOM/DOMObjectCache.cpp | 233 |
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); +} + +} |