WebKit Bugzilla
Attachment 361295 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-20190206075201.patch (text/plain), 64.75 KB, created by
zalan
on 2019-02-06 07:52:20 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
zalan
Created:
2019-02-06 07:52:20 PST
Size:
64.75 KB
patch
obsolete
>Subversion Revision: 241016 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 76da75cb9ade9294c2662b05d6b433b3ce5307c2..2d52feeaafec42f451422aaf866fff8c6880d1ab 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 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-05 Ryosuke Niwa <rniwa@webkit.org> > > REGRESSION (r240909): Release assert in FrameLoader::loadURL when navigating with a non-existent target name >diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt >index 3f4296d5242f423e590a386d02b2b929a8d8e6a1..543630cb276da7947da9107ddb3340eece4f7d5e 100644 >--- a/Source/WebCore/Sources.txt >+++ b/Source/WebCore/Sources.txt >@@ -1340,11 +1340,11 @@ layout/floats/FloatingContext.cpp > 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 >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index dab111437bd5c3799ebf0bad1a4121017fdef197..efb2308b86f4920feb37c6206fa0ee2fa3a9c068 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -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>"; >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..d4e10c2691f166feff618c933fd2cd325ddf1753 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >@@ -29,15 +29,17 @@ > > #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; >+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 @@ 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; > > private: >- struct TrailingTrimmableContent { >- LayoutUnit width; >- unsigned length; >- }; >- Optional<TrailingTrimmableContent> m_trailingTrimmableContent; >- Optional<InlineRunProvider::Run::Type> m_lastRunType; >- bool m_lastRunCanExpand { false }; >+ 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; > >- Display::Box::Rect m_logicalRect; >- LayoutUnit m_availableWidth; >+ private: >+ static void justifyRuns(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/InlineFormattingContextLineLayout.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContextLineLayout.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..7b13ab0b4ebcbd2571fe6c368a8f77016fcaa0e4 >--- /dev/null >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContextLineLayout.cpp >@@ -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 >diff --git a/Source/WebCore/layout/inlineformatting/Line.cpp b/Source/WebCore/layout/inlineformatting/Line.cpp >deleted file mode 100644 >index b1273d1b1c5fc69ff9abe08059ec465777255622..0000000000000000000000000000000000000000 >--- a/Source/WebCore/layout/inlineformatting/Line.cpp >+++ /dev/null >@@ -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
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