Changeset 283047 in webkit
- Timestamp:
- Sep 24, 2021, 10:44:14 AM (4 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r283046 r283047 1 2021-09-24 Alan Bujtas <zalan@apple.com> 2 3 [LFC][IFC] Line breaking only uses a few style properties 4 https://bugs.webkit.org/show_bug.cgi?id=230757 5 6 Reviewed by Antti Koivisto. 7 8 The continuous runs, input to the line breaking, should only contain style properties that line breaking actually uses. 9 This is in preparation for supporting first-line. 10 11 * layout/formattingContexts/inline/InlineContentBreaker.cpp: 12 (WebCore::Layout::InlineContentBreaker::isWrappingAllowed): 13 (WebCore::Layout::InlineContentBreaker::shouldKeepEndOfLineWhitespace const): 14 (WebCore::Layout::InlineContentBreaker::processOverflowingContent const): 15 (WebCore::Layout::InlineContentBreaker::processOverflowingContentWithText const): 16 (WebCore::Layout::InlineContentBreaker::wordBreakBehavior const): 17 (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const): 18 (WebCore::Layout::InlineContentBreaker::ContinuousContent::append): 19 (WebCore::Layout::InlineContentBreaker::ContinuousContent::reset): 20 * layout/formattingContexts/inline/InlineContentBreaker.h: 21 (WebCore::Layout::InlineContentBreaker::ContinuousContent::Run::Run): 22 * layout/formattingContexts/inline/InlineLineBuilder.cpp: 23 (WebCore::Layout::LineCandidate::InlineContent::appendInlineItem): 24 (WebCore::Layout::LineBuilder::handleInlineContent): 25 1 26 2021-09-24 Youenn Fablet <youenn@apple.com> 2 27 -
trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp
r282779 r283047 113 113 } 114 114 115 bool InlineContentBreaker::isWrappingAllowed(const InlineItem& inlineItem) 116 { 117 auto& styleToUse = inlineItem.isBox() ? inlineItem.layoutBox().parent().style() : inlineItem.layoutBox().style(); 115 bool InlineContentBreaker::isWrappingAllowed(const ContinuousContent::Run& run) 116 { 118 117 // Do not try to wrap overflown 'pre' and 'no-wrap' content to next line. 119 return styleToUse.whiteSpace() != WhiteSpace::Pre && styleToUse.whiteSpace()!= WhiteSpace::NoWrap;118 return run.style.whiteSpace != WhiteSpace::Pre && run.style.whiteSpace != WhiteSpace::NoWrap; 120 119 } 121 120 … … 126 125 // It might very well get collapsed when we close the line (normal/nowrap/pre-line). 127 126 // See https://www.w3.org/TR/css-text-3/#white-space-property 128 auto whitespace = continuousContent.runs()[*firstTextRunIndex(continuousContent)]. inlineItem.style().whiteSpace();127 auto whitespace = continuousContent.runs()[*firstTextRunIndex(continuousContent)].style.whiteSpace; 129 128 return whitespace == WhiteSpace::Normal || whitespace == WhiteSpace::NoWrap || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::PreLine; 130 129 } … … 267 266 // e.g. <div style="white-space: nowrap">some text<div style="display: inline-block; white-space: pre-wrap"></div></div>. 268 267 // While the inline-block has pre-wrap which allows wrapping, the content lives in a nowrap context. 269 return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex] .inlineItem);268 return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex]); 270 269 }; 271 270 if (shouldWrapUnbreakableContentToNextLine()) … … 289 288 } 290 289 // Check if this text run needs to stay on the current line. 291 return isWrappingAllowed(run .inlineItem);290 return isWrappingAllowed(run); 292 291 }; 293 292 … … 372 371 ASSERT(run.inlineItem.isText()); 373 372 // We know that this run does not fit the available space. If we can break it at any position, let's just use the start of the run. 374 if (wordBreakBehavior(run. inlineItem.style(), lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) {373 if (wordBreakBehavior(run.style, lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) { 375 374 // We must be on an inline box boundary. Let's go back to the run in front of the inline box start run. 376 375 // e.g. <span>unbreakable_and_overflow<span style="word-break: break-all">breakable</span> … … 406 405 } 407 406 408 OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const407 OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const ContinuousContent::Run::Style& style, bool hasWrapOpportunityAtPreviousPosition) const 409 408 { 410 409 // Disregard any prohibition against line breaks mandated by the word-break property. 411 410 // The different wrapping opportunities must not be prioritized. 412 411 // Note hyphenation is not applied. 413 if (style.lineBreak ()== LineBreak::Anywhere)412 if (style.lineBreak == LineBreak::Anywhere) 414 413 return { WordBreakRule::AtArbitraryPosition }; 415 414 416 415 auto includeHyphenationIfAllowed = [&](std::optional<InlineContentBreaker::WordBreakRule> wordBreakRule) -> OptionSet<InlineContentBreaker::WordBreakRule> { 417 auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens () == Hyphens::Auto && canHyphenate(style.computedLocale());416 auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens == Hyphens::Auto && canHyphenate(style.locale); 418 417 if (hyphenationIsAllowed) { 419 418 if (wordBreakRule) … … 426 425 }; 427 426 // Breaking is allowed within “words”. 428 if (style.wordBreak ()== WordBreak::BreakAll)427 if (style.wordBreak == WordBreak::BreakAll) 429 428 return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition); 430 429 // For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword. 431 430 // When specified, this has the same effect as word-break: normal and overflow-wrap: anywhere, regardless of the actual value of the overflow-wrap property. 432 if (style.wordBreak ()== WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition)431 if (style.wordBreak == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition) 433 432 return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition); 434 433 // OverflowWrap::BreakWord/Anywhere An otherwise unbreakable sequence of characters may be broken at an arbitrary point if there are no otherwise-acceptable break points in the line. 435 434 // Note that this applies to content where CSS properties (e.g. WordBreak::KeepAll) make it unbreakable. 436 if ((style.overflowWrap () == OverflowWrap::BreakWord || style.overflowWrap()== OverflowWrap::Anywhere) && !hasWrapOpportunityAtPreviousPosition)435 if ((style.overflowWrap == OverflowWrap::BreakWord || style.overflowWrap == OverflowWrap::Anywhere) && !hasWrapOpportunityAtPreviousPosition) 437 436 return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition); 438 437 // Breaking is forbidden within “words”. 439 if (style.wordBreak ()== WordBreak::KeepAll)438 if (style.wordBreak == WordBreak::KeepAll) 440 439 return { }; 441 440 return includeHyphenationIfAllowed({ }); … … 446 445 ASSERT(overflowingRun.inlineItem.isText()); 447 446 auto& inlineTextItem = downcast<InlineTextItem>(overflowingRun.inlineItem); 448 auto& style = inlineTextItem.style();447 auto& style = overflowingRun.style; 449 448 auto availableSpaceIsInfinite = !availableWidth.has_value(); 450 449 … … 463 462 } 464 463 auto runLength = inlineTextItem.length(); 465 unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore();466 unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter();464 auto limitBefore = style.hyphenationLimitBefore.value_or(0); 465 auto limitAfter = style.hyphenationLimitAfter.value_or(0); 467 466 // Check if this run can accommodate the before/after limits at all before start measuring text. 468 467 if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength) … … 470 469 471 470 unsigned leftSideLength = runLength; 472 auto& fontCascade = style.fontCascade(); 473 auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) }; 471 auto hyphenWidth = InlineLayoutUnit { style.fontCascade.width(TextRun { StringView { style.hyphenString } }) }; 474 472 if (!availableSpaceIsInfinite) { 475 473 auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth; 476 if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize()))474 if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, style.fontCascade.pixelSize())) 477 475 return { }; 478 476 leftSideLength = TextUtil::midWordBreak(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length; … … 482 480 // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run). 483 481 auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1; 484 unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style. computedLocale());482 unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.locale); 485 483 if (!hyphenLocation || hyphenLocation < limitBefore) 486 484 return { }; … … 519 517 } 520 518 521 void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth)522 { 523 m_runs.append({ inlineItem, logicalWidth });519 void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth) 520 { 521 m_runs.append({ inlineItem, style, logicalWidth }); 524 522 m_logicalWidth = clampTo<InlineLayoutUnit>(m_logicalWidth + logicalWidth); 525 523 if (!collapsibleWidth) { … … 544 542 m_runs.clear(); 545 543 } 546 547 544 } 548 545 } -
trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h
r278253 r283047 29 29 30 30 #include "LayoutUnits.h" 31 #include "RenderStyle.h" 31 32 32 33 namespace WebCore { 33 34 class RenderStyle;35 36 34 namespace Layout { 37 35 … … 85 83 bool isFullyCollapsible() const { return logicalWidth() == collapsibleLogicalWidth(); } 86 84 87 void append(const InlineItem&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth);85 void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth); 88 86 void reset(); 89 87 90 88 struct Run { 91 Run(const InlineItem&, InlineLayoutUnit logicalWidth);89 Run(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth); 92 90 Run(const Run&); 93 91 Run& operator=(const Run&); 94 92 95 93 const InlineItem& inlineItem; 94 struct Style { 95 WhiteSpace whiteSpace { WhiteSpace::Normal }; 96 LineBreak lineBreak { LineBreak::Auto }; 97 WordBreak wordBreak { WordBreak::Normal }; 98 OverflowWrap overflowWrap { OverflowWrap::Normal }; 99 Hyphens hyphens { Hyphens::None }; 100 std::optional<unsigned> hyphenationLimitBefore; 101 std::optional<unsigned> hyphenationLimitAfter; 102 const FontCascade& fontCascade; 103 const AtomString& hyphenString; 104 const AtomString& locale; 105 }; 106 Style style; 96 107 InlineLayoutUnit logicalWidth { 0 }; 97 108 }; … … 117 128 void setHyphenationDisabled() { n_hyphenationIsDisabled = true; } 118 129 119 static bool isWrappingAllowed(const InlineItem&);130 static bool isWrappingAllowed(const ContinuousContent::Run&); 120 131 121 132 private: … … 128 139 AtHyphenationOpportunities = 1 << 1 129 140 }; 130 OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;141 OptionSet<WordBreakRule> wordBreakBehavior(const ContinuousContent::Run::Style&, bool hasWrapOpportunityAtPreviousPosition) const; 131 142 bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const; 132 143 … … 134 145 }; 135 146 136 inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)147 inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth) 137 148 : inlineItem(inlineItem) 149 , style({ style.whiteSpace() 150 , style.lineBreak() 151 , style.wordBreak() 152 , style.overflowWrap() 153 , style.hyphens() 154 , style.hyphenationLimitBefore() != style.initialHyphenationLimitBefore() ? std::make_optional(style.hyphenationLimitBefore()) : std::nullopt 155 , style.hyphenationLimitAfter() != style.initialHyphenationLimitAfter() ? std::make_optional(style.hyphenationLimitAfter()) : std::nullopt 156 , style.fontCascade() 157 , style.hyphenString() 158 , style.fontDescription().computedLocale() }) 138 159 , logicalWidth(logicalWidth) 139 160 { … … 142 163 inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other) 143 164 : inlineItem(other.inlineItem) 165 , style(other.style) 144 166 , logicalWidth(other.logicalWidth) 145 167 { -
trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp
r281754 r283047 196 196 return letterSpacing; 197 197 }; 198 m_continuousContent.append(inlineItem, logicalWidth, collapsibleWidth()); 198 // FIXME: While the line breaking related properties for atomic level boxes do not depend on the line index (first line style) it'd be great to figure out the correct style to pass in. 199 m_continuousContent.append(inlineItem, !inlineItem.isBox() ? inlineItem.style() : inlineItem.layoutBox().parent().style(), logicalWidth, collapsibleWidth()); 199 200 m_hasInlineLevelBox = m_hasInlineLevelBox || inlineItem.isBox() || inlineItem.isInlineBoxStart(); 200 201 } … … 693 694 if (lineCandidate.inlineContent.hasTrailingSoftWrapOpportunity()) { 694 695 // Check if we are allowed to wrap at this position. 695 auto& trailing Item = candidateRuns.last().inlineItem;696 auto& trailingRun = candidateRuns.last(); 696 697 // FIXME: There must be a way to decide if the trailing run actually ended up on the line. 697 698 // Let's just deal with collapsed leading whitespace for now. 698 if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailing Item))699 m_wrapOpportunityList.append(&trailing Item);699 if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailingRun)) 700 m_wrapOpportunityList.append(&trailingRun.inlineItem); 700 701 } 701 702 return { result.isEndOfLine, { candidateRuns.size(), false } };
Note:
See TracChangeset
for help on using the changeset viewer.