blob: 0167a842a2f8422e7dce4b3da6caba8a9071ba22 [file] [log] [blame]
/*
* Copyright (C) 2003, 2006, 2008 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/root_inline_box.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/line/ellipsis_box.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/vertical_position_cache.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/root_inline_box_painter.h"
#include "third_party/blink/renderer/platform/text/bidi_resolver.h"
#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
namespace blink {
struct SameSizeAsRootInlineBox : public InlineFlowBox {
unsigned unsigned_variable;
void* pointers[3];
LayoutUnit layout_variables[6];
};
static_assert(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox),
"RootInlineBox should stay small");
typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap;
static EllipsisBoxMap* g_ellipsis_box_map = nullptr;
RootInlineBox::RootInlineBox(LineLayoutItem block)
: InlineFlowBox(block), line_break_pos_(0), line_break_obj_(nullptr) {
SetIsHorizontal(block.IsHorizontalWritingMode());
}
void RootInlineBox::Destroy() {
DetachEllipsisBox();
InlineFlowBox::Destroy();
}
void RootInlineBox::DetachEllipsisBox() {
if (HasEllipsisBox()) {
EllipsisBox* box = g_ellipsis_box_map->Take(this);
box->SetParent(nullptr);
box->Destroy();
SetHasEllipsisBox(false);
}
}
LineBoxList* RootInlineBox::LineBoxes() const {
return Block().LineBoxes();
}
void RootInlineBox::ClearTruncation() {
if (HasEllipsisBox()) {
DetachEllipsisBox();
InlineFlowBox::ClearTruncation();
}
}
LayoutUnit RootInlineBox::BaselinePosition(FontBaseline baseline_type) const {
return BoxModelObject().BaselinePosition(
baseline_type, IsFirstLineStyle(),
IsHorizontal() ? kHorizontalLine : kVerticalLine,
kPositionOfInteriorLineBoxes);
}
LayoutUnit RootInlineBox::LineHeight() const {
return BoxModelObject().LineHeight(
IsFirstLineStyle(), IsHorizontal() ? kHorizontalLine : kVerticalLine,
kPositionOfInteriorLineBoxes);
}
bool RootInlineBox::LineCanAccommodateEllipsis(bool ltr,
LayoutUnit block_edge,
LayoutUnit line_box_edge,
LayoutUnit ellipsis_width) {
// First sanity-check the unoverflowed width of the whole line to see if there
// is sufficient room.
LayoutUnit delta =
ltr ? line_box_edge - block_edge : block_edge - line_box_edge;
if (LogicalWidth() - delta < ellipsis_width)
return false;
// Next iterate over all the line boxes on the line. If we find a replaced
// element that intersects then we refuse to accommodate the ellipsis.
// Otherwise we're ok.
return InlineFlowBox::CanAccommodateEllipsis(ltr, block_edge, ellipsis_width);
}
LayoutUnit RootInlineBox::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) {
// Create an ellipsis box if we don't already have one. If we already have one
// we're just here to blank out (truncate) the text boxes.
if (!*found_box) {
EllipsisBox* ellipsis_box = new EllipsisBox(
GetLineLayoutItem(), ellipsis_str, this, ellipsis_width,
LogicalHeight(), Location(), !PrevRootBox(), IsHorizontal());
if (!g_ellipsis_box_map)
g_ellipsis_box_map = new EllipsisBoxMap();
g_ellipsis_box_map->insert(this, ellipsis_box);
SetHasEllipsisBox(true);
}
// FIXME: Do we need an RTL version of this?
LayoutUnit adjusted_logical_left = logical_left_offset + LogicalLeft();
if (force_ellipsis == ForceEllipsis && ltr &&
(adjusted_logical_left + LogicalWidth() + ellipsis_width) <=
block_right_edge) {
if (HasEllipsisBox())
GetEllipsisBox()->SetLogicalLeft(LogicalLeft() + LogicalWidth());
return LogicalWidth() + ellipsis_width;
}
// Now attempt to find the nearest glyph horizontally and place just to the
// right (or left in RTL) of that glyph. Mark all of the objects that
// intersect the ellipsis box as not painting (as being truncated).
LayoutUnit truncated_width;
LayoutUnit position =
PlaceEllipsisBox(ltr, block_left_edge, block_right_edge, ellipsis_width,
truncated_width, found_box, logical_left_offset);
if (HasEllipsisBox())
GetEllipsisBox()->SetLogicalLeft(position);
return truncated_width;
}
LayoutUnit RootInlineBox::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 = InlineFlowBox::PlaceEllipsisBox(
ltr, block_left_edge, block_right_edge, ellipsis_width, truncated_width,
found_box, logical_left_offset);
if (result == -1) {
result = ltr ? std::max<LayoutUnit>(
LayoutUnit(),
block_right_edge - ellipsis_width - logical_left_offset)
: block_left_edge - logical_left_offset;
truncated_width = block_right_edge - block_left_edge - logical_left_offset;
}
return result;
}
void RootInlineBox::Paint(const PaintInfo& paint_info,
const LayoutPoint& paint_offset,
LayoutUnit line_top,
LayoutUnit line_bottom) const {
RootInlineBoxPainter(*this).Paint(paint_info, paint_offset, line_top,
line_bottom);
}
bool RootInlineBox::NodeAtPoint(HitTestResult& result,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
LayoutUnit line_top,
LayoutUnit line_bottom) {
if (HasEllipsisBox() && VisibleToHitTestRequest(result.GetHitTestRequest())) {
if (GetEllipsisBox()->NodeAtPoint(result, location_in_container,
accumulated_offset, line_top,
line_bottom)) {
GetLineLayoutItem().UpdateHitTestResult(
result,
location_in_container.Point() - ToLayoutSize(accumulated_offset));
return true;
}
}
return InlineFlowBox::NodeAtPoint(result, location_in_container,
accumulated_offset, line_top, line_bottom);
}
void RootInlineBox::Move(const LayoutSize& delta) {
InlineFlowBox::Move(delta);
LayoutUnit block_direction_delta =
IsHorizontal() ? delta.Height() : delta.Width();
line_top_ += block_direction_delta;
line_bottom_ += block_direction_delta;
line_top_with_leading_ += block_direction_delta;
line_bottom_with_leading_ += block_direction_delta;
selection_bottom_ += block_direction_delta;
if (HasEllipsisBox())
GetEllipsisBox()->Move(delta);
}
void RootInlineBox::ChildRemoved(InlineBox* box) {
if (box->GetLineLayoutItem() == line_break_obj_)
SetLineBreakInfo(nullptr, 0, BidiStatus());
for (RootInlineBox* prev = PrevRootBox();
prev && prev->LineBreakObj() == box->GetLineLayoutItem();
prev = prev->PrevRootBox()) {
prev->SetLineBreakInfo(nullptr, 0, BidiStatus());
prev->MarkDirty();
}
}
static inline void ApplyLineHeightStep(uint8_t line_height_step,
LayoutUnit& max_ascent,
LayoutUnit& max_descent) {
// Round up to the multiple of units, by adding spaces to over/under equally.
// https://drafts.csswg.org/css-rhythm/#line-height-step
int remainder = (max_ascent + max_descent).ToInt() % line_height_step;
if (!remainder)
return;
DCHECK_GT(remainder, 0);
int space = line_height_step - remainder;
max_descent += space / 2;
max_ascent += space - space / 2;
}
LayoutUnit RootInlineBox::AlignBoxesInBlockDirection(
LayoutUnit height_of_block,
GlyphOverflowAndFallbackFontsMap& text_box_data_map,
VerticalPositionCache& vertical_position_cache) {
// SVG will handle vertical alignment on its own.
if (IsSVGRootInlineBox())
return LayoutUnit();
LayoutUnit max_position_top;
LayoutUnit max_position_bottom;
LayoutUnit max_ascent;
LayoutUnit max_descent;
bool set_max_ascent = false;
bool set_max_descent = false;
// Figure out if we're in no-quirks mode.
bool no_quirks_mode = GetLineLayoutItem().GetDocument().InNoQuirksMode();
baseline_type_ = DominantBaseline();
ComputeLogicalBoxHeights(this, max_position_top, max_position_bottom,
max_ascent, max_descent, set_max_ascent,
set_max_descent, no_quirks_mode, text_box_data_map,
BaselineType(), vertical_position_cache);
if (max_ascent + max_descent <
std::max(max_position_top, max_position_bottom))
AdjustMaxAscentAndDescent(max_ascent, max_descent, max_position_top.ToInt(),
max_position_bottom.ToInt());
if (uint8_t line_height_step =
GetLineLayoutItem().StyleRef().LineHeightStep())
ApplyLineHeightStep(line_height_step, max_ascent, max_descent);
LayoutUnit max_height = LayoutUnit(max_ascent + max_descent);
LayoutUnit line_top = height_of_block;
LayoutUnit line_bottom = height_of_block;
LayoutUnit line_top_including_margins = height_of_block;
LayoutUnit line_bottom_including_margins = height_of_block;
LayoutUnit selection_bottom = height_of_block;
bool set_line_top = false;
bool has_annotations_before = false;
bool has_annotations_after = false;
PlaceBoxesInBlockDirection(
height_of_block, 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, BaselineType());
has_annotations_before_ = has_annotations_before;
has_annotations_after_ = has_annotations_after;
max_height = max_height.ClampNegativeToZero();
SetLineTopBottomPositions(line_top, line_bottom, height_of_block,
height_of_block + max_height, selection_bottom);
LayoutUnit annotations_adjustment = BeforeAnnotationsAdjustment();
if (annotations_adjustment) {
// FIXME: Need to handle pagination here. We might have to move to the next
// page/column as a result of the ruby expansion.
MoveInBlockDirection(annotations_adjustment);
height_of_block += annotations_adjustment;
}
return height_of_block + max_height;
}
LayoutUnit RootInlineBox::BeforeAnnotationsAdjustment() const {
LayoutUnit result;
if (!GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()) {
// Annotations under the previous line may push us down.
if (PrevRootBox() && PrevRootBox()->HasAnnotationsAfter())
result = PrevRootBox()->ComputeUnderAnnotationAdjustment(LineTop());
if (!HasAnnotationsBefore())
return result;
// Annotations over this line may push us further down.
LayoutUnit highest_allowed_position =
PrevRootBox()
? std::min(PrevRootBox()->LineBottom(), LineTop()) + result
: static_cast<LayoutUnit>(Block().BorderBefore());
result = ComputeOverAnnotationAdjustment(highest_allowed_position);
} else {
// Annotations under this line may push us up.
if (HasAnnotationsBefore())
result = ComputeUnderAnnotationAdjustment(
PrevRootBox() ? PrevRootBox()->LineBottom()
: static_cast<LayoutUnit>(Block().BorderBefore()));
if (!PrevRootBox() || !PrevRootBox()->HasAnnotationsAfter())
return result;
// We have to compute the expansion for annotations over the previous line
// to see how much we should move.
LayoutUnit lowest_allowed_position =
std::max(PrevRootBox()->LineBottom(), LineTop()) - result;
result =
PrevRootBox()->ComputeOverAnnotationAdjustment(lowest_allowed_position);
}
return result;
}
bool RootInlineBox::IsSelected() const {
// Walk over all of the selected boxes.
for (InlineBox* box = FirstLeafChild(); box; box = box->NextLeafChild()) {
if (box->IsSelected())
return true;
}
return false;
}
InlineBox* RootInlineBox::FirstSelectedBox() const {
for (InlineBox* box = FirstLeafChild(); box; box = box->NextLeafChild()) {
if (box->IsSelected())
return box;
}
return nullptr;
}
InlineBox* RootInlineBox::LastSelectedBox() const {
for (InlineBox* box = LastLeafChild(); box; box = box->PrevLeafChild()) {
if (box->IsSelected())
return box;
}
return nullptr;
}
LayoutUnit RootInlineBox::SelectionTop() const {
LayoutUnit selection_top = line_top_;
if (has_annotations_before_)
selection_top -= !GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()
? ComputeOverAnnotationAdjustment(line_top_)
: ComputeUnderAnnotationAdjustment(line_top_);
if (GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() ||
!PrevRootBox())
return selection_top;
return std::min(selection_top, PrevRootBox()->SelectionBottom());
}
LayoutUnit RootInlineBox::SelectionBottom() const {
LayoutUnit selection_bottom =
GetLineLayoutItem().GetDocument().InNoQuirksMode() ? selection_bottom_
: line_bottom_;
if (has_annotations_after_)
selection_bottom +=
!GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode()
? ComputeUnderAnnotationAdjustment(line_bottom_)
: ComputeOverAnnotationAdjustment(line_bottom_);
if (!GetLineLayoutItem().StyleRef().IsFlippedLinesWritingMode() ||
!NextRootBox())
return selection_bottom;
return std::max(selection_bottom, NextRootBox()->SelectionTop());
}
LayoutUnit RootInlineBox::BlockDirectionPointInLine() const {
return !Block().StyleRef().IsFlippedBlocksWritingMode()
? std::max(LineTop(), SelectionTop())
: std::min(LineBottom(), SelectionBottom());
}
LineLayoutBlockFlow RootInlineBox::Block() const {
return LineLayoutBlockFlow(GetLineLayoutItem());
}
static bool IsEditableLeaf(InlineBox* leaf) {
return leaf && leaf->GetLineLayoutItem().GetNode() &&
HasEditableStyle(*leaf->GetLineLayoutItem().GetNode());
}
InlineBox* RootInlineBox::ClosestLeafChildForPoint(
const LayoutPoint& point_in_contents,
bool only_editable_leaves) const {
return ClosestLeafChildForLogicalLeftPosition(
Block().IsHorizontalWritingMode() ? point_in_contents.X()
: point_in_contents.Y(),
only_editable_leaves);
}
InlineBox* RootInlineBox::ClosestLeafChildForLogicalLeftPosition(
LayoutUnit left_position,
bool only_editable_leaves) const {
InlineBox* first_leaf = FirstLeafChild();
InlineBox* last_leaf = LastLeafChild();
if (first_leaf != last_leaf) {
if (first_leaf->IsLineBreak())
first_leaf = first_leaf->NextLeafChildIgnoringLineBreak();
else if (last_leaf->IsLineBreak())
last_leaf = last_leaf->PrevLeafChildIgnoringLineBreak();
}
if (first_leaf == last_leaf &&
(!only_editable_leaves || IsEditableLeaf(first_leaf)))
return first_leaf;
// Avoid returning a list marker when possible.
if (left_position <= first_leaf->LogicalLeft() &&
!first_leaf->GetLineLayoutItem().IsListMarker() &&
(!only_editable_leaves || IsEditableLeaf(first_leaf))) {
// The leftPosition coordinate is less or equal to left edge of the
// firstLeaf. Return it.
return first_leaf;
}
if (left_position >= last_leaf->LogicalRight() &&
!last_leaf->GetLineLayoutItem().IsListMarker() &&
(!only_editable_leaves || IsEditableLeaf(last_leaf))) {
// The leftPosition coordinate is greater or equal to right edge of the
// lastLeaf. Return it.
return last_leaf;
}
InlineBox* closest_leaf = nullptr;
for (InlineBox* leaf = first_leaf; leaf;
leaf = leaf->NextLeafChildIgnoringLineBreak()) {
if (!leaf->GetLineLayoutItem().IsListMarker() &&
(!only_editable_leaves || IsEditableLeaf(leaf))) {
closest_leaf = leaf;
if (left_position < leaf->LogicalRight()) {
// The x coordinate is less than the right edge of the box.
// Return it.
return leaf;
}
}
}
return closest_leaf ? closest_leaf : last_leaf;
}
BidiStatus RootInlineBox::LineBreakBidiStatus() const {
return BidiStatus(
static_cast<WTF::Unicode::CharDirection>(line_break_bidi_status_eor_),
static_cast<WTF::Unicode::CharDirection>(
line_break_bidi_status_last_strong_),
static_cast<WTF::Unicode::CharDirection>(line_break_bidi_status_last_),
line_break_context_);
}
void RootInlineBox::SetLineBreakInfo(LineLayoutItem obj,
unsigned break_pos,
const BidiStatus& status) {
// When setting lineBreakObj, the LayoutObject must not be a LayoutInline
// with no line boxes, otherwise all sorts of invariants are broken later.
// This has security implications because if the LayoutObject does not point
// to at least one line box, then that LayoutInline can be deleted later
// without resetting the lineBreakObj, leading to use-after-free.
SECURITY_DCHECK(!obj || obj.IsText() ||
!(obj.IsLayoutInline() && obj.IsBox() &&
!LineLayoutBox(obj).InlineBoxWrapper()));
line_break_obj_ = obj;
line_break_pos_ = break_pos;
line_break_bidi_status_eor_ = status.eor;
line_break_bidi_status_last_strong_ = status.last_strong;
line_break_bidi_status_last_ = status.last;
line_break_context_ = status.context;
}
EllipsisBox* RootInlineBox::GetEllipsisBox() const {
if (!HasEllipsisBox())
return nullptr;
return g_ellipsis_box_map->at(this);
}
void RootInlineBox::RemoveLineBoxFromLayoutObject() {
Block().LineBoxes()->RemoveLineBox(this);
}
void RootInlineBox::ExtractLineBoxFromLayoutObject() {
Block().LineBoxes()->ExtractLineBox(this);
}
void RootInlineBox::AttachLineBoxToLayoutObject() {
Block().LineBoxes()->AttachLineBox(this);
}
LayoutRect RootInlineBox::PaddedLayoutOverflowRect(
LayoutUnit end_padding) const {
LayoutRect line_layout_overflow = LayoutOverflowRect(LineTop(), LineBottom());
if (!end_padding)
return line_layout_overflow;
if (IsHorizontal()) {
if (IsLeftToRightDirection())
line_layout_overflow.ShiftMaxXEdgeTo(std::max<LayoutUnit>(
line_layout_overflow.MaxX(), LogicalRight() + end_padding));
else
line_layout_overflow.ShiftXEdgeTo(std::min<LayoutUnit>(
line_layout_overflow.X(), LogicalLeft() - end_padding));
} else {
if (IsLeftToRightDirection())
line_layout_overflow.ShiftMaxYEdgeTo(std::max<LayoutUnit>(
line_layout_overflow.MaxY(), LogicalRight() + end_padding));
else
line_layout_overflow.ShiftYEdgeTo(std::min<LayoutUnit>(
line_layout_overflow.Y(), LogicalLeft() - end_padding));
}
return line_layout_overflow;
}
static void SetAscentAndDescent(LayoutUnit& ascent,
LayoutUnit& descent,
LayoutUnit new_ascent,
LayoutUnit new_descent,
bool& ascent_descent_set) {
if (!ascent_descent_set) {
ascent_descent_set = true;
ascent = new_ascent;
descent = new_descent;
} else {
ascent = std::max(ascent, new_ascent);
descent = std::max(descent, new_descent);
}
}
void RootInlineBox::AscentAndDescentForBox(
InlineBox* box,
GlyphOverflowAndFallbackFontsMap& text_box_data_map,
LayoutUnit& ascent,
LayoutUnit& descent,
bool& affects_ascent,
bool& affects_descent) const {
bool ascent_descent_set = false;
if (box->GetLineLayoutItem().IsAtomicInlineLevel()) {
ascent = box->BaselinePosition(BaselineType());
descent = box->LineHeight() - ascent;
// Replaced elements always affect both the ascent and descent.
affects_ascent = true;
affects_descent = true;
return;
}
Vector<const SimpleFontData*>* used_fonts = nullptr;
if (box->IsText()) {
GlyphOverflowAndFallbackFontsMap::iterator it =
text_box_data_map.find(ToInlineTextBox(box));
used_fonts = it == text_box_data_map.end() ? nullptr : &it->value.first;
}
bool include_leading = IncludeLeadingForBox(box);
bool set_used_font_with_leading = false;
if (used_fonts && !used_fonts->IsEmpty() &&
(box->GetLineLayoutItem()
.Style(IsFirstLineStyle())
->LineHeight()
.IsNegative() &&
include_leading)) {
const SimpleFontData* primary_font = box->GetLineLayoutItem()
.Style(IsFirstLineStyle())
->GetFont()
.PrimaryFont();
if (primary_font)
used_fonts->push_back(primary_font);
for (size_t i = 0; i < used_fonts->size(); ++i) {
const FontMetrics& font_metrics = used_fonts->at(i)->GetFontMetrics();
LayoutUnit used_font_ascent(font_metrics.Ascent(BaselineType()));
LayoutUnit used_font_descent(font_metrics.Descent(BaselineType()));
LayoutUnit half_leading(
(font_metrics.LineSpacing() - font_metrics.Height()) / 2);
LayoutUnit used_font_ascent_and_leading = used_font_ascent + half_leading;
LayoutUnit used_font_descent_and_leading =
font_metrics.LineSpacing() - used_font_ascent_and_leading;
if (include_leading) {
SetAscentAndDescent(ascent, descent, used_font_ascent_and_leading,
used_font_descent_and_leading, ascent_descent_set);
set_used_font_with_leading = true;
}
if (!affects_ascent)
affects_ascent = used_font_ascent - box->LogicalTop() > 0;
if (!affects_descent)
affects_descent = used_font_descent + box->LogicalTop() > 0;
}
}
// If leading is included for the box, then we compute that box.
if (include_leading && !set_used_font_with_leading) {
LayoutUnit ascent_with_leading = box->BaselinePosition(BaselineType());
LayoutUnit descent_with_leading = box->LineHeight() - ascent_with_leading;
SetAscentAndDescent(ascent, descent, ascent_with_leading,
descent_with_leading, ascent_descent_set);
// Examine the font box for inline flows and text boxes to see if any part
// of it is above the baseline. If the top of our font box relative to the
// root box baseline is above the root box baseline, then we are
// contributing to the maxAscent value. Descent is similar. If any part of
// our font box is below the root box's baseline, then we contribute to the
// maxDescent value.
affects_ascent = ascent_with_leading - box->LogicalTop() > 0;
affects_descent = descent_with_leading + box->LogicalTop() > 0;
}
}
LayoutUnit RootInlineBox::VerticalPositionForBox(
InlineBox* box,
VerticalPositionCache& vertical_position_cache) {
if (box->GetLineLayoutItem().IsText())
return box->Parent()->LogicalTop();
LineLayoutBoxModel box_model = box->BoxModelObject();
DCHECK(box_model.IsInline());
if (!box_model.IsInline())
return LayoutUnit();
// This method determines the vertical position for inline elements.
bool first_line = IsFirstLineStyle();
if (first_line &&
!box_model.GetDocument().GetStyleEngine().UsesFirstLineRules())
first_line = false;
// Check the cache.
bool is_layout_inline = box_model.IsLayoutInline();
if (is_layout_inline && !first_line) {
LayoutUnit vertical_position =
LayoutUnit(vertical_position_cache.Get(box_model, BaselineType()));
if (vertical_position != kPositionUndefined)
return vertical_position;
}
LayoutUnit vertical_position;
EVerticalAlign vertical_align = box_model.StyleRef().VerticalAlign();
if (vertical_align == EVerticalAlign::kTop ||
vertical_align == EVerticalAlign::kBottom)
return LayoutUnit();
LineLayoutItem parent = box_model.Parent();
if (parent.IsLayoutInline() &&
parent.StyleRef().VerticalAlign() != EVerticalAlign::kTop &&
parent.StyleRef().VerticalAlign() != EVerticalAlign::kBottom)
vertical_position = box->Parent()->LogicalTop();
if (vertical_align != EVerticalAlign::kBaseline) {
const Font& font = parent.Style(first_line)->GetFont();
const SimpleFontData* font_data = font.PrimaryFont();
DCHECK(font_data);
if (!font_data)
return LayoutUnit();
const FontMetrics& font_metrics = font_data->GetFontMetrics();
int font_size = font.GetFontDescription().ComputedPixelSize();
LineDirectionMode line_direction =
parent.IsHorizontalWritingMode() ? kHorizontalLine : kVerticalLine;
if (vertical_align == EVerticalAlign::kSub) {
vertical_position += font_size / 5 + 1;
} else if (vertical_align == EVerticalAlign::kSuper) {
vertical_position -= font_size / 3 + 1;
} else if (vertical_align == EVerticalAlign::kTextTop) {
vertical_position += box_model.BaselinePosition(
BaselineType(), first_line, line_direction) -
font_metrics.Ascent(BaselineType());
} else if (vertical_align == EVerticalAlign::kMiddle) {
vertical_position = vertical_position -
LayoutUnit(font_metrics.XHeight() / 2) -
box_model.LineHeight(first_line, line_direction) / 2 +
box_model.BaselinePosition(BaselineType(), first_line,
line_direction);
} else if (vertical_align == EVerticalAlign::kTextBottom) {
vertical_position += font_metrics.Descent(BaselineType());
// lineHeight - baselinePosition is always 0 for replaced elements (except
// inline blocks), so don't bother wasting time in that case.
if (!box_model.IsAtomicInlineLevel() ||
box_model.IsInlineBlockOrInlineTable())
vertical_position -= (box_model.LineHeight(first_line, line_direction) -
box_model.BaselinePosition(
BaselineType(), first_line, line_direction));
} else if (vertical_align == EVerticalAlign::kBaselineMiddle) {
vertical_position +=
-box_model.LineHeight(first_line, line_direction) / 2 +
box_model.BaselinePosition(BaselineType(), first_line,
line_direction);
} else if (vertical_align == EVerticalAlign::kLength) {
LayoutUnit line_height;
// Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align:
// 'Percentages: refer to the 'line-height' of the element itself'.
if (box_model.StyleRef().GetVerticalAlignLength().IsPercentOrCalc())
line_height = LayoutUnit(box_model.StyleRef().ComputedLineHeight());
else
line_height = box_model.LineHeight(first_line, line_direction);
vertical_position -= ValueForLength(
box_model.StyleRef().GetVerticalAlignLength(), line_height);
}
}
// Store the cached value.
if (is_layout_inline && !first_line)
vertical_position_cache.Set(box_model, BaselineType(),
vertical_position.ToInt());
return vertical_position;
}
bool RootInlineBox::IncludeLeadingForBox(InlineBox* box) const {
return !(box->GetLineLayoutItem().IsAtomicInlineLevel() ||
(box->GetLineLayoutItem().IsText() && !box->IsText()));
}
const InlineBox* RootInlineBox::GetLogicalStartNonPseudoBox() const {
Vector<InlineBox*> leaf_boxes_in_logical_order;
CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order);
for (size_t i = 0; i < leaf_boxes_in_logical_order.size(); ++i) {
if (leaf_boxes_in_logical_order[i]->GetLineLayoutItem().NonPseudoNode())
return leaf_boxes_in_logical_order[i];
}
return nullptr;
}
const InlineBox* RootInlineBox::GetLogicalEndNonPseudoBox() const {
Vector<InlineBox*> leaf_boxes_in_logical_order;
CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order);
for (size_t i = leaf_boxes_in_logical_order.size(); i > 0; --i) {
if (leaf_boxes_in_logical_order[i - 1]
->GetLineLayoutItem()
.NonPseudoNode()) {
return leaf_boxes_in_logical_order[i - 1];
}
}
return nullptr;
}
const char* RootInlineBox::BoxName() const {
return "RootInlineBox";
}
} // namespace blink