Diff
Modified: trunk/Source/WebCore/ChangeLog (241022 => 241023)
--- trunk/Source/WebCore/ChangeLog 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/ChangeLog 2019-02-06 17:04:44 UTC (rev 241023)
@@ -1,3 +1,65 @@
+2019-02-06 Zalan Bujtas <[email protected]>
+
+ [LFC][IFC] Move line layout code to a dedicated file
+ https://bugs.webkit.org/show_bug.cgi?id=194328
+
+ Reviewed by Antti Koivisto.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::layout const):
+ (WebCore::Layout::isTrimmableContent): Deleted.
+ (WebCore::Layout::InlineFormattingContext::initializeNewLine const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::splitInlineRunIfNeeded const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::createFinalRuns const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::postProcessInlineRuns const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::closeLine const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::appendContentToLine const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::layoutInlineContent const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::computeFloatPosition const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::placeInFlowPositionedChildren const): Deleted.
+ * layout/inlineformatting/InlineFormattingContext.h:
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::hasContent const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::isClosed const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::isFirstLine const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::runs):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::contentLogicalLeft const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::availableWidth const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::lastRunType const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::logicalTop const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::logicalBottom const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::logicalHeight const):
+ (WebCore::Layout::InlineFormattingContext::Line::hasContent const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::isClosed const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::isFirstLine const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::runs): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::contentLogicalLeft const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::availableWidth const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::lastRunType const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::logicalTop const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::logicalBottom const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::logicalHeight const): Deleted.
+ * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
+ (WebCore::Layout::adjustedLineLogicalLeft): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Geometry::justifyRuns): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Geometry::computeExpansionOpportunities): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Geometry::alignRuns): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Geometry::runWidth): Deleted.
+ * layout/inlineformatting/Line.cpp:
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::init):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::adjustLogicalLeft):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::adjustLogicalRight):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::contentLogicalRight const):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::appendContent):
+ (WebCore::Layout::InlineFormattingContext::LineLayout::Line::close):
+ (WebCore::Layout::InlineFormattingContext::Line::init): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::adjustLogicalLeft): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::adjustLogicalRight): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::contentLogicalRight const): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::appendContent): Deleted.
+ (WebCore::Layout::InlineFormattingContext::Line::close): Deleted.
+
2019-02-06 Youenn Fablet <[email protected]>
CoreAudioCaptureSource should not configure its audio unit until it starts producing data
Modified: trunk/Source/WebCore/Sources.txt (241022 => 241023)
--- trunk/Source/WebCore/Sources.txt 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/Sources.txt 2019-02-06 17:04:44 UTC (rev 241023)
@@ -1340,11 +1340,11 @@
layout/floats/FloatingState.cpp
layout/inlineformatting/InlineFormattingContext.cpp
layout/inlineformatting/InlineFormattingContextGeometry.cpp
+layout/inlineformatting/InlineFormattingContextLineLayout.cpp
layout/inlineformatting/InlineFormattingState.cpp
layout/inlineformatting/InlineInvalidation.cpp
layout/inlineformatting/InlineLineBreaker.cpp
layout/inlineformatting/InlineRunProvider.cpp
-layout/inlineformatting/Line.cpp
layout/inlineformatting/text/TextUtil.cpp
layout/layouttree/LayoutBlockContainer.cpp
layout/layouttree/LayoutBox.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (241022 => 241023)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-02-06 17:04:44 UTC (rev 241023)
@@ -9196,9 +9196,9 @@
6EE8A77110F803F3005A4A24 /* JSWebGLContextAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebGLContextAttributes.h; sourceTree = "<group>"; };
6F0830DF20B46951008A945B /* BlockFormattingContextGeometry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BlockFormattingContextGeometry.cpp; sourceTree = "<group>"; };
6F219D742178D37100BB033C /* InlineRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineRun.h; sourceTree = "<group>"; };
- 6F219D762178D37100BB033C /* Line.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Line.cpp; sourceTree = "<group>"; };
6F222B741AB52D640094651A /* WebGLVertexArrayObjectBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebGLVertexArrayObjectBase.h; sourceTree = "<group>"; };
6F222B751AB52D8A0094651A /* WebGLVertexArrayObjectBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebGLVertexArrayObjectBase.cpp; sourceTree = "<group>"; };
+ 6F25B200220A85AB0000011B /* InlineFormattingContextLineLayout.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineFormattingContextLineLayout.cpp; sourceTree = "<group>"; };
6F35EFAF2187CBD50044E0F4 /* InlineFormattingContextGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineFormattingContextGeometry.cpp; sourceTree = "<group>"; };
6F3E1F5F2136141700A65A08 /* FloatBox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FloatBox.cpp; sourceTree = "<group>"; };
6F3E1F612136141700A65A08 /* FloatBox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FloatBox.h; sourceTree = "<group>"; };
@@ -16456,6 +16456,7 @@
6F7CA3C9208C2B2E002F29AB /* InlineFormattingContext.cpp */,
6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */,
6F35EFAF2187CBD50044E0F4 /* InlineFormattingContextGeometry.cpp */,
+ 6F25B200220A85AB0000011B /* InlineFormattingContextLineLayout.cpp */,
115CFA7D208B8E10001E6991 /* InlineFormattingState.cpp */,
115CFA7C208B8E10001E6991 /* InlineFormattingState.h */,
1123AFDD209ABBBA00736ACC /* InlineInvalidation.cpp */,
@@ -16466,7 +16467,6 @@
6F219D742178D37100BB033C /* InlineRun.h */,
6F5217C62177F5A6006583BB /* InlineRunProvider.cpp */,
6F5217C42177F5A6006583BB /* InlineRunProvider.h */,
- 6F219D762178D37100BB033C /* Line.cpp */,
);
path = inlineformatting;
sourceTree = "<group>";
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (241022 => 241023)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2019-02-06 17:04:44 UTC (rev 241023)
@@ -28,8 +28,6 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-#include "FloatingContext.h"
-#include "FloatingState.h"
#include "InlineFormattingState.h"
#include "InlineLineBreaker.h"
#include "InlineRunProvider.h"
@@ -87,260 +85,10 @@
InlineRunProvider inlineRunProvider;
collectInlineContent(inlineRunProvider);
- layoutInlineContent(inlineRunProvider);
+ LineLayout(*this).layout(inlineRunProvider);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root << ")");
}
-static bool isTrimmableContent(const InlineLineBreaker::Run& run)
-{
- return run.content.isWhitespace() && run.content.style().collapseWhiteSpace();
-}
-
-void InlineFormattingContext::initializeNewLine(Line& line) const
-{
- auto& formattingRoot = downcast<Container>(root());
- auto& formattingRootDisplayBox = layoutState().displayBoxForLayoutBox(formattingRoot);
-
- auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
- auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
- auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
-
- // Check for intruding floats and adjust logical left/available width for this line accordingly.
- auto& floatingState = formattingState().floatingState();
- if (!floatingState.isEmpty()) {
- auto floatConstraints = floatingState.constraints({ lineLogicalTop }, formattingRoot);
- // Check if these constraints actually put limitation on the line.
- if (floatConstraints.left && *floatConstraints.left <= formattingRootDisplayBox.contentBoxLeft())
- floatConstraints.left = { };
-
- if (floatConstraints.right && *floatConstraints.right >= formattingRootDisplayBox.contentBoxRight())
- floatConstraints.right = { };
-
- if (floatConstraints.left && floatConstraints.right) {
- ASSERT(*floatConstraints.left < *floatConstraints.right);
- availableWidth = *floatConstraints.right - *floatConstraints.left;
- lineLogicalLeft = *floatConstraints.left;
- } else if (floatConstraints.left) {
- ASSERT(*floatConstraints.left > lineLogicalLeft);
- availableWidth -= (*floatConstraints.left - lineLogicalLeft);
- lineLogicalLeft = *floatConstraints.left;
- } else if (floatConstraints.right) {
- ASSERT(*floatConstraints.right > lineLogicalLeft);
- availableWidth = *floatConstraints.right - lineLogicalLeft;
- }
- }
-
- line.init({ lineLogicalLeft, lineLogicalTop }, availableWidth, formattingRoot.style().computedLineHeight());
-}
-
-void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun, InlineRuns& splitRuns) const
-{
- ASSERT(inlineRun.textContext());
- ASSERT(inlineRun.overlapsMultipleInlineItems());
- // In certain cases, a run can overlap multiple inline elements like this:
- // <span>normal text content</span><span style="position: relative; left: 10px;">but this one needs a dedicated run</span><span>end of text</span>
- // The content above generates one long run <normal text contentbut this one needs dedicated runend of text>
- // However, since the middle run is positioned, it needs to be moved independently from the rest of the content, hence it needs a dedicated inline run.
-
- // 1. Start with the first inline item (element) and travers the list until
- // 2. either find an inline item that needs a dedicated run or we reach the end of the run
- // 3. Create dedicate inline runs.
- auto& inlineContent = formattingState().inlineContent();
- auto contentStart = inlineRun.logicalLeft();
- auto startPosition = inlineRun.textContext()->start();
- auto remaningLength = inlineRun.textContext()->length();
-
- struct Uncommitted {
- const InlineItem* firstInlineItem { nullptr };
- const InlineItem* lastInlineItem { nullptr };
- unsigned length { 0 };
- };
- Optional<Uncommitted> uncommitted;
-
- auto commit = [&] {
- if (!uncommitted)
- return;
-
- contentStart += uncommitted->firstInlineItem->nonBreakableStart();
-
- auto runWidth = Geometry::runWidth(inlineContent, *uncommitted->firstInlineItem, startPosition, uncommitted->length, contentStart);
- auto run = InlineRun { { inlineRun.logicalTop(), contentStart, runWidth, inlineRun.logicalHeight() }, *uncommitted->firstInlineItem };
- run.setTextContext({ startPosition, uncommitted->length });
- splitRuns.append(run);
-
- contentStart += runWidth + uncommitted->lastInlineItem->nonBreakableEnd();
-
- startPosition = 0;
- uncommitted = { };
- };
-
- for (auto iterator = inlineContent.find(const_cast<InlineItem*>(&inlineRun.inlineItem())); iterator != inlineContent.end() && remaningLength > 0; ++iterator) {
- auto& inlineItem = **iterator;
-
- // Skip all non-inflow boxes (floats, out-of-flow positioned elements). They don't participate in the inline run context.
- if (!inlineItem.layoutBox().isInFlow())
- continue;
-
- auto currentLength = [&] {
- return std::min(remaningLength, inlineItem.textContent().length() - startPosition);
- };
-
- // 1. Break before/after -> requires dedicated run -> commit what we've got so far and also commit the current inline element as a separate inline run.
- // 2. Break at the beginning of the inline element -> commit what we've got so far. Current element becomes the first uncommitted.
- // 3. Break at the end of the inline element -> commit what we've got so far including the current element.
- // 4. Inline element does not require run breaking -> add current inline element to uncommitted. Jump to the next element.
- auto detachingRules = inlineItem.detachingRules();
-
- // #1
- if (detachingRules.containsAll({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd })) {
- commit();
- auto contentLength = currentLength();
- uncommitted = Uncommitted { &inlineItem, &inlineItem, contentLength };
- remaningLength -= contentLength;
- commit();
- continue;
- }
-
- // #2
- if (detachingRules.contains(InlineItem::DetachingRule::BreakAtStart))
- commit();
-
- // Add current inline item to uncommitted.
- // #3 and #4
- auto contentLength = currentLength();
- if (!uncommitted)
- uncommitted = Uncommitted { &inlineItem, &inlineItem, 0 };
- uncommitted->length += contentLength;
- uncommitted->lastInlineItem = &inlineItem;
- remaningLength -= contentLength;
-
- // #3
- if (detachingRules.contains(InlineItem::DetachingRule::BreakAtEnd))
- commit();
- }
- // Either all inline elements needed dedicated runs or neither of them.
- if (!remaningLength || remaningLength == inlineRun.textContext()->length())
- return;
-
- commit();
-}
-
-void InlineFormattingContext::createFinalRuns(Line& line) const
-{
- auto& inlineFormattingState = formattingState();
- for (auto& inlineRun : line.runs()) {
- if (inlineRun.overlapsMultipleInlineItems()) {
- InlineRuns splitRuns;
- splitInlineRunIfNeeded(inlineRun, splitRuns);
- for (auto& splitRun : splitRuns)
- inlineFormattingState.appendInlineRun(splitRun);
-
- if (!splitRuns.isEmpty())
- continue;
- }
-
- auto finalRun = [&] {
- auto& inlineItem = inlineRun.inlineItem();
- if (inlineItem.detachingRules().isEmpty())
- return inlineRun;
-
- InlineRun adjustedRun = inlineRun;
- auto width = inlineRun.logicalWidth() - inlineItem.nonBreakableStart() - inlineItem.nonBreakableEnd();
- adjustedRun.setLogicalLeft(inlineRun.logicalLeft() + inlineItem.nonBreakableStart());
- adjustedRun.setLogicalWidth(width);
- return adjustedRun;
- };
-
- inlineFormattingState.appendInlineRun(finalRun());
- }
-}
-
-void InlineFormattingContext::postProcessInlineRuns(Line& line, IsLastLine isLastLine) const
-{
- Geometry::alignRuns(root().style().textAlign(), line, isLastLine);
- auto firstRunIndex = formattingState().inlineRuns().size();
- createFinalRuns(line);
-
- placeInFlowPositionedChildren(firstRunIndex);
-}
-
-void InlineFormattingContext::closeLine(Line& line, IsLastLine isLastLine) const
-{
- line.close();
- if (!line.hasContent())
- return;
-
- postProcessInlineRuns(line, isLastLine);
-}
-
-void InlineFormattingContext::appendContentToLine(Line& line, const InlineRunProvider::Run& run, const LayoutSize& runSize) const
-{
- auto lastRunType = line.lastRunType();
- line.appendContent(run, runSize);
-
- if (root().style().textAlign() == TextAlignMode::Justify)
- Geometry::computeExpansionOpportunities(line, run, lastRunType.valueOr(InlineRunProvider::Run::Type::NonWhitespace));
-}
-
-void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
-{
- auto& layoutState = this->layoutState();
- auto& inlineFormattingState = formattingState();
- auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
-
- Line line;
- initializeNewLine(line);
-
- InlineLineBreaker lineBreaker(layoutState, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
- while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
- auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
- auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
- auto generatesInlineRun = true;
-
- // Position float and adjust the runs on line.
- if (run->content.isFloat()) {
- auto& floatBox = run->content.inlineItem().layoutBox();
- computeFloatPosition(floatingContext, line, floatBox);
- inlineFormattingState.floatingState().append(floatBox);
-
- auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).marginBox().width();
- // Shrink availble space for current line and move existing inline runs.
- floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
-
- generatesInlineRun = false;
- }
-
- // 1. Initialize new line if needed.
- // 2. Append inline run unless it is skipped.
- // 3. Close current line if needed.
- if (isFirstRun) {
- // When the first run does not generate an actual inline run, the next run comes in first-run as well.
- // No need to spend time on closing/initializing.
- // Skip leading whitespace.
- if (!generatesInlineRun || isTrimmableContent(*run))
- continue;
-
- if (line.hasContent()) {
- // Previous run ended up being at the line end. Adjust the line accordingly.
- if (!line.isClosed())
- closeLine(line, IsLastLine::No);
- initializeNewLine(line);
- }
- }
-
- if (generatesInlineRun) {
- auto width = run->width;
- auto height = run->content.isText() ? LayoutUnit(root().style().computedLineHeight()) : layoutState.displayBoxForLayoutBox(run->content.inlineItem().layoutBox()).height();
- appendContentToLine(line, run->content, { width, height });
- }
-
- if (isLastRun)
- closeLine(line, IsLastLine::No);
- }
-
- closeLine(line, IsLastLine::Yes);
-}
-
void InlineFormattingContext::computeMarginBorderAndPadding(const InlineContainer& inlineContainer) const
{
// Non-replaced, non-formatting root containers (<span></span>) don't have width property -> non width computation.
@@ -419,42 +167,6 @@
computeHeightAndMargin(layoutBox);
}
-void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
-{
- auto& layoutState = this->layoutState();
- ASSERT(layoutState.hasDisplayBox(floatBox));
- auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
-
- // Set static position first.
- displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
- // Float it.
- displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
-}
-
-void InlineFormattingContext::placeInFlowPositionedChildren(unsigned fistRunIndex) const
-{
- auto& inlineRuns = formattingState().inlineRuns();
- for (auto runIndex = fistRunIndex; runIndex < inlineRuns.size(); ++runIndex) {
- auto& inlineRun = inlineRuns[runIndex];
-
- auto positionOffset = [&](auto& layoutBox) {
- // FIXME: Need to figure out whether in-flow offset should stick. This might very well be temporary.
- Optional<LayoutSize> offset;
- for (auto* box = &layoutBox; box != &root(); box = box->parent()) {
- if (!box->isInFlowPositioned())
- continue;
- offset = offset.valueOr(LayoutSize()) + Geometry::inFlowPositionedPositionOffset(layoutState(), *box);
- }
- return offset;
- };
-
- if (auto offset = positionOffset(inlineRun.inlineItem().layoutBox())) {
- inlineRun.moveVertically(offset->height());
- inlineRun.moveHorizontally(offset->width());
- }
- }
-}
-
static void addDetachingRules(InlineItem& inlineItem, Optional<LayoutUnit> nonBreakableStartWidth, Optional<LayoutUnit> nonBreakableEndWidth)
{
OptionSet<InlineItem::DetachingRule> detachingRules;
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (241022 => 241023)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2019-02-06 17:04:44 UTC (rev 241023)
@@ -29,6 +29,7 @@
#include "DisplayBox.h"
#include "FormattingContext.h"
+#include "InlineFormattingState.h"
#include "InlineRun.h"
#include <wtf/IsoMalloc.h>
@@ -35,9 +36,10 @@
namespace WebCore {
namespace Layout {
+class FloatingState;
class InlineContainer;
-class InlineFormattingState;
class InlineRunProvider;
+class Line;
// This class implements the layout logic for inline formatting contexts.
// https://www.w3.org/TR/CSS22/visuren.html#inline-formatting
@@ -45,79 +47,49 @@
WTF_MAKE_ISO_ALLOCATED(InlineFormattingContext);
public:
InlineFormattingContext(const Box& formattingContextRoot, InlineFormattingState&);
-
void layout() const override;
private:
- class Line {
+ class LineLayout {
public:
- void init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight);
- void close();
+ LineLayout(const InlineFormattingContext&);
+ void layout(const InlineRunProvider&) const;
- void appendContent(const InlineRunProvider::Run&, const LayoutSize&);
+ private:
+ enum class IsLastLine { No, Yes };
+ void initializeNewLine(Line&) const;
+ void closeLine(Line&, IsLastLine) const;
+ void appendContentToLine(Line&, const InlineRunProvider::Run&, const LayoutSize&) const;
+ void postProcessInlineRuns(Line&, IsLastLine) const;
+ void createFinalRuns(Line&) const;
+ void splitInlineRunIfNeeded(const InlineRun&, InlineRuns& splitRuns) const;
+ void computeFloatPosition(const FloatingContext&, Line&, const Box&) const;
+ void placeInFlowPositionedChildren(unsigned firstRunIndex) const;
+ void alignRuns(TextAlignMode, Line&, IsLastLine) const;
+ void computeExpansionOpportunities(Line&, const InlineRunProvider::Run&, InlineRunProvider::Run::Type lastRunType) const;
+ LayoutUnit runWidth(const InlineContent&, const InlineItem&, ItemPosition from, unsigned length, LayoutUnit contentLogicalLeft) const;
- void adjustLogicalLeft(LayoutUnit delta);
- void adjustLogicalRight(LayoutUnit delta);
+ private:
+ static void justifyRuns(Line&);
- bool hasContent() const { return !m_inlineRuns.isEmpty(); }
- bool isClosed() const { return m_closed; }
- bool isFirstLine() const { return m_isFirstLine; }
- Vector<InlineRun>& runs() { return m_inlineRuns; }
-
- LayoutUnit contentLogicalRight() const;
- LayoutUnit contentLogicalLeft() const { return m_logicalRect.left(); }
- LayoutUnit availableWidth() const { return m_availableWidth; }
- Optional<InlineRunProvider::Run::Type> lastRunType() const { return m_lastRunType; }
-
- LayoutUnit logicalTop() const { return m_logicalRect.top(); }
- LayoutUnit logicalBottom() const { return m_logicalRect.bottom(); }
- LayoutUnit logicalHeight() const { return logicalBottom() - logicalTop(); }
-
private:
- struct TrailingTrimmableContent {
- LayoutUnit width;
- unsigned length;
- };
- Optional<TrailingTrimmableContent> m_trailingTrimmableContent;
- Optional<InlineRunProvider::Run::Type> m_lastRunType;
- bool m_lastRunCanExpand { false };
-
- Display::Box::Rect m_logicalRect;
- LayoutUnit m_availableWidth;
-
- Vector<InlineRun> m_inlineRuns;
- bool m_isFirstLine { true };
- bool m_closed { true };
+ const InlineFormattingContext& m_formattingContext;
+ InlineFormattingState& m_formattingState;
+ FloatingState& m_floatingState;
+ const Container& m_formattingRoot;
};
- enum class IsLastLine { No, Yes };
class Geometry : public FormattingContext::Geometry {
public:
static HeightAndMargin inlineBlockHeightAndMargin(const LayoutState&, const Box&);
static WidthAndMargin inlineBlockWidthAndMargin(LayoutState&, const Box&);
- static void alignRuns(TextAlignMode, Line&, IsLastLine);
- static void computeExpansionOpportunities(Line&, const InlineRunProvider::Run&, InlineRunProvider::Run::Type lastRunType);
- static LayoutUnit runWidth(const InlineContent&, const InlineItem&, ItemPosition from, unsigned length, LayoutUnit contentLogicalLeft);
-
- private:
- static void justifyRuns(Line&);
};
- void layoutInlineContent(const InlineRunProvider&) const;
- void initializeNewLine(Line&) const;
- void closeLine(Line&, IsLastLine) const;
- void appendContentToLine(Line&, const InlineRunProvider::Run&, const LayoutSize&) const;
- void postProcessInlineRuns(Line&, IsLastLine) const;
- void createFinalRuns(Line&) const;
- void splitInlineRunIfNeeded(const InlineRun&, InlineRuns& splitRuns) const;
-
void layoutFormattingContextRoot(const Box&) const;
void computeWidthAndHeightForReplacedInlineBox(const Box&) const;
void computeMarginBorderAndPadding(const InlineContainer&) const;
void computeHeightAndMargin(const Box&) const;
void computeWidthAndMargin(const Box&) const;
- void computeFloatPosition(const FloatingContext&, Line&, const Box&) const;
- void placeInFlowPositionedChildren(unsigned firstRunIndex) const;
void collectInlineContent(InlineRunProvider&) const;
InstrinsicWidthConstraints instrinsicWidthConstraints() const override;
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp (241022 => 241023)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp 2019-02-06 17:04:44 UTC (rev 241023)
@@ -78,129 +78,7 @@
return complicatedCases(layoutState, layoutBox);
}
-static LayoutUnit adjustedLineLogicalLeft(TextAlignMode align, LayoutUnit lineLogicalLeft, LayoutUnit remainingWidth)
-{
- switch (align) {
- case TextAlignMode::Left:
- case TextAlignMode::WebKitLeft:
- case TextAlignMode::Start:
- return lineLogicalLeft;
- case TextAlignMode::Right:
- case TextAlignMode::WebKitRight:
- case TextAlignMode::End:
- return lineLogicalLeft + std::max(remainingWidth, 0_lu);
- case TextAlignMode::Center:
- case TextAlignMode::WebKitCenter:
- return lineLogicalLeft + std::max(remainingWidth / 2, 0_lu);
- case TextAlignMode::Justify:
- ASSERT_NOT_REACHED();
- break;
- }
- ASSERT_NOT_REACHED();
- return lineLogicalLeft;
}
-
-void InlineFormattingContext::Geometry::justifyRuns(Line& line)
-{
- auto& inlineRuns = line.runs();
- auto& lastInlineRun = inlineRuns.last();
-
- // Adjust (forbid) trailing expansion for the last text run on line.
- auto expansionBehavior = lastInlineRun.expansionOpportunity().behavior;
- // Remove allow and add forbid.
- expansionBehavior ^= AllowTrailingExpansion;
- expansionBehavior |= ForbidTrailingExpansion;
- lastInlineRun.expansionOpportunity().behavior = expansionBehavior;
-
- // Collect expansion opportunities and justify the runs.
- auto widthToDistribute = line.availableWidth();
- if (widthToDistribute <= 0)
- return;
-
- auto expansionOpportunities = 0;
- for (auto& inlineRun : inlineRuns)
- expansionOpportunities += inlineRun.expansionOpportunity().count;
-
- if (!expansionOpportunities)
- return;
-
- float expansion = widthToDistribute.toFloat() / expansionOpportunities;
- LayoutUnit accumulatedExpansion;
- for (auto& inlineRun : inlineRuns) {
- auto expansionForRun = inlineRun.expansionOpportunity().count * expansion;
-
- inlineRun.expansionOpportunity().expansion = expansionForRun;
- inlineRun.setLogicalLeft(inlineRun.logicalLeft() + accumulatedExpansion);
- inlineRun.setLogicalWidth(inlineRun.logicalWidth() + expansionForRun);
- accumulatedExpansion += expansionForRun;
- }
}
-void InlineFormattingContext::Geometry::computeExpansionOpportunities(Line& line, const InlineRunProvider::Run& run, InlineRunProvider::Run::Type lastRunType)
-{
- auto isExpansionOpportunity = [](auto currentRunIsWhitespace, auto lastRunIsWhitespace) {
- return currentRunIsWhitespace || (!currentRunIsWhitespace && !lastRunIsWhitespace);
- };
-
- auto expansionBehavior = [](auto isAtExpansionOpportunity) {
- ExpansionBehavior expansionBehavior = AllowTrailingExpansion;
- expansionBehavior |= isAtExpansionOpportunity ? ForbidLeadingExpansion : AllowLeadingExpansion;
- return expansionBehavior;
- };
-
- auto isAtExpansionOpportunity = isExpansionOpportunity(run.isWhitespace(), lastRunType == InlineRunProvider::Run::Type::Whitespace);
-
- auto& currentInlineRun = line.runs().last();
- auto& expansionOpportunity = currentInlineRun.expansionOpportunity();
- if (isAtExpansionOpportunity)
- ++expansionOpportunity.count;
-
- expansionOpportunity.behavior = expansionBehavior(isAtExpansionOpportunity);
-}
-
-void InlineFormattingContext::Geometry::alignRuns(TextAlignMode textAlign, Line& line, IsLastLine isLastLine)
-{
- auto adjutedTextAlignment = textAlign != TextAlignMode::Justify ? textAlign : isLastLine == IsLastLine::No ? TextAlignMode::Justify : TextAlignMode::Left;
- if (adjutedTextAlignment == TextAlignMode::Justify) {
- justifyRuns(line);
- return;
- }
-
- auto lineLogicalLeft = line.contentLogicalLeft();
- auto adjustedLogicalLeft = adjustedLineLogicalLeft(adjutedTextAlignment, lineLogicalLeft, line.availableWidth());
- if (adjustedLogicalLeft == lineLogicalLeft)
- return;
-
- auto delta = adjustedLogicalLeft - lineLogicalLeft;
- for (auto& inlineRun : line.runs())
- inlineRun.setLogicalLeft(inlineRun.logicalLeft() + delta);
-}
-
-LayoutUnit InlineFormattingContext::Geometry::runWidth(const InlineContent& inlineContent, const InlineItem& inlineItem, ItemPosition from, unsigned length, LayoutUnit contentLogicalLeft)
-{
- LayoutUnit width;
- auto startPosition = from;
- auto iterator = inlineContent.find(const_cast<InlineItem*>(&inlineItem));
-#if !ASSERT_DISABLED
- auto inlineItemEnd = inlineContent.end();
#endif
- while (length) {
- ASSERT(iterator != inlineItemEnd);
- auto& currentInlineItem = **iterator;
- auto endPosition = std::min<ItemPosition>(startPosition + length, currentInlineItem.textContent().length());
- auto textWidth = TextUtil::width(currentInlineItem, startPosition, endPosition, contentLogicalLeft);
-
- contentLogicalLeft += textWidth;
- width += textWidth;
- length -= (endPosition - startPosition);
-
- startPosition = 0;
- ++iterator;
- }
- return width;
-}
-
-}
-}
-
-#endif
Added: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextLineLayout.cpp (0 => 241023)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextLineLayout.cpp (rev 0)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextLineLayout.cpp 2019-02-06 17:04:44 UTC (rev 241023)
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2019 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 "InlineFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FloatingContext.h"
+#include "FloatingState.h"
+#include "InlineFormattingState.h"
+#include "InlineLineBreaker.h"
+#include "InlineRunProvider.h"
+#include "LayoutBox.h"
+#include "LayoutContainer.h"
+#include "LayoutState.h"
+#include "TextUtil.h"
+
+namespace WebCore {
+namespace Layout {
+
+class Line {
+public:
+ void init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight);
+ void close();
+
+ void appendContent(const InlineRunProvider::Run&, const LayoutSize&);
+
+ void adjustLogicalLeft(LayoutUnit delta);
+ void adjustLogicalRight(LayoutUnit delta);
+
+ bool hasContent() const { return !m_inlineRuns.isEmpty(); }
+ bool isClosed() const { return m_closed; }
+ bool isFirstLine() const { return m_isFirstLine; }
+ Vector<InlineRun>& runs() { return m_inlineRuns; }
+
+ LayoutUnit contentLogicalRight() const;
+ LayoutUnit contentLogicalLeft() const { return m_logicalRect.left(); }
+ LayoutUnit availableWidth() const { return m_availableWidth; }
+ Optional<InlineRunProvider::Run::Type> lastRunType() const { return m_lastRunType; }
+
+ LayoutUnit logicalTop() const { return m_logicalRect.top(); }
+ LayoutUnit logicalBottom() const { return m_logicalRect.bottom(); }
+ LayoutUnit logicalHeight() const { return logicalBottom() - logicalTop(); }
+
+private:
+ struct TrailingTrimmableContent {
+ LayoutUnit width;
+ unsigned length;
+ };
+ Optional<TrailingTrimmableContent> m_trailingTrimmableContent;
+ Optional<InlineRunProvider::Run::Type> m_lastRunType;
+ bool m_lastRunCanExpand { false };
+
+ Display::Box::Rect m_logicalRect;
+ LayoutUnit m_availableWidth;
+
+ Vector<InlineRun> m_inlineRuns;
+ bool m_isFirstLine { true };
+ bool m_closed { true };
+};
+
+void Line::init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight)
+{
+ m_logicalRect.setTopLeft(topLeft);
+ m_logicalRect.setWidth(availableWidth);
+ m_logicalRect.setHeight(minimalHeight);
+ m_availableWidth = availableWidth;
+
+ m_inlineRuns.clear();
+ m_lastRunType = { };
+ m_lastRunCanExpand = false;
+ m_trailingTrimmableContent = { };
+ m_closed = false;
+}
+
+void Line::adjustLogicalLeft(LayoutUnit delta)
+{
+ ASSERT(delta > 0);
+
+ m_availableWidth -= delta;
+ m_logicalRect.shiftLeftTo(m_logicalRect.left() + delta);
+
+ for (auto& inlineRun : m_inlineRuns)
+ inlineRun.moveHorizontally(delta);
+}
+
+void Line::adjustLogicalRight(LayoutUnit delta)
+{
+ ASSERT(delta > 0);
+
+ m_availableWidth -= delta;
+ m_logicalRect.shiftRightTo(m_logicalRect.right() - delta);
+}
+
+static bool isTrimmableContent(const InlineRunProvider::Run& inlineRun)
+{
+ return inlineRun.isWhitespace() && inlineRun.style().collapseWhiteSpace();
+}
+
+LayoutUnit Line::contentLogicalRight() const
+{
+ if (m_inlineRuns.isEmpty())
+ return m_logicalRect.left();
+
+ return m_inlineRuns.last().logicalRight();
+}
+
+void Line::appendContent(const InlineRunProvider::Run& run, const LayoutSize& runSize)
+{
+ ASSERT(!isClosed());
+
+ // Append this text run to the end of the last text run, if the last run is continuous.
+ Optional<InlineRun::TextContext> textRun;
+ if (run.isText()) {
+ auto textContext = run.textContext();
+ auto runLength = textContext->isCollapsed() ? 1 : textContext->length();
+ textRun = InlineRun::TextContext { textContext->start(), runLength };
+ }
+
+ auto requiresNewInlineRun = !hasContent() || !run.isText() || !m_lastRunCanExpand;
+ if (requiresNewInlineRun) {
+ // FIXME: This needs proper baseline handling
+ auto inlineRun = InlineRun { { logicalTop(), contentLogicalRight(), runSize.width(), runSize.height() }, run.inlineItem() };
+ if (textRun)
+ inlineRun.setTextContext({ textRun->start(), textRun->length() });
+ m_inlineRuns.append(inlineRun);
+ m_logicalRect.setHeight(std::max(runSize.height(), m_logicalRect.height()));
+ } else {
+ // Non-text runs always require new inline run.
+ ASSERT(textRun);
+ auto& inlineRun = m_inlineRuns.last();
+ ASSERT(runSize.height() == inlineRun.logicalHeight());
+ inlineRun.setLogicalWidth(inlineRun.logicalWidth() + runSize.width());
+ inlineRun.textContext()->setLength(inlineRun.textContext()->length() + textRun->length());
+ }
+
+ m_availableWidth -= runSize.width();
+ m_lastRunType = run.type();
+ m_lastRunCanExpand = run.isText() && !run.textContext()->isCollapsed();
+ m_trailingTrimmableContent = { };
+ if (isTrimmableContent(run))
+ m_trailingTrimmableContent = TrailingTrimmableContent { runSize.width(), textRun->length() };
+}
+
+void Line::close()
+{
+ auto trimTrailingContent = [&] {
+ if (!m_trailingTrimmableContent)
+ return;
+ auto& lastInlineRun = m_inlineRuns.last();
+ lastInlineRun.setLogicalWidth(lastInlineRun.logicalWidth() - m_trailingTrimmableContent->width);
+ lastInlineRun.textContext()->setLength(lastInlineRun.textContext()->length() - m_trailingTrimmableContent->length);
+
+ if (!lastInlineRun.textContext()->length())
+ m_inlineRuns.removeLast();
+ m_availableWidth += m_trailingTrimmableContent->width;
+ m_trailingTrimmableContent = { };
+ };
+
+ if (!hasContent())
+ return;
+
+ trimTrailingContent();
+ m_isFirstLine = false;
+ m_closed = true;
+}
+
+InlineFormattingContext::LineLayout::LineLayout(const InlineFormattingContext& inlineFormattingContext)
+ : m_formattingContext(inlineFormattingContext)
+ , m_formattingState(m_formattingContext.formattingState())
+ , m_floatingState(m_formattingState.floatingState())
+ , m_formattingRoot(downcast<Container>(m_formattingContext.root()))
+{
+}
+
+static bool isTrimmableContent(const InlineLineBreaker::Run& run)
+{
+ return run.content.isWhitespace() && run.content.style().collapseWhiteSpace();
+}
+
+void InlineFormattingContext::LineLayout::layout(const InlineRunProvider& inlineRunProvider) const
+{
+ auto& layoutState = m_formattingContext.layoutState();
+ auto floatingContext = FloatingContext { m_floatingState };
+
+ Line line;
+ initializeNewLine(line);
+
+ InlineLineBreaker lineBreaker(layoutState, m_formattingState.inlineContent(), inlineRunProvider.runs());
+ while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
+ auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
+ auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
+ auto generatesInlineRun = true;
+
+ // Position float and adjust the runs on line.
+ if (run->content.isFloat()) {
+ auto& floatBox = run->content.inlineItem().layoutBox();
+ computeFloatPosition(floatingContext, line, floatBox);
+ m_floatingState.append(floatBox);
+
+ auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).marginBox().width();
+ // Shrink availble space for current line and move existing inline runs.
+ floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
+
+ generatesInlineRun = false;
+ }
+
+ // 1. Initialize new line if needed.
+ // 2. Append inline run unless it is skipped.
+ // 3. Close current line if needed.
+ if (isFirstRun) {
+ // When the first run does not generate an actual inline run, the next run comes in first-run as well.
+ // No need to spend time on closing/initializing.
+ // Skip leading whitespace.
+ if (!generatesInlineRun || isTrimmableContent(*run))
+ continue;
+
+ if (line.hasContent()) {
+ // Previous run ended up being at the line end. Adjust the line accordingly.
+ if (!line.isClosed())
+ closeLine(line, IsLastLine::No);
+ initializeNewLine(line);
+ }
+ }
+
+ if (generatesInlineRun) {
+ auto width = run->width;
+ auto height = run->content.isText() ? LayoutUnit(m_formattingRoot.style().computedLineHeight()) : layoutState.displayBoxForLayoutBox(run->content.inlineItem().layoutBox()).height();
+ appendContentToLine(line, run->content, { width, height });
+ }
+
+ if (isLastRun)
+ closeLine(line, IsLastLine::No);
+ }
+
+ closeLine(line, IsLastLine::Yes);
+}
+
+void InlineFormattingContext::LineLayout::initializeNewLine(Line& line) const
+{
+ auto& formattingRootDisplayBox = m_formattingContext.layoutState().displayBoxForLayoutBox(m_formattingRoot);
+
+ auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
+ auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
+ auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
+
+ // Check for intruding floats and adjust logical left/available width for this line accordingly.
+ if (!m_floatingState.isEmpty()) {
+ auto floatConstraints = m_floatingState.constraints({ lineLogicalTop }, m_formattingRoot);
+ // Check if these constraints actually put limitation on the line.
+ if (floatConstraints.left && *floatConstraints.left <= formattingRootDisplayBox.contentBoxLeft())
+ floatConstraints.left = { };
+
+ if (floatConstraints.right && *floatConstraints.right >= formattingRootDisplayBox.contentBoxRight())
+ floatConstraints.right = { };
+
+ if (floatConstraints.left && floatConstraints.right) {
+ ASSERT(*floatConstraints.left < *floatConstraints.right);
+ availableWidth = *floatConstraints.right - *floatConstraints.left;
+ lineLogicalLeft = *floatConstraints.left;
+ } else if (floatConstraints.left) {
+ ASSERT(*floatConstraints.left > lineLogicalLeft);
+ availableWidth -= (*floatConstraints.left - lineLogicalLeft);
+ lineLogicalLeft = *floatConstraints.left;
+ } else if (floatConstraints.right) {
+ ASSERT(*floatConstraints.right > lineLogicalLeft);
+ availableWidth = *floatConstraints.right - lineLogicalLeft;
+ }
+ }
+
+ line.init({ lineLogicalLeft, lineLogicalTop }, availableWidth, m_formattingRoot.style().computedLineHeight());
+}
+
+void InlineFormattingContext::LineLayout::splitInlineRunIfNeeded(const InlineRun& inlineRun, InlineRuns& splitRuns) const
+{
+ ASSERT(inlineRun.textContext());
+ ASSERT(inlineRun.overlapsMultipleInlineItems());
+ // In certain cases, a run can overlap multiple inline elements like this:
+ // <span>normal text content</span><span style="position: relative; left: 10px;">but this one needs a dedicated run</span><span>end of text</span>
+ // The content above generates one long run <normal text contentbut this one needs dedicated runend of text>
+ // However, since the middle run is positioned, it needs to be moved independently from the rest of the content, hence it needs a dedicated inline run.
+
+ // 1. Start with the first inline item (element) and travers the list until
+ // 2. either find an inline item that needs a dedicated run or we reach the end of the run
+ // 3. Create dedicate inline runs.
+ auto& inlineContent = m_formattingState.inlineContent();
+ auto contentStart = inlineRun.logicalLeft();
+ auto startPosition = inlineRun.textContext()->start();
+ auto remaningLength = inlineRun.textContext()->length();
+
+ struct Uncommitted {
+ const InlineItem* firstInlineItem { nullptr };
+ const InlineItem* lastInlineItem { nullptr };
+ unsigned length { 0 };
+ };
+ Optional<Uncommitted> uncommitted;
+
+ auto commit = [&] {
+ if (!uncommitted)
+ return;
+
+ contentStart += uncommitted->firstInlineItem->nonBreakableStart();
+
+ auto runWidth = this->runWidth(inlineContent, *uncommitted->firstInlineItem, startPosition, uncommitted->length, contentStart);
+ auto run = InlineRun { { inlineRun.logicalTop(), contentStart, runWidth, inlineRun.logicalHeight() }, *uncommitted->firstInlineItem };
+ run.setTextContext({ startPosition, uncommitted->length });
+ splitRuns.append(run);
+
+ contentStart += runWidth + uncommitted->lastInlineItem->nonBreakableEnd();
+
+ startPosition = 0;
+ uncommitted = { };
+ };
+
+ for (auto iterator = inlineContent.find(const_cast<InlineItem*>(&inlineRun.inlineItem())); iterator != inlineContent.end() && remaningLength > 0; ++iterator) {
+ auto& inlineItem = **iterator;
+
+ // Skip all non-inflow boxes (floats, out-of-flow positioned elements). They don't participate in the inline run context.
+ if (!inlineItem.layoutBox().isInFlow())
+ continue;
+
+ auto currentLength = [&] {
+ return std::min(remaningLength, inlineItem.textContent().length() - startPosition);
+ };
+
+ // 1. Break before/after -> requires dedicated run -> commit what we've got so far and also commit the current inline element as a separate inline run.
+ // 2. Break at the beginning of the inline element -> commit what we've got so far. Current element becomes the first uncommitted.
+ // 3. Break at the end of the inline element -> commit what we've got so far including the current element.
+ // 4. Inline element does not require run breaking -> add current inline element to uncommitted. Jump to the next element.
+ auto detachingRules = inlineItem.detachingRules();
+
+ // #1
+ if (detachingRules.containsAll({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd })) {
+ commit();
+ auto contentLength = currentLength();
+ uncommitted = Uncommitted { &inlineItem, &inlineItem, contentLength };
+ remaningLength -= contentLength;
+ commit();
+ continue;
+ }
+
+ // #2
+ if (detachingRules.contains(InlineItem::DetachingRule::BreakAtStart))
+ commit();
+
+ // Add current inline item to uncommitted.
+ // #3 and #4
+ auto contentLength = currentLength();
+ if (!uncommitted)
+ uncommitted = Uncommitted { &inlineItem, &inlineItem, 0 };
+ uncommitted->length += contentLength;
+ uncommitted->lastInlineItem = &inlineItem;
+ remaningLength -= contentLength;
+
+ // #3
+ if (detachingRules.contains(InlineItem::DetachingRule::BreakAtEnd))
+ commit();
+ }
+ // Either all inline elements needed dedicated runs or neither of them.
+ if (!remaningLength || remaningLength == inlineRun.textContext()->length())
+ return;
+
+ commit();
+}
+
+void InlineFormattingContext::LineLayout::createFinalRuns(Line& line) const
+{
+ for (auto& inlineRun : line.runs()) {
+ if (inlineRun.overlapsMultipleInlineItems()) {
+ InlineRuns splitRuns;
+ splitInlineRunIfNeeded(inlineRun, splitRuns);
+ for (auto& splitRun : splitRuns)
+ m_formattingState.appendInlineRun(splitRun);
+
+ if (!splitRuns.isEmpty())
+ continue;
+ }
+
+ auto finalRun = [&] {
+ auto& inlineItem = inlineRun.inlineItem();
+ if (inlineItem.detachingRules().isEmpty())
+ return inlineRun;
+
+ InlineRun adjustedRun = inlineRun;
+ auto width = inlineRun.logicalWidth() - inlineItem.nonBreakableStart() - inlineItem.nonBreakableEnd();
+ adjustedRun.setLogicalLeft(inlineRun.logicalLeft() + inlineItem.nonBreakableStart());
+ adjustedRun.setLogicalWidth(width);
+ return adjustedRun;
+ };
+
+ m_formattingState.appendInlineRun(finalRun());
+ }
+}
+
+void InlineFormattingContext::LineLayout::postProcessInlineRuns(Line& line, IsLastLine isLastLine) const
+{
+ alignRuns(m_formattingRoot.style().textAlign(), line, isLastLine);
+ auto firstRunIndex = m_formattingState.inlineRuns().size();
+ createFinalRuns(line);
+
+ placeInFlowPositionedChildren(firstRunIndex);
+}
+
+void InlineFormattingContext::LineLayout::closeLine(Line& line, IsLastLine isLastLine) const
+{
+ line.close();
+ if (!line.hasContent())
+ return;
+
+ postProcessInlineRuns(line, isLastLine);
+}
+
+void InlineFormattingContext::LineLayout::appendContentToLine(Line& line, const InlineRunProvider::Run& run, const LayoutSize& runSize) const
+{
+ auto lastRunType = line.lastRunType();
+ line.appendContent(run, runSize);
+
+ if (m_formattingRoot.style().textAlign() == TextAlignMode::Justify)
+ computeExpansionOpportunities(line, run, lastRunType.valueOr(InlineRunProvider::Run::Type::NonWhitespace));
+}
+
+void InlineFormattingContext::LineLayout::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
+{
+ auto& layoutState = m_formattingContext.layoutState();
+ ASSERT(layoutState.hasDisplayBox(floatBox));
+ auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
+
+ // Set static position first.
+ displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
+ // Float it.
+ displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
+}
+
+void InlineFormattingContext::LineLayout::placeInFlowPositionedChildren(unsigned fistRunIndex) const
+{
+ auto& layoutState = m_formattingContext.layoutState();
+ auto& inlineRuns = m_formattingState.inlineRuns();
+ for (auto runIndex = fistRunIndex; runIndex < inlineRuns.size(); ++runIndex) {
+ auto& inlineRun = inlineRuns[runIndex];
+
+ auto positionOffset = [&](auto& layoutBox) {
+ // FIXME: Need to figure out whether in-flow offset should stick. This might very well be temporary.
+ Optional<LayoutSize> offset;
+ for (auto* box = &layoutBox; box != &m_formattingRoot; box = box->parent()) {
+ if (!box->isInFlowPositioned())
+ continue;
+ offset = offset.valueOr(LayoutSize()) + Geometry::inFlowPositionedPositionOffset(layoutState, *box);
+ }
+ return offset;
+ };
+
+ if (auto offset = positionOffset(inlineRun.inlineItem().layoutBox())) {
+ inlineRun.moveVertically(offset->height());
+ inlineRun.moveHorizontally(offset->width());
+ }
+ }
+}
+
+static LayoutUnit adjustedLineLogicalLeft(TextAlignMode align, LayoutUnit lineLogicalLeft, LayoutUnit remainingWidth)
+{
+ switch (align) {
+ case TextAlignMode::Left:
+ case TextAlignMode::WebKitLeft:
+ case TextAlignMode::Start:
+ return lineLogicalLeft;
+ case TextAlignMode::Right:
+ case TextAlignMode::WebKitRight:
+ case TextAlignMode::End:
+ return lineLogicalLeft + std::max(remainingWidth, 0_lu);
+ case TextAlignMode::Center:
+ case TextAlignMode::WebKitCenter:
+ return lineLogicalLeft + std::max(remainingWidth / 2, 0_lu);
+ case TextAlignMode::Justify:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ ASSERT_NOT_REACHED();
+ return lineLogicalLeft;
+}
+
+void InlineFormattingContext::LineLayout::justifyRuns(Line& line)
+{
+ auto& inlineRuns = line.runs();
+ auto& lastInlineRun = inlineRuns.last();
+
+ // Adjust (forbid) trailing expansion for the last text run on line.
+ auto expansionBehavior = lastInlineRun.expansionOpportunity().behavior;
+ // Remove allow and add forbid.
+ expansionBehavior ^= AllowTrailingExpansion;
+ expansionBehavior |= ForbidTrailingExpansion;
+ lastInlineRun.expansionOpportunity().behavior = expansionBehavior;
+
+ // Collect expansion opportunities and justify the runs.
+ auto widthToDistribute = line.availableWidth();
+ if (widthToDistribute <= 0)
+ return;
+
+ auto expansionOpportunities = 0;
+ for (auto& inlineRun : inlineRuns)
+ expansionOpportunities += inlineRun.expansionOpportunity().count;
+
+ if (!expansionOpportunities)
+ return;
+
+ float expansion = widthToDistribute.toFloat() / expansionOpportunities;
+ LayoutUnit accumulatedExpansion;
+ for (auto& inlineRun : inlineRuns) {
+ auto expansionForRun = inlineRun.expansionOpportunity().count * expansion;
+
+ inlineRun.expansionOpportunity().expansion = expansionForRun;
+ inlineRun.setLogicalLeft(inlineRun.logicalLeft() + accumulatedExpansion);
+ inlineRun.setLogicalWidth(inlineRun.logicalWidth() + expansionForRun);
+ accumulatedExpansion += expansionForRun;
+ }
+}
+
+void InlineFormattingContext::LineLayout::computeExpansionOpportunities(Line& line, const InlineRunProvider::Run& run, InlineRunProvider::Run::Type lastRunType) const
+{
+ auto isExpansionOpportunity = [](auto currentRunIsWhitespace, auto lastRunIsWhitespace) {
+ return currentRunIsWhitespace || (!currentRunIsWhitespace && !lastRunIsWhitespace);
+ };
+
+ auto expansionBehavior = [](auto isAtExpansionOpportunity) {
+ ExpansionBehavior expansionBehavior = AllowTrailingExpansion;
+ expansionBehavior |= isAtExpansionOpportunity ? ForbidLeadingExpansion : AllowLeadingExpansion;
+ return expansionBehavior;
+ };
+
+ auto isAtExpansionOpportunity = isExpansionOpportunity(run.isWhitespace(), lastRunType == InlineRunProvider::Run::Type::Whitespace);
+
+ auto& currentInlineRun = line.runs().last();
+ auto& expansionOpportunity = currentInlineRun.expansionOpportunity();
+ if (isAtExpansionOpportunity)
+ ++expansionOpportunity.count;
+
+ expansionOpportunity.behavior = expansionBehavior(isAtExpansionOpportunity);
+}
+
+void InlineFormattingContext::LineLayout::alignRuns(TextAlignMode textAlign, Line& line, IsLastLine isLastLine) const
+{
+ auto adjutedTextAlignment = textAlign != TextAlignMode::Justify ? textAlign : isLastLine == IsLastLine::No ? TextAlignMode::Justify : TextAlignMode::Left;
+ if (adjutedTextAlignment == TextAlignMode::Justify) {
+ justifyRuns(line);
+ return;
+ }
+
+ auto lineLogicalLeft = line.contentLogicalLeft();
+ auto adjustedLogicalLeft = adjustedLineLogicalLeft(adjutedTextAlignment, lineLogicalLeft, line.availableWidth());
+ if (adjustedLogicalLeft == lineLogicalLeft)
+ return;
+
+ auto delta = adjustedLogicalLeft - lineLogicalLeft;
+ for (auto& inlineRun : line.runs())
+ inlineRun.setLogicalLeft(inlineRun.logicalLeft() + delta);
+}
+
+LayoutUnit InlineFormattingContext::LineLayout::runWidth(const InlineContent& inlineContent, const InlineItem& inlineItem, ItemPosition from, unsigned length, LayoutUnit contentLogicalLeft) const
+{
+ LayoutUnit width;
+ auto startPosition = from;
+ auto iterator = inlineContent.find(const_cast<InlineItem*>(&inlineItem));
+#if !ASSERT_DISABLED
+ auto inlineItemEnd = inlineContent.end();
+#endif
+ while (length) {
+ ASSERT(iterator != inlineItemEnd);
+ auto& currentInlineItem = **iterator;
+ auto endPosition = std::min<ItemPosition>(startPosition + length, currentInlineItem.textContent().length());
+ auto textWidth = TextUtil::width(currentInlineItem, startPosition, endPosition, contentLogicalLeft);
+
+ contentLogicalLeft += textWidth;
+ width += textWidth;
+ length -= (endPosition - startPosition);
+
+ startPosition = 0;
+ ++iterator;
+ }
+ return width;
+}
+
+}
+}
+
+#endif
Deleted: trunk/Source/WebCore/layout/inlineformatting/Line.cpp (241022 => 241023)
--- trunk/Source/WebCore/layout/inlineformatting/Line.cpp 2019-02-06 16:59:41 UTC (rev 241022)
+++ trunk/Source/WebCore/layout/inlineformatting/Line.cpp 2019-02-06 17:04:44 UTC (rev 241023)
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2018 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 "InlineFormattingContext.h"
-#include "InlineRunProvider.h"
-
-#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-
-namespace WebCore {
-namespace Layout {
-
-void InlineFormattingContext::Line::init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight)
-{
- m_logicalRect.setTopLeft(topLeft);
- m_logicalRect.setWidth(availableWidth);
- m_logicalRect.setHeight(minimalHeight);
- m_availableWidth = availableWidth;
-
- m_inlineRuns.clear();
- m_lastRunType = { };
- m_lastRunCanExpand = false;
- m_trailingTrimmableContent = { };
- m_closed = false;
-}
-
-void InlineFormattingContext::Line::adjustLogicalLeft(LayoutUnit delta)
-{
- ASSERT(delta > 0);
-
- m_availableWidth -= delta;
- m_logicalRect.shiftLeftTo(m_logicalRect.left() + delta);
-
- for (auto& inlineRun : m_inlineRuns)
- inlineRun.moveHorizontally(delta);
-}
-
-void InlineFormattingContext::Line::adjustLogicalRight(LayoutUnit delta)
-{
- ASSERT(delta > 0);
-
- m_availableWidth -= delta;
- m_logicalRect.shiftRightTo(m_logicalRect.right() - delta);
-}
-
-static bool isTrimmableContent(const InlineRunProvider::Run& inlineRun)
-{
- return inlineRun.isWhitespace() && inlineRun.style().collapseWhiteSpace();
-}
-
-LayoutUnit InlineFormattingContext::Line::contentLogicalRight() const
-{
- if (m_inlineRuns.isEmpty())
- return m_logicalRect.left();
-
- return m_inlineRuns.last().logicalRight();
-}
-
-void InlineFormattingContext::Line::appendContent(const InlineRunProvider::Run& run, const LayoutSize& runSize)
-{
- ASSERT(!isClosed());
-
- // Append this text run to the end of the last text run, if the last run is continuous.
- Optional<InlineRun::TextContext> textRun;
- if (run.isText()) {
- auto textContext = run.textContext();
- auto runLength = textContext->isCollapsed() ? 1 : textContext->length();
- textRun = InlineRun::TextContext { textContext->start(), runLength };
- }
-
- auto requiresNewInlineRun = !hasContent() || !run.isText() || !m_lastRunCanExpand;
- if (requiresNewInlineRun) {
- // FIXME: This needs proper baseline handling
- auto inlineRun = InlineRun { { logicalTop(), contentLogicalRight(), runSize.width(), runSize.height() }, run.inlineItem() };
- if (textRun)
- inlineRun.setTextContext({ textRun->start(), textRun->length() });
- m_inlineRuns.append(inlineRun);
- m_logicalRect.setHeight(std::max(runSize.height(), m_logicalRect.height()));
- } else {
- // Non-text runs always require new inline run.
- ASSERT(textRun);
- auto& inlineRun = m_inlineRuns.last();
- ASSERT(runSize.height() == inlineRun.logicalHeight());
- inlineRun.setLogicalWidth(inlineRun.logicalWidth() + runSize.width());
- inlineRun.textContext()->setLength(inlineRun.textContext()->length() + textRun->length());
- }
-
- m_availableWidth -= runSize.width();
- m_lastRunType = run.type();
- m_lastRunCanExpand = run.isText() && !run.textContext()->isCollapsed();
- m_trailingTrimmableContent = { };
- if (isTrimmableContent(run))
- m_trailingTrimmableContent = TrailingTrimmableContent { runSize.width(), textRun->length() };
-}
-
-void InlineFormattingContext::Line::close()
-{
- auto trimTrailingContent = [&]{
-
- if (!m_trailingTrimmableContent)
- return;
-
- auto& lastInlineRun = m_inlineRuns.last();
- lastInlineRun.setLogicalWidth(lastInlineRun.logicalWidth() - m_trailingTrimmableContent->width);
- lastInlineRun.textContext()->setLength(lastInlineRun.textContext()->length() - m_trailingTrimmableContent->length);
-
- if (!lastInlineRun.textContext()->length())
- m_inlineRuns.removeLast();
- m_availableWidth += m_trailingTrimmableContent->width;
- m_trailingTrimmableContent = { };
- };
-
- if (!hasContent())
- return;
-
- trimTrailingContent();
- m_isFirstLine = false;
- m_closed = true;
-}
-
-}
-}
-
-#endif