blob: e2a158f4026dde2daa2e4b2e5691085174073894 [file] [log] [blame]
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "LayoutIntegrationInlineContentBuilder.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "InlineDisplayBox.h"
#include "InlineFormattingState.h"
#include "LayoutBoxGeometry.h"
#include "LayoutIntegrationBoxTree.h"
#include "LayoutIntegrationInlineContent.h"
#include "LayoutReplacedBox.h"
#include "LayoutState.h"
#include "RenderBlockFlow.h"
#include "StringTruncator.h"
namespace WebCore {
namespace LayoutIntegration {
inline InlineDisplay::Line::EnclosingTopAndBottom operator+(const InlineDisplay::Line::EnclosingTopAndBottom enclosingTopAndBottom, float offset)
{
return { enclosingTopAndBottom.top + offset, enclosingTopAndBottom.bottom + offset };
}
inline static float lineOverflowWidth(const RenderBlockFlow& flow, Layout::InlineLayoutUnit lineContentWidth)
{
// FIXME: It's the copy of the lets-adjust-overflow-for-the-caret behavior from LegacyLineLayout::addOverflowFromInlineChildren.
auto endPadding = flow.hasNonVisibleOverflow() ? flow.paddingEnd() : 0_lu;
if (!endPadding)
endPadding = flow.endPaddingWidthForCaret();
if (flow.hasNonVisibleOverflow() && !endPadding && flow.element() && flow.element()->isRootEditableElement())
endPadding = 1;
return lineContentWidth + endPadding;
}
InlineContentBuilder::InlineContentBuilder(const RenderBlockFlow& blockFlow, BoxTree& boxTree)
: m_blockFlow(blockFlow)
, m_boxTree(boxTree)
{
}
void InlineContentBuilder::build(Layout::InlineFormattingState& inlineFormattingState, InlineContent& inlineContent) const
{
// FIXME: This might need a different approach with partial layout where the layout code needs to know about the boxes.
inlineContent.boxes = WTFMove(inlineFormattingState.boxes());
auto updateIfTextRenderersNeedVisualReordering = [&] {
// FIXME: We may want to have a global, "is this a bidi paragraph" flag to avoid this loop for non-rtl, non-bidi content.
for (auto& displayBox : inlineContent.boxes) {
auto& layoutBox = displayBox.layoutBox();
if (!is<Layout::InlineTextBox>(layoutBox))
continue;
if (displayBox.bidiLevel() != UBIDI_DEFAULT_LTR)
downcast<RenderText>(m_boxTree.rendererForLayoutBox(layoutBox)).setNeedsVisualReordering();
}
};
updateIfTextRenderersNeedVisualReordering();
createDisplayLines(inlineFormattingState, inlineContent);
}
void InlineContentBuilder::createDisplayLines(Layout::InlineFormattingState& inlineFormattingState, InlineContent& inlineContent) const
{
auto& lines = inlineFormattingState.lines();
auto& boxes = inlineContent.boxes;
size_t boxIndex = 0;
inlineContent.lines.reserveInitialCapacity(lines.size());
for (size_t lineIndex = 0; lineIndex < lines.size(); ++lineIndex) {
auto& line = lines[lineIndex];
auto scrollableOverflowRect = FloatRect { line.scrollableOverflow() };
if (auto overflowWidth = lineOverflowWidth(m_blockFlow, line.contentWidth()); overflowWidth > scrollableOverflowRect.width()) {
auto overflowValue = overflowWidth - scrollableOverflowRect.width();
m_blockFlow.style().isLeftToRightDirection() ? scrollableOverflowRect.shiftMaxXEdgeBy(overflowValue) : scrollableOverflowRect.shiftXEdgeBy(-overflowValue);
}
auto firstBoxIndex = boxIndex;
auto lineInkOverflowRect = scrollableOverflowRect;
// Collect overflow from boxes.
for (; boxIndex < boxes.size() && boxes[boxIndex].lineIndex() == lineIndex; ++boxIndex) {
auto& box = boxes[boxIndex];
lineInkOverflowRect.unite(box.inkOverflow());
auto& layoutBox = box.layoutBox();
if (layoutBox.isReplacedBox()) {
// Similar to LegacyInlineFlowBox::addReplacedChildOverflow.
auto& renderer = downcast<RenderBox>(m_boxTree.rendererForLayoutBox(layoutBox));
if (!renderer.hasSelfPaintingLayer()) {
auto childInkOverflow = renderer.logicalVisualOverflowRectForPropagation(&renderer.parent()->style());
childInkOverflow.move(box.left(), box.top());
lineInkOverflowRect.unite(childInkOverflow);
}
auto childScrollableOverflow = renderer.logicalLayoutOverflowRectForPropagation(&renderer.parent()->style());
childScrollableOverflow.move(box.left(), box.top());
scrollableOverflowRect.unite(childScrollableOverflow);
}
}
if (!inlineContent.lines.isEmpty()) {
auto& lastInkOverflow = inlineContent.lines.last().inkOverflow();
if (lineInkOverflowRect.y() <= lastInkOverflow.y() || lastInkOverflow.maxY() >= lineInkOverflowRect.maxY())
inlineContent.hasMultilinePaintOverlap = true;
}
auto boxCount = boxIndex - firstBoxIndex;
inlineContent.lines.append({ firstBoxIndex, boxCount, FloatRect { line.lineBoxRect() }, line.enclosingTopAndBottom().top, line.enclosingTopAndBottom().bottom, scrollableOverflowRect, lineInkOverflowRect, line.baseline(), line.contentLeft(), line.contentWidth() });
}
}
}
}
#endif