mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:12:45 +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()); | ||||
|     } | ||||
| 
 | ||||
|     Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase); | ||||
| 
 | ||||
|     if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { | ||||
|         auto content_rect = absolute_rect(); | ||||
| 
 | ||||
|  | @ -179,23 +177,6 @@ StackingContext* Box::enclosing_stacking_context() | |||
|     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() | ||||
| { | ||||
|     if (m_line_boxes.is_empty()) | ||||
|  |  | |||
|  | @ -104,7 +104,6 @@ public: | |||
| 
 | ||||
|     void set_containing_line_box_fragment(LineBoxFragment&); | ||||
| 
 | ||||
|     bool establishes_stacking_context() const; | ||||
|     StackingContext* stacking_context() { 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); } | ||||
|  |  | |||
|  | @ -56,18 +56,7 @@ void InitialContainingBlockBox::paint_document_background(PaintContext& context) | |||
| void InitialContainingBlockBox::paint_all_phases(PaintContext& context) | ||||
| { | ||||
|     paint_document_background(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); | ||||
|     stacking_context()->paint(context); | ||||
| } | ||||
| 
 | ||||
| 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()); } | ||||
| 
 | ||||
|     void paint_all_phases(PaintContext&); | ||||
|     virtual void paint(PaintContext&, PaintPhase) override; | ||||
| 
 | ||||
|     void paint_document_background(PaintContext&); | ||||
| 
 | ||||
|  |  | |||
|  | @ -67,6 +67,18 @@ const BlockBox* Node::containing_block() const | |||
|     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) | ||||
| { | ||||
|     if (!is_visible()) | ||||
|  |  | |||
|  | @ -112,6 +112,8 @@ public: | |||
|     const BlockBox* containing_block() const; | ||||
|     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; | ||||
| 
 | ||||
|     const Gfx::Font& font() const; | ||||
|  |  | |||
|  | @ -22,24 +22,80 @@ StackingContext::StackingContext(Box& box, StackingContext* parent) | |||
|         m_parent->m_children.append(this); | ||||
| 
 | ||||
|         // 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) { | ||||
|             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)) { | ||||
|         m_box.paint(context, phase); | ||||
|     } else { | ||||
|         // NOTE: InitialContainingBlockBox::paint() merely calls StackingContext::paint()
 | ||||
|         //       so we call its base class instead.
 | ||||
|         downcast<InitialContainingBlockBox>(m_box).BlockBox::paint(context, phase); | ||||
|     } | ||||
|     box.for_each_child([&](auto& child) { | ||||
|         switch (phase) { | ||||
|         case StackingContextPaintPhase::BackgroundAndBorders: | ||||
|             if (!child.is_floating() && !child.is_positioned()) { | ||||
|                 child.paint(context, PaintPhase::Background); | ||||
|                 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) { | ||||
|         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 | ||||
|  |  | |||
|  | @ -18,7 +18,15 @@ public: | |||
|     StackingContext* parent() { 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; | ||||
| 
 | ||||
|     void dump(int indent = 0) const; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Egor Ananyin
						Egor Ananyin