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