Diff
Modified: trunk/Source/WebCore/ChangeLog (238086 => 238087)
--- trunk/Source/WebCore/ChangeLog 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/ChangeLog 2018-11-12 16:18:08 UTC (rev 238087)
@@ -1,3 +1,40 @@
+2018-11-12 Zalan Bujtas <za...@apple.com>
+
+ [LFC][IFC] Construct dedicated runs when the inline element requires it.
+ https://bugs.webkit.org/show_bug.cgi?id=191509
+
+ Reviewed by Antti Koivisto.
+
+ 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> <- input to line breaking.
+ 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.
+
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::layout const):
+ (WebCore::Layout::contentRequiresSeparateRun):
+ (WebCore::Layout::InlineFormattingContext::splitInlineRunIfNeeded const):
+ (WebCore::Layout::InlineFormattingContext::postProcessInlineRuns const):
+ (WebCore::Layout::InlineFormattingContext::closeLine const):
+ (WebCore::Layout::InlineFormattingContext::appendContentToLine const):
+ (WebCore::Layout::InlineFormattingContext::layoutInlineContent const):
+ (WebCore::Layout::InlineFormattingContext::instrinsicWidthConstraints const):
+ * layout/inlineformatting/InlineFormattingContext.h:
+ (WebCore::Layout::InlineFormattingContext::inlineFormattingState const):
+ * layout/inlineformatting/InlineLineBreaker.cpp:
+ (WebCore::Layout::InlineLineBreaker::nextRun): mid-word breaking is not implemented yet.
+ * layout/inlineformatting/InlineRun.h:
+ (WebCore::Layout::InlineRun::overlapsMultipleInlineItems const):
+ * layout/inlineformatting/InlineRunProvider.cpp:
+ (WebCore::Layout::InlineRunProvider::processInlineTextItem):
+ * layout/inlineformatting/InlineRunProvider.h:
+ (WebCore::Layout::InlineRunProvider::Run::TextContext::expand):
+ (WebCore::Layout::InlineRunProvider::Run::textContext):
+ (WebCore::Layout::InlineRunProvider::Run::TextContext::setStart): Deleted.
+ (WebCore::Layout::InlineRunProvider::Run::TextContext::setLength): Deleted.
+
2018-11-12 Jer Noble <jer.no...@apple.com>
[MSE] Frame re-ordering can cause iframes to never be enqueued
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2018-11-12 16:18:08 UTC (rev 238087)
@@ -58,9 +58,7 @@
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")");
- auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
- InlineRunProvider inlineRunProvider(inlineFormattingState);
-
+ InlineRunProvider inlineRunProvider(inlineFormattingState());
collectInlineContent(inlineRunProvider);
// Compute width/height for non-text content.
for (auto& inlineRun : inlineRunProvider.runs()) {
@@ -127,6 +125,119 @@
line.init(logicalRect);
}
+bool InlineFormattingContext::contentRequiresSeparateRun(const InlineItem& inlineItem) const
+{
+ // FIXME: This is way too inefficient. We should pre-mark the runs instead while flattening the inline formatting context.
+ for (auto* inlineContainer = inlineItem.layoutBox().parent(); inlineContainer != &root(); inlineContainer = inlineContainer->parent()) {
+ if (inlineContainer->isPositioned())
+ return true;
+ }
+ return false;
+}
+
+void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun, InlineRuns& splitRuns) const
+{
+ if (!inlineRun.overlapsMultipleInlineItems())
+ return;
+
+ ASSERT(inlineRun.textContext());
+ // 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. Shrink the original inline run and create a new one.
+ auto& inlineContent = inlineFormattingState().inlineContent();
+ auto textUtil = TextUtil { inlineContent };
+
+ auto split=[&](const auto& inlineItem, auto startPosition, auto length, auto contentStart) {
+ auto width = textUtil.width(inlineItem, startPosition, length, contentStart);
+
+ auto run = InlineRun { { inlineRun.logicalTop(), contentStart, width, inlineRun.height() }, inlineItem };
+ run.setTextContext({ startPosition, length });
+ splitRuns.append(run);
+ return contentStart + width;
+ };
+
+ auto contentStart = inlineRun.logicalLeft();
+ auto startPosition = inlineRun.textContext()->start();
+ auto remaningLength = inlineRun.textContext()->length();
+
+ unsigned uncommittedLength = 0;
+ InlineItem* firstUncommittedInlineItem = nullptr;
+ for (auto iterator = inlineContent.find<const InlineItem&, InlineItemHashTranslator>(inlineRun.inlineItem()); iterator != inlineContent.end() && remaningLength > 0; ++iterator) {
+ auto& inlineItem = **iterator;
+
+ if (!contentRequiresSeparateRun(inlineItem)) {
+ uncommittedLength += std::min(remaningLength, inlineItem.textContent().length() - startPosition);
+ firstUncommittedInlineItem = !firstUncommittedInlineItem ? &inlineItem : firstUncommittedInlineItem;
+ continue;
+ }
+
+ // Commit the items that don't need dedicated run.
+ if (firstUncommittedInlineItem) {
+ contentStart = split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
+
+ remaningLength -= uncommittedLength;
+ startPosition = 0;
+ uncommittedLength = 0;
+ }
+
+ // Create a dedicated run for this inline item.
+ auto length = std::min(remaningLength, inlineItem.textContent().length() - startPosition);
+ contentStart = split(inlineItem, startPosition, length, contentStart);
+
+ startPosition = 0;
+ remaningLength -= length;
+ firstUncommittedInlineItem = nullptr;
+ }
+
+ // Either all inline elements needed dedicated runs or neither of them.
+ if (!remaningLength || remaningLength == inlineRun.textContext()->length())
+ return;
+
+ ASSERT(remaningLength == uncommittedLength);
+ split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
+}
+
+void InlineFormattingContext::postProcessInlineRuns(Line& line, IsLastLine isLastLine, Line::RunRange runRange) const
+{
+ auto& inlineFormattingState = this->inlineFormattingState();
+ Geometry::alignRuns(inlineFormattingState, root().style().textAlign(), line, runRange, isLastLine);
+
+ auto& inlineRuns = inlineFormattingState.inlineRuns();
+ ASSERT(*runRange.lastRunIndex < inlineRuns.size());
+
+ auto runIndex = *runRange.firstRunIndex;
+ auto& lastInlineRun = inlineRuns[*runRange.lastRunIndex];
+ while (runIndex < inlineRuns.size()) {
+ auto& inlineRun = inlineRuns[runIndex];
+ auto isLastRunInRange = &inlineRun == &lastInlineRun;
+
+ InlineRuns splitRuns;
+ splitInlineRunIfNeeded(inlineRun, splitRuns);
+ if (!splitRuns.isEmpty()) {
+ ASSERT(splitRuns.size() > 1);
+ // Replace the continous run with new ones.
+ // Reuse the original one.
+ auto& firstRun = splitRuns.first();
+ inlineRun.setWidth(firstRun.width());
+ inlineRun.textContext()->setLength(firstRun.textContext()->length());
+ splitRuns.remove(0);
+ // Insert the rest.
+ for (auto& splitRun : splitRuns)
+ inlineRuns.insert(++runIndex, splitRun);
+ }
+
+ if (isLastRunInRange)
+ break;
+
+ ++runIndex;
+ }
+}
+
void InlineFormattingContext::closeLine(Line& line, IsLastLine isLastLine) const
{
auto runRange = line.close();
@@ -135,7 +246,7 @@
if (!runRange.firstRunIndex)
return;
- Geometry::alignRuns(downcast<InlineFormattingState>(formattingState()), root().style().textAlign(), line, runRange, isLastLine);
+ postProcessInlineRuns(line, isLastLine, runRange);
}
void InlineFormattingContext::appendContentToLine(Line& line, const InlineLineBreaker::Run& run) const
@@ -144,13 +255,13 @@
line.appendContent(run);
if (root().style().textAlign() == TextAlignMode::Justify)
- Geometry::computeExpansionOpportunities(downcast<InlineFormattingState>(formattingState()), run.content, lastRunType.value_or(InlineRunProvider::Run::Type::NonWhitespace));
+ Geometry::computeExpansionOpportunities(inlineFormattingState(), run.content, lastRunType.value_or(InlineRunProvider::Run::Type::NonWhitespace));
}
void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
{
auto& layoutState = this->layoutState();
- auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
+ auto& inlineFormattingState = this->inlineFormattingState();
auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
Line line(inlineFormattingState);
@@ -328,7 +439,7 @@
if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
return *instrinsicWidthConstraints;
- auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
+ auto& inlineFormattingState = this->inlineFormattingState();
InlineRunProvider inlineRunProvider(inlineFormattingState);
collectInlineContent(inlineRunProvider);
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2018-11-12 16:18:08 UTC (rev 238087)
@@ -30,6 +30,7 @@
#include "DisplayBox.h"
#include "FormattingContext.h"
#include "InlineLineBreaker.h"
+#include "InlineRun.h"
#include <wtf/IsoMalloc.h>
namespace WebCore {
@@ -111,6 +112,9 @@
void initializeNewLine(Line&) const;
void closeLine(Line&, IsLastLine) const;
void appendContentToLine(Line&, const InlineLineBreaker::Run&) const;
+ void postProcessInlineRuns(Line&, IsLastLine, Line::RunRange) const;
+ void splitInlineRunIfNeeded(const InlineRun&, InlineRuns& splitRuns) const;
+ bool contentRequiresSeparateRun(const InlineItem&) const;
void layoutFormattingContextRoot(const Box&) const;
void computeWidthAndHeightForReplacedInlineBox(const Box&) const;
@@ -121,6 +125,8 @@
void collectInlineContent(InlineRunProvider&) const;
InstrinsicWidthConstraints instrinsicWidthConstraints() const override;
+
+ InlineFormattingState& inlineFormattingState() const { return downcast<InlineFormattingState>(formattingState()); }
};
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp 2018-11-12 16:18:08 UTC (rev 238087)
@@ -54,7 +54,6 @@
// Adjust the current run if it is split midword.
if (m_splitPosition) {
ASSERT(currentInlineRun.isText());
- currentInlineRun.textContext()->setStart(*m_splitPosition);
m_splitPosition = std::nullopt;
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineRun.h (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineRun.h 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineRun.h 2018-11-12 16:18:08 UTC (rev 238087)
@@ -43,6 +43,7 @@
LayoutUnit width() const { return m_logicalRect.width(); }
LayoutUnit height() const { return m_logicalRect.height(); }
+ bool overlapsMultipleInlineItems() const;
void setWidth(LayoutUnit width) { m_logicalRect.setWidth(width); }
void setLogicalLeft(LayoutUnit logicalLeft) { m_logicalRect.setLeft(logicalLeft); }
@@ -98,6 +99,16 @@
{
}
+inline bool InlineRun::overlapsMultipleInlineItems() const
+{
+ // Only text content can overlap multiple inline elements.
+ if (!m_textContext)
+ return false;
+
+ auto endPosition = m_textContext->start() + m_textContext->length();
+ return endPosition > m_inlineItem.textContent().length();
}
+
}
+}
#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.cpp (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.cpp 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.cpp 2018-11-12 16:18:08 UTC (rev 238087)
@@ -130,10 +130,9 @@
auto isWhitespaceRun = isWhitespace(text[currentItemPosition], style.preserveNewline());
auto length = isWhitespaceRun ? moveToNextNonWhitespacePosition(inlineItem, currentItemPosition) : moveToNextBreakablePosition(inlineItem, currentItemPosition);
- if (isContinousContent(isWhitespaceRun ? InlineRunProvider::Run::Type::Whitespace : InlineRunProvider::Run::Type::NonWhitespace, inlineItem)) {
- auto textContext = m_inlineRuns.last().textContext();
- textContext->setLength(textContext->length() + length);
- } else {
+ if (isContinousContent(isWhitespaceRun ? InlineRunProvider::Run::Type::Whitespace : InlineRunProvider::Run::Type::NonWhitespace, inlineItem))
+ m_inlineRuns.last().textContext()->expand(length);
+ else {
m_inlineRuns.append(isWhitespaceRun ? InlineRunProvider::Run::createWhitespaceRun(inlineItem, currentItemPosition, length, style.collapseWhiteSpace())
: InlineRunProvider::Run::createNonWhitespaceRun(inlineItem, currentItemPosition, length));
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.h (238086 => 238087)
--- trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.h 2018-11-12 15:12:35 UTC (rev 238086)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineRunProvider.h 2018-11-12 16:18:08 UTC (rev 238087)
@@ -82,10 +82,11 @@
unsigned length() const { return m_length; }
bool isCollapsed() const { return m_isCollapsed == IsCollapsed::Yes; }
- void setStart(ItemPosition start) { m_start = start; }
- void setLength(unsigned length) { m_length = length; }
+ private:
+ friend class InlineRunProvider;
- private:
+ void expand(unsigned length) { m_length += length; }
+
ItemPosition m_start { 0 };
unsigned m_length { 0 };
IsCollapsed m_isCollapsed { IsCollapsed::No };
@@ -96,7 +97,10 @@
const InlineItem& inlineItem() const { return m_inlineItem; }
private:
+ friend class InlineRunProvider;
+
Run(const InlineItem&, Type, std::optional<TextContext>);
+ std::optional<TextContext>& textContext() { return m_textContext; }
const Type m_type;
const InlineItem& m_inlineItem;