1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "third_party/blink/renderer/core/layout/line/inline_box.h"
21 
22 #include "base/allocator/partition_allocator/partition_alloc.h"
23 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
24 #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
25 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
26 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
27 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
28 #include "third_party/blink/renderer/core/layout/line/inline_flow_box.h"
29 #include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
30 #include "third_party/blink/renderer/core/paint/block_painter.h"
31 #include "third_party/blink/renderer/core/paint/paint_info.h"
32 #include "third_party/blink/renderer/platform/fonts/font_metrics.h"
33 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
34 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
35 
36 namespace blink {
37 
38 class LayoutObject;
39 
40 struct SameSizeAsInlineBox : DisplayItemClient {
41   ~SameSizeAsInlineBox() override = default;
42   void* a[4];
43   LayoutPoint b;
44   LayoutUnit c;
45   uint32_t bitfields;
46 #if DCHECK_IS_ON()
47   bool f;
48 #endif
49 };
50 
51 ASSERT_SIZE(InlineBox, SameSizeAsInlineBox);
52 
53 #if DCHECK_IS_ON()
~InlineBox()54 InlineBox::~InlineBox() {
55   if (!has_bad_parent_ && parent_)
56     parent_->SetHasBadChildList();
57 }
58 #endif
59 
60 DISABLE_CFI_PERF
Destroy()61 void InlineBox::Destroy() {
62   // We do not need to issue invalidations if the page is being destroyed
63   // since these objects will never be repainted.
64   if (!line_layout_item_.DocumentBeingDestroyed()) {
65     SetLineLayoutItemShouldDoFullPaintInvalidationIfNeeded();
66 
67     // TODO(crbug.com/619630): Make this fast.
68     line_layout_item_.SlowSetPaintingLayerNeedsRepaint();
69   }
70 
71   delete this;
72 }
73 
Remove(MarkLineBoxes mark_line_boxes)74 void InlineBox::Remove(MarkLineBoxes mark_line_boxes) {
75   if (Parent())
76     Parent()->RemoveChild(this, mark_line_boxes);
77 }
78 
operator new(size_t sz)79 void* InlineBox::operator new(size_t sz) {
80   return WTF::Partitions::LayoutPartition()->Alloc(
81       sz, WTF_HEAP_PROFILER_TYPE_NAME(InlineBox));
82 }
83 
operator delete(void * ptr)84 void InlineBox::operator delete(void* ptr) {
85   WTF::Partitions::LayoutPartition()->Free(ptr);
86 }
87 
BoxName() const88 const char* InlineBox::BoxName() const {
89   return "InlineBox";
90 }
91 
DebugName() const92 String InlineBox::DebugName() const {
93   return BoxName();
94 }
95 
OwnerNodeId() const96 DOMNodeId InlineBox::OwnerNodeId() const {
97   return GetLineLayoutItem().GetNodeForOwnerNodeId()
98              ? DOMNodeIds::IdForNode(
99                    GetLineLayoutItem().GetNodeForOwnerNodeId())
100              : kInvalidDOMNodeId;
101 }
102 
103 #if DCHECK_IS_ON()
ShowTreeForThis() const104 void InlineBox::ShowTreeForThis() const {
105   GetLineLayoutItem().ShowTreeForThis();
106 }
107 
ShowLineTreeForThis() const108 void InlineBox::ShowLineTreeForThis() const {
109   const LayoutBlock* containing_block =
110       LineLayoutAPIShim::LayoutObjectFrom(GetLineLayoutItem())
111           ->InclusiveContainingBlock();
112   if (containing_block) {
113     LineLayoutBox(const_cast<LayoutBlock*>(containing_block))
114         .ShowLineTreeAndMark(this, "*");
115   }
116 }
117 
DumpLineTreeAndMark(StringBuilder & string_builder,const InlineBox * marked_box1,const char * marked_label1,const InlineBox * marked_box2,const char * marked_label2,const LayoutObject * obj,int depth) const118 void InlineBox::DumpLineTreeAndMark(StringBuilder& string_builder,
119                                     const InlineBox* marked_box1,
120                                     const char* marked_label1,
121                                     const InlineBox* marked_box2,
122                                     const char* marked_label2,
123                                     const LayoutObject* obj,
124                                     int depth) const {
125   StringBuilder string_inlinebox;
126   if (this == marked_box1)
127     string_inlinebox.Append(marked_label1);
128   if (this == marked_box2)
129     string_inlinebox.Append(marked_label2);
130   if (GetLineLayoutItem().IsEqual(obj))
131     string_inlinebox.Append('*');
132   while ((int)string_inlinebox.length() < (depth * 2))
133     string_inlinebox.Append(' ');
134 
135   DumpBox(string_inlinebox);
136   string_builder.Append('\n');
137   string_builder.Append(string_inlinebox);
138 }
139 
DumpBox(StringBuilder & string_inlinebox) const140 void InlineBox::DumpBox(StringBuilder& string_inlinebox) const {
141   string_inlinebox.AppendFormat("%s %p", BoxName(), this);
142   while (string_inlinebox.length() < kShowTreeCharacterOffset)
143     string_inlinebox.Append(' ');
144   string_inlinebox.AppendFormat(
145       "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i",
146       GetLineLayoutItem().DecoratedName().Ascii().c_str(),
147       GetLineLayoutItem().DebugPointer(), X().ToFloat(), Y().ToFloat(),
148       Width().ToFloat(), Height().ToFloat(),
149       BaselinePosition(kAlphabeticBaseline).ToInt(),
150       BaselinePosition(kIdeographicBaseline).ToInt());
151 }
152 #endif  // DCHECK_IS_ON()
153 
LogicalHeight() const154 LayoutUnit InlineBox::LogicalHeight() const {
155   if (HasVirtualLogicalHeight())
156     return VirtualLogicalHeight();
157 
158   const SimpleFontData* font_data =
159       GetLineLayoutItem().Style(IsFirstLineStyle())->GetFont().PrimaryFont();
160   if (GetLineLayoutItem().IsText()) {
161     DCHECK(font_data);
162     return bitfields_.IsText() && font_data
163                ? LayoutUnit(font_data->GetFontMetrics().Height())
164                : LayoutUnit();
165   }
166   if (GetLineLayoutItem().IsBox() && Parent()) {
167     return IsHorizontal() ? LineLayoutBox(GetLineLayoutItem()).Size().Height()
168                           : LineLayoutBox(GetLineLayoutItem()).Size().Width();
169   }
170 
171   DCHECK(IsInlineFlowBox());
172   LineLayoutBoxModel flow_object = BoxModelObject();
173   DCHECK(font_data);
174   LayoutUnit result(font_data ? font_data->GetFontMetrics().Height() : 0);
175   if (Parent())
176     result += flow_object.BorderAndPaddingLogicalHeight();
177   return result;
178 }
179 
BaselinePosition(FontBaseline baseline_type) const180 LayoutUnit InlineBox::BaselinePosition(FontBaseline baseline_type) const {
181   return BoxModelObject().BaselinePosition(
182       baseline_type, bitfields_.FirstLine(),
183       IsHorizontal() ? kHorizontalLine : kVerticalLine,
184       kPositionOnContainingLine);
185 }
186 
LineHeight() const187 LayoutUnit InlineBox::LineHeight() const {
188   return BoxModelObject().LineHeight(
189       bitfields_.FirstLine(), IsHorizontal() ? kHorizontalLine : kVerticalLine,
190       kPositionOnContainingLine);
191 }
192 
CaretMinOffset() const193 int InlineBox::CaretMinOffset() const {
194   return GetLineLayoutItem().CaretMinOffset();
195 }
196 
CaretMaxOffset() const197 int InlineBox::CaretMaxOffset() const {
198   return GetLineLayoutItem().CaretMaxOffset();
199 }
200 
DirtyLineBoxes()201 void InlineBox::DirtyLineBoxes() {
202   MarkDirty();
203   for (InlineFlowBox* curr = Parent(); curr && !curr->IsDirty();
204        curr = curr->Parent())
205     curr->MarkDirty();
206 }
207 
DeleteLine()208 void InlineBox::DeleteLine() {
209   if (!bitfields_.Extracted() && GetLineLayoutItem().IsBox())
210     LineLayoutBox(GetLineLayoutItem()).SetInlineBoxWrapper(nullptr);
211   Destroy();
212 }
213 
ExtractLine()214 void InlineBox::ExtractLine() {
215   bitfields_.SetExtracted(true);
216   if (GetLineLayoutItem().IsBox())
217     LineLayoutBox(GetLineLayoutItem()).SetInlineBoxWrapper(nullptr);
218 }
219 
AttachLine()220 void InlineBox::AttachLine() {
221   bitfields_.SetExtracted(false);
222   if (GetLineLayoutItem().IsBox())
223     LineLayoutBox(GetLineLayoutItem()).SetInlineBoxWrapper(this);
224 }
225 
Move(const LayoutSize & delta)226 void InlineBox::Move(const LayoutSize& delta) {
227   location_.Move(delta);
228 
229   if (GetLineLayoutItem().IsAtomicInlineLevel())
230     LineLayoutBox(GetLineLayoutItem()).Move(delta.Width(), delta.Height());
231 
232   SetLineLayoutItemShouldDoFullPaintInvalidationIfNeeded();
233 }
234 
Paint(const PaintInfo & paint_info,const PhysicalOffset &,LayoutUnit,LayoutUnit) const235 void InlineBox::Paint(const PaintInfo& paint_info,
236                       const PhysicalOffset&,
237                       LayoutUnit,
238                       LayoutUnit) const {
239   BlockPainter::PaintInlineBox(*this, paint_info);
240 }
241 
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,LayoutUnit,LayoutUnit)242 bool InlineBox::NodeAtPoint(HitTestResult& result,
243                             const HitTestLocation& hit_test_location,
244                             const PhysicalOffset& accumulated_offset,
245                             LayoutUnit /* lineTop */,
246                             LayoutUnit /* lineBottom */) {
247   // Hit test all phases of replaced elements atomically, as though the replaced
248   // element established its own stacking context. (See Appendix E.2, section
249   // 6.4 on inline block/table elements in the CSS2.1 specification.)
250   PhysicalOffset layout_item_accumulated_offset = accumulated_offset;
251   if (GetLineLayoutItem().IsBox()) {
252     layout_item_accumulated_offset +=
253         LineLayoutBox(GetLineLayoutItem()).PhysicalLocation();
254   }
255   return GetLineLayoutItem().HitTestAllPhases(result, hit_test_location,
256                                               layout_item_accumulated_offset);
257 }
258 
Root() const259 const RootInlineBox& InlineBox::Root() const {
260   if (parent_)
261     return parent_->Root();
262   DCHECK(IsRootInlineBox());
263   return static_cast<const RootInlineBox&>(*this);
264 }
265 
Root()266 RootInlineBox& InlineBox::Root() {
267   if (parent_)
268     return parent_->Root();
269   DCHECK(IsRootInlineBox());
270   return static_cast<RootInlineBox&>(*this);
271 }
272 
NextLeafChild() const273 InlineBox* InlineBox::NextLeafChild() const {
274   InlineBox* leaf = nullptr;
275   for (InlineBox* box = NextOnLine(); box && !leaf; box = box->NextOnLine())
276     leaf = box->IsLeaf() ? box : To<InlineFlowBox>(box)->FirstLeafChild();
277   if (!leaf && Parent())
278     leaf = Parent()->NextLeafChild();
279   return leaf;
280 }
281 
PrevLeafChild() const282 InlineBox* InlineBox::PrevLeafChild() const {
283   InlineBox* leaf = nullptr;
284   for (InlineBox* box = PrevOnLine(); box && !leaf; box = box->PrevOnLine())
285     leaf = box->IsLeaf() ? box : To<InlineFlowBox>(box)->LastLeafChild();
286   if (!leaf && Parent())
287     leaf = Parent()->PrevLeafChild();
288   return leaf;
289 }
290 
NextLeafChildIgnoringLineBreak() const291 InlineBox* InlineBox::NextLeafChildIgnoringLineBreak() const {
292   InlineBox* leaf = NextLeafChild();
293   return (leaf && leaf->IsLineBreak()) ? nullptr : leaf;
294 }
295 
PrevLeafChildIgnoringLineBreak() const296 InlineBox* InlineBox::PrevLeafChildIgnoringLineBreak() const {
297   InlineBox* leaf = PrevLeafChild();
298   return (leaf && leaf->IsLineBreak()) ? nullptr : leaf;
299 }
300 
IsSelected() const301 bool InlineBox::IsSelected() const {
302   return GetLineLayoutItem().IsSelected();
303 }
304 
CanAccommodateEllipsis(bool ltr,LayoutUnit block_edge,LayoutUnit ellipsis_width) const305 bool InlineBox::CanAccommodateEllipsis(bool ltr,
306                                        LayoutUnit block_edge,
307                                        LayoutUnit ellipsis_width) const {
308   // Non-atomic inline-level elements can always accommodate an ellipsis.
309   // Skip list markers and try the next box.
310   if (!GetLineLayoutItem().IsAtomicInlineLevel() ||
311       GetLineLayoutItem().IsListMarker())
312     return true;
313 
314   LayoutRect box_rect(X(), LayoutUnit(), logical_width_, LayoutUnit(10));
315   LayoutRect ellipsis_rect(ltr ? block_edge - ellipsis_width : block_edge,
316                            LayoutUnit(), ellipsis_width, LayoutUnit(10));
317   return !(box_rect.Intersects(ellipsis_rect));
318 }
319 
PlaceEllipsisBox(bool,LayoutUnit,LayoutUnit,LayoutUnit,LayoutUnit & truncated_width,InlineBox **,LayoutUnit)320 LayoutUnit InlineBox::PlaceEllipsisBox(bool,
321                                        LayoutUnit,
322                                        LayoutUnit,
323                                        LayoutUnit,
324                                        LayoutUnit& truncated_width,
325                                        InlineBox**,
326                                        LayoutUnit) {
327   // Use -1 to mean "we didn't set the position."
328   truncated_width += LogicalWidth();
329   return LayoutUnit(-1);
330 }
331 
ClearKnownToHaveNoOverflow()332 void InlineBox::ClearKnownToHaveNoOverflow() {
333   bitfields_.SetKnownToHaveNoOverflow(false);
334   if (Parent() && Parent()->KnownToHaveNoOverflow())
335     Parent()->ClearKnownToHaveNoOverflow();
336 }
337 
PhysicalLocation() const338 PhysicalOffset InlineBox::PhysicalLocation() const {
339   LayoutRect rect(Location(), Size());
340   FlipForWritingMode(rect);
341   return PhysicalOffset(rect.Location());
342 }
343 
FlipForWritingMode(LayoutRect & rect) const344 void InlineBox::FlipForWritingMode(LayoutRect& rect) const {
345   if (!UNLIKELY(GetLineLayoutItem().HasFlippedBlocksWritingMode()))
346     return;
347   Root().Block().FlipForWritingMode(rect);
348 }
349 
FlipForWritingMode(const LayoutPoint & point) const350 LayoutPoint InlineBox::FlipForWritingMode(const LayoutPoint& point) const {
351   if (!UNLIKELY(GetLineLayoutItem().HasFlippedBlocksWritingMode()))
352     return point;
353   return Root().Block().FlipForWritingMode(point);
354 }
355 
SetShouldDoFullPaintInvalidationForFirstLine()356 void InlineBox::SetShouldDoFullPaintInvalidationForFirstLine() {
357   GetLineLayoutItem().StyleRef().ClearCachedPseudoElementStyles();
358   GetLineLayoutItem().SetShouldDoFullPaintInvalidation();
359   if (!IsInlineFlowBox())
360     return;
361   for (InlineBox* child = To<InlineFlowBox>(this)->FirstChild(); child;
362        child = child->NextOnLine())
363     child->SetShouldDoFullPaintInvalidationForFirstLine();
364 }
365 
SetLineLayoutItemShouldDoFullPaintInvalidationIfNeeded()366 void InlineBox::SetLineLayoutItemShouldDoFullPaintInvalidationIfNeeded() {
367   // For RootInlineBox, we only need to invalidate if it's using the first line
368   // style. Otherwise it paints nothing so we don't need to invalidate it.
369   if (!IsRootInlineBox() || IsFirstLineStyle())
370     line_layout_item_.SetShouldDoFullPaintInvalidation();
371 }
372 
CanUseInlineBox(const LayoutObject & node)373 bool CanUseInlineBox(const LayoutObject& node) {
374   DCHECK(node.IsText() || node.IsInline() || node.IsLayoutBlockFlow());
375   return !RuntimeEnabledFeatures::LayoutNGEnabled() ||
376          !node.ContainingNGBlockFlow();
377 }
378 
379 }  // namespace blink
380 
381 #if DCHECK_IS_ON()
382 
showTree(const blink::InlineBox * b)383 void showTree(const blink::InlineBox* b) {
384   if (b)
385     b->ShowTreeForThis();
386   else
387     fprintf(stderr, "Cannot showTree for (nil) InlineBox.\n");
388 }
389 
showLineTree(const blink::InlineBox * b)390 void showLineTree(const blink::InlineBox* b) {
391   if (b)
392     b->ShowLineTreeForThis();
393   else
394     fprintf(stderr, "Cannot showLineTree for (nil) InlineBox.\n");
395 }
396 
397 #endif
398