1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h"
6 
7 #include "third_party/blink/renderer/core/layout/layout_fieldset.h"
8 #include "third_party/blink/renderer/core/layout/layout_object_factory.h"
9 
10 namespace blink {
11 
LayoutNGFieldset(Element * element)12 LayoutNGFieldset::LayoutNGFieldset(Element* element)
13     : LayoutNGBlockFlow(element) {
14   SetChildrenInline(false);
15 }
16 
AddChild(LayoutObject * new_child,LayoutObject * before_child)17 void LayoutNGFieldset::AddChild(LayoutObject* new_child,
18                                 LayoutObject* before_child) {
19   LayoutBlock* fieldset_content = To<LayoutBlock>(FirstChild());
20   if (!fieldset_content) {
21     // We wrap everything inside an anonymous child, which will take care of the
22     // fieldset contents. This parent will only be responsible for the fieldset
23     // border and the rendered legend, if there is one. Everything else will be
24     // done by the anonymous child. This includes display type, multicol,
25     // scrollbars, and even padding. Note that the rendered legend (if any) will
26     // also be a child of the anonymous object, although it'd be more natural to
27     // have it as the first child of this object. The reason is that our layout
28     // object tree builder cannot handle such discrepancies between DOM tree and
29     // layout tree. Inserting anonymous wrappers is one thing (that is
30     // supported). Removing it from its actual DOM siblings and putting it
31     // elsewhere, on the other hand, does not work well.
32 
33     // TODO(crbug.com/875235): Consider other display types not mentioned in the
34     // spec (ex. EDisplay::kLayoutCustom).
35     EDisplay display = EDisplay::kFlowRoot;
36     switch (StyleRef().Display()) {
37       case EDisplay::kFlex:
38       case EDisplay::kInlineFlex:
39         display = EDisplay::kFlex;
40         break;
41       case EDisplay::kGrid:
42       case EDisplay::kInlineGrid:
43         display = EDisplay::kGrid;
44         break;
45       default:
46         break;
47     }
48 
49     fieldset_content =
50         LayoutBlock::CreateAnonymousWithParentAndDisplay(this, display);
51     LayoutBox::AddChild(fieldset_content);
52   }
53   fieldset_content->AddChild(new_child, before_child);
54 }
55 
56 // TODO(mstensho): Should probably remove the anonymous child if it becomes
57 // childless. While an empty anonymous child should have no effect, it doesn't
58 // seem right to leave it around.
59 
UpdateAnonymousChildStyle(const LayoutObject *,ComputedStyle & child_style) const60 void LayoutNGFieldset::UpdateAnonymousChildStyle(
61     const LayoutObject*,
62     ComputedStyle& child_style) const {
63   // Inherit all properties listed here:
64   // https://html.spec.whatwg.org/C/#anonymous-fieldset-content-box
65 
66   child_style.SetAlignContent(StyleRef().AlignContent());
67   child_style.SetAlignItems(StyleRef().AlignItems());
68 
69   child_style.SetBorderBottomLeftRadius(StyleRef().BorderBottomLeftRadius());
70   child_style.SetBorderBottomRightRadius(StyleRef().BorderBottomRightRadius());
71   child_style.SetBorderTopLeftRadius(StyleRef().BorderTopLeftRadius());
72   child_style.SetBorderTopRightRadius(StyleRef().BorderTopRightRadius());
73 
74   child_style.SetPaddingTop(StyleRef().PaddingTop());
75   child_style.SetPaddingRight(StyleRef().PaddingRight());
76   child_style.SetPaddingBottom(StyleRef().PaddingBottom());
77   child_style.SetPaddingLeft(StyleRef().PaddingLeft());
78 
79   if (StyleRef().SpecifiesColumns()) {
80     child_style.SetColumnCount(StyleRef().ColumnCount());
81     child_style.SetColumnWidth(StyleRef().ColumnWidth());
82   } else {
83     child_style.SetHasAutoColumnCount();
84     child_style.SetHasAutoColumnWidth();
85   }
86   child_style.SetColumnGap(StyleRef().ColumnGap());
87   child_style.SetColumnFill(StyleRef().GetColumnFill());
88   child_style.SetColumnRuleColor(StyleColor(
89       LayoutObject::ResolveColor(StyleRef(), GetCSSPropertyColumnRuleColor())));
90   child_style.SetColumnRuleStyle(StyleRef().ColumnRuleStyle());
91   child_style.SetColumnRuleWidth(StyleRef().ColumnRuleWidth());
92 
93   child_style.SetFlexDirection(StyleRef().FlexDirection());
94   child_style.SetFlexWrap(StyleRef().FlexWrap());
95 
96   child_style.SetGridAutoColumns(StyleRef().GridAutoColumns());
97   child_style.SetGridAutoFlow(StyleRef().GetGridAutoFlow());
98   child_style.SetGridAutoRows(StyleRef().GridAutoRows());
99   child_style.SetGridColumnEnd(StyleRef().GridColumnEnd());
100   child_style.SetGridColumnStart(StyleRef().GridColumnStart());
101   child_style.SetGridRowEnd(StyleRef().GridRowEnd());
102   child_style.SetGridRowStart(StyleRef().GridRowStart());
103   child_style.SetGridTemplateColumns(StyleRef().GridTemplateColumns());
104   child_style.SetGridTemplateRows(StyleRef().GridTemplateRows());
105   child_style.SetNamedGridArea(StyleRef().NamedGridArea());
106   child_style.SetNamedGridAreaColumnCount(
107       StyleRef().NamedGridAreaColumnCount());
108   child_style.SetNamedGridAreaRowCount(StyleRef().NamedGridAreaRowCount());
109   child_style.SetRowGap(StyleRef().RowGap());
110 
111   child_style.SetJustifyContent(StyleRef().JustifyContent());
112   child_style.SetJustifyItems(StyleRef().JustifyItems());
113   child_style.SetOverflowX(StyleRef().OverflowX());
114   child_style.SetOverflowY(StyleRef().OverflowY());
115   child_style.SetUnicodeBidi(StyleRef().GetUnicodeBidi());
116 
117   // If the FIELDSET is an OOF container, the anonymous content box should be
118   // an OOF container to steal OOF objects under the FIELDSET.
119   if (CanContainFixedPositionObjects())
120     child_style.SetContain(kContainsPaint);
121   else if (StyleRef().CanContainAbsolutePositionObjects())
122     child_style.SetPosition(EPosition::kRelative);
123 }
124 
IsOfType(LayoutObjectType type) const125 bool LayoutNGFieldset::IsOfType(LayoutObjectType type) const {
126   return type == kLayoutObjectNGFieldset || LayoutNGBlockFlow::IsOfType(type);
127 }
128 
InvalidatePaint(const PaintInvalidatorContext & context) const129 void LayoutNGFieldset::InvalidatePaint(
130     const PaintInvalidatorContext& context) const {
131   // Fieldset's box decoration painting depends on the legend geometry.
132   const LayoutBox* legend_box = LayoutFieldset::FindInFlowLegend(*this);
133   if (legend_box && legend_box->ShouldCheckGeometryForPaintInvalidation()) {
134     GetMutableForPainting().SetShouldDoFullPaintInvalidation(
135         PaintInvalidationReason::kGeometry);
136   }
137   LayoutNGBlockFlow::InvalidatePaint(context);
138 }
139 
BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect & local_rect) const140 bool LayoutNGFieldset::BackgroundIsKnownToBeOpaqueInRect(
141     const PhysicalRect& local_rect) const {
142   // If the field set has a legend, then it probably does not completely fill
143   // its background.
144   if (LayoutFieldset::FindInFlowLegend(*this))
145     return false;
146 
147   return LayoutBlockFlow::BackgroundIsKnownToBeOpaqueInRect(local_rect);
148 }
149 
HitTestChildren(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction hit_test_action)150 bool LayoutNGFieldset::HitTestChildren(HitTestResult& result,
151                                        const HitTestLocation& hit_test_location,
152                                        const PhysicalOffset& accumulated_offset,
153                                        HitTestAction hit_test_action) {
154   if (LayoutNGBlockFlow::HitTestChildren(result, hit_test_location,
155                                          accumulated_offset, hit_test_action))
156     return true;
157 
158   DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled());
159   LayoutBox* legend = LayoutFieldset::FindInFlowLegend(*this);
160   if (!legend || legend->HasSelfPaintingLayer() || legend->IsColumnSpanAll())
161     return false;
162   if (legend->NodeAtPoint(result, hit_test_location,
163                           accumulated_offset + legend->PhysicalLocation(this),
164                           hit_test_action == kHitTestChildBlockBackgrounds
165                               ? kHitTestChildBlockBackground
166                               : hit_test_action)) {
167     UpdateHitTestResult(result, hit_test_location.Point() - accumulated_offset);
168     return true;
169   }
170   return false;
171 }
172 
ScrollWidth() const173 LayoutUnit LayoutNGFieldset::ScrollWidth() const {
174   const LayoutObject* child = FirstChild();
175   if (child && child->IsAnonymous())
176     return To<LayoutBox>(child)->ScrollWidth();
177   return LayoutNGBlockFlow::ScrollWidth();
178 }
179 
ScrollHeight() const180 LayoutUnit LayoutNGFieldset::ScrollHeight() const {
181   const LayoutObject* child = FirstChild();
182   if (child && child->IsAnonymous())
183     return To<LayoutBox>(child)->ScrollHeight();
184   return LayoutNGBlockFlow::ScrollHeight();
185 }
186 
187 }  // namespace blink
188