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/text/unicode.h"
37 
38 namespace blink {
39 
40 struct SameSizeAsRootInlineBox : public InlineFlowBox {
41   unsigned unsigned_variable;
42   void* pointers[3];
43   LayoutUnit layout_variables[6];
44 };
45 
46 static_assert(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox),
47               "RootInlineBox should stay small");
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 LayoutPoint & paint_offset,LayoutUnit line_top,LayoutUnit line_bottom) const175 void RootInlineBox::Paint(const PaintInfo& paint_info,
176                           const LayoutPoint& 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(ToInlineTextBox(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