WebKit Bugzilla
Attachment 361274 Details for
Bug 194328
: [LFC][IFC] Move line layout code to a dedicated file
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-194328-20190205205625.patch (text/plain), 56.85 KB, created by
zalan
on 2019-02-05 20:56:43 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
zalan
Created:
2019-02-05 20:56:43 PST
Size:
56.85 KB
patch
obsolete
>Subversion Revision: 240991 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 6ebd36c6181f4d2a1cd97db58dc5d851fd5c781d..ae915c35c2c9571f3c1fbff6ebdb04506e39638a 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,65 @@ >+2019-02-05 Zalan Bujtas <zalan@apple.com> >+ >+ [LFC][IFC] Move line layout code to a dedicated file >+ https://bugs.webkit.org/show_bug.cgi?id=194328 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * 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-05 Alex Christensen <achristensen@webkit.org> > > Stop using blobRegistry in NetworkProcess >diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt >index 3f4296d5242f423e590a386d02b2b929a8d8e6a1..02ec543e662878e80022c96ab4aa598eeb58a78d 100644 >--- a/Source/WebCore/Sources.txt >+++ b/Source/WebCore/Sources.txt >@@ -1343,6 +1343,7 @@ layout/inlineformatting/InlineFormattingContextGeometry.cpp > layout/inlineformatting/InlineFormattingState.cpp > layout/inlineformatting/InlineInvalidation.cpp > layout/inlineformatting/InlineLineBreaker.cpp >+layout/inlineformatting/InlineLineLayout.cpp > layout/inlineformatting/InlineRunProvider.cpp > layout/inlineformatting/Line.cpp > layout/inlineformatting/text/TextUtil.cpp >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index 85590f6db27880e892ed5d0a311a1271784a6399..4dc12ae6e30b5c59685f80f2285966d2bd007f6d 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -9199,6 +9199,7 @@ > 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 /* InlineLineLayout.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineLayout.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>"; }; >@@ -16463,6 +16464,7 @@ > 6FE7CFA02177EEF1005B1573 /* InlineItem.h */, > 6FE198132178397B00446F08 /* InlineLineBreaker.cpp */, > 6FE198152178397C00446F08 /* InlineLineBreaker.h */, >+ 6F25B200220A85AB0000011B /* InlineLineLayout.cpp */, > 6F219D742178D37100BB033C /* InlineRun.h */, > 6F5217C62177F5A6006583BB /* InlineRunProvider.cpp */, > 6F5217C42177F5A6006583BB /* InlineRunProvider.h */, >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >index 1d1ca9ecc7f2aaf662b6887e40e703daf476b584..4b3b316d4613b5981f622e7c600b08f58777ac4f 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >@@ -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 @@ void InlineFormattingContext::layout() const > > 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 @@ void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Bo > 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; >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >index 97f8ed6bac731cc02a2bfa0fa3c5c60555defbad..1e8c27f8b86476deea89238b93d5f72505a32c3e 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >@@ -29,14 +29,15 @@ > > #include "DisplayBox.h" > #include "FormattingContext.h" >+#include "InlineFormattingState.h" > #include "InlineRun.h" > #include <wtf/IsoMalloc.h> > > namespace WebCore { > namespace Layout { > >+class FloatingState; > class InlineContainer; >-class InlineFormattingState; > class InlineRunProvider; > > // This class implements the layout logic for inline formatting contexts. >@@ -45,79 +46,90 @@ class InlineFormattingContext : public FormattingContext { > 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(); >- >- 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(); } >+ LineLayout(const InlineFormattingContext&); >+ void layout(const InlineRunProvider&) const; >+ >+ enum class IsLastLine { No, Yes }; >+ 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 }; >+ }; > > private: >- struct TrailingTrimmableContent { >- LayoutUnit width; >- unsigned length; >- }; >- Optional<TrailingTrimmableContent> m_trailingTrimmableContent; >- Optional<InlineRunProvider::Run::Type> m_lastRunType; >- bool m_lastRunCanExpand { false }; >+ 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; > >- Display::Box::Rect m_logicalRect; >- LayoutUnit m_availableWidth; >+ private: >+ static void justifyRuns(LineLayout::Line&); > >- Vector<InlineRun> m_inlineRuns; >- bool m_isFirstLine { true }; >- bool m_closed { true }; >+ private: >+ 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; >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >index e25e5b2c23532dee6783b09783279bbabcce37e8..cf6bdd07454b76a358dc888c93a5c72ce1fe26a6 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >@@ -78,128 +78,6 @@ HeightAndMargin InlineFormattingContext::Geometry::inlineBlockHeightAndMargin(co > 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; >-} >- > } > } > >diff --git a/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp b/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..1c74d22ad62359a9c475bcc25a124a5f5790bca6 >--- /dev/null >+++ b/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp >@@ -0,0 +1,459 @@ >+/* >+ * 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" >+ >+namespace WebCore { >+namespace Layout { >+ >+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 >diff --git a/Source/WebCore/layout/inlineformatting/Line.cpp b/Source/WebCore/layout/inlineformatting/Line.cpp >index b1273d1b1c5fc69ff9abe08059ec465777255622..e6a4a6bdbfe9adfb835cef8b602ba196ad5f3309 100644 >--- a/Source/WebCore/layout/inlineformatting/Line.cpp >+++ b/Source/WebCore/layout/inlineformatting/Line.cpp >@@ -32,7 +32,7 @@ > namespace WebCore { > namespace Layout { > >-void InlineFormattingContext::Line::init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight) >+void InlineFormattingContext::LineLayout::Line::init(const LayoutPoint& topLeft, LayoutUnit availableWidth, LayoutUnit minimalHeight) > { > m_logicalRect.setTopLeft(topLeft); > m_logicalRect.setWidth(availableWidth); >@@ -46,7 +46,7 @@ void InlineFormattingContext::Line::init(const LayoutPoint& topLeft, LayoutUnit > m_closed = false; > } > >-void InlineFormattingContext::Line::adjustLogicalLeft(LayoutUnit delta) >+void InlineFormattingContext::LineLayout::Line::adjustLogicalLeft(LayoutUnit delta) > { > ASSERT(delta > 0); > >@@ -57,7 +57,7 @@ void InlineFormattingContext::Line::adjustLogicalLeft(LayoutUnit delta) > inlineRun.moveHorizontally(delta); > } > >-void InlineFormattingContext::Line::adjustLogicalRight(LayoutUnit delta) >+void InlineFormattingContext::LineLayout::Line::adjustLogicalRight(LayoutUnit delta) > { > ASSERT(delta > 0); > >@@ -70,7 +70,7 @@ static bool isTrimmableContent(const InlineRunProvider::Run& inlineRun) > return inlineRun.isWhitespace() && inlineRun.style().collapseWhiteSpace(); > } > >-LayoutUnit InlineFormattingContext::Line::contentLogicalRight() const >+LayoutUnit InlineFormattingContext::LineLayout::Line::contentLogicalRight() const > { > if (m_inlineRuns.isEmpty()) > return m_logicalRect.left(); >@@ -78,7 +78,7 @@ LayoutUnit InlineFormattingContext::Line::contentLogicalRight() const > return m_inlineRuns.last().logicalRight(); > } > >-void InlineFormattingContext::Line::appendContent(const InlineRunProvider::Run& run, const LayoutSize& runSize) >+void InlineFormattingContext::LineLayout::Line::appendContent(const InlineRunProvider::Run& run, const LayoutSize& runSize) > { > ASSERT(!isClosed()); > >@@ -115,7 +115,7 @@ void InlineFormattingContext::Line::appendContent(const InlineRunProvider::Run& > m_trailingTrimmableContent = TrailingTrimmableContent { runSize.width(), textRun->length() }; > } > >-void InlineFormattingContext::Line::close() >+void InlineFormattingContext::LineLayout::Line::close() > { > auto trimTrailingContent = [&]{ >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 194328
:
361271
|
361274
|
361295