Changeset 274008 in webkit
- Timestamp:
- Mar 5, 2021 2:31:07 PM (17 months ago)
- Location:
- trunk
- Files:
-
- 2 added
- 4 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html (added)
-
LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp (modified) (2 diffs)
-
Source/WebCore/layout/inlineformatting/InlineContentBreaker.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r274007 r274008 1 2021-03-05 Zalan Bujtas <zalan@apple.com> 2 3 word-wrap/overflow-wrap "overwrite" hyphens 4 https://bugs.webkit.org/show_bug.cgi?id=222548 5 <rdar://problem/75061741> 6 7 Reviewed by Antti Koivisto. 8 9 * fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html: Added. 10 * fast/inline/hyphenation-when-overflow-wrap-is-break-word.html: Added. 11 1 12 2021-03-05 Ryan Haddad <ryanhaddad@apple.com> 2 13 -
trunk/Source/WebCore/ChangeLog
r273986 r274008 1 2021-03-05 Zalan Bujtas <zalan@apple.com> 2 3 word-wrap/overflow-wrap "overwrite" hyphens 4 https://bugs.webkit.org/show_bug.cgi?id=222548 5 <rdar://problem/75061741> 6 7 Reviewed by Antti Koivisto. 8 9 According to https://drafts.csswg.org/css-text-3/#overflow-wrap-property 10 11 "overflow-wrap/word-wrap property specifies whether the UA may break at otherwise disallowed points within a line to prevent overflow, 12 when an otherwise-unbreakable string is too long to fit within the line box." 13 14 which means that in case of "hyphen: auto", we should only try to break the content at arbitrary position when there's no prior hyphenation opportunity. 15 This patch turns WordBreakRule into and OptionSet so that we can put both the hyphenation and arbitrary position break runles in there and prioritize them 16 in tryBreakingTextRun (check for the hyphenation value first/break the content at hyphenation position and do arbitrary position only 17 if no hyphenation opportunities are found). 18 19 Test: fast/inline/hyphenation-when-overflow-wrap-is-break-word.html 20 21 * layout/inlineformatting/InlineContentBreaker.cpp: 22 (WebCore::Layout::InlineContentBreaker::wordBreakBehavior const): 23 (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const): 24 * layout/inlineformatting/InlineContentBreaker.h: 25 1 26 2021-03-05 Lauro Moura <lmoura@igalia.com> 2 27 -
trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp
r273918 r274008 393 393 } 394 394 395 InlineContentBreaker::WordBreakRuleInlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const395 OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const 396 396 { 397 397 // Disregard any prohibition against line breaks mandated by the word-break property. 398 398 // The different wrapping opportunities must not be prioritized. Hyphenation is not applied. 399 399 if (style.lineBreak() == LineBreak::Anywhere) 400 return WordBreakRule::AtArbitraryPosition;400 return { WordBreakRule::AtArbitraryPosition }; 401 401 // Breaking is allowed within “words”. 402 402 if (style.wordBreak() == WordBreak::BreakAll) 403 return WordBreakRule::AtArbitraryPosition;403 return { WordBreakRule::AtArbitraryPosition }; 404 404 // Breaking is forbidden within “words”. 405 405 if (style.wordBreak() == WordBreak::KeepAll) 406 return WordBreakRule::NoBreak; 406 return { }; 407 408 auto breakRules = OptionSet<WordBreakRule> { }; 409 auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale()); 410 if (hyphenationIsAllowed) 411 breakRules.add({ WordBreakRule::AtHyphenationOpportunities }); 407 412 // For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword. 408 413 // 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. 409 if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition) 410 return WordBreakRule::AtArbitraryPosition; 414 if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition) { 415 breakRules.add({ WordBreakRule::AtArbitraryPosition }); 416 return breakRules; 417 } 411 418 // OverflowWrap::Break: An otherwise unbreakable sequence of characters may be broken at an arbitrary point if there are no otherwise-acceptable break points in the line. 412 if (style.overflowWrap() == OverflowWrap::Break && !hasWrapOpportunityAtPreviousPosition) 413 return WordBreakRule::AtArbitraryPosition; 414 415 if (!n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale())) 416 return WordBreakRule::OnlyHyphenationAllowed; 417 418 return WordBreakRule::NoBreak; 419 if (style.overflowWrap() == OverflowWrap::Break && !hasWrapOpportunityAtPreviousPosition) { 420 breakRules.add({ WordBreakRule::AtArbitraryPosition }); 421 return breakRules; 422 } 423 return breakRules; 419 424 } 420 425 … … 426 431 auto availableSpaceIsInfinite = !availableWidth.hasValue(); 427 432 428 auto breakRule = wordBreakBehavior(style, hasWrapOpportunityAtPreviousPosition); 429 if (breakRule == WordBreakRule::AtArbitraryPosition) { 430 if (!inlineTextItem.length()) { 431 // Empty text runs may be breakable based on style, but in practice we can't really split them any further. 432 return PartialRun { }; 433 } 434 if (availableSpaceIsInfinite) { 435 // When the run can be split at arbitrary position let's just return the entire run when it is intended to fit on the line. 436 ASSERT(inlineTextItem.length()); 437 auto trailingPartialRunWidth = TextUtil::width(inlineTextItem, logicalLeft); 438 return PartialRun { inlineTextItem.length(), trailingPartialRunWidth }; 439 } 440 if (!*availableWidth) { 441 // Fast path for cases when there's no room at all. The content is breakable but we don't have space for it. 442 return PartialRun { }; 443 } 444 auto splitData = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, *availableWidth, logicalLeft); 445 return PartialRun { splitData.length, splitData.logicalWidth }; 446 } 447 448 if (breakRule == WordBreakRule::OnlyHyphenationAllowed) { 449 // Find the hyphen position as follows: 450 // 1. Split the text by taking the hyphen width into account 451 // 2. Find the last hyphen position before the split position 452 if (!availableSpaceIsInfinite && !*availableWidth) { 453 // We won't be able to find hyphen location when there's no available space. 454 return { }; 455 } 456 auto runLength = inlineTextItem.length(); 457 unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore(); 458 unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter(); 459 // Check if this run can accommodate the before/after limits at all before start measuring text. 460 if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength) 461 return { }; 462 463 unsigned leftSideLength = runLength; 464 auto& fontCascade = style.fontCascade(); 465 auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) }; 466 if (!availableSpaceIsInfinite) { 467 auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth; 468 if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize())) 469 return { }; 470 leftSideLength = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length; 471 } 472 if (leftSideLength < limitBefore) 473 return { }; 474 // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run). 475 auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1; 476 unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale()); 477 if (!hyphenLocation || hyphenLocation < limitBefore) 478 return { }; 479 // hyphenLocation is relative to the start of this InlineItemText. 480 ASSERT(inlineTextItem.start() + hyphenLocation < inlineTextItem.end()); 481 auto trailingPartialRunWidthWithHyphen = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + hyphenLocation, logicalLeft); 482 return PartialRun { hyphenLocation, trailingPartialRunWidthWithHyphen, hyphenWidth }; 483 } 484 485 ASSERT(breakRule == WordBreakRule::NoBreak); 433 auto breakRules = wordBreakBehavior(style, hasWrapOpportunityAtPreviousPosition); 434 if (breakRules.isEmpty()) 435 return { }; 436 437 if (breakRules.contains(WordBreakRule::AtHyphenationOpportunities)) { 438 auto tryBreakingAtHyphenationOpportunity = [&]() -> Optional<PartialRun> { 439 // Find the hyphen position as follows: 440 // 1. Split the text by taking the hyphen width into account 441 // 2. Find the last hyphen position before the split position 442 if (!availableSpaceIsInfinite && !*availableWidth) { 443 // We won't be able to find hyphen location when there's no available space. 444 return { }; 445 } 446 auto runLength = inlineTextItem.length(); 447 unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore(); 448 unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter(); 449 // Check if this run can accommodate the before/after limits at all before start measuring text. 450 if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength) 451 return { }; 452 453 unsigned leftSideLength = runLength; 454 auto& fontCascade = style.fontCascade(); 455 auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) }; 456 if (!availableSpaceIsInfinite) { 457 auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth; 458 if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize())) 459 return { }; 460 leftSideLength = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length; 461 } 462 if (leftSideLength < limitBefore) 463 return { }; 464 // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run). 465 auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1; 466 unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale()); 467 if (!hyphenLocation || hyphenLocation < limitBefore) 468 return { }; 469 // hyphenLocation is relative to the start of this InlineItemText. 470 ASSERT(inlineTextItem.start() + hyphenLocation < inlineTextItem.end()); 471 auto trailingPartialRunWidthWithHyphen = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + hyphenLocation, logicalLeft); 472 return PartialRun { hyphenLocation, trailingPartialRunWidthWithHyphen, hyphenWidth }; 473 }; 474 if (auto partialRun = tryBreakingAtHyphenationOpportunity()) 475 return partialRun; 476 } 477 478 if (breakRules.contains(WordBreakRule::AtArbitraryPosition)) { 479 auto tryBreakingAtArbitraryPosition = [&]() -> PartialRun { 480 if (!inlineTextItem.length()) { 481 // Empty text runs may be breakable based on style, but in practice we can't really split them any further. 482 return { }; 483 } 484 if (availableSpaceIsInfinite) { 485 // When the run can be split at arbitrary position let's just return the entire run when it is intended to fit on the line. 486 ASSERT(inlineTextItem.length()); 487 auto trailingPartialRunWidth = TextUtil::width(inlineTextItem, logicalLeft); 488 return { inlineTextItem.length(), trailingPartialRunWidth }; 489 } 490 if (!*availableWidth) { 491 // Fast path for cases when there's no room at all. The content is breakable but we don't have space for it. 492 return { }; 493 } 494 auto splitData = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, *availableWidth, logicalLeft); 495 return { splitData.length, splitData.logicalWidth }; 496 }; 497 // With arbitrary breaking there's always a valid breaking position (even if it is before the first position). 498 return tryBreakingAtArbitraryPosition(); 499 } 486 500 return { }; 487 501 } -
trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h
r273556 r274008 125 125 126 126 enum class WordBreakRule { 127 NoBreak, 128 AtArbitraryPosition, 129 OnlyHyphenationAllowed 127 AtArbitraryPosition = 1 << 0, 128 AtHyphenationOpportunities = 1 << 1 130 129 }; 131 WordBreakRulewordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;130 OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const; 132 131 bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const; 133 132
Note: See TracChangeset
for help on using the changeset viewer.