1 /*
2 * Copyright (C) 2003, 2006, 2008 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/root_inline_box.h"
21
22 #include "third_party/blink/renderer/core/css/style_engine.h"
23 #include "third_party/blink/renderer/core/dom/document.h"
24 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
25 #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
26 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
27 #include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
28 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
29 #include "third_party/blink/renderer/core/layout/line/ellipsis_box.h"
30 #include "third_party/blink/renderer/core/layout/line/glyph_overflow.h"
31 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
32 #include "third_party/blink/renderer/core/layout/vertical_position_cache.h"
33 #include "third_party/blink/renderer/core/paint/paint_info.h"
34 #include "third_party/blink/renderer/core/paint/root_inline_box_painter.h"
35 #include "third_party/blink/renderer/platform/text/bidi_resolver.h"
36 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
37 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
38
39 namespace blink {
40
41 struct SameSizeAsRootInlineBox : public InlineFlowBox {
42 unsigned unsigned_variable;
43 void* pointers[3];
44 LayoutUnit layout_variables[6];
45 };
46
47 ASSERT_SIZE(RootInlineBox, SameSizeAsRootInlineBox);
48
49 typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap;
50 static EllipsisBoxMap* g_ellipsis_box_map = nullptr;
51
RootInlineBox(LineLayoutItem block)52 RootInlineBox::RootInlineBox(LineLayoutItem block)
53 : InlineFlowBox(block), line_break_pos_(0), line_break_obj_(nullptr) {
54 SetIsHorizontal(block.IsHorizontalWritingMode());
55 }
56
Destroy()57 void RootInlineBox::Destroy() {
58 DetachEllipsisBox();
59 InlineFlowBox::Destroy();
60 }
61
DetachEllipsisBox()62 void RootInlineBox::DetachEllipsisBox() {
63 if (HasEllipsisBox()) {
64 EllipsisBox* box = g_ellipsis_box_map->Take(this);
65 box->SetParent(nullptr);
66 box->Destroy();
67 SetHasEllipsisBox(false);
68 }
69 }
70
LineBoxes() const71 LineBoxList* RootInlineBox::LineBoxes() const {
72 return Block().LineBoxes();
73 }
74
ClearTruncation()75 void RootInlineBox::ClearTruncation() {
76 if (HasEllipsisBox()) {
77 DetachEllipsisBox();
78 InlineFlowBox::ClearTruncation();
79 }
80 }
81
BaselinePosition(FontBaseline baseline_type) const82 LayoutUnit RootInlineBox::BaselinePosition(FontBaseline baseline_type) const {
83 return BoxModelObject().BaselinePosition(
84 baseline_type, IsFirstLineStyle(),
85 IsHorizontal() ? kHorizontalLine : kVerticalLine,
86 kPositionOfInteriorLineBoxes);
87 }
88
LineHeight() const89 LayoutUnit RootInlineBox::LineHeight() const {
90 return BoxModelObject().LineHeight(
91 IsFirstLineStyle(), IsHorizontal() ? kHorizontalLine : kVerticalLine,
92 kPositionOfInteriorLineBoxes);
93 }
94
LineCanAccommodateEllipsis(bool ltr,LayoutUnit block_edge,LayoutUnit line_box_edge,LayoutUnit ellipsis_width)95 bool RootInlineBox::LineCanAccommodateEllipsis(bool ltr,
96 LayoutUnit block_edge,
97 LayoutUnit line_box_edge,
98 LayoutUnit ellipsis_width) {
99 // First sanity-check the unoverflowed width of the whole line to see if there
100 // is sufficient room.
101 LayoutUnit delta =
102 ltr ? line_box_edge - block_edge : block_edge - line_box_edge;
103 if (LogicalWidth() - delta < ellipsis_width)
104 return false;
105
106 // Next iterate over all the line boxes on the line. If we find a replaced
107 // element that intersects then we refuse to accommodate the ellipsis.
108 // Otherwise we're ok.
109 return InlineFlowBox::CanAccommodateEllipsis(ltr, block_edge, ellipsis_width);
110 }
111
PlaceEllipsis(const AtomicString & ellipsis_str,bool ltr,LayoutUnit block_left_edge,LayoutUnit block_right_edge,LayoutUnit ellipsis_width,LayoutUnit logical_left_offset,InlineBox ** found_box,ForceEllipsisOnLine force_ellipsis)112 LayoutUnit RootInlineBox::PlaceEllipsis(const AtomicString& ellipsis_str,
113 bool ltr,
114 LayoutUnit block_left_edge,
115 LayoutUnit block_right_edge,
116 LayoutUnit ellipsis_width,
117 LayoutUnit logical_left_offset,
118 InlineBox** found_box,
119 ForceEllipsisOnLine force_ellipsis) {
120 // Create an ellipsis box if we don't already have one. If we already have one
121 // we're just here to blank out (truncate) the text boxes.
122 if (!*found_box) {
123 EllipsisBox* ellipsis_box = new EllipsisBox(
124 GetLineLayoutItem(), ellipsis_str, this, ellipsis_width,
125 LogicalHeight(), Location(), !PrevRootBox(), IsHorizontal());
126
127 if (!g_ellipsis_box_map)
128 g_ellipsis_box_map = new EllipsisBoxMap();
129 g_ellipsis_box_map->insert(this, ellipsis_box);
130 SetHasEllipsisBox(true);
131 }
132
133 // FIXME: Do we need an RTL version of this?
134 LayoutUnit adjusted_logical_left = logical_left_offset + LogicalLeft();
135 if (force_ellipsis == ForceEllipsis && ltr &&
136 (adjusted_logical_left + LogicalWidth() + ellipsis_width) <=
137 block_right_edge) {
138 if (HasEllipsisBox())
139 GetEllipsisBox()->SetLogicalLeft(LogicalLeft() + LogicalWidth());
140 return LogicalWidth() + ellipsis_width;
141 }
142
143 // Now attempt to find the nearest glyph horizontally and place just to the
144 // right (or left in RTL) of that glyph. Mark all of the objects that
145 // intersect the ellipsis box as not painting (as being truncated).
146 LayoutUnit truncated_width;
147 LayoutUnit position =
148 PlaceEllipsisBox(ltr, block_left_edge, block_right_edge, ellipsis_width,
149 truncated_width, found_box, logical_left_offset);
150 if (HasEllipsisBox())
151 GetEllipsisBox()->SetLogicalLeft(position);
152 return truncated_width;
153 }
154
PlaceEllipsisBox(bool ltr,LayoutUnit block_left_edge,LayoutUnit block_right_edge,LayoutUnit ellipsis_width,LayoutUnit & truncated_width,InlineBox ** found_box,LayoutUnit logical_left_offset)155 LayoutUnit RootInlineBox::PlaceEllipsisBox(bool ltr,
156 LayoutUnit block_left_edge,
157 LayoutUnit block_right_edge,
158 LayoutUnit ellipsis_width,
159 LayoutUnit& truncated_width,
160 InlineBox** found_box,
161 LayoutUnit logical_left_offset) {
162 LayoutUnit result = InlineFlowBox::PlaceEllipsisBox(
163 ltr, block_left_edge, block_right_edge, ellipsis_width, truncated_width,
164 found_box, logical_left_offset);
165 if (result == -1) {
166 result = ltr ? std::max<LayoutUnit>(
167 LayoutUnit(),
168 block_right_edge - ellipsis_width - logical_left_offset)
169 : block_left_edge - logical_left_offset;
170 truncated_width = block_right_edge - block_left_edge - logical_left_offset;
171 }
172 return result;
173 }
174
Paint(const PaintInfo & paint_info,const PhysicalOffset & paint_offset,LayoutUnit line_top,LayoutUnit line_bottom) const175 void RootInlineBox::Paint(const PaintInfo& paint_info,
176 const PhysicalOffset& paint_offset,
177 LayoutUnit line_top,
178 LayoutUnit line_bottom) const {
179 RootInlineBoxPainter(*this).Paint(paint_info, paint_offset, line_top,
180 line_bottom);
181 }
182
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,LayoutUnit line_top,LayoutUnit line_bottom)183 bool RootInlineBox::NodeAtPoint(HitTestResult& result,
184 const HitTestLocation& hit_test_location,
185 const PhysicalOffset& accumulated_offset,
186 LayoutUnit line_top,
187 LayoutUnit line_bottom) {
188 if (HasEllipsisBox() && VisibleToHitTestRequest(result.GetHitTestRequest())) {
189 if (GetEllipsisBox()->NodeAtPoint(result, hit_test_location,
190 accumulated_offset, line_top,
191 line_bottom)) {
192 GetLineLayoutItem().UpdateHitTestResult(
193 result, hit_test_location.Point() - accumulated_offset);
194 return true;
195 }
196 }
197 return InlineFlowBox::NodeAtPoint(result, hit_test_location,
198 accumulated_offset, line_top, line_bottom);
199 }
200
Move(const LayoutSize & delta)201 void RootInlineBox::Move(const LayoutSize& delta) {
202 InlineFlowBox::Move(delta);
203 LayoutUnit block_direction_delta =
204 IsHorizontal() ? delta.Height() : delta.Width();
205 line_top_ += block_direction_delta;
206 line_bottom_ += block_direction_delta;
207 line_top_with_leading_ += block_direction_delta;
208 line_bottom_with_leading_ += block_direction_delta;
209 selection_bottom_ += block_direction_delta;
210 if (HasEllipsisBox())
211 GetEllipsisBox()->Move(delta);
212 }
213
ChildRemoved(InlineBox * box)214 void RootInlineBox::ChildRemoved(InlineBox* box) {
215 if (box->GetLineLayoutItem() == line_break_obj_)
216 SetLineBreakInfo(nullptr, 0, BidiStatus());
217
218 for (RootInlineBox* prev = PrevRootBox();
219 prev && prev->LineBreakObj() == box->GetLineLayoutItem();
220 prev = prev->PrevRootBox()) {
221 prev->SetLineBreakInfo(nullptr, 0, BidiStatus());
222 prev->MarkDirty();
223 }
224 }
225
ApplyLineHeightStep(uint8_t line_height_step,LayoutUnit & max_ascent,LayoutUnit & max_descent)226 static inline void ApplyLineHeightStep(uint8_t line_height_step,
227 LayoutUnit& max_ascent,
228 LayoutUnit& max_descent) {
229 // Round up to the multiple of units, by adding spaces to over/under equally.
230 // https://drafts.csswg.org/css-rhythm/#line-height-step
231 int remainder = (max_ascent + max_descent).ToInt() % line_height_step;
232 if (!remainder)
233 return;
234 DCHECK_GT(remainder, 0);
235 int space = line_height_step - remainder;
236 max_descent += space / 2;
237 max_ascent += space - space / 2;
238 }
239
AlignBoxesInBlockDirection(LayoutUnit height_of_block,GlyphOverflowAndFallbackFontsMap & text_box_data_map,VerticalPositionCache & vertical_position_cache)240 LayoutUnit RootInlineBox::AlignBoxesInBlockDirection(
241 LayoutUnit height_of_block,
242 GlyphOverflowAndFallbackFontsMap& text_box_data_map,
243 VerticalPositionCache& vertical_position_cache) {
244 // SVG will handle vertical alignment on its own.
245 if (IsSVGRootInlineBox())
246 return LayoutUnit();
247
248 LayoutUnit max_position_top;
249 LayoutUnit max_position_bottom;
250 LayoutUnit max_ascent;
251 LayoutUnit max_descent;
252 bool set_max_ascent = false;
253 bool set_max_descent = false;
254
255 // Figure out if we're in no-quirks mode.
256 bool no_quirks_mode = GetLineLayoutItem().GetDocument().InNoQuirksMode();
257
258 baseline_type_ = DominantBaseline();
259
260 ComputeLogicalBoxHeights(this, max_position_top, max_position_bottom,
261 max_ascent, max_descent, set_max_ascent,
262 set_max_descent, no_quirks_mode, text_box_data_map,
263 BaselineType(), vertical_position_cache);
264
265 if (max_ascent + max_descent <
266 std::max(max_position_top, max_position_bottom))
267 AdjustMaxAscentAndDescent(max_ascent, max_descent, max_position_top.ToInt(),
268 max_position_bottom.ToInt());
269
270 if (uint8_t line_height_step =
271 GetLineLayoutItem().StyleRef().LineHeightStep())
272 ApplyLineHeightStep(line_height_step, max_ascent, max_descent);
273
274 LayoutUnit max_height = LayoutUnit(max_ascent + max_descent);
275 LayoutUnit line_top = height_of_block;
276 LayoutUnit line_bottom = height_of_block;
277 LayoutUnit line_top_including_margins = height_of_block;
278 LayoutUnit line_bottom_including_margins = height_of_block;
279 LayoutUnit selection_bottom = height_of_block;
280 bool set_line_top = false;
281 bool has_annotations_before = false;
282 bool has_annotations_after = false;
283 PlaceBoxesInBlockDirection(
284 height_of_block, max_height, max_ascent, no_quirks_mode, line_top,
285 line_bottom, selection_bottom, set_line_top, line_top_including_margins,
286 line_bottom_including_margins, has_annotations_before,
287 has_annotations_after, BaselineType());
288 has_annotations_before_ = has_annotations_before;
289 has_annotations_after_ = has_annotations_after;
290
291 max_height = max_height.ClampNegativeToZero();
292
293 SetLineTopBottomPositions(line_top, line_bottom, height_of_block,
294 height_of_block + max_height, selection_bottom);
295
296 LayoutUnit annotations_adjustment = BeforeAnnotationsAdjustment();
297 if (annotations_adjustment) {
298 // FIXME: Need to handle pagination here. We might have to move to the next
299 // page/column as a result of the ruby expansion.
300 MoveInBlockDirection(annotations_adjustment);
301 height_of_block += annotations_adjustment;
302 }
303
304 return height_of_block + max_height;
305 }
306
BeforeAnnotationsAdjustment() const307 LayoutUnit RootInlineBox::BeforeAnnotationsAdjustment() const {
308 LayoutUnit result;
309
310 if (!GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()) {
311 // Annotations under the previous line may push us down.
312 if (PrevRootBox() && PrevRootBox()->HasAnnotationsAfter())
313 result = PrevRootBox()->ComputeUnderAnnotationAdjustment(LineTop());
314
315 if (!HasAnnotationsBefore())
316 return result;
317
318 // Annotations over this line may push us further down.
319 LayoutUnit highest_allowed_position =
320 PrevRootBox()
321 ? std::min(PrevRootBox()->LineBottom(), LineTop()) + result
322 : static_cast<LayoutUnit>(Block().BorderBefore());
323 result = ComputeOverAnnotationAdjustment(highest_allowed_position);
324 } else {
325 // Annotations under this line may push us up.
326 if (HasAnnotationsBefore())
327 result = ComputeUnderAnnotationAdjustment(
328 PrevRootBox() ? PrevRootBox()->LineBottom()
329 : static_cast<LayoutUnit>(Block().BorderBefore()));
330
331 if (!PrevRootBox() || !PrevRootBox()->HasAnnotationsAfter())
332 return result;
333
334 // We have to compute the expansion for annotations over the previous line
335 // to see how much we should move.
336 LayoutUnit lowest_allowed_position =
337 std::max(PrevRootBox()->LineBottom(), LineTop()) - result;
338 result =
339 PrevRootBox()->ComputeOverAnnotationAdjustment(lowest_allowed_position);
340 }
341
342 return result;
343 }
344
IsSelected() const345 bool RootInlineBox::IsSelected() const {
346 // Walk over all of the selected boxes.
347 for (InlineBox* box = FirstLeafChild(); box; box = box->NextLeafChild()) {
348 if (box->IsSelected())
349 return true;
350 }
351 return false;
352 }
353
FirstSelectedBox() const354 InlineBox* RootInlineBox::FirstSelectedBox() const {
355 for (InlineBox* box = FirstLeafChild(); box; box = box->NextLeafChild()) {
356 if (box->IsSelected())
357 return box;
358 }
359
360 return nullptr;
361 }
362
LastSelectedBox() const363 InlineBox* RootInlineBox::LastSelectedBox() const {
364 for (InlineBox* box = LastLeafChild(); box; box = box->PrevLeafChild()) {
365 if (box->IsSelected())
366 return box;
367 }
368
369 return nullptr;
370 }
371
SelectionTop() const372 LayoutUnit RootInlineBox::SelectionTop() const {
373 LayoutUnit selection_top = line_top_;
374 if (has_annotations_before_)
375 selection_top -= !GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()
376 ? ComputeOverAnnotationAdjustment(line_top_)
377 : ComputeUnderAnnotationAdjustment(line_top_);
378
379 if (GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() ||
380 !PrevRootBox())
381 return selection_top;
382
383 return std::min(selection_top, PrevRootBox()->SelectionBottom());
384 }
385
SelectionBottom() const386 LayoutUnit RootInlineBox::SelectionBottom() const {
387 LayoutUnit selection_bottom =
388 GetLineLayoutItem().GetDocument().InNoQuirksMode() ? selection_bottom_
389 : line_bottom_;
390
391 if (has_annotations_after_)
392 selection_bottom +=
393 !GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()
394 ? ComputeUnderAnnotationAdjustment(line_bottom_)
395 : ComputeOverAnnotationAdjustment(line_bottom_);
396
397 if (!GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() ||
398 !NextRootBox())
399 return selection_bottom;
400
401 return std::max(selection_bottom, NextRootBox()->SelectionTop());
402 }
403
BlockDirectionPointInLine() const404 LayoutUnit RootInlineBox::BlockDirectionPointInLine() const {
405 return !Block().StyleRef().IsFlippedBlocksWritingMode()
406 ? std::max(LineTop(), SelectionTop())
407 : std::min(LineBottom(), SelectionBottom());
408 }
409
Block() const410 LineLayoutBlockFlow RootInlineBox::Block() const {
411 return LineLayoutBlockFlow(GetLineLayoutItem());
412 }
413
IsEditableLeaf(InlineBox * leaf)414 static bool IsEditableLeaf(InlineBox* leaf) {
415 return leaf && leaf->GetLineLayoutItem().GetNode() &&
416 HasEditableStyle(*leaf->GetLineLayoutItem().GetNode());
417 }
418
ClosestLeafChildForPoint(const LayoutPoint & point_in_contents,bool only_editable_leaves) const419 const LayoutObject* RootInlineBox::ClosestLeafChildForPoint(
420 const LayoutPoint& point_in_contents,
421 bool only_editable_leaves) const {
422 InlineBox* closest_box = ClosestLeafChildForLogicalLeftPosition(
423 Block().IsHorizontalWritingMode() ? point_in_contents.X()
424 : point_in_contents.Y(),
425 only_editable_leaves);
426 return LineLayoutAPIShim::LayoutObjectFrom(closest_box->GetLineLayoutItem());
427 }
428
ClosestLeafChildForLogicalLeftPosition(LayoutUnit left_position,bool only_editable_leaves) const429 InlineBox* RootInlineBox::ClosestLeafChildForLogicalLeftPosition(
430 LayoutUnit left_position,
431 bool only_editable_leaves) const {
432 InlineBox* first_leaf = FirstLeafChild();
433 InlineBox* last_leaf = LastLeafChild();
434
435 if (first_leaf != last_leaf) {
436 if (first_leaf->IsLineBreak())
437 first_leaf = first_leaf->NextLeafChildIgnoringLineBreak();
438 else if (last_leaf->IsLineBreak())
439 last_leaf = last_leaf->PrevLeafChildIgnoringLineBreak();
440 }
441
442 if (first_leaf == last_leaf &&
443 (!only_editable_leaves || IsEditableLeaf(first_leaf)))
444 return first_leaf;
445
446 // Avoid returning a list marker when possible.
447 if (left_position <= first_leaf->LogicalLeft() &&
448 !first_leaf->GetLineLayoutItem().IsListMarker() &&
449 (!only_editable_leaves || IsEditableLeaf(first_leaf))) {
450 // The leftPosition coordinate is less or equal to left edge of the
451 // firstLeaf. Return it.
452 return first_leaf;
453 }
454
455 if (left_position >= last_leaf->LogicalRight() &&
456 !last_leaf->GetLineLayoutItem().IsListMarker() &&
457 (!only_editable_leaves || IsEditableLeaf(last_leaf))) {
458 // The leftPosition coordinate is greater or equal to right edge of the
459 // lastLeaf. Return it.
460 return last_leaf;
461 }
462
463 InlineBox* closest_leaf = nullptr;
464 for (InlineBox* leaf = first_leaf; leaf;
465 leaf = leaf->NextLeafChildIgnoringLineBreak()) {
466 if (!leaf->GetLineLayoutItem().IsListMarker() &&
467 (!only_editable_leaves || IsEditableLeaf(leaf))) {
468 closest_leaf = leaf;
469 if (left_position < leaf->LogicalRight()) {
470 // The x coordinate is less than the right edge of the box.
471 // Return it.
472 return leaf;
473 }
474 }
475 }
476
477 return closest_leaf ? closest_leaf : last_leaf;
478 }
479
LineBreakBidiStatus() const480 BidiStatus RootInlineBox::LineBreakBidiStatus() const {
481 return BidiStatus(
482 static_cast<WTF::unicode::CharDirection>(line_break_bidi_status_eor_),
483 static_cast<WTF::unicode::CharDirection>(
484 line_break_bidi_status_last_strong_),
485 static_cast<WTF::unicode::CharDirection>(line_break_bidi_status_last_),
486 line_break_context_);
487 }
488
SetLineBreakInfo(LineLayoutItem obj,unsigned break_pos,const BidiStatus & status)489 void RootInlineBox::SetLineBreakInfo(LineLayoutItem obj,
490 unsigned break_pos,
491 const BidiStatus& status) {
492 // When setting lineBreakObj, the LayoutObject must not be a LayoutInline
493 // with no line boxes, otherwise all sorts of invariants are broken later.
494 // This has security implications because if the LayoutObject does not point
495 // to at least one line box, then that LayoutInline can be deleted later
496 // without resetting the lineBreakObj, leading to use-after-free.
497 SECURITY_DCHECK(!obj || obj.IsText() ||
498 !(obj.IsLayoutInline() && obj.IsBox() &&
499 !LineLayoutBox(obj).InlineBoxWrapper()));
500
501 line_break_obj_ = obj;
502 line_break_pos_ = break_pos;
503 line_break_bidi_status_eor_ = status.eor;
504 line_break_bidi_status_last_strong_ = status.last_strong;
505 line_break_bidi_status_last_ = status.last;
506 line_break_context_ = status.context;
507 }
508
GetEllipsisBox() const509 EllipsisBox* RootInlineBox::GetEllipsisBox() const {
510 if (!HasEllipsisBox())
511 return nullptr;
512 return g_ellipsis_box_map->at(this);
513 }
514
RemoveLineBoxFromLayoutObject()515 void RootInlineBox::RemoveLineBoxFromLayoutObject() {
516 Block().LineBoxes()->RemoveLineBox(this);
517 }
518
ExtractLineBoxFromLayoutObject()519 void RootInlineBox::ExtractLineBoxFromLayoutObject() {
520 Block().LineBoxes()->ExtractLineBox(this);
521 }
522
AttachLineBoxToLayoutObject()523 void RootInlineBox::AttachLineBoxToLayoutObject() {
524 Block().LineBoxes()->AttachLineBox(this);
525 }
526
PaddedLayoutOverflowRect(LayoutUnit end_padding) const527 LayoutRect RootInlineBox::PaddedLayoutOverflowRect(
528 LayoutUnit end_padding) const {
529 LayoutRect line_layout_overflow = LayoutOverflowRect(LineTop(), LineBottom());
530 if (!end_padding)
531 return line_layout_overflow;
532
533 if (IsHorizontal()) {
534 if (IsLeftToRightDirection())
535 line_layout_overflow.ShiftMaxXEdgeTo(std::max<LayoutUnit>(
536 line_layout_overflow.MaxX(), LogicalRight() + end_padding));
537 else
538 line_layout_overflow.ShiftXEdgeTo(std::min<LayoutUnit>(
539 line_layout_overflow.X(), LogicalLeft() - end_padding));
540 } else {
541 if (IsLeftToRightDirection())
542 line_layout_overflow.ShiftMaxYEdgeTo(std::max<LayoutUnit>(
543 line_layout_overflow.MaxY(), LogicalRight() + end_padding));
544 else
545 line_layout_overflow.ShiftYEdgeTo(std::min<LayoutUnit>(
546 line_layout_overflow.Y(), LogicalLeft() - end_padding));
547 }
548
549 return line_layout_overflow;
550 }
551
SetAscentAndDescent(LayoutUnit & ascent,LayoutUnit & descent,LayoutUnit new_ascent,LayoutUnit new_descent,bool & ascent_descent_set)552 static void SetAscentAndDescent(LayoutUnit& ascent,
553 LayoutUnit& descent,
554 LayoutUnit new_ascent,
555 LayoutUnit new_descent,
556 bool& ascent_descent_set) {
557 if (!ascent_descent_set) {
558 ascent_descent_set = true;
559 ascent = new_ascent;
560 descent = new_descent;
561 } else {
562 ascent = std::max(ascent, new_ascent);
563 descent = std::max(descent, new_descent);
564 }
565 }
566
AscentAndDescentForBox(InlineBox * box,GlyphOverflowAndFallbackFontsMap & text_box_data_map,LayoutUnit & ascent,LayoutUnit & descent,bool & affects_ascent,bool & affects_descent) const567 void RootInlineBox::AscentAndDescentForBox(
568 InlineBox* box,
569 GlyphOverflowAndFallbackFontsMap& text_box_data_map,
570 LayoutUnit& ascent,
571 LayoutUnit& descent,
572 bool& affects_ascent,
573 bool& affects_descent) const {
574 bool ascent_descent_set = false;
575
576 if (box->GetLineLayoutItem().IsAtomicInlineLevel()) {
577 ascent = box->BaselinePosition(BaselineType());
578 descent = box->LineHeight() - ascent;
579
580 // Replaced elements always affect both the ascent and descent.
581 affects_ascent = true;
582 affects_descent = true;
583 return;
584 }
585
586 Vector<const SimpleFontData*>* used_fonts = nullptr;
587 if (box->IsText()) {
588 GlyphOverflowAndFallbackFontsMap::iterator it =
589 text_box_data_map.find(To<InlineTextBox>(box));
590 used_fonts = it == text_box_data_map.end() ? nullptr : &it->value.first;
591 }
592
593 bool include_leading = IncludeLeadingForBox(box);
594 bool set_used_font_with_leading = false;
595
596 if (used_fonts && !used_fonts->IsEmpty() &&
597 (box->GetLineLayoutItem()
598 .Style(IsFirstLineStyle())
599 ->LineHeight()
600 .IsNegative() &&
601 include_leading)) {
602 const SimpleFontData* primary_font = box->GetLineLayoutItem()
603 .Style(IsFirstLineStyle())
604 ->GetFont()
605 .PrimaryFont();
606 if (primary_font)
607 used_fonts->push_back(primary_font);
608 for (const SimpleFontData* font_data : *used_fonts) {
609 const FontMetrics& font_metrics = font_data->GetFontMetrics();
610 LayoutUnit used_font_ascent(font_metrics.Ascent(BaselineType()));
611 LayoutUnit used_font_descent(font_metrics.Descent(BaselineType()));
612 LayoutUnit half_leading(
613 (font_metrics.LineSpacing() - font_metrics.Height()) / 2);
614 LayoutUnit used_font_ascent_and_leading = used_font_ascent + half_leading;
615 LayoutUnit used_font_descent_and_leading =
616 font_metrics.LineSpacing() - used_font_ascent_and_leading;
617 if (include_leading) {
618 SetAscentAndDescent(ascent, descent, used_font_ascent_and_leading,
619 used_font_descent_and_leading, ascent_descent_set);
620 set_used_font_with_leading = true;
621 }
622 if (!affects_ascent)
623 affects_ascent = used_font_ascent - box->LogicalTop() > 0;
624 if (!affects_descent)
625 affects_descent = used_font_descent + box->LogicalTop() > 0;
626 }
627 }
628
629 // If leading is included for the box, then we compute that box.
630 if (include_leading && !set_used_font_with_leading) {
631 LayoutUnit ascent_with_leading = box->BaselinePosition(BaselineType());
632 LayoutUnit descent_with_leading = box->LineHeight() - ascent_with_leading;
633 SetAscentAndDescent(ascent, descent, ascent_with_leading,
634 descent_with_leading, ascent_descent_set);
635
636 // Examine the font box for inline flows and text boxes to see if any part
637 // of it is above the baseline. If the top of our font box relative to the
638 // root box baseline is above the root box baseline, then we are
639 // contributing to the maxAscent value. Descent is similar. If any part of
640 // our font box is below the root box's baseline, then we contribute to the
641 // maxDescent value.
642 affects_ascent = ascent_with_leading - box->LogicalTop() > 0;
643 affects_descent = descent_with_leading + box->LogicalTop() > 0;
644 }
645 }
646
VerticalPositionForBox(InlineBox * box,VerticalPositionCache & vertical_position_cache)647 LayoutUnit RootInlineBox::VerticalPositionForBox(
648 InlineBox* box,
649 VerticalPositionCache& vertical_position_cache) {
650 if (box->GetLineLayoutItem().IsText())
651 return box->Parent()->LogicalTop();
652
653 LineLayoutBoxModel box_model = box->BoxModelObject();
654 DCHECK(box_model.IsInline());
655 if (!box_model.IsInline())
656 return LayoutUnit();
657
658 // This method determines the vertical position for inline elements.
659 bool first_line = IsFirstLineStyle();
660 if (first_line &&
661 !box_model.GetDocument().GetStyleEngine().UsesFirstLineRules())
662 first_line = false;
663
664 // Check the cache.
665 bool is_layout_inline = box_model.IsLayoutInline();
666 if (is_layout_inline && !first_line) {
667 LayoutUnit vertical_position =
668 LayoutUnit(vertical_position_cache.Get(box_model, BaselineType()));
669 if (vertical_position != kPositionUndefined)
670 return vertical_position;
671 }
672
673 LayoutUnit vertical_position;
674 EVerticalAlign vertical_align = box_model.StyleRef().VerticalAlign();
675 if (vertical_align == EVerticalAlign::kTop ||
676 vertical_align == EVerticalAlign::kBottom)
677 return LayoutUnit();
678
679 LineLayoutItem parent = box_model.Parent();
680 if (parent.IsLayoutInline() &&
681 parent.StyleRef().VerticalAlign() != EVerticalAlign::kTop &&
682 parent.StyleRef().VerticalAlign() != EVerticalAlign::kBottom)
683 vertical_position = box->Parent()->LogicalTop();
684
685 if (vertical_align != EVerticalAlign::kBaseline) {
686 const Font& font = parent.Style(first_line)->GetFont();
687 const SimpleFontData* font_data = font.PrimaryFont();
688 DCHECK(font_data);
689 if (!font_data)
690 return LayoutUnit();
691
692 const FontMetrics& font_metrics = font_data->GetFontMetrics();
693 int font_size = font.GetFontDescription().ComputedPixelSize();
694
695 LineDirectionMode line_direction =
696 parent.IsHorizontalWritingMode() ? kHorizontalLine : kVerticalLine;
697
698 if (vertical_align == EVerticalAlign::kSub) {
699 vertical_position += font_size / 5 + 1;
700 } else if (vertical_align == EVerticalAlign::kSuper) {
701 vertical_position -= font_size / 3 + 1;
702 } else if (vertical_align == EVerticalAlign::kTextTop) {
703 vertical_position += box_model.BaselinePosition(
704 BaselineType(), first_line, line_direction) -
705 font_metrics.Ascent(BaselineType());
706 } else if (vertical_align == EVerticalAlign::kMiddle) {
707 vertical_position = vertical_position -
708 LayoutUnit(font_metrics.XHeight() / 2) -
709 box_model.LineHeight(first_line, line_direction) / 2 +
710 box_model.BaselinePosition(BaselineType(), first_line,
711 line_direction);
712 } else if (vertical_align == EVerticalAlign::kTextBottom) {
713 vertical_position += font_metrics.Descent(BaselineType());
714 // lineHeight - baselinePosition is always 0 for replaced elements (except
715 // inline blocks), so don't bother wasting time in that case.
716 if (!box_model.IsAtomicInlineLevel() ||
717 box_model.IsInlineBlockOrInlineTable())
718 vertical_position -= (box_model.LineHeight(first_line, line_direction) -
719 box_model.BaselinePosition(
720 BaselineType(), first_line, line_direction));
721 } else if (vertical_align == EVerticalAlign::kBaselineMiddle) {
722 vertical_position +=
723 -box_model.LineHeight(first_line, line_direction) / 2 +
724 box_model.BaselinePosition(BaselineType(), first_line,
725 line_direction);
726 } else if (vertical_align == EVerticalAlign::kLength) {
727 LayoutUnit line_height;
728 // Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align:
729 // 'Percentages: refer to the 'line-height' of the element itself'.
730 if (box_model.StyleRef().GetVerticalAlignLength().IsPercentOrCalc())
731 line_height = LayoutUnit(box_model.StyleRef().ComputedLineHeight());
732 else
733 line_height = box_model.LineHeight(first_line, line_direction);
734 vertical_position -= ValueForLength(
735 box_model.StyleRef().GetVerticalAlignLength(), line_height);
736 }
737 }
738
739 // Store the cached value.
740 if (is_layout_inline && !first_line)
741 vertical_position_cache.Set(box_model, BaselineType(),
742 vertical_position.ToInt());
743
744 return vertical_position;
745 }
746
IncludeLeadingForBox(InlineBox * box) const747 bool RootInlineBox::IncludeLeadingForBox(InlineBox* box) const {
748 return !(box->GetLineLayoutItem().IsAtomicInlineLevel() ||
749 (box->GetLineLayoutItem().IsText() && !box->IsText()));
750 }
751
CollectLeafBoxesInLogicalOrder(Vector<InlineBox * > & leaf_boxes_in_logical_order,CustomInlineBoxRangeReverse custom_reverse_implementation) const752 void RootInlineBox::CollectLeafBoxesInLogicalOrder(
753 Vector<InlineBox*>& leaf_boxes_in_logical_order,
754 CustomInlineBoxRangeReverse custom_reverse_implementation) const {
755 InlineBox* leaf = FirstLeafChild();
756
757 // FIXME: The reordering code is a copy of parts from BidiResolver::
758 // createBidiRunsForLine, operating directly on InlineBoxes, instead of
759 // BidiRuns. Investigate on how this code could possibly be shared.
760 unsigned char min_level = 128;
761 unsigned char max_level = 0;
762
763 // First find highest and lowest levels, and initialize
764 // leafBoxesInLogicalOrder with the leaf boxes in visual order.
765 for (; leaf; leaf = leaf->NextLeafChild()) {
766 min_level = std::min(min_level, leaf->BidiLevel());
767 max_level = std::max(max_level, leaf->BidiLevel());
768 leaf_boxes_in_logical_order.push_back(leaf);
769 }
770
771 if (GetLineLayoutItem().StyleRef().RtlOrdering() == EOrder::kVisual)
772 return;
773
774 // Reverse of reordering of the line (L2 according to Bidi spec):
775 // L2. From the highest level found in the text to the lowest odd level on
776 // each line, reverse any contiguous sequence of characters that are at that
777 // level or higher.
778
779 // Reversing the reordering of the line is only done up to the lowest odd
780 // level.
781 if (!(min_level % 2))
782 ++min_level;
783
784 Vector<InlineBox*>::iterator end = leaf_boxes_in_logical_order.end();
785 while (min_level <= max_level) {
786 Vector<InlineBox*>::iterator it = leaf_boxes_in_logical_order.begin();
787 while (it != end) {
788 while (it != end) {
789 if ((*it)->BidiLevel() >= min_level)
790 break;
791 ++it;
792 }
793 Vector<InlineBox*>::iterator first = it;
794 while (it != end) {
795 if ((*it)->BidiLevel() < min_level)
796 break;
797 ++it;
798 }
799 Vector<InlineBox*>::iterator last = it;
800 if (custom_reverse_implementation)
801 (*custom_reverse_implementation)(first, last);
802 else
803 std::reverse(first, last);
804 }
805 ++min_level;
806 }
807 }
808
GetLogicalStartNonPseudoBox() const809 const InlineBox* RootInlineBox::GetLogicalStartNonPseudoBox() const {
810 Vector<InlineBox*> leaf_boxes_in_logical_order;
811 CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order);
812 for (InlineBox* box : leaf_boxes_in_logical_order) {
813 if (box->GetLineLayoutItem().NonPseudoNode())
814 return box;
815 }
816 return nullptr;
817 }
818
GetLogicalEndNonPseudoBox() const819 const InlineBox* RootInlineBox::GetLogicalEndNonPseudoBox() const {
820 Vector<InlineBox*> leaf_boxes_in_logical_order;
821 CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order);
822 for (wtf_size_t i = leaf_boxes_in_logical_order.size(); i > 0; --i) {
823 if (leaf_boxes_in_logical_order[i - 1]
824 ->GetLineLayoutItem()
825 .NonPseudoNode()) {
826 return leaf_boxes_in_logical_order[i - 1];
827 }
828 }
829 return nullptr;
830 }
831
BoxName() const832 const char* RootInlineBox::BoxName() const {
833 return "RootInlineBox";
834 }
835
836 } // namespace blink
837