| /* |
| * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. |
| * All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/layout/line/inline_flow_box.h" |
| |
| #include <math.h> |
| #include <algorithm> |
| #include "third_party/blink/renderer/core/css_property_names.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_box.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_inline.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_list_marker.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_ruby_base.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_ruby_run.h" |
| #include "third_party/blink/renderer/core/layout/api/line_layout_ruby_text.h" |
| #include "third_party/blink/renderer/core/layout/hit_test_result.h" |
| #include "third_party/blink/renderer/core/layout/line/glyph_overflow.h" |
| #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" |
| #include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" |
| #include "third_party/blink/renderer/core/layout/line/root_inline_box.h" |
| #include "third_party/blink/renderer/core/paint/box_painter.h" |
| #include "third_party/blink/renderer/core/paint/inline_flow_box_painter.h" |
| #include "third_party/blink/renderer/core/style/shadow_list.h" |
| #include "third_party/blink/renderer/platform/fonts/font.h" |
| |
| namespace blink { |
| |
| struct SameSizeAsInlineFlowBox : public InlineBox { |
| void* pointers[5]; |
| uint32_t bitfields : 23; |
| }; |
| |
| static_assert(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), |
| "InlineFlowBox should stay small"); |
| |
| #if DCHECK_IS_ON() |
| InlineFlowBox::~InlineFlowBox() { |
| if (!has_bad_child_list_) |
| for (InlineBox* child = FirstChild(); child; child = child->NextOnLine()) |
| child->SetHasBadParent(); |
| } |
| #endif |
| |
| LayoutUnit InlineFlowBox::GetFlowSpacingLogicalWidth() { |
| LayoutUnit tot_width = |
| MarginBorderPaddingLogicalLeft() + MarginBorderPaddingLogicalRight(); |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->IsInlineFlowBox()) |
| tot_width += ToInlineFlowBox(curr)->GetFlowSpacingLogicalWidth(); |
| } |
| return tot_width; |
| } |
| |
| LayoutRect InlineFlowBox::FrameRect() const { |
| return LayoutRect(Location(), Size()); |
| } |
| |
| static void SetHasTextDescendantsOnAncestors(InlineFlowBox* box) { |
| while (box && !box->HasTextDescendants()) { |
| box->SetHasTextDescendants(); |
| box = box->Parent(); |
| } |
| } |
| |
| static inline bool HasIdenticalLineHeightProperties( |
| const ComputedStyle& parent_style, |
| const ComputedStyle& child_style, |
| bool is_root) { |
| return parent_style.HasIdenticalAscentDescentAndLineGap(child_style) && |
| parent_style.LineHeight() == child_style.LineHeight() && |
| (parent_style.VerticalAlign() == EVerticalAlign::kBaseline || |
| is_root) && |
| child_style.VerticalAlign() == EVerticalAlign::kBaseline; |
| } |
| |
| inline bool InlineFlowBox::HasEmphasisMarkBefore( |
| const InlineTextBox* text_box) const { |
| TextEmphasisPosition emphasis_mark_position; |
| const auto& style = |
| text_box->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| if (!text_box->GetEmphasisMarkPosition(style, emphasis_mark_position)) |
| return false; |
| LineLogicalSide side = style.GetTextEmphasisLineLogicalSide(); |
| if (IsHorizontal() || !style.IsFlippedLinesWritingMode()) |
| return side == LineLogicalSide::kOver; |
| return side == LineLogicalSide::kUnder; |
| } |
| |
| inline bool InlineFlowBox::HasEmphasisMarkOver( |
| const InlineTextBox* text_box) const { |
| const auto& style = |
| text_box->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| TextEmphasisPosition emphasis_mark_position; |
| return text_box->GetEmphasisMarkPosition(style, emphasis_mark_position) && |
| style.GetTextEmphasisLineLogicalSide() == LineLogicalSide::kOver; |
| } |
| |
| inline bool InlineFlowBox::HasEmphasisMarkUnder( |
| const InlineTextBox* text_box) const { |
| const auto& style = |
| text_box->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| TextEmphasisPosition emphasis_mark_position; |
| return text_box->GetEmphasisMarkPosition(style, emphasis_mark_position) && |
| style.GetTextEmphasisLineLogicalSide() == LineLogicalSide::kUnder; |
| } |
| |
| void InlineFlowBox::AddToLine(InlineBox* child) { |
| DCHECK(!child->Parent()); |
| DCHECK(!child->NextOnLine()); |
| DCHECK(!child->PrevOnLine()); |
| |
| child->SetParent(this); |
| if (!first_child_) { |
| first_child_ = child; |
| last_child_ = child; |
| } else { |
| last_child_->SetNextOnLine(child); |
| child->SetPrevOnLine(last_child_); |
| last_child_ = child; |
| } |
| child->SetFirstLineStyleBit(IsFirstLineStyle()); |
| child->SetIsHorizontal(IsHorizontal()); |
| if (child->IsText()) { |
| if (child->GetLineLayoutItem().Parent() == GetLineLayoutItem()) |
| has_text_children_ = true; |
| SetHasTextDescendantsOnAncestors(this); |
| } else if (child->IsInlineFlowBox()) { |
| if (ToInlineFlowBox(child)->HasTextDescendants()) |
| SetHasTextDescendantsOnAncestors(this); |
| } |
| |
| if (DescendantsHaveSameLineHeightAndBaseline() && |
| !child->GetLineLayoutItem().IsOutOfFlowPositioned()) { |
| const ComputedStyle& parent_style = |
| GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| const ComputedStyle& child_style = |
| child->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| bool root = IsRootInlineBox(); |
| bool should_clear_descendants_have_same_line_height_and_baseline = false; |
| if (child->GetLineLayoutItem().IsAtomicInlineLevel()) { |
| should_clear_descendants_have_same_line_height_and_baseline = true; |
| } else if (child->IsText()) { |
| if (child->GetLineLayoutItem().IsBR() || |
| (child->GetLineLayoutItem().Parent() != GetLineLayoutItem())) { |
| if (!HasIdenticalLineHeightProperties(parent_style, child_style, root)) |
| should_clear_descendants_have_same_line_height_and_baseline = true; |
| } |
| if (child_style.HasTextCombine() || |
| child_style.GetTextEmphasisMark() != TextEmphasisMark::kNone) |
| should_clear_descendants_have_same_line_height_and_baseline = true; |
| } else { |
| if (child->GetLineLayoutItem().IsBR()) { |
| // FIXME: This is dumb. We only turn off because current layout test |
| // results expect the <br> to be 0-height on the baseline. |
| // Other than making a zillion tests have to regenerate results, there's |
| // no reason to ditch the optimization here. |
| should_clear_descendants_have_same_line_height_and_baseline = true; |
| } else { |
| DCHECK(IsInlineFlowBox()); |
| InlineFlowBox* child_flow_box = ToInlineFlowBox(child); |
| // Check the child's bit, and then also check for differences in font, |
| // line-height, vertical-align |
| if (!child_flow_box->DescendantsHaveSameLineHeightAndBaseline() || |
| !HasIdenticalLineHeightProperties(parent_style, child_style, |
| root) || |
| child_style.HasBorder() || child_style.HasPadding() || |
| child_style.HasTextCombine()) { |
| should_clear_descendants_have_same_line_height_and_baseline = true; |
| } |
| } |
| } |
| |
| if (should_clear_descendants_have_same_line_height_and_baseline) |
| ClearDescendantsHaveSameLineHeightAndBaseline(); |
| } |
| |
| if (!child->GetLineLayoutItem().IsOutOfFlowPositioned()) { |
| if (child->IsText()) { |
| const ComputedStyle& child_style = |
| child->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| if (child_style.LetterSpacing() < 0 || child_style.TextShadow() || |
| child_style.GetTextEmphasisMark() != TextEmphasisMark::kNone || |
| child_style.TextStrokeWidth() || child->IsLineBreak()) |
| child->ClearKnownToHaveNoOverflow(); |
| } else if (child->GetLineLayoutItem().IsAtomicInlineLevel()) { |
| LineLayoutBox box = LineLayoutBox(child->GetLineLayoutItem()); |
| if (box.HasOverflowModel() || box.HasSelfPaintingLayer()) |
| child->ClearKnownToHaveNoOverflow(); |
| } else if (!child->GetLineLayoutItem().IsBR() && |
| (child->GetLineLayoutItem() |
| .Style(IsFirstLineStyle()) |
| ->BoxShadow() || |
| child->BoxModelObject().HasSelfPaintingLayer() || |
| (child->GetLineLayoutItem().IsListMarker() && |
| !LineLayoutListMarker(child->GetLineLayoutItem()) |
| .IsInside()) || |
| child->GetLineLayoutItem() |
| .Style(IsFirstLineStyle()) |
| ->HasBorderImageOutsets() || |
| child->GetLineLayoutItem() |
| .Style(IsFirstLineStyle()) |
| ->HasOutline())) { |
| child->ClearKnownToHaveNoOverflow(); |
| } |
| |
| if (KnownToHaveNoOverflow() && child->IsInlineFlowBox() && |
| !ToInlineFlowBox(child)->KnownToHaveNoOverflow()) |
| ClearKnownToHaveNoOverflow(); |
| } |
| } |
| |
| void InlineFlowBox::RemoveChild(InlineBox* child, MarkLineBoxes mark_dirty) { |
| if (mark_dirty == kMarkLineBoxesDirty && !IsDirty()) |
| DirtyLineBoxes(); |
| |
| Root().ChildRemoved(child); |
| |
| if (child == first_child_) |
| first_child_ = child->NextOnLine(); |
| if (child == last_child_) |
| last_child_ = child->PrevOnLine(); |
| if (child->NextOnLine()) |
| child->NextOnLine()->SetPrevOnLine(child->PrevOnLine()); |
| if (child->PrevOnLine()) |
| child->PrevOnLine()->SetNextOnLine(child->NextOnLine()); |
| |
| child->SetParent(nullptr); |
| } |
| |
| void InlineFlowBox::DeleteLine() { |
| InlineBox* child = FirstChild(); |
| InlineBox* next = nullptr; |
| while (child) { |
| DCHECK_EQ(this, child->Parent()); |
| next = child->NextOnLine(); |
| #if DCHECK_IS_ON() |
| child->SetParent(nullptr); |
| #endif |
| child->DeleteLine(); |
| child = next; |
| } |
| #if DCHECK_IS_ON() |
| first_child_ = nullptr; |
| last_child_ = nullptr; |
| #endif |
| |
| RemoveLineBoxFromLayoutObject(); |
| Destroy(); |
| } |
| |
| void InlineFlowBox::RemoveLineBoxFromLayoutObject() { |
| LineBoxes()->RemoveLineBox(this); |
| } |
| |
| void InlineFlowBox::ExtractLine() { |
| if (!Extracted()) |
| ExtractLineBoxFromLayoutObject(); |
| for (InlineBox* child = FirstChild(); child; child = child->NextOnLine()) |
| child->ExtractLine(); |
| } |
| |
| void InlineFlowBox::ExtractLineBoxFromLayoutObject() { |
| LineBoxes()->ExtractLineBox(this); |
| } |
| |
| void InlineFlowBox::AttachLine() { |
| if (Extracted()) |
| AttachLineBoxToLayoutObject(); |
| for (InlineBox* child = FirstChild(); child; child = child->NextOnLine()) |
| child->AttachLine(); |
| } |
| |
| void InlineFlowBox::AttachLineBoxToLayoutObject() { |
| LineBoxes()->AttachLineBox(this); |
| } |
| |
| void InlineFlowBox::Move(const LayoutSize& delta) { |
| InlineBox::Move(delta); |
| for (InlineBox* child = FirstChild(); child; child = child->NextOnLine()) { |
| if (child->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; |
| child->Move(delta); |
| } |
| if (overflow_) { |
| // FIXME: Rounding error here since overflow was pixel snapped, but nobody |
| // other than list markers passes non-integral values here. |
| overflow_->Move(delta.Width(), delta.Height()); |
| } |
| } |
| |
| LineBoxList* InlineFlowBox::LineBoxes() const { |
| return LineLayoutInline(GetLineLayoutItem()).LineBoxes(); |
| } |
| |
| static inline bool IsLastChildForLayoutObject(LineLayoutItem ancestor, |
| LineLayoutItem child) { |
| if (!child) |
| return false; |
| |
| if (child == ancestor) |
| return true; |
| |
| LineLayoutItem curr = child; |
| LineLayoutItem parent = curr.Parent(); |
| while (parent && (!parent.IsLayoutBlock() || parent.IsInline())) { |
| if (parent.SlowLastChild() != curr) |
| return false; |
| if (parent == ancestor) |
| return true; |
| |
| curr = parent; |
| parent = curr.Parent(); |
| } |
| |
| return true; |
| } |
| |
| static bool IsAncestorAndWithinBlock(LineLayoutItem ancestor, |
| LineLayoutItem child) { |
| LineLayoutItem item = child; |
| while (item && (!item.IsLayoutBlock() || item.IsInline())) { |
| if (item == ancestor) |
| return true; |
| item = item.Parent(); |
| } |
| return false; |
| } |
| |
| void InlineFlowBox::DetermineSpacingForFlowBoxes( |
| bool last_line, |
| bool is_logically_last_run_wrapped, |
| LineLayoutItem logically_last_run_layout_object) { |
| // All boxes start off open. They will not apply any margins/border/padding |
| // on any side. |
| bool include_left_edge = false; |
| bool include_right_edge = false; |
| |
| // The root inline box never has borders/margins/padding. |
| if (Parent()) { |
| bool ltr = GetLineLayoutItem().StyleRef().IsLeftToRightDirection(); |
| |
| // Check to see if all initial lines are unconstructed. If so, then |
| // we know the inline began on this line (unless we are a continuation). |
| LineBoxList* line_box_list = LineBoxes(); |
| if (!line_box_list->First()->IsConstructed() && |
| !GetLineLayoutItem().IsInlineElementContinuation()) { |
| if (GetLineLayoutItem().StyleRef().BoxDecorationBreak() == |
| EBoxDecorationBreak::kClone) |
| include_left_edge = include_right_edge = true; |
| else if (ltr && line_box_list->First() == this) |
| include_left_edge = true; |
| else if (!ltr && line_box_list->Last() == this) |
| include_right_edge = true; |
| } |
| |
| if (!line_box_list->Last()->IsConstructed()) { |
| LineLayoutInline inline_flow = LineLayoutInline(GetLineLayoutItem()); |
| LineLayoutItem logically_last_run_layout_item( |
| logically_last_run_layout_object); |
| bool is_last_object_on_line = |
| !IsAncestorAndWithinBlock(GetLineLayoutItem(), |
| logically_last_run_layout_item) || |
| (IsLastChildForLayoutObject(GetLineLayoutItem(), |
| logically_last_run_layout_item) && |
| !is_logically_last_run_wrapped); |
| |
| // We include the border under these conditions: |
| // (1) The next line was not created, or it is constructed. We check the |
| // previous line for rtl. |
| // (2) The logicallyLastRun is not a descendant of this layout object. |
| // (3) The logicallyLastRun is a descendant of this layout object, but it |
| // is the last child of this layout object and it does not wrap to the |
| // next line. |
| // (4) The decoration break is set to clone therefore there will be |
| // borders on every sides. |
| if (GetLineLayoutItem().StyleRef().BoxDecorationBreak() == |
| EBoxDecorationBreak::kClone) { |
| include_left_edge = include_right_edge = true; |
| } else if (ltr) { |
| if (!NextForSameLayoutObject() && |
| ((last_line || is_last_object_on_line) && |
| !inline_flow.Continuation())) |
| include_right_edge = true; |
| } else { |
| if ((!PrevForSameLayoutObject() || |
| PrevForSameLayoutObject()->IsConstructed()) && |
| ((last_line || is_last_object_on_line) && |
| !inline_flow.Continuation())) |
| include_left_edge = true; |
| } |
| } |
| } |
| |
| SetEdges(include_left_edge, include_right_edge); |
| |
| // Recur into our children. |
| for (InlineBox* curr_child = FirstChild(); curr_child; |
| curr_child = curr_child->NextOnLine()) { |
| if (curr_child->IsInlineFlowBox()) { |
| InlineFlowBox* curr_flow = ToInlineFlowBox(curr_child); |
| curr_flow->DetermineSpacingForFlowBoxes(last_line, |
| is_logically_last_run_wrapped, |
| logically_last_run_layout_object); |
| } |
| } |
| } |
| |
| LayoutUnit InlineFlowBox::PlaceBoxesInInlineDirection( |
| LayoutUnit logical_left, |
| bool& needs_word_spacing) { |
| // Set our x position. |
| BeginPlacingBoxRangesInInlineDirection(logical_left); |
| |
| LayoutUnit start_logical_left = logical_left; |
| logical_left += BorderLogicalLeft() + PaddingLogicalLeft(); |
| |
| LayoutUnit min_logical_left = start_logical_left; |
| LayoutUnit max_logical_right = logical_left; |
| |
| PlaceBoxRangeInInlineDirection(FirstChild(), nullptr, logical_left, |
| min_logical_left, max_logical_right, |
| needs_word_spacing); |
| |
| logical_left += BorderLogicalRight() + PaddingLogicalRight(); |
| EndPlacingBoxRangesInInlineDirection(start_logical_left, logical_left, |
| min_logical_left, max_logical_right); |
| return logical_left; |
| } |
| |
| // TODO(wkorman): needsWordSpacing may not need to be a reference in the below. |
| // Seek a test case. |
| void InlineFlowBox::PlaceBoxRangeInInlineDirection( |
| InlineBox* first_child, |
| InlineBox* last_child, |
| LayoutUnit& logical_left, |
| LayoutUnit& min_logical_left, |
| LayoutUnit& max_logical_right, |
| bool& needs_word_spacing) { |
| for (InlineBox* curr = first_child; curr && curr != last_child; |
| curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsText()) { |
| InlineTextBox* text = ToInlineTextBox(curr); |
| LineLayoutText rt = text->GetLineLayoutItem(); |
| LayoutUnit space; |
| if (rt.TextLength()) { |
| if (needs_word_spacing && |
| IsSpaceOrNewline(rt.CharacterAt(text->Start()))) |
| space = LayoutUnit(rt.Style(IsFirstLineStyle()) |
| ->GetFont() |
| .GetFontDescription() |
| .WordSpacing()); |
| needs_word_spacing = !IsSpaceOrNewline(rt.CharacterAt(text->end())); |
| } |
| if (IsLeftToRightDirection()) { |
| logical_left += space; |
| text->SetLogicalLeft(logical_left); |
| } else { |
| text->SetLogicalLeft(logical_left); |
| logical_left += space; |
| } |
| if (KnownToHaveNoOverflow()) |
| min_logical_left = std::min(logical_left, min_logical_left); |
| logical_left += text->LogicalWidth(); |
| if (KnownToHaveNoOverflow()) |
| max_logical_right = std::max(logical_left, max_logical_right); |
| } else { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) { |
| if (curr->GetLineLayoutItem() |
| .Parent() |
| .StyleRef() |
| .IsLeftToRightDirection()) { |
| curr->SetLogicalLeft(logical_left); |
| } else { |
| // Our offset that we cache needs to be from the edge of the right |
| // border box and not the left border box. We have to subtract |x| |
| // from the width of the block (which can be obtained from the root |
| // line box). |
| curr->SetLogicalLeft(Root().Block().LogicalWidth() - logical_left); |
| } |
| continue; // The positioned object has no effect on the width. |
| } |
| if (curr->GetLineLayoutItem().IsLayoutInline()) { |
| InlineFlowBox* flow = ToInlineFlowBox(curr); |
| logical_left += flow->MarginLogicalLeft(); |
| if (KnownToHaveNoOverflow()) |
| min_logical_left = std::min(logical_left, min_logical_left); |
| logical_left = |
| flow->PlaceBoxesInInlineDirection(logical_left, needs_word_spacing); |
| if (KnownToHaveNoOverflow()) |
| max_logical_right = std::max(logical_left, max_logical_right); |
| logical_left += flow->MarginLogicalRight(); |
| } else if (!curr->GetLineLayoutItem().IsListMarker() || |
| LineLayoutListMarker(curr->GetLineLayoutItem()).IsInside()) { |
| // The box can have a different writing-mode than the overall line, so |
| // this is a bit complicated. Just get all the physical margin and |
| // overflow values by hand based off |isHorizontal|. |
| LineLayoutBoxModel box = curr->BoxModelObject(); |
| LayoutUnit logical_left_margin; |
| LayoutUnit logical_right_margin; |
| if (IsHorizontal()) { |
| logical_left_margin = box.MarginLeft(); |
| logical_right_margin = box.MarginRight(); |
| } else { |
| logical_left_margin = box.MarginTop(); |
| logical_right_margin = box.MarginBottom(); |
| } |
| |
| logical_left += logical_left_margin; |
| curr->SetLogicalLeft(logical_left); |
| if (KnownToHaveNoOverflow()) |
| min_logical_left = std::min(logical_left, min_logical_left); |
| logical_left += curr->LogicalWidth(); |
| if (KnownToHaveNoOverflow()) |
| max_logical_right = std::max(logical_left, max_logical_right); |
| logical_left += logical_right_margin; |
| // If we encounter any space after this inline block then ensure it is |
| // treated as the space between two words. |
| needs_word_spacing = true; |
| } |
| } |
| } |
| } |
| |
| FontBaseline InlineFlowBox::DominantBaseline() const { |
| // Use "central" (Ideographic) baseline if writing-mode is vertical-* and |
| // text-orientation is not sideways-*. |
| // https://drafts.csswg.org/css-writing-modes/#text-baselines |
| if (!IsHorizontal() && GetLineLayoutItem() |
| .Style(IsFirstLineStyle()) |
| ->GetFontDescription() |
| .IsVerticalAnyUpright()) |
| return kIdeographicBaseline; |
| return kAlphabeticBaseline; |
| } |
| |
| void InlineFlowBox::AdjustMaxAscentAndDescent(LayoutUnit& max_ascent, |
| LayoutUnit& max_descent, |
| int max_position_top, |
| int max_position_bottom) { |
| LayoutUnit original_max_ascent(max_ascent); |
| LayoutUnit original_max_descent(max_descent); |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| // The computed lineheight needs to be extended for the |
| // positioned elements |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| if (curr->VerticalAlign() == EVerticalAlign::kTop || |
| curr->VerticalAlign() == EVerticalAlign::kBottom) { |
| int line_height = curr->LineHeight().Round(); |
| if (curr->VerticalAlign() == EVerticalAlign::kTop) { |
| if (max_ascent + max_descent < line_height) |
| max_descent = line_height - max_ascent; |
| } else { |
| if (max_ascent + max_descent < line_height) |
| max_ascent = line_height - max_descent; |
| } |
| |
| if (max_ascent + max_descent >= |
| std::max(max_position_top, max_position_bottom)) |
| break; |
| max_ascent = original_max_ascent; |
| max_descent = original_max_descent; |
| } |
| |
| if (curr->IsInlineFlowBox()) |
| ToInlineFlowBox(curr)->AdjustMaxAscentAndDescent( |
| max_ascent, max_descent, max_position_top, max_position_bottom); |
| } |
| } |
| |
| void InlineFlowBox::ComputeLogicalBoxHeights( |
| RootInlineBox* root_box, |
| LayoutUnit& max_position_top, |
| LayoutUnit& max_position_bottom, |
| LayoutUnit& max_ascent, |
| LayoutUnit& max_descent, |
| bool& set_max_ascent, |
| bool& set_max_descent, |
| bool no_quirks_mode, |
| GlyphOverflowAndFallbackFontsMap& text_box_data_map, |
| FontBaseline baseline_type, |
| VerticalPositionCache& vertical_position_cache) { |
| // The primary purpose of this function is to compute the maximal ascent and |
| // descent values for a line. |
| // |
| // The maxAscent value represents the distance of the highest point of any box |
| // (typically including line-height) from the root box's baseline. The |
| // maxDescent value represents the distance of the lowest point of any box |
| // (also typically including line-height) from the root box baseline. These |
| // values can be negative. |
| // |
| // A secondary purpose of this function is to store the offset of every box's |
| // baseline from the root box's baseline. This information is cached in the |
| // logicalTop() of every box. We're effectively just using the logicalTop() as |
| // scratch space. |
| // |
| // Because a box can be positioned such that it ends up fully above or fully |
| // below the root line box, we only consider it to affect the maxAscent and |
| // maxDescent values if some part of the box (EXCLUDING leading) is above (for |
| // ascent) or below (for descent) the root box's baseline. |
| bool affects_ascent = false; |
| bool affects_descent = false; |
| bool check_children = !DescendantsHaveSameLineHeightAndBaseline(); |
| |
| DCHECK(root_box); |
| if (!root_box) |
| return; |
| |
| if (IsRootInlineBox()) { |
| // Examine our root box. |
| LayoutUnit ascent; |
| LayoutUnit descent; |
| root_box->AscentAndDescentForBox(root_box, text_box_data_map, ascent, |
| descent, affects_ascent, affects_descent); |
| if (no_quirks_mode || HasTextChildren() || |
| (!check_children && HasTextDescendants())) { |
| if (max_ascent < ascent || !set_max_ascent) { |
| max_ascent = ascent; |
| set_max_ascent = true; |
| } |
| if (max_descent < descent || !set_max_descent) { |
| max_descent = descent; |
| set_max_descent = true; |
| } |
| } |
| } |
| |
| if (!check_children) |
| return; |
| |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| InlineFlowBox* inline_flow_box = |
| curr->IsInlineFlowBox() ? ToInlineFlowBox(curr) : nullptr; |
| |
| bool affects_ascent = false; |
| bool affects_descent = false; |
| |
| // The verticalPositionForBox function returns the distance between the |
| // child box's baseline and the root box's baseline. The value is negative |
| // if the child box's baseline is above the root box's baseline, and it is |
| // positive if the child box's baseline is below the root box's baseline. |
| DCHECK(root_box); |
| curr->SetLogicalTop( |
| root_box->VerticalPositionForBox(curr, vertical_position_cache)); |
| |
| LayoutUnit ascent; |
| LayoutUnit descent; |
| root_box->AscentAndDescentForBox(curr, text_box_data_map, ascent, descent, |
| affects_ascent, affects_descent); |
| |
| LayoutUnit box_height(ascent + descent); |
| if (curr->VerticalAlign() == EVerticalAlign::kTop) { |
| if (max_position_top < box_height) |
| max_position_top = box_height; |
| } else if (curr->VerticalAlign() == EVerticalAlign::kBottom) { |
| if (max_position_bottom < box_height) |
| max_position_bottom = box_height; |
| } else if (!inline_flow_box || no_quirks_mode || |
| inline_flow_box->HasTextChildren() || |
| (inline_flow_box->DescendantsHaveSameLineHeightAndBaseline() && |
| inline_flow_box->HasTextDescendants()) || |
| inline_flow_box->BoxModelObject() |
| .HasInlineDirectionBordersOrPadding()) { |
| // Note that these values can be negative. Even though we only affect the |
| // maxAscent and maxDescent values if our box (excluding line-height) was |
| // above (for ascent) or below (for descent) the root baseline, once you |
| // factor in line-height the final box can end up being fully above or |
| // fully below the root box's baseline! This is ok, but what it means is |
| // that ascent and descent (including leading), can end up being negative. |
| // The setMaxAscent and setMaxDescent booleans are used to ensure that |
| // we're willing to initially set maxAscent/Descent to negative values. |
| ascent -= curr->LogicalTop(); |
| descent += curr->LogicalTop(); |
| if (affects_ascent && (max_ascent < ascent || !set_max_ascent)) { |
| max_ascent = ascent; |
| set_max_ascent = true; |
| } |
| |
| if (affects_descent && (max_descent < descent || !set_max_descent)) { |
| max_descent = descent; |
| set_max_descent = true; |
| } |
| } |
| |
| if (inline_flow_box) |
| inline_flow_box->ComputeLogicalBoxHeights( |
| root_box, max_position_top, max_position_bottom, max_ascent, |
| max_descent, set_max_ascent, set_max_descent, no_quirks_mode, |
| text_box_data_map, baseline_type, vertical_position_cache); |
| } |
| } |
| |
| void InlineFlowBox::PlaceBoxesInBlockDirection( |
| LayoutUnit top, |
| LayoutUnit max_height, |
| LayoutUnit max_ascent, |
| bool no_quirks_mode, |
| LayoutUnit& line_top, |
| LayoutUnit& line_bottom, |
| LayoutUnit& selection_bottom, |
| bool& set_line_top, |
| LayoutUnit& line_top_including_margins, |
| LayoutUnit& line_bottom_including_margins, |
| bool& has_annotations_before, |
| bool& has_annotations_after, |
| FontBaseline baseline_type) { |
| bool is_root_box = IsRootInlineBox(); |
| if (is_root_box) { |
| const SimpleFontData* font_data = |
| GetLineLayoutItem().Style(IsFirstLineStyle())->GetFont().PrimaryFont(); |
| DCHECK(font_data); |
| if (!font_data) |
| return; |
| const FontMetrics& font_metrics = font_data->GetFontMetrics(); |
| // RootInlineBoxes are always placed at pixel boundaries in their logical y |
| // direction. Not doing so results in incorrect layout of text decorations, |
| // most notably underlines. |
| SetLogicalTop(top + max_ascent - font_metrics.Ascent(baseline_type)); |
| } |
| |
| LayoutUnit adjustment_for_children_with_same_line_height_and_baseline; |
| if (DescendantsHaveSameLineHeightAndBaseline()) { |
| adjustment_for_children_with_same_line_height_and_baseline = LogicalTop(); |
| if (Parent()) |
| adjustment_for_children_with_same_line_height_and_baseline += |
| BoxModelObject().BorderAndPaddingOver(); |
| } |
| |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| if (DescendantsHaveSameLineHeightAndBaseline()) { |
| curr->MoveInBlockDirection( |
| adjustment_for_children_with_same_line_height_and_baseline); |
| continue; |
| } |
| |
| InlineFlowBox* inline_flow_box = |
| curr->IsInlineFlowBox() ? ToInlineFlowBox(curr) : nullptr; |
| bool child_affects_top_bottom_pos = true; |
| if (curr->VerticalAlign() == EVerticalAlign::kTop) { |
| curr->SetLogicalTop(top); |
| } else if (curr->VerticalAlign() == EVerticalAlign::kBottom) { |
| curr->SetLogicalTop((top + max_height - curr->LineHeight())); |
| } else { |
| if (!no_quirks_mode && inline_flow_box && |
| !inline_flow_box->HasTextChildren() && |
| !curr->BoxModelObject().HasInlineDirectionBordersOrPadding() && |
| !(inline_flow_box->DescendantsHaveSameLineHeightAndBaseline() && |
| inline_flow_box->HasTextDescendants())) |
| child_affects_top_bottom_pos = false; |
| LayoutUnit pos_adjust = |
| max_ascent - curr->BaselinePosition(baseline_type); |
| curr->SetLogicalTop(curr->LogicalTop() + top + pos_adjust); |
| } |
| |
| LayoutUnit new_logical_top = curr->LogicalTop(); |
| LayoutUnit new_logical_top_including_margins = new_logical_top; |
| LayoutUnit box_height = curr->LogicalHeight(); |
| LayoutUnit box_height_including_margins = box_height; |
| LayoutUnit border_padding_height; |
| if (curr->IsText() || curr->IsInlineFlowBox()) { |
| const SimpleFontData* font_data = curr->GetLineLayoutItem() |
| .Style(IsFirstLineStyle()) |
| ->GetFont() |
| .PrimaryFont(); |
| DCHECK(font_data); |
| if (!font_data) |
| continue; |
| |
| const FontMetrics& font_metrics = font_data->GetFontMetrics(); |
| new_logical_top += curr->BaselinePosition(baseline_type) - |
| font_metrics.Ascent(baseline_type); |
| if (curr->IsInlineFlowBox()) { |
| LineLayoutBoxModel box_object = |
| LineLayoutBoxModel(curr->GetLineLayoutItem()); |
| new_logical_top -= box_object.BorderAndPaddingOver(); |
| border_padding_height = box_object.BorderAndPaddingLogicalHeight(); |
| } |
| new_logical_top_including_margins = new_logical_top; |
| } else if (!curr->GetLineLayoutItem().IsBR()) { |
| LineLayoutBox box = LineLayoutBox(curr->GetLineLayoutItem()); |
| new_logical_top_including_margins = new_logical_top; |
| // TODO(kojii): isHorizontal() does not match to |
| // m_layoutObject.isHorizontalWritingMode(). crbug.com/552954 |
| // DCHECK_EQ(curr->isHorizontal(), |
| // curr->getLineLayoutItem().style()->isHorizontalWritingMode()); |
| // We may flip lines in case of verticalLR mode, so we can |
| // assume verticalRL for now. |
| LayoutUnit over_side_margin = |
| curr->IsHorizontal() ? box.MarginTop() : box.MarginRight(); |
| LayoutUnit under_side_margin = |
| curr->IsHorizontal() ? box.MarginBottom() : box.MarginLeft(); |
| new_logical_top += over_side_margin; |
| box_height_including_margins += over_side_margin + under_side_margin; |
| } |
| |
| curr->SetLogicalTop(new_logical_top); |
| |
| if (child_affects_top_bottom_pos) { |
| if (curr->GetLineLayoutItem().IsRubyRun()) { |
| // Treat the leading on the first and last lines of ruby runs as not |
| // being part of the overall lineTop/lineBottom. |
| // Really this is a workaround hack for the fact that ruby should have |
| // been done as line layout and not done using inline-block. |
| if (GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() == |
| (curr->GetLineLayoutItem().StyleRef().GetRubyPosition() == |
| RubyPosition::kAfter)) |
| has_annotations_before = true; |
| else |
| has_annotations_after = true; |
| |
| LineLayoutRubyRun ruby_run = |
| LineLayoutRubyRun(curr->GetLineLayoutItem()); |
| if (LineLayoutRubyBase ruby_base = ruby_run.RubyBase()) { |
| LayoutUnit bottom_ruby_base_leading = |
| (curr->LogicalHeight() - ruby_base.LogicalBottom()) + |
| ruby_base.LogicalHeight() - |
| (ruby_base.LastRootBox() ? ruby_base.LastRootBox()->LineBottom() |
| : LayoutUnit()); |
| LayoutUnit top_ruby_base_leading = |
| ruby_base.LogicalTop() + |
| (ruby_base.FirstRootBox() ? ruby_base.FirstRootBox()->LineTop() |
| : LayoutUnit()); |
| new_logical_top += |
| !GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() |
| ? top_ruby_base_leading |
| : bottom_ruby_base_leading; |
| box_height -= (top_ruby_base_leading + bottom_ruby_base_leading); |
| } |
| } |
| if (curr->IsInlineTextBox()) { |
| TextEmphasisPosition emphasis_mark_position; |
| if (ToInlineTextBox(curr)->GetEmphasisMarkPosition( |
| curr->GetLineLayoutItem().StyleRef(IsFirstLineStyle()), |
| emphasis_mark_position)) { |
| if (HasEmphasisMarkBefore(ToInlineTextBox(curr))) |
| has_annotations_before = true; |
| else |
| has_annotations_after = true; |
| } |
| } |
| |
| if (!set_line_top) { |
| set_line_top = true; |
| line_top = new_logical_top; |
| line_top_including_margins = |
| std::min(line_top, new_logical_top_including_margins); |
| } else { |
| line_top = std::min(line_top, new_logical_top); |
| line_top_including_margins = |
| std::min(line_top, std::min(line_top_including_margins, |
| new_logical_top_including_margins)); |
| } |
| selection_bottom = |
| std::max(selection_bottom, |
| new_logical_top + box_height - border_padding_height); |
| line_bottom = std::max(line_bottom, new_logical_top + box_height); |
| line_bottom_including_margins = |
| std::max(line_bottom, std::max(line_bottom_including_margins, |
| new_logical_top_including_margins + |
| box_height_including_margins)); |
| } |
| |
| // Adjust boxes to use their real box y/height and not the logical height |
| // (as dictated by line-height). |
| if (inline_flow_box) |
| inline_flow_box->PlaceBoxesInBlockDirection( |
| top, max_height, max_ascent, no_quirks_mode, line_top, line_bottom, |
| selection_bottom, set_line_top, line_top_including_margins, |
| line_bottom_including_margins, has_annotations_before, |
| has_annotations_after, baseline_type); |
| } |
| |
| if (is_root_box) { |
| if (no_quirks_mode || HasTextChildren() || |
| (DescendantsHaveSameLineHeightAndBaseline() && HasTextDescendants())) { |
| if (!set_line_top) { |
| set_line_top = true; |
| line_top = LogicalTop(); |
| line_top_including_margins = line_top; |
| } else { |
| line_top = std::min(line_top, LogicalTop()); |
| line_top_including_margins = |
| std::min(line_top, line_top_including_margins); |
| } |
| selection_bottom = std::max(selection_bottom, LogicalBottom()); |
| line_bottom = std::max(line_bottom, LogicalBottom()); |
| line_bottom_including_margins = |
| std::max(line_bottom, line_bottom_including_margins); |
| } |
| |
| if (GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()) |
| FlipLinesInBlockDirection(line_top_including_margins, |
| line_bottom_including_margins); |
| } |
| } |
| |
| LayoutUnit InlineFlowBox::FarthestPositionForUnderline( |
| LineLayoutItem decorating_box, |
| FontVerticalPositionType position_type, |
| FontBaseline baseline_type, |
| LayoutUnit farthest) const { |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| // If the text decoration isn't in effect on the child, it must be outside |
| // of |decorationObject|. |
| if (!EnumHasFlags(curr->LineStyleRef().TextDecorationsInEffect(), |
| TextDecoration::kUnderline)) |
| continue; |
| |
| if (decorating_box && decorating_box.IsLayoutInline() && |
| !IsAncestorAndWithinBlock(decorating_box, curr->GetLineLayoutItem())) |
| continue; |
| |
| if (curr->IsInlineFlowBox()) { |
| farthest = ToInlineFlowBox(curr)->FarthestPositionForUnderline( |
| decorating_box, position_type, baseline_type, farthest); |
| } else if (curr->IsInlineTextBox()) { |
| LayoutUnit position = |
| ToInlineTextBox(curr)->VerticalPosition(position_type, baseline_type); |
| if (IsLineOverSide(position_type)) |
| farthest = std::min(farthest, position); |
| else |
| farthest = std::max(farthest, position); |
| } |
| } |
| return farthest; |
| } |
| |
| void InlineFlowBox::FlipLinesInBlockDirection(LayoutUnit line_top, |
| LayoutUnit line_bottom) { |
| // Flip the box on the line such that the top is now relative to the |
| // lineBottom instead of the lineTop. |
| SetLogicalTop(line_bottom - (LogicalTop() - line_top) - LogicalHeight()); |
| |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders aren't affected here. |
| |
| if (curr->IsInlineFlowBox()) |
| ToInlineFlowBox(curr)->FlipLinesInBlockDirection(line_top, line_bottom); |
| else |
| curr->SetLogicalTop(line_bottom - (curr->LogicalTop() - line_top) - |
| curr->LogicalHeight()); |
| } |
| } |
| |
| inline void InlineFlowBox::AddBoxShadowVisualOverflow( |
| LayoutRect& logical_visual_overflow) { |
| const ComputedStyle& style = GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| |
| // box-shadow on the block element applies to the block and not to the lines, |
| // unless it is modified by :first-line pseudo element. |
| if (!Parent() && |
| (!IsFirstLineStyle() || &style == GetLineLayoutItem().Style())) |
| return; |
| |
| WritingMode writing_mode = style.GetWritingMode(); |
| ShadowList* box_shadow = style.BoxShadow(); |
| if (!box_shadow) |
| return; |
| |
| LayoutRectOutsets outsets(box_shadow->RectOutsetsIncludingOriginal()); |
| // Similar to how glyph overflow works, if our lines are flipped, then it's |
| // actually the opposite shadow that applies, since the line is "upside down" |
| // in terms of block coordinates. |
| LayoutRectOutsets logical_outsets = |
| LineOrientationLayoutRectOutsetsWithFlippedLines(outsets, writing_mode); |
| |
| LayoutRect shadow_bounds(LogicalFrameRect()); |
| shadow_bounds.Expand(logical_outsets); |
| logical_visual_overflow.Unite(shadow_bounds); |
| } |
| |
| inline void InlineFlowBox::AddBorderOutsetVisualOverflow( |
| LayoutRect& logical_visual_overflow) { |
| const ComputedStyle& style = GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| |
| // border-image-outset on the block element applies to the block and not to |
| // the lines, unless it is modified by :first-line pseudo element. |
| if (!Parent() && |
| (!IsFirstLineStyle() || &style == GetLineLayoutItem().Style())) |
| return; |
| |
| if (!style.HasBorderImageOutsets()) |
| return; |
| |
| // Similar to how glyph overflow works, if our lines are flipped, then it's |
| // actually the opposite border that applies, since the line is "upside down" |
| // in terms of block coordinates. vertical-rl is the flipped line mode. |
| LayoutRectOutsets logical_outsets = |
| LineOrientationLayoutRectOutsetsWithFlippedLines( |
| style.BorderImageOutsets(), style.GetWritingMode()); |
| |
| if (!IncludeLogicalLeftEdge()) |
| logical_outsets.SetLeft(LayoutUnit()); |
| if (!IncludeLogicalRightEdge()) |
| logical_outsets.SetRight(LayoutUnit()); |
| |
| LayoutRect border_outset_bounds(LogicalFrameRect()); |
| border_outset_bounds.Expand(logical_outsets); |
| logical_visual_overflow.Unite(border_outset_bounds); |
| } |
| |
| inline void InlineFlowBox::AddOutlineVisualOverflow( |
| LayoutRect& logical_visual_overflow) { |
| // Outline on root line boxes is applied to the block and not to the lines. |
| if (!Parent()) |
| return; |
| |
| const ComputedStyle& style = GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| if (!style.HasOutline()) |
| return; |
| |
| logical_visual_overflow.Inflate(style.OutlineOutsetExtent()); |
| } |
| |
| inline void InlineFlowBox::AddTextBoxVisualOverflow( |
| InlineTextBox* text_box, |
| GlyphOverflowAndFallbackFontsMap& text_box_data_map, |
| LayoutRect& logical_visual_overflow) { |
| if (text_box->KnownToHaveNoOverflow()) |
| return; |
| |
| LayoutRectOutsets visual_rect_outsets; |
| const ComputedStyle& style = |
| text_box->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| |
| GlyphOverflowAndFallbackFontsMap::iterator it = |
| text_box_data_map.find(text_box); |
| if (it != text_box_data_map.end()) { |
| const GlyphOverflow& glyph_overflow = it->value.second; |
| bool is_flipped_line = style.IsFlippedLinesWritingMode(); |
| visual_rect_outsets = EnclosingLayoutRectOutsets(FloatRectOutsets( |
| is_flipped_line ? glyph_overflow.bottom : glyph_overflow.top, |
| glyph_overflow.right, |
| is_flipped_line ? glyph_overflow.top : glyph_overflow.bottom, |
| glyph_overflow.left)); |
| } |
| |
| if (float stroke_width = style.TextStrokeWidth()) { |
| visual_rect_outsets += LayoutUnit::FromFloatCeil(stroke_width / 2.0f); |
| } |
| |
| TextEmphasisPosition emphasis_mark_position; |
| if (style.GetTextEmphasisMark() != TextEmphasisMark::kNone && |
| text_box->GetEmphasisMarkPosition(style, emphasis_mark_position)) { |
| LayoutUnit emphasis_mark_height = LayoutUnit( |
| style.GetFont().EmphasisMarkHeight(style.TextEmphasisMarkString())); |
| if (HasEmphasisMarkBefore(text_box)) { |
| visual_rect_outsets.SetTop( |
| std::max(visual_rect_outsets.Top(), emphasis_mark_height)); |
| } else { |
| visual_rect_outsets.SetBottom( |
| std::max(visual_rect_outsets.Bottom(), emphasis_mark_height)); |
| } |
| } |
| |
| if (ShadowList* text_shadow = style.TextShadow()) { |
| LayoutRectOutsets text_shadow_logical_outsets = |
| LineOrientationLayoutRectOutsets( |
| LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()), |
| style.GetWritingMode()); |
| text_shadow_logical_outsets.ClampNegativeToZero(); |
| visual_rect_outsets += text_shadow_logical_outsets; |
| } |
| |
| LayoutRect frame_rect = text_box->LogicalFrameRect(); |
| frame_rect.Expand(visual_rect_outsets); |
| frame_rect = LayoutRect(EnclosingIntRect(frame_rect)); |
| logical_visual_overflow.Unite(frame_rect); |
| |
| if (logical_visual_overflow != text_box->LogicalFrameRect()) |
| text_box->SetLogicalOverflowRect(logical_visual_overflow); |
| } |
| |
| inline void InlineFlowBox::AddReplacedChildOverflow( |
| const InlineBox* inline_box, |
| LayoutRect& logical_layout_overflow, |
| LayoutRect& logical_visual_overflow) { |
| LineLayoutBox box = LineLayoutBox(inline_box->GetLineLayoutItem()); |
| |
| // Visual overflow only propagates if the box doesn't have a self-painting |
| // layer. This rectangle does not include transforms or relative positioning |
| // (since those objects always have self-painting layers), but it does need to |
| // be adjusted for writing-mode differences. |
| if (!box.HasSelfPaintingLayer()) { |
| LayoutRect child_logical_visual_overflow = |
| box.LogicalVisualOverflowRectForPropagation(); |
| child_logical_visual_overflow.Move(inline_box->LogicalLeft(), |
| inline_box->LogicalTop()); |
| logical_visual_overflow.Unite(child_logical_visual_overflow); |
| } |
| |
| // Layout overflow internal to the child box only propagates if the child box |
| // doesn't have overflow clip set. Otherwise the child border box propagates |
| // as layout overflow. This rectangle must include transforms and relative |
| // positioning and be adjusted for writing-mode differences. |
| LayoutRect child_logical_layout_overflow = |
| box.LogicalLayoutOverflowRectForPropagation(); |
| child_logical_layout_overflow.Move(inline_box->LogicalLeft(), |
| inline_box->LogicalTop()); |
| logical_layout_overflow.Unite(child_logical_layout_overflow); |
| } |
| |
| static void ComputeGlyphOverflow( |
| InlineTextBox* text, |
| const LineLayoutText& layout_text, |
| GlyphOverflowAndFallbackFontsMap& text_box_data_map) { |
| HashSet<const SimpleFontData*> fallback_fonts; |
| FloatRect glyph_bounds; |
| GlyphOverflow glyph_overflow; |
| float measured_width = layout_text.Width( |
| text->Start(), text->Len(), LayoutUnit(), text->Direction(), false, |
| &fallback_fonts, &glyph_bounds); |
| const Font& font = layout_text.StyleRef().GetFont(); |
| glyph_overflow.SetFromBounds(glyph_bounds, font, measured_width); |
| if (!fallback_fonts.IsEmpty()) { |
| GlyphOverflowAndFallbackFontsMap::ValueType* it = |
| text_box_data_map |
| .insert(text, std::make_pair(Vector<const SimpleFontData*>(), |
| GlyphOverflow())) |
| .stored_value; |
| DCHECK(it->value.first.IsEmpty()); |
| CopyToVector(fallback_fonts, it->value.first); |
| } |
| if (!glyph_overflow.IsApproximatelyZero()) { |
| GlyphOverflowAndFallbackFontsMap::ValueType* it = |
| text_box_data_map |
| .insert(text, std::make_pair(Vector<const SimpleFontData*>(), |
| GlyphOverflow())) |
| .stored_value; |
| it->value.second = glyph_overflow; |
| } |
| } |
| |
| void InlineFlowBox::ComputeOverflow( |
| LayoutUnit line_top, |
| LayoutUnit line_bottom, |
| GlyphOverflowAndFallbackFontsMap& text_box_data_map) { |
| // If we know we have no overflow, we can just bail. |
| if (KnownToHaveNoOverflow()) { |
| DCHECK(!overflow_); |
| return; |
| } |
| |
| if (overflow_) |
| overflow_.reset(); |
| |
| // Visual overflow just includes overflow for stuff we need to issues paint |
| // invalidations for ourselves. Self-painting layers are ignored. |
| // Layout overflow is used to determine scrolling extent, so it still includes |
| // child layers and also factors in transforms, relative positioning, etc. |
| LayoutRect logical_layout_overflow; |
| LayoutRect logical_visual_overflow( |
| LogicalFrameRectIncludingLineHeight(line_top, line_bottom)); |
| |
| AddBoxShadowVisualOverflow(logical_visual_overflow); |
| AddBorderOutsetVisualOverflow(logical_visual_overflow); |
| AddOutlineVisualOverflow(logical_visual_overflow); |
| |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| if (curr->GetLineLayoutItem().IsText()) { |
| InlineTextBox* text = ToInlineTextBox(curr); |
| LayoutRect text_box_overflow(text->LogicalFrameRect()); |
| if (text->IsLineBreak()) { |
| text_box_overflow.SetWidth( |
| LayoutUnit(text_box_overflow.Width() + text->NewlineSpaceWidth())); |
| } else { |
| if (text_box_data_map.IsEmpty()) { |
| // An empty glyph map means that we're computing overflow without |
| // a layout, so calculate the glyph overflow on the fly. |
| GlyphOverflowAndFallbackFontsMap glyph_overflow_for_text; |
| ComputeGlyphOverflow(text, text->GetLineLayoutItem(), |
| glyph_overflow_for_text); |
| AddTextBoxVisualOverflow(text, glyph_overflow_for_text, |
| text_box_overflow); |
| } else { |
| AddTextBoxVisualOverflow(text, text_box_data_map, text_box_overflow); |
| } |
| } |
| logical_visual_overflow.Unite(text_box_overflow); |
| } else if (curr->GetLineLayoutItem().IsLayoutInline()) { |
| InlineFlowBox* flow = ToInlineFlowBox(curr); |
| flow->ComputeOverflow(line_top, line_bottom, text_box_data_map); |
| if (!flow->BoxModelObject().HasSelfPaintingLayer()) |
| logical_visual_overflow.Unite( |
| flow->LogicalVisualOverflowRect(line_top, line_bottom)); |
| LayoutRect child_layout_overflow = |
| flow->LogicalLayoutOverflowRect(line_top, line_bottom); |
| child_layout_overflow.Unite( |
| LogicalFrameRectIncludingLineHeight(line_top, line_bottom)); |
| if (flow->BoxModelObject().IsRelPositioned()) { |
| child_layout_overflow.Move( |
| flow->BoxModelObject().RelativePositionLogicalOffset()); |
| } |
| logical_layout_overflow.Unite(child_layout_overflow); |
| } else { |
| if (logical_layout_overflow.IsEmpty()) { |
| logical_layout_overflow = |
| LogicalFrameRectIncludingLineHeight(line_top, line_bottom); |
| } |
| AddReplacedChildOverflow(curr, logical_layout_overflow, |
| logical_visual_overflow); |
| } |
| } |
| |
| SetOverflowFromLogicalRects(logical_layout_overflow, logical_visual_overflow, |
| line_top, line_bottom); |
| } |
| |
| void InlineFlowBox::SetLayoutOverflow(const LayoutRect& rect, |
| const LayoutRect& frame_box) { |
| DCHECK(!KnownToHaveNoOverflow()); |
| if (frame_box.Contains(rect) || rect.IsEmpty()) |
| return; |
| |
| if (!overflow_) |
| overflow_ = std::make_unique<SimpleOverflowModel>(frame_box, frame_box); |
| |
| overflow_->SetLayoutOverflow(rect); |
| } |
| |
| void InlineFlowBox::SetVisualOverflow(const LayoutRect& rect, |
| const LayoutRect& frame_box) { |
| DCHECK(!KnownToHaveNoOverflow()); |
| if (frame_box.Contains(rect) || rect.IsEmpty()) |
| return; |
| |
| if (!overflow_) |
| overflow_ = std::make_unique<SimpleOverflowModel>(frame_box, frame_box); |
| |
| overflow_->SetVisualOverflow(rect); |
| } |
| |
| void InlineFlowBox::SetOverflowFromLogicalRects( |
| const LayoutRect& logical_layout_overflow, |
| const LayoutRect& logical_visual_overflow, |
| LayoutUnit line_top, |
| LayoutUnit line_bottom) { |
| DCHECK(!KnownToHaveNoOverflow()); |
| LayoutRect frame_box = FrameRectIncludingLineHeight(line_top, line_bottom); |
| |
| LayoutRect layout_overflow(IsHorizontal() |
| ? logical_layout_overflow |
| : logical_layout_overflow.TransposedRect()); |
| SetLayoutOverflow(layout_overflow, frame_box); |
| |
| LayoutRect visual_overflow(IsHorizontal() |
| ? logical_visual_overflow |
| : logical_visual_overflow.TransposedRect()); |
| SetVisualOverflow(visual_overflow, frame_box); |
| } |
| |
| bool InlineFlowBox::NodeAtPoint(HitTestResult& result, |
| const HitTestLocation& location_in_container, |
| const LayoutPoint& accumulated_offset, |
| LayoutUnit line_top, |
| LayoutUnit line_bottom) { |
| LayoutRect overflow_rect(VisualOverflowRect(line_top, line_bottom)); |
| FlipForWritingMode(overflow_rect); |
| overflow_rect.MoveBy(accumulated_offset); |
| if (!location_in_container.Intersects(overflow_rect)) |
| return false; |
| |
| // We need to hit test both our inline children (Inline Boxes) and culled |
| // inlines (LayoutObjects). We check our inlines in the same order as line |
| // layout but for each inline we additionally need to hit test its culled |
| // inline parents. While hit testing culled inline parents, we can stop once |
| // we reach a non-inline parent or a culled inline associated with a different |
| // inline box. |
| InlineBox* prev; |
| for (InlineBox* curr = LastChild(); curr; curr = prev) { |
| prev = curr->PrevOnLine(); |
| |
| // Layers will handle hit testing themselves. |
| if (!curr->BoxModelObject() || |
| !curr->BoxModelObject().HasSelfPaintingLayer()) { |
| if (curr->NodeAtPoint(result, location_in_container, accumulated_offset, |
| line_top, line_bottom)) { |
| GetLineLayoutItem().UpdateHitTestResult( |
| result, |
| location_in_container.Point() - ToLayoutSize(accumulated_offset)); |
| return true; |
| } |
| } |
| |
| // If the current inline box's layout object and the previous inline box's |
| // layout object are same, we should yield the hit-test to the previous |
| // inline box. |
| if (prev && curr->GetLineLayoutItem() == prev->GetLineLayoutItem()) |
| continue; |
| |
| // Hit test the culled inline if necessary. |
| LineLayoutItem curr_layout_item = curr->GetLineLayoutItem(); |
| while (true) { |
| // If the previous inline box is not a descendant of a current inline's |
| // parent, the parent is a culled inline and we hit test it. |
| // Otherwise, move to the previous inline box because we hit test first |
| // all candidate inline boxes under the parent to take a pre-order tree |
| // traversal in reverse. |
| bool has_sibling = |
| curr_layout_item.PreviousSibling() || curr_layout_item.NextSibling(); |
| LineLayoutItem culled_parent = curr_layout_item.Parent(); |
| DCHECK(culled_parent); |
| |
| if (culled_parent == GetLineLayoutItem() || |
| (has_sibling && prev && |
| prev->GetLineLayoutItem().IsDescendantOf(culled_parent))) |
| break; |
| |
| if (culled_parent.IsLayoutInline() && |
| LineLayoutInline(culled_parent) |
| .HitTestCulledInline(result, location_in_container, |
| accumulated_offset)) |
| return true; |
| |
| curr_layout_item = culled_parent; |
| } |
| } |
| |
| if (GetLineLayoutItem().IsBox() && |
| ToLayoutBox(LineLayoutAPIShim::LayoutObjectFrom(GetLineLayoutItem())) |
| ->HitTestClippedOutByBorder(location_in_container, |
| overflow_rect.Location())) |
| return false; |
| |
| if (GetLineLayoutItem().StyleRef().HasBorderRadius()) { |
| LayoutRect border_rect = LogicalFrameRect(); |
| border_rect.MoveBy(accumulated_offset); |
| FloatRoundedRect border = |
| GetLineLayoutItem().StyleRef().GetRoundedBorderFor( |
| border_rect, IncludeLogicalLeftEdge(), IncludeLogicalRightEdge()); |
| if (!location_in_container.Intersects(border)) |
| return false; |
| } |
| |
| // Now check ourselves. |
| LayoutRect rect = |
| InlineFlowBoxPainter(*this).FrameRectClampedToLineTopAndBottomIfNeeded(); |
| |
| FlipForWritingMode(rect); |
| rect.MoveBy(accumulated_offset); |
| |
| // Pixel snap hit testing. |
| rect = LayoutRect(PixelSnappedIntRect(rect)); |
| if (VisibleToHitTestRequest(result.GetHitTestRequest()) && |
| location_in_container.Intersects(rect)) { |
| // Don't add in m_topLeft here, we want coords in the containing block's |
| // coordinate space. |
| GetLineLayoutItem().UpdateHitTestResult( |
| result, FlipForWritingMode(location_in_container.Point() - |
| ToLayoutSize(accumulated_offset))); |
| if (result.AddNodeToListBasedTestResult(GetLineLayoutItem().GetNode(), |
| location_in_container, |
| rect) == kStopHitTesting) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void InlineFlowBox::Paint(const PaintInfo& paint_info, |
| const LayoutPoint& paint_offset, |
| LayoutUnit line_top, |
| LayoutUnit line_bottom) const { |
| InlineFlowBoxPainter(*this).Paint(paint_info, paint_offset, line_top, |
| line_bottom); |
| } |
| |
| bool InlineFlowBox::BoxShadowCanBeAppliedToBackground( |
| const FillLayer& last_background_layer) const { |
| // The checks here match how paintFillLayer() decides whether to clip (if it |
| // does, the shadow |
| // would be clipped out, so it has to be drawn separately). |
| StyleImage* image = last_background_layer.GetImage(); |
| bool has_fill_image = image && image->CanRender(); |
| return (!has_fill_image && |
| !GetLineLayoutItem().StyleRef().HasBorderRadius()) || |
| (!PrevForSameLayoutObject() && !NextForSameLayoutObject()) || |
| !Parent(); |
| } |
| |
| InlineBox* InlineFlowBox::FirstLeafChild() const { |
| InlineBox* leaf = nullptr; |
| for (InlineBox* child = FirstChild(); child && !leaf; |
| child = child->NextOnLine()) |
| leaf = child->IsLeaf() ? child : ToInlineFlowBox(child)->FirstLeafChild(); |
| return leaf; |
| } |
| |
| InlineBox* InlineFlowBox::LastLeafChild() const { |
| InlineBox* leaf = nullptr; |
| for (InlineBox* child = LastChild(); child && !leaf; |
| child = child->PrevOnLine()) |
| leaf = child->IsLeaf() ? child : ToInlineFlowBox(child)->LastLeafChild(); |
| return leaf; |
| } |
| |
| bool InlineFlowBox::CanAccommodateEllipsis(bool ltr, |
| LayoutUnit block_edge, |
| LayoutUnit ellipsis_width) const { |
| for (InlineBox* box = FirstChild(); box; box = box->NextOnLine()) { |
| if (!box->CanAccommodateEllipsis(ltr, block_edge, ellipsis_width)) |
| return false; |
| } |
| return true; |
| } |
| |
| LayoutUnit InlineFlowBox::PlaceEllipsisBox(bool ltr, |
| LayoutUnit block_left_edge, |
| LayoutUnit block_right_edge, |
| LayoutUnit ellipsis_width, |
| LayoutUnit& truncated_width, |
| InlineBox** found_box, |
| LayoutUnit logical_left_offset) { |
| LayoutUnit result(-1); |
| // We iterate over all children, the foundBox variable tells us when we've |
| // found the box containing the ellipsis. All boxes after that one in the |
| // flow are hidden. |
| // If our flow is ltr then iterate over the boxes from left to right, |
| // otherwise iterate from right to left. Varying the order allows us to |
| // correctly hide the boxes following the ellipsis. |
| LayoutUnit relative_offset = |
| BoxModelObject().IsInline() && BoxModelObject().IsRelPositioned() |
| ? BoxModelObject().RelativePositionLogicalOffset().Width() |
| : LayoutUnit(); |
| logical_left_offset += relative_offset; |
| InlineBox* box = ltr ? FirstChild() : LastChild(); |
| |
| // NOTE: these will cross after foundBox = true. |
| LayoutUnit visible_left_edge = block_left_edge; |
| LayoutUnit visible_right_edge = block_right_edge; |
| |
| while (box) { |
| bool had_found_box = *found_box; |
| LayoutUnit curr_result = box->PlaceEllipsisBox( |
| ltr, visible_left_edge, visible_right_edge, ellipsis_width, |
| truncated_width, found_box, logical_left_offset); |
| if (IsRootInlineBox() && *found_box && !had_found_box) |
| *found_box = box; |
| if (curr_result != -1 && result == -1) |
| result = curr_result; |
| |
| // List markers will sit outside the box so don't let them contribute |
| // width. |
| LayoutUnit box_width = box->GetLineLayoutItem().IsListMarker() |
| ? LayoutUnit() |
| : box->LogicalWidth(); |
| if (ltr) { |
| visible_left_edge += box_width; |
| box = box->NextOnLine(); |
| } else { |
| visible_right_edge -= box_width; |
| box = box->PrevOnLine(); |
| } |
| } |
| return result + relative_offset; |
| } |
| |
| void InlineFlowBox::ClearTruncation() { |
| for (InlineBox* box = FirstChild(); box; box = box->NextOnLine()) |
| box->ClearTruncation(); |
| } |
| |
| LayoutUnit InlineFlowBox::ComputeOverAnnotationAdjustment( |
| LayoutUnit allowed_position) const { |
| LayoutUnit result; |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| if (curr->IsInlineFlowBox()) |
| result = std::max(result, |
| ToInlineFlowBox(curr)->ComputeOverAnnotationAdjustment( |
| allowed_position)); |
| |
| if (curr->GetLineLayoutItem().IsAtomicInlineLevel() && |
| curr->GetLineLayoutItem().IsRubyRun() && |
| curr->GetLineLayoutItem().StyleRef().GetRubyPosition() == |
| RubyPosition::kBefore) { |
| LineLayoutRubyRun ruby_run = LineLayoutRubyRun(curr->GetLineLayoutItem()); |
| LineLayoutRubyText ruby_text = ruby_run.RubyText(); |
| if (!ruby_text) |
| continue; |
| |
| if (!ruby_run.StyleRef().IsFlippedLinesWritingMode()) { |
| LayoutUnit top_of_first_ruby_text_line = |
| ruby_text.LogicalTop() + (ruby_text.FirstRootBox() |
| ? ruby_text.FirstRootBox()->LineTop() |
| : LayoutUnit()); |
| if (top_of_first_ruby_text_line >= 0) |
| continue; |
| top_of_first_ruby_text_line += curr->LogicalTop(); |
| result = |
| std::max(result, allowed_position - top_of_first_ruby_text_line); |
| } else { |
| LayoutUnit bottom_of_last_ruby_text_line = |
| ruby_text.LogicalTop() + |
| (ruby_text.LastRootBox() ? ruby_text.LastRootBox()->LineBottom() |
| : ruby_text.LogicalHeight()); |
| if (bottom_of_last_ruby_text_line <= curr->LogicalHeight()) |
| continue; |
| bottom_of_last_ruby_text_line += curr->LogicalTop(); |
| result = |
| std::max(result, bottom_of_last_ruby_text_line - allowed_position); |
| } |
| } |
| |
| if (curr->IsInlineTextBox()) { |
| const ComputedStyle& style = |
| curr->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| TextEmphasisPosition emphasis_mark_position; |
| if (style.GetTextEmphasisMark() != TextEmphasisMark::kNone && |
| ToInlineTextBox(curr)->GetEmphasisMarkPosition( |
| style, emphasis_mark_position) && |
| HasEmphasisMarkOver(ToInlineTextBox(curr))) { |
| if (!style.IsFlippedLinesWritingMode()) { |
| int top_of_emphasis_mark = |
| (curr->LogicalTop() - style.GetFont().EmphasisMarkHeight( |
| style.TextEmphasisMarkString())) |
| .ToInt(); |
| result = std::max(result, allowed_position - top_of_emphasis_mark); |
| } else { |
| int bottom_of_emphasis_mark = |
| (curr->LogicalBottom() + style.GetFont().EmphasisMarkHeight( |
| style.TextEmphasisMarkString())) |
| .ToInt(); |
| result = std::max(result, bottom_of_emphasis_mark - allowed_position); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| LayoutUnit InlineFlowBox::ComputeUnderAnnotationAdjustment( |
| LayoutUnit allowed_position) const { |
| LayoutUnit result; |
| for (InlineBox* curr = FirstChild(); curr; curr = curr->NextOnLine()) { |
| if (curr->GetLineLayoutItem().IsOutOfFlowPositioned()) |
| continue; // Positioned placeholders don't affect calculations. |
| |
| if (curr->IsInlineFlowBox()) |
| result = std::max(result, |
| ToInlineFlowBox(curr)->ComputeUnderAnnotationAdjustment( |
| allowed_position)); |
| |
| if (curr->GetLineLayoutItem().IsAtomicInlineLevel() && |
| curr->GetLineLayoutItem().IsRubyRun() && |
| curr->GetLineLayoutItem().StyleRef().GetRubyPosition() == |
| RubyPosition::kAfter) { |
| LineLayoutRubyRun ruby_run = LineLayoutRubyRun(curr->GetLineLayoutItem()); |
| LineLayoutRubyText ruby_text = ruby_run.RubyText(); |
| if (!ruby_text) |
| continue; |
| |
| if (ruby_run.StyleRef().IsFlippedLinesWritingMode()) { |
| LayoutUnit top_of_first_ruby_text_line = |
| ruby_text.LogicalTop() + (ruby_text.FirstRootBox() |
| ? ruby_text.FirstRootBox()->LineTop() |
| : LayoutUnit()); |
| if (top_of_first_ruby_text_line >= 0) |
| continue; |
| top_of_first_ruby_text_line += curr->LogicalTop(); |
| result = |
| std::max(result, allowed_position - top_of_first_ruby_text_line); |
| } else { |
| LayoutUnit bottom_of_last_ruby_text_line = |
| ruby_text.LogicalTop() + |
| (ruby_text.LastRootBox() ? ruby_text.LastRootBox()->LineBottom() |
| : ruby_text.LogicalHeight()); |
| if (bottom_of_last_ruby_text_line <= curr->LogicalHeight()) |
| continue; |
| bottom_of_last_ruby_text_line += curr->LogicalTop(); |
| result = |
| std::max(result, bottom_of_last_ruby_text_line - allowed_position); |
| } |
| } |
| |
| if (curr->IsInlineTextBox()) { |
| const ComputedStyle& style = |
| curr->GetLineLayoutItem().StyleRef(IsFirstLineStyle()); |
| if (style.GetTextEmphasisMark() != TextEmphasisMark::kNone && |
| HasEmphasisMarkUnder(ToInlineTextBox(curr))) { |
| if (!style.IsFlippedLinesWritingMode()) { |
| LayoutUnit bottom_of_emphasis_mark = |
| curr->LogicalBottom() + style.GetFont().EmphasisMarkHeight( |
| style.TextEmphasisMarkString()); |
| result = std::max(result, bottom_of_emphasis_mark - allowed_position); |
| } else { |
| LayoutUnit top_of_emphasis_mark = |
| curr->LogicalTop() - style.GetFont().EmphasisMarkHeight( |
| style.TextEmphasisMarkString()); |
| result = std::max(result, allowed_position - top_of_emphasis_mark); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| void InlineFlowBox::CollectLeafBoxesInLogicalOrder( |
| Vector<InlineBox*>& leaf_boxes_in_logical_order, |
| CustomInlineBoxRangeReverse custom_reverse_implementation) const { |
| InlineBox* leaf = FirstLeafChild(); |
| |
| // FIXME: The reordering code is a copy of parts from BidiResolver:: |
| // createBidiRunsForLine, operating directly on InlineBoxes, instead of |
| // BidiRuns. Investigate on how this code could possibly be shared. |
| unsigned char min_level = 128; |
| unsigned char max_level = 0; |
| |
| // First find highest and lowest levels, and initialize |
| // leafBoxesInLogicalOrder with the leaf boxes in visual order. |
| for (; leaf; leaf = leaf->NextLeafChild()) { |
| min_level = std::min(min_level, leaf->BidiLevel()); |
| max_level = std::max(max_level, leaf->BidiLevel()); |
| leaf_boxes_in_logical_order.push_back(leaf); |
| } |
| |
| if (GetLineLayoutItem().StyleRef().RtlOrdering() == EOrder::kVisual) |
| return; |
| |
| // Reverse of reordering of the line (L2 according to Bidi spec): |
| // L2. From the highest level found in the text to the lowest odd level on |
| // each line, reverse any contiguous sequence of characters that are at that |
| // level or higher. |
| |
| // Reversing the reordering of the line is only done up to the lowest odd |
| // level. |
| if (!(min_level % 2)) |
| ++min_level; |
| |
| Vector<InlineBox*>::iterator end = leaf_boxes_in_logical_order.end(); |
| while (min_level <= max_level) { |
| Vector<InlineBox*>::iterator it = leaf_boxes_in_logical_order.begin(); |
| while (it != end) { |
| while (it != end) { |
| if ((*it)->BidiLevel() >= min_level) |
| break; |
| ++it; |
| } |
| Vector<InlineBox*>::iterator first = it; |
| while (it != end) { |
| if ((*it)->BidiLevel() < min_level) |
| break; |
| ++it; |
| } |
| Vector<InlineBox*>::iterator last = it; |
| if (custom_reverse_implementation) |
| (*custom_reverse_implementation)(first, last); |
| else |
| std::reverse(first, last); |
| } |
| ++min_level; |
| } |
| } |
| |
| const char* InlineFlowBox::BoxName() const { |
| return "InlineFlowBox"; |
| } |
| |
| #ifndef NDEBUG |
| |
| void InlineFlowBox::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) const { |
| InlineBox::DumpLineTreeAndMark(string_builder, marked_box1, marked_label1, |
| marked_box2, marked_label2, obj, depth); |
| for (const InlineBox* box = FirstChild(); box; box = box->NextOnLine()) { |
| box->DumpLineTreeAndMark(string_builder, marked_box1, marked_label1, |
| marked_box2, marked_label2, obj, depth + 1); |
| } |
| } |
| |
| #endif |
| |
| } // namespace blink |