mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 00:42:44 +00:00 
			
		
		
		
	LibWeb: Make painting order more spec-compliant
Now our painting order inside stacking contexts is closer to the algorithm specified by CSS 2.1 (see section 9.9 and Appendix E)
This commit is contained in:
		
							parent
							
								
									33af7075e7
								
							
						
					
					
						commit
						d2b6148787
					
				
					 8 changed files with 89 additions and 43 deletions
				
			
		|  | @ -42,8 +42,6 @@ void Box::paint(PaintContext& context, PaintPhase phase) | ||||||
|         Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values()); |         Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase); |  | ||||||
| 
 |  | ||||||
|     if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { |     if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { | ||||||
|         auto content_rect = absolute_rect(); |         auto content_rect = absolute_rect(); | ||||||
| 
 | 
 | ||||||
|  | @ -179,23 +177,6 @@ StackingContext* Box::enclosing_stacking_context() | ||||||
|     VERIFY_NOT_REACHED(); |     VERIFY_NOT_REACHED(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Box::establishes_stacking_context() const |  | ||||||
| { |  | ||||||
|     if (!has_style()) |  | ||||||
|         return false; |  | ||||||
|     if (dom_node() == document().root()) |  | ||||||
|         return true; |  | ||||||
|     auto position = computed_values().position(); |  | ||||||
|     auto z_index = computed_values().z_index(); |  | ||||||
|     if (position == CSS::Position::Absolute || position == CSS::Position::Relative) { |  | ||||||
|         if (z_index.has_value()) |  | ||||||
|             return true; |  | ||||||
|     } |  | ||||||
|     if (position == CSS::Position::Fixed || position == CSS::Position::Sticky) |  | ||||||
|         return true; |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| LineBox& Box::ensure_last_line_box() | LineBox& Box::ensure_last_line_box() | ||||||
| { | { | ||||||
|     if (m_line_boxes.is_empty()) |     if (m_line_boxes.is_empty()) | ||||||
|  |  | ||||||
|  | @ -104,7 +104,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     void set_containing_line_box_fragment(LineBoxFragment&); |     void set_containing_line_box_fragment(LineBoxFragment&); | ||||||
| 
 | 
 | ||||||
|     bool establishes_stacking_context() const; |  | ||||||
|     StackingContext* stacking_context() { return m_stacking_context; } |     StackingContext* stacking_context() { return m_stacking_context; } | ||||||
|     const StackingContext* stacking_context() const { return m_stacking_context; } |     const StackingContext* stacking_context() const { return m_stacking_context; } | ||||||
|     void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); } |     void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); } | ||||||
|  |  | ||||||
|  | @ -56,18 +56,7 @@ void InitialContainingBlockBox::paint_document_background(PaintContext& context) | ||||||
| void InitialContainingBlockBox::paint_all_phases(PaintContext& context) | void InitialContainingBlockBox::paint_all_phases(PaintContext& context) | ||||||
| { | { | ||||||
|     paint_document_background(context); |     paint_document_background(context); | ||||||
| 
 |     stacking_context()->paint(context); | ||||||
|     paint(context, PaintPhase::Background); |  | ||||||
|     paint(context, PaintPhase::Border); |  | ||||||
|     paint(context, PaintPhase::Foreground); |  | ||||||
|     if (context.has_focus()) |  | ||||||
|         paint(context, PaintPhase::FocusOutline); |  | ||||||
|     paint(context, PaintPhase::Overlay); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InitialContainingBlockBox::paint(PaintContext& context, PaintPhase phase) |  | ||||||
| { |  | ||||||
|     stacking_context()->paint(context, phase); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const | HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const | ||||||
|  |  | ||||||
|  | @ -19,7 +19,6 @@ public: | ||||||
|     const DOM::Document& dom_node() const { return static_cast<const DOM::Document&>(*Node::dom_node()); } |     const DOM::Document& dom_node() const { return static_cast<const DOM::Document&>(*Node::dom_node()); } | ||||||
| 
 | 
 | ||||||
|     void paint_all_phases(PaintContext&); |     void paint_all_phases(PaintContext&); | ||||||
|     virtual void paint(PaintContext&, PaintPhase) override; |  | ||||||
| 
 | 
 | ||||||
|     void paint_document_background(PaintContext&); |     void paint_document_background(PaintContext&); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -67,6 +67,18 @@ const BlockBox* Node::containing_block() const | ||||||
|     return nearest_block_ancestor(); |     return nearest_block_ancestor(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool Node::establishes_stacking_context() const | ||||||
|  | { | ||||||
|  |     if (!has_style()) | ||||||
|  |         return false; | ||||||
|  |     if (dom_node() == document().root()) | ||||||
|  |         return true; | ||||||
|  |     auto position = computed_values().position(); | ||||||
|  |     if (position == CSS::Position::Absolute || position == CSS::Position::Relative || position == CSS::Position::Fixed || position == CSS::Position::Sticky) | ||||||
|  |         return true; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Node::paint(PaintContext& context, PaintPhase phase) | void Node::paint(PaintContext& context, PaintPhase phase) | ||||||
| { | { | ||||||
|     if (!is_visible()) |     if (!is_visible()) | ||||||
|  |  | ||||||
|  | @ -112,6 +112,8 @@ public: | ||||||
|     const BlockBox* containing_block() const; |     const BlockBox* containing_block() const; | ||||||
|     BlockBox* containing_block() { return const_cast<BlockBox*>(const_cast<const Node*>(this)->containing_block()); } |     BlockBox* containing_block() { return const_cast<BlockBox*>(const_cast<const Node*>(this)->containing_block()); } | ||||||
| 
 | 
 | ||||||
|  |     bool establishes_stacking_context() const; | ||||||
|  | 
 | ||||||
|     bool can_contain_boxes_with_position_absolute() const; |     bool can_contain_boxes_with_position_absolute() const; | ||||||
| 
 | 
 | ||||||
|     const Gfx::Font& font() const; |     const Gfx::Font& font() const; | ||||||
|  |  | ||||||
|  | @ -22,24 +22,80 @@ StackingContext::StackingContext(Box& box, StackingContext* parent) | ||||||
|         m_parent->m_children.append(this); |         m_parent->m_children.append(this); | ||||||
| 
 | 
 | ||||||
|         // FIXME: Don't sort on every append..
 |         // FIXME: Don't sort on every append..
 | ||||||
|  |         // FIXME: Apparently this also breaks tree order inside layers
 | ||||||
|         quick_sort(m_parent->m_children, [](auto& a, auto& b) { |         quick_sort(m_parent->m_children, [](auto& a, auto& b) { | ||||||
|             return a->m_box.computed_values().z_index().value_or(0) < b->m_box.computed_values().z_index().value_or(0); |             return a->m_box.computed_values().z_index().value_or(0) < b->m_box.computed_values().z_index().value_or(0); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StackingContext::paint(PaintContext& context, PaintPhase phase) | void StackingContext::paint_descendants(PaintContext& context, Node& box, StackingContextPaintPhase phase) | ||||||
| { | { | ||||||
|     if (!is<InitialContainingBlockBox>(m_box)) { |     box.for_each_child([&](auto& child) { | ||||||
|         m_box.paint(context, phase); |         switch (phase) { | ||||||
|     } else { |         case StackingContextPaintPhase::BackgroundAndBorders: | ||||||
|         // NOTE: InitialContainingBlockBox::paint() merely calls StackingContext::paint()
 |             if (!child.is_floating() && !child.is_positioned()) { | ||||||
|         //       so we call its base class instead.
 |                 child.paint(context, PaintPhase::Background); | ||||||
|         downcast<InitialContainingBlockBox>(m_box).BlockBox::paint(context, phase); |                 child.paint(context, PaintPhase::Border); | ||||||
|  |                 paint_descendants(context, child, phase); | ||||||
|             } |             } | ||||||
|  |             break; | ||||||
|  |         case StackingContextPaintPhase::Floats: | ||||||
|  |             if (!child.is_positioned()) { | ||||||
|  |                 if (child.is_floating()) { | ||||||
|  |                     child.paint(context, PaintPhase::Background); | ||||||
|  |                     child.paint(context, PaintPhase::Border); | ||||||
|  |                     paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders); | ||||||
|  |                 } | ||||||
|  |                 paint_descendants(context, child, phase); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case StackingContextPaintPhase::Foreground: | ||||||
|  |             if (!child.is_positioned()) { | ||||||
|  |                 child.paint(context, PaintPhase::Foreground); | ||||||
|  |                 child.before_children_paint(context, PaintPhase::Foreground); | ||||||
|  |                 paint_descendants(context, child, phase); | ||||||
|  |                 child.after_children_paint(context, PaintPhase::Foreground); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case StackingContextPaintPhase::FocusAndOverlay: | ||||||
|  |             if (context.has_focus()) { | ||||||
|  |                 child.paint(context, PaintPhase::FocusOutline); | ||||||
|  |             } | ||||||
|  |             child.paint(context, PaintPhase::Overlay); | ||||||
|  |             paint_descendants(context, child, phase); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StackingContext::paint(PaintContext& context) | ||||||
|  | { | ||||||
|  |     // For a more elaborate description of the algorithm, see CSS 2.1 Appendix E
 | ||||||
|  |     // Draw the background and borders for the context root (steps 1, 2)
 | ||||||
|  |     m_box.paint(context, PaintPhase::Background); | ||||||
|  |     m_box.paint(context, PaintPhase::Border); | ||||||
|  |     // Draw positioned descendants with negative z-indices (step 3)
 | ||||||
|     for (auto* child : m_children) { |     for (auto* child : m_children) { | ||||||
|         child->paint(context, phase); |         if (child->m_box.computed_values().z_index().has_value() && child->m_box.computed_values().z_index().value() < 0) | ||||||
|  |             child->paint(context); | ||||||
|     } |     } | ||||||
|  |     // Draw the background and borders for block-level children (step 4)
 | ||||||
|  |     paint_descendants(context, m_box, StackingContextPaintPhase::BackgroundAndBorders); | ||||||
|  |     // Draw the non-positioned floats (step 5)
 | ||||||
|  |     paint_descendants(context, m_box, StackingContextPaintPhase::Floats); | ||||||
|  |     // Draw inline content, replaced content, etc. (steps 6, 7)
 | ||||||
|  |     paint_descendants(context, m_box, StackingContextPaintPhase::Foreground); | ||||||
|  |     // Draw other positioned descendants (steps 8, 9)
 | ||||||
|  |     for (auto* child : m_children) { | ||||||
|  |         if (child->m_box.computed_values().z_index().has_value() && child->m_box.computed_values().z_index().value() < 0) | ||||||
|  |             continue; | ||||||
|  |         child->paint(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_box.paint(context, PaintPhase::FocusOutline); | ||||||
|  |     m_box.paint(context, PaintPhase::Overlay); | ||||||
|  |     paint_descendants(context, m_box, StackingContextPaintPhase::FocusAndOverlay); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestType type) const | HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestType type) const | ||||||
|  |  | ||||||
|  | @ -18,7 +18,15 @@ public: | ||||||
|     StackingContext* parent() { return m_parent; } |     StackingContext* parent() { return m_parent; } | ||||||
|     const StackingContext* parent() const { return m_parent; } |     const StackingContext* parent() const { return m_parent; } | ||||||
| 
 | 
 | ||||||
|     void paint(PaintContext&, PaintPhase); |     enum class StackingContextPaintPhase { | ||||||
|  |         BackgroundAndBorders, | ||||||
|  |         Floats, | ||||||
|  |         Foreground, | ||||||
|  |         FocusAndOverlay, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     void paint_descendants(PaintContext&, Node&, StackingContextPaintPhase); | ||||||
|  |     void paint(PaintContext&); | ||||||
|     HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const; |     HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const; | ||||||
| 
 | 
 | ||||||
|     void dump(int indent = 0) const; |     void dump(int indent = 0) const; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Egor Ananyin
						Egor Ananyin