Title: [257503] trunk/Source/WebKit
Revision
257503
Author
[email protected]
Date
2020-02-26 12:57:49 -0800 (Wed, 26 Feb 2020)

Log Message

Make sure a client cannot cause a whole DOM tree to get leaked by simply holding on to a WKBundleNodeHandle
https://bugs.webkit.org/show_bug.cgi?id=208218

Reviewed by Ryosuke Niwa.

Make sure a client cannot cause a whole DOM tree to get leaked by simply holding on to a WKBundleNodeHandle.
Previously, WKBundleNodeHandle would ref its node, which would keep the whole HTML document alive. To protect
against this, InjectedBundleNodeHandle is now an ActiveDOMObject which nulls out its node RefPtr when the
document is getting ready for destruction (i.e. ActiveDOMObject::stop() is called).

* WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.cpp:
(WebKit::InjectedBundleNodeHandle::InjectedBundleNodeHandle):
(WebKit::InjectedBundleNodeHandle::~InjectedBundleNodeHandle):
(WebKit::InjectedBundleNodeHandle::coreNode):
(WebKit::InjectedBundleNodeHandle::document):
(WebKit::InjectedBundleNodeHandle::elementBounds):
(WebKit::InjectedBundleNodeHandle::renderRect):
(WebKit::InjectedBundleNodeHandle::renderedImage):
(WebKit::InjectedBundleNodeHandle::visibleRange):
(WebKit::InjectedBundleNodeHandle::setHTMLInputElementValueForUser):
(WebKit::InjectedBundleNodeHandle::setHTMLInputElementSpellcheckEnabled):
(WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFilled const):
(WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFilledAndViewable const):
(WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFilled):
(WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFilledAndViewable):
(WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFillButtonEnabled const):
(WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFillButtonEnabled):
(WebKit::InjectedBundleNodeHandle::htmlInputElementAutoFillButtonType const):
(WebKit::InjectedBundleNodeHandle::htmlInputElementLastAutoFillButtonType const):
(WebKit::InjectedBundleNodeHandle::isAutoFillAvailable const):
(WebKit::InjectedBundleNodeHandle::setAutoFillAvailable):
(WebKit::InjectedBundleNodeHandle::htmlInputElementAutoFillButtonBounds):
(WebKit::InjectedBundleNodeHandle::htmlInputElementLastChangeWasUserEdit):
(WebKit::InjectedBundleNodeHandle::htmlTextAreaElementLastChangeWasUserEdit):
(WebKit::InjectedBundleNodeHandle::isTextField const):
(WebKit::InjectedBundleNodeHandle::htmlTableCellElementCellAbove):
(WebKit::InjectedBundleNodeHandle::documentFrame):
(WebKit::InjectedBundleNodeHandle::htmlFrameElementContentFrame):
(WebKit::InjectedBundleNodeHandle::htmlIFrameElementContentFrame):
(WebKit::InjectedBundleNodeHandle::stop):
(WebKit::InjectedBundleNodeHandle::activeDOMObjectName const):
* WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.h:

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (257502 => 257503)


--- trunk/Source/WebKit/ChangeLog	2020-02-26 20:05:05 UTC (rev 257502)
+++ trunk/Source/WebKit/ChangeLog	2020-02-26 20:57:49 UTC (rev 257503)
@@ -1,3 +1,48 @@
+2020-02-26  Chris Dumez  <[email protected]>
+
+        Make sure a client cannot cause a whole DOM tree to get leaked by simply holding on to a WKBundleNodeHandle
+        https://bugs.webkit.org/show_bug.cgi?id=208218
+
+        Reviewed by Ryosuke Niwa.
+
+        Make sure a client cannot cause a whole DOM tree to get leaked by simply holding on to a WKBundleNodeHandle.
+        Previously, WKBundleNodeHandle would ref its node, which would keep the whole HTML document alive. To protect
+        against this, InjectedBundleNodeHandle is now an ActiveDOMObject which nulls out its node RefPtr when the
+        document is getting ready for destruction (i.e. ActiveDOMObject::stop() is called).
+
+        * WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.cpp:
+        (WebKit::InjectedBundleNodeHandle::InjectedBundleNodeHandle):
+        (WebKit::InjectedBundleNodeHandle::~InjectedBundleNodeHandle):
+        (WebKit::InjectedBundleNodeHandle::coreNode):
+        (WebKit::InjectedBundleNodeHandle::document):
+        (WebKit::InjectedBundleNodeHandle::elementBounds):
+        (WebKit::InjectedBundleNodeHandle::renderRect):
+        (WebKit::InjectedBundleNodeHandle::renderedImage):
+        (WebKit::InjectedBundleNodeHandle::visibleRange):
+        (WebKit::InjectedBundleNodeHandle::setHTMLInputElementValueForUser):
+        (WebKit::InjectedBundleNodeHandle::setHTMLInputElementSpellcheckEnabled):
+        (WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFilled const):
+        (WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFilledAndViewable const):
+        (WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFilled):
+        (WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFilledAndViewable):
+        (WebKit::InjectedBundleNodeHandle::isHTMLInputElementAutoFillButtonEnabled const):
+        (WebKit::InjectedBundleNodeHandle::setHTMLInputElementAutoFillButtonEnabled):
+        (WebKit::InjectedBundleNodeHandle::htmlInputElementAutoFillButtonType const):
+        (WebKit::InjectedBundleNodeHandle::htmlInputElementLastAutoFillButtonType const):
+        (WebKit::InjectedBundleNodeHandle::isAutoFillAvailable const):
+        (WebKit::InjectedBundleNodeHandle::setAutoFillAvailable):
+        (WebKit::InjectedBundleNodeHandle::htmlInputElementAutoFillButtonBounds):
+        (WebKit::InjectedBundleNodeHandle::htmlInputElementLastChangeWasUserEdit):
+        (WebKit::InjectedBundleNodeHandle::htmlTextAreaElementLastChangeWasUserEdit):
+        (WebKit::InjectedBundleNodeHandle::isTextField const):
+        (WebKit::InjectedBundleNodeHandle::htmlTableCellElementCellAbove):
+        (WebKit::InjectedBundleNodeHandle::documentFrame):
+        (WebKit::InjectedBundleNodeHandle::htmlFrameElementContentFrame):
+        (WebKit::InjectedBundleNodeHandle::htmlIFrameElementContentFrame):
+        (WebKit::InjectedBundleNodeHandle::stop):
+        (WebKit::InjectedBundleNodeHandle::activeDOMObjectName const):
+        * WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.h:
+
 2020-02-26  Daniel Bates  <[email protected]>
 
         [iOS] Send focus update immediately on becoming or resigning first responder

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.cpp (257502 => 257503)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.cpp	2020-02-26 20:05:05 UTC (rev 257502)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.cpp	2020-02-26 20:57:49 UTC (rev 257503)
@@ -84,37 +84,44 @@
 
 Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(Node& node)
 {
-    DOMNodeHandleCache::AddResult result = domNodeHandleCache().add(&node, nullptr);
-    if (!result.isNewEntry)
-        return Ref<InjectedBundleNodeHandle>(*result.iterator->value);
+    if (auto* existingHandle = domNodeHandleCache().get(&node))
+        return Ref<InjectedBundleNodeHandle>(*existingHandle);
 
-    Ref<InjectedBundleNodeHandle> nodeHandle = InjectedBundleNodeHandle::create(node);
-    result.iterator->value = nodeHandle.ptr();
+    auto nodeHandle = InjectedBundleNodeHandle::create(node);
+    if (nodeHandle->coreNode())
+        domNodeHandleCache().add(nodeHandle->coreNode(), nodeHandle.ptr());
     return nodeHandle;
 }
 
 Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::create(Node& node)
 {
-    return adoptRef(*new InjectedBundleNodeHandle(node));
+    auto handle = adoptRef(*new InjectedBundleNodeHandle(node));
+    handle->suspendIfNeeded();
+    return handle;
 }
 
 InjectedBundleNodeHandle::InjectedBundleNodeHandle(Node& node)
-    : m_node(node)
+    : ActiveDOMObject(node.document())
+    , m_node(&node)
 {
 }
 
 InjectedBundleNodeHandle::~InjectedBundleNodeHandle()
 {
-    domNodeHandleCache().remove(m_node.ptr());
+    if (m_node)
+        domNodeHandleCache().remove(m_node.get());
 }
 
 Node* InjectedBundleNodeHandle::coreNode()
 {
-    return m_node.ptr();
+    return m_node.get();
 }
 
-Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::document()
+RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::document()
 {
+    if (!m_node)
+        return nullptr;
+
     return getOrCreate(m_node->document());
 }
 
@@ -126,11 +133,14 @@
     if (!is<Element>(m_node))
         return IntRect();
 
-    return downcast<Element>(m_node.get()).boundsInRootViewSpace();
+    return downcast<Element>(*m_node).boundsInRootViewSpace();
 }
     
 IntRect InjectedBundleNodeHandle::renderRect(bool* isReplaced)
 {
+    if (!m_node)
+        return { };
+
     return m_node->pixelSnappedRenderRect(isReplaced);
 }
 
@@ -188,6 +198,9 @@
 
 RefPtr<WebImage> InjectedBundleNodeHandle::renderedImage(SnapshotOptions options, bool shouldExcludeOverflow, const Optional<float>& bitmapWidth)
 {
+    if (!m_node)
+        return nullptr;
+
     Frame* frame = m_node->document().frame();
     if (!frame)
         return nullptr;
@@ -210,7 +223,7 @@
         paintingRect = snappedIntRect(renderer->paintingRootRect(topLevelRect));
     }
 
-    frameView->setNodeToDraw(m_node.ptr());
+    frameView->setNodeToDraw(m_node.get());
     auto image = imageForRect(frameView, paintingRect, bitmapWidth, options);
     frameView->setNodeToDraw(0);
 
@@ -219,9 +232,12 @@
 
 RefPtr<InjectedBundleRangeHandle> InjectedBundleNodeHandle::visibleRange()
 {
-    VisiblePosition start = firstPositionInNode(m_node.ptr());
-    VisiblePosition end = lastPositionInNode(m_node.ptr());
+    if (!m_node)
+        return nullptr;
 
+    VisiblePosition start = firstPositionInNode(m_node.get());
+    VisiblePosition end = lastPositionInNode(m_node.get());
+
     RefPtr<Range> range = makeRange(start, end);
     return InjectedBundleRangeHandle::getOrCreate(range.get());
 }
@@ -231,7 +247,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setValueForUser(value);
+    downcast<HTMLInputElement>(*m_node).setValueForUser(value);
 }
 
 void InjectedBundleNodeHandle::setHTMLInputElementSpellcheckEnabled(bool enabled)
@@ -239,7 +255,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setSpellcheckDisabledExceptTextReplacement(!enabled);
+    downcast<HTMLInputElement>(*m_node).setSpellcheckDisabledExceptTextReplacement(!enabled);
 }
 
 bool InjectedBundleNodeHandle::isHTMLInputElementAutoFilled() const
@@ -247,7 +263,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
     
-    return downcast<HTMLInputElement>(m_node.get()).isAutoFilled();
+    return downcast<HTMLInputElement>(*m_node).isAutoFilled();
 }
 
 bool InjectedBundleNodeHandle::isHTMLInputElementAutoFilledAndViewable() const
@@ -255,7 +271,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
 
-    return downcast<HTMLInputElement>(m_node.get()).isAutoFilledAndViewable();
+    return downcast<HTMLInputElement>(*m_node).isAutoFilledAndViewable();
 }
 
 void InjectedBundleNodeHandle::setHTMLInputElementAutoFilled(bool filled)
@@ -263,7 +279,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setAutoFilled(filled);
+    downcast<HTMLInputElement>(*m_node).setAutoFilled(filled);
 }
 
 void InjectedBundleNodeHandle::setHTMLInputElementAutoFilledAndViewable(bool autoFilledAndViewable)
@@ -271,7 +287,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setAutoFilledAndViewable(autoFilledAndViewable);
+    downcast<HTMLInputElement>(*m_node).setAutoFilledAndViewable(autoFilledAndViewable);
 }
 
 bool InjectedBundleNodeHandle::isHTMLInputElementAutoFillButtonEnabled() const
@@ -279,7 +295,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
     
-    return downcast<HTMLInputElement>(m_node.get()).autoFillButtonType() != AutoFillButtonType::None;
+    return downcast<HTMLInputElement>(*m_node).autoFillButtonType() != AutoFillButtonType::None;
 }
 
 void InjectedBundleNodeHandle::setHTMLInputElementAutoFillButtonEnabled(AutoFillButtonType autoFillButtonType)
@@ -287,7 +303,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setShowAutoFillButton(autoFillButtonType);
+    downcast<HTMLInputElement>(*m_node).setShowAutoFillButton(autoFillButtonType);
 }
 
 AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementAutoFillButtonType() const
@@ -294,7 +310,7 @@
 {
     if (!is<HTMLInputElement>(m_node))
         return AutoFillButtonType::None;
-    return downcast<HTMLInputElement>(m_node.get()).autoFillButtonType();
+    return downcast<HTMLInputElement>(*m_node).autoFillButtonType();
 }
 
 AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementLastAutoFillButtonType() const
@@ -301,7 +317,7 @@
 {
     if (!is<HTMLInputElement>(m_node))
         return AutoFillButtonType::None;
-    return downcast<HTMLInputElement>(m_node.get()).lastAutoFillButtonType();
+    return downcast<HTMLInputElement>(*m_node).lastAutoFillButtonType();
 }
 
 bool InjectedBundleNodeHandle::isAutoFillAvailable() const
@@ -309,7 +325,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
 
-    return downcast<HTMLInputElement>(m_node.get()).isAutoFillAvailable();
+    return downcast<HTMLInputElement>(*m_node).isAutoFillAvailable();
 }
 
 void InjectedBundleNodeHandle::setAutoFillAvailable(bool autoFillAvailable)
@@ -317,7 +333,7 @@
     if (!is<HTMLInputElement>(m_node))
         return;
 
-    downcast<HTMLInputElement>(m_node.get()).setAutoFillAvailable(autoFillAvailable);
+    downcast<HTMLInputElement>(*m_node).setAutoFillAvailable(autoFillAvailable);
 }
 
 IntRect InjectedBundleNodeHandle::htmlInputElementAutoFillButtonBounds()
@@ -325,7 +341,7 @@
     if (!is<HTMLInputElement>(m_node))
         return IntRect();
 
-    auto autoFillButton = downcast<HTMLInputElement>(m_node.get()).autoFillButtonElement();
+    auto autoFillButton = downcast<HTMLInputElement>(*m_node).autoFillButtonElement();
     if (!autoFillButton)
         return IntRect();
 
@@ -337,7 +353,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
 
-    return downcast<HTMLInputElement>(m_node.get()).lastChangeWasUserEdit();
+    return downcast<HTMLInputElement>(*m_node).lastChangeWasUserEdit();
 }
 
 bool InjectedBundleNodeHandle::htmlTextAreaElementLastChangeWasUserEdit()
@@ -345,7 +361,7 @@
     if (!is<HTMLTextAreaElement>(m_node))
         return false;
 
-    return downcast<HTMLTextAreaElement>(m_node.get()).lastChangeWasUserEdit();
+    return downcast<HTMLTextAreaElement>(*m_node).lastChangeWasUserEdit();
 }
 
 bool InjectedBundleNodeHandle::isTextField() const
@@ -353,7 +369,7 @@
     if (!is<HTMLInputElement>(m_node))
         return false;
 
-    return downcast<HTMLInputElement>(m_node.get()).isTextField();
+    return downcast<HTMLInputElement>(*m_node).isTextField();
 }
 
 bool InjectedBundleNodeHandle::isSelectElement() const
@@ -366,15 +382,15 @@
     if (!is<HTMLTableCellElement>(m_node))
         return nullptr;
 
-    return getOrCreate(downcast<HTMLTableCellElement>(m_node.get()).cellAbove());
+    return getOrCreate(downcast<HTMLTableCellElement>(*m_node).cellAbove());
 }
 
 RefPtr<WebFrame> InjectedBundleNodeHandle::documentFrame()
 {
-    if (!m_node->isDocumentNode())
+    if (!m_node || !m_node->isDocumentNode())
         return nullptr;
 
-    Frame* frame = downcast<Document>(m_node.get()).frame();
+    Frame* frame = downcast<Document>(*m_node).frame();
     if (!frame)
         return nullptr;
 
@@ -386,7 +402,7 @@
     if (!is<HTMLFrameElement>(m_node))
         return nullptr;
 
-    Frame* frame = downcast<HTMLFrameElement>(m_node.get()).contentFrame();
+    Frame* frame = downcast<HTMLFrameElement>(*m_node).contentFrame();
     if (!frame)
         return nullptr;
 
@@ -398,7 +414,7 @@
     if (!is<HTMLIFrameElement>(m_node))
         return nullptr;
 
-    Frame* frame = downcast<HTMLIFrameElement>(m_node.get()).contentFrame();
+    Frame* frame = downcast<HTMLIFrameElement>(*m_node).contentFrame();
     if (!frame)
         return nullptr;
 
@@ -405,4 +421,18 @@
     return WebFrame::fromCoreFrame(*frame);
 }
 
+void InjectedBundleNodeHandle::stop()
+{
+    // Invalidate handles to nodes inside documents that are about to be destroyed in order to prevent leaks.
+    if (m_node) {
+        domNodeHandleCache().remove(m_node.get());
+        m_node = nullptr;
+    }
+}
+
+const char* InjectedBundleNodeHandle::activeDOMObjectName() const
+{
+    return "InjectedBundleNodeHandle";
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.h (257502 => 257503)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.h	2020-02-26 20:05:05 UTC (rev 257502)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/DOM/InjectedBundleNodeHandle.h	2020-02-26 20:57:49 UTC (rev 257503)
@@ -28,6 +28,7 @@
 #include "APIObject.h"
 #include "ImageOptions.h"
 #include <_javascript_Core/JSBase.h>
+#include <WebCore/ActiveDOMObject.h>
 #include <wtf/Forward.h>
 #include <wtf/Optional.h>
 #include <wtf/RefPtr.h>
@@ -45,7 +46,7 @@
 class WebFrame;
 class WebImage;
 
-class InjectedBundleNodeHandle : public API::ObjectImpl<API::Object::Type::BundleNodeHandle> {
+class InjectedBundleNodeHandle : public API::ObjectImpl<API::Object::Type::BundleNodeHandle>, public WebCore::ActiveDOMObject {
 public:
     static RefPtr<InjectedBundleNodeHandle> getOrCreate(JSContextRef, JSObjectRef);
     static RefPtr<InjectedBundleNodeHandle> getOrCreate(WebCore::Node*);
@@ -56,7 +57,7 @@
     WebCore::Node* coreNode();
 
     // Convenience DOM Operations
-    Ref<InjectedBundleNodeHandle> document();
+    RefPtr<InjectedBundleNodeHandle> document();
 
     // Additional DOM Operations
     // Note: These should only be operations that are not exposed to _javascript_.
@@ -92,7 +93,11 @@
     static Ref<InjectedBundleNodeHandle> create(WebCore::Node&);
     InjectedBundleNodeHandle(WebCore::Node&);
 
-    Ref<WebCore::Node> m_node;
+    // ActiveDOMObject.
+    void stop() final;
+    const char* activeDOMObjectName() const final;
+
+    RefPtr<WebCore::Node> m_node;
 };
 
 } // namespace WebKit
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to