Bläddra i källkod

LibWeb: Null layout and paintable pointers of removed DOM::Node

When a node is removed from the DOM tree, its paintable needs to be
removed to ensure that it is not used to obtain sizes that are no
longer valid.

This change enables the ResizeObserver to send a notification if a node
is removed, as it should, because a removed node now has a size of zero

It should be okay to nullify pointers without concerning
parent/sibling/child relationships because the layout and paintable
trees will be rebuilt following any DOM mutation anyway.
Aliaksandr Kalenik 1 år sedan
förälder
incheckning
036cd9b2dd

+ 2 - 0
Tests/LibWeb/Text/expected/ResizeObserver/notify-if-node-is-removed.txt

@@ -0,0 +1,2 @@
+    Size changed: 200px x 200px
+Size changed: 0px x 0px

+ 2 - 0
Tests/LibWeb/Text/expected/ResizeObserver/notify-if-parent-is-removed.txt

@@ -0,0 +1,2 @@
+    Size changed: 200px x 200px
+Size changed: 0px x 0px

+ 46 - 0
Tests/LibWeb/Text/input/ResizeObserver/notify-if-node-is-removed.html

@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+    <style>
+        #box {
+            width: 200px;
+            height: 200px;
+            background-color: lightblue;
+        }
+    </style>
+</head>
+<body>
+    <div id="box"></div>
+</body>
+<script src="../include.js"></script>
+<script>
+    asyncTest(async done => {
+        const box = document.getElementById("box");
+
+        let resolve = null;
+        function createResizeObserverPromise() {
+            return new Promise(r => {
+                resolve = r;
+            });
+        }
+
+        const resizeObserver = new ResizeObserver(entries => {
+            for (let entry of entries) {
+                const { width, height } = entry.contentRect;
+                println(`Size changed: ${width}px x ${height}px`);
+            }
+
+            if (resolve) resolve();
+        });
+
+        let observerCallbackInvocation = createResizeObserverPromise();
+        resizeObserver.observe(box);
+        await observerCallbackInvocation;
+
+        box.remove();
+
+        observerCallbackInvocation = createResizeObserverPromise();
+        await observerCallbackInvocation;
+
+        done();
+    });
+</script>

+ 46 - 0
Tests/LibWeb/Text/input/ResizeObserver/notify-if-parent-is-removed.html

@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+    <style>
+        #box {
+            width: 200px;
+            height: 200px;
+            background-color: lightblue;
+        }
+    </style>
+</head>
+<body>
+    <div><div id="box"></div></div>
+</body>
+<script src="../include.js"></script>
+<script>
+    asyncTest(async done => {
+        const box = document.getElementById("box");
+
+        let resolve = null;
+        function createResizeObserverPromise() {
+            return new Promise(r => {
+                resolve = r;
+            });
+        }
+
+        const resizeObserver = new ResizeObserver(entries => {
+            for (let entry of entries) {
+                const { width, height } = entry.contentRect;
+                println(`Size changed: ${width}px x ${height}px`);
+            }
+
+            if (resolve) resolve();
+        });
+
+        let observerCallbackInvocation = createResizeObserverPromise();
+        resizeObserver.observe(box);
+        await observerCallbackInvocation;
+
+        box.parentElement.remove();
+
+        observerCallbackInvocation = createResizeObserverPromise();
+        await observerCallbackInvocation;
+
+        done();
+    });
+</script>

+ 6 - 0
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -974,6 +974,12 @@ void Node::inserted()
     set_needs_style_update(true);
 }
 
+void Node::removed_from(Node*)
+{
+    m_layout_node = nullptr;
+    m_paintable = nullptr;
+}
+
 ParentNode* Node::parent_or_shadow_host()
 {
     if (is<ShadowRoot>(*this))

+ 1 - 1
Userland/Libraries/LibWeb/DOM/Node.h

@@ -187,7 +187,7 @@ public:
     Element const* parent_element() const;
 
     virtual void inserted();
-    virtual void removed_from(Node*) { }
+    virtual void removed_from(Node*);
     virtual void children_changed() { }
     virtual void adopted_from(Document&) { }
     virtual void cloned(Node&, bool) {};