Sfoglia il codice sorgente

LibWeb: Ignore svg elements outside of <svg> when building layout tree

An svg layout element without a `SVGSVGElement` ancestor caused a failed
assertion before, because the svg context does not exist when `paint()`
is called
K-Adam 4 anni fa
parent
commit
e8d10fb429

+ 3 - 0
Userland/Libraries/LibWeb/DOM/Node.h

@@ -69,6 +69,9 @@ public:
     bool is_parent_node() const { return is_element() || is_document() || is_document_fragment(); }
     bool is_slottable() const { return is_element() || is_text(); }
 
+    virtual bool requires_svg_container() const { return false; }
+    virtual bool is_svg_container() const { return false; }
+
     // NOTE: This is intended for the JS bindings.
     u16 node_type() const { return (u16)m_type; }
 

+ 15 - 4
Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp

@@ -4,6 +4,8 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/Optional.h>
+#include <AK/TemporaryChange.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/ParentNode.h>
@@ -70,12 +72,20 @@ static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent
     return layout_parent;
 }
 
-void TreeBuilder::create_layout_tree(DOM::Node& dom_node)
+void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& context)
 {
     // If the parent doesn't have a layout node, we don't need one either.
     if (dom_node.parent_or_shadow_host() && !dom_node.parent_or_shadow_host()->layout_node())
         return;
 
+    Optional<TemporaryChange<bool>> has_svg_root_change;
+
+    if (dom_node.is_svg_container()) {
+        has_svg_root_change.emplace(context.has_svg_root, true);
+    } else if (dom_node.requires_svg_container() && !context.has_svg_root) {
+        return;
+    }
+
     auto layout_node = dom_node.create_layout_node();
     if (!layout_node)
         return;
@@ -108,9 +118,9 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node)
     if ((dom_node.has_children() || shadow_root) && layout_node->can_have_children()) {
         push_parent(verify_cast<NodeWithStyle>(*layout_node));
         if (shadow_root)
-            create_layout_tree(*shadow_root);
+            create_layout_tree(*shadow_root, context);
         verify_cast<DOM::ParentNode>(dom_node).for_each_child([&](auto& dom_child) {
-            create_layout_tree(dom_child);
+            create_layout_tree(dom_child, context);
         });
         pop_parent();
     }
@@ -124,7 +134,8 @@ RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node)
             m_parent_stack.prepend(verify_cast<NodeWithStyle>(ancestor));
     }
 
-    create_layout_tree(dom_node);
+    Context context;
+    create_layout_tree(dom_node, context);
 
     if (auto* root = dom_node.document().layout_node())
         fixup_tables(*root);

+ 5 - 1
Userland/Libraries/LibWeb/Layout/TreeBuilder.h

@@ -19,7 +19,11 @@ public:
     RefPtr<Layout::Node> build(DOM::Node&);
 
 private:
-    void create_layout_tree(DOM::Node&);
+    struct Context {
+        bool has_svg_root = false;
+    };
+
+    void create_layout_tree(DOM::Node&, Context&);
 
     void push_parent(Layout::NodeWithStyle& node) { m_parent_stack.append(&node); }
     void pop_parent() { m_parent_stack.take_last(); }

+ 2 - 0
Userland/Libraries/LibWeb/SVG/SVGElement.h

@@ -14,6 +14,8 @@ class SVGElement : public DOM::Element {
 public:
     using WrapperType = Bindings::SVGElementWrapper;
 
+    virtual bool requires_svg_container() const override { return true; }
+
 protected:
     SVGElement(DOM::Document&, QualifiedName);
 };

+ 3 - 0
Userland/Libraries/LibWeb/SVG/SVGSVGElement.h

@@ -22,6 +22,9 @@ public:
     unsigned width() const;
     unsigned height() const;
 
+    virtual bool requires_svg_container() const override { return false; }
+    virtual bool is_svg_container() const override { return true; }
+
 private:
     RefPtr<Gfx::Bitmap> m_bitmap;
 };