Commit b4b56a2c authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Implement break-after-space style line breaking

This patch implements break-after-space style line breaking. This style
is needed for 'white-space:pre-wrap', and when in editing.

The current layout engine handles this in BreakingContext. This patch
implements the style in LazyTextBreakIterator so that ShapingLineBreaker
can implement the behavior without additional code.

The current layout engine supports 3 modes:
1. break-before-space (most cases)
2. break-after-space (white-space:pre-wrap)
3. break-after-space + hanging space (editing)

This patch does not support 3rd mode yet, and that editing behavior is
slightly different from the current layout engine, closer to Edge. The
editing test failure is caused by this difference. I'll evaluate the
impact and the needs of the 3rd mode later.

There are more tests that actually pass, but they fail for other
reasons, such as the lack of quirks mode support.

BUG=636993

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_layout_ng
Change-Id: Iec1b684da4dcf45f10eaa408889ebf77d387e668
Reviewed-on: https://chromium-review.googlesource.com/574903
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488169}
parent 38b4a11f
...@@ -2961,6 +2961,7 @@ crbug.com/591099 editing/selection/extend-selection-bidi.html [ Failure ] ...@@ -2961,6 +2961,7 @@ crbug.com/591099 editing/selection/extend-selection-bidi.html [ Failure ]
crbug.com/591099 editing/selection/extend-selection-character.html [ Timeout ] crbug.com/591099 editing/selection/extend-selection-character.html [ Timeout ]
crbug.com/591099 editing/selection/extend-selection-home-end.html [ Timeout ] crbug.com/591099 editing/selection/extend-selection-home-end.html [ Timeout ]
crbug.com/591099 editing/selection/extend-selection-word.html [ Timeout ] crbug.com/591099 editing/selection/extend-selection-word.html [ Timeout ]
crbug.com/591099 editing/selection/extend-to-line-boundary.html [ Failure ]
crbug.com/591099 editing/selection/extend-to-trailing-spaces.html [ Failure ] crbug.com/591099 editing/selection/extend-to-trailing-spaces.html [ Failure ]
crbug.com/591099 editing/selection/find-in-text-control.html [ Crash Failure ] crbug.com/591099 editing/selection/find-in-text-control.html [ Crash Failure ]
crbug.com/591099 editing/selection/firstRect-crash.html [ Failure ] crbug.com/591099 editing/selection/firstRect-crash.html [ Failure ]
...@@ -11431,7 +11432,6 @@ crbug.com/591099 fast/text/word-break-run-rounding.html [ Failure ] ...@@ -11431,7 +11432,6 @@ crbug.com/591099 fast/text/word-break-run-rounding.html [ Failure ]
crbug.com/591099 fast/text/word-break-soft-hyphen.html [ Failure ] crbug.com/591099 fast/text/word-break-soft-hyphen.html [ Failure ]
crbug.com/591099 fast/text/word-space-between-inlines.html [ Failure ] crbug.com/591099 fast/text/word-space-between-inlines.html [ Failure ]
crbug.com/591099 fast/text/word-space.html [ Failure ] crbug.com/591099 fast/text/word-space.html [ Failure ]
crbug.com/591099 fast/text/word-wrap-whitespace-pre.html [ Failure Pass ]
crbug.com/591099 fast/text/writing-root-with-overflow-clip-baseline.html [ Crash Failure Pass ] crbug.com/591099 fast/text/writing-root-with-overflow-clip-baseline.html [ Crash Failure Pass ]
crbug.com/591099 fast/text/zero-font-size.html [ Failure ] crbug.com/591099 fast/text/zero-font-size.html [ Failure ]
crbug.com/591099 fast/text/zero-width-characters-complex-script.html [ Failure ] crbug.com/591099 fast/text/zero-width-characters-complex-script.html [ Failure ]
...@@ -640,6 +640,7 @@ void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) { ...@@ -640,6 +640,7 @@ void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) {
break_iterator_.SetBreakType(LineBreakType::kKeepAll); break_iterator_.SetBreakType(LineBreakType::kKeepAll);
break; break;
} }
break_iterator_.SetBreakAfterSpace(style.BreakOnlyAfterWhiteSpace());
} }
spacing_.SetSpacing(style.GetFontDescription()); spacing_.SetSpacing(style.GetFontDescription());
......
...@@ -264,7 +264,9 @@ inline bool NeedsLineBreakIterator(UChar ch) { ...@@ -264,7 +264,9 @@ inline bool NeedsLineBreakIterator(UChar ch) {
return ch > kAsciiLineBreakTableLastChar && ch != kNoBreakSpaceCharacter; return ch > kAsciiLineBreakTableLastChar && ch != kNoBreakSpaceCharacter;
} }
template <typename CharacterType, LineBreakType lineBreakType> template <typename CharacterType,
LineBreakType lineBreakType,
bool break_after_space>
inline int LazyLineBreakIterator::NextBreakablePosition( inline int LazyLineBreakIterator::NextBreakablePosition(
int pos, int pos,
const CharacterType* str) const { const CharacterType* str) const {
...@@ -285,7 +287,17 @@ inline int LazyLineBreakIterator::NextBreakablePosition( ...@@ -285,7 +287,17 @@ inline int LazyLineBreakIterator::NextBreakablePosition(
ch = str[i]; ch = str[i];
is_space = IsBreakableSpace(ch); is_space = IsBreakableSpace(ch);
if (is_space || ShouldBreakAfter(last_last_ch, last_ch, ch)) if (!break_after_space) {
if (is_space)
return i;
} else {
if (is_space)
continue;
if (is_last_space)
return i;
}
if (ShouldBreakAfter(last_last_ch, last_ch, ch))
return i; return i;
if (lineBreakType == LineBreakType::kBreakAll && !U16_IS_LEAD(ch)) { if (lineBreakType == LineBreakType::kBreakAll && !U16_IS_LEAD(ch)) {
...@@ -317,7 +329,7 @@ inline int LazyLineBreakIterator::NextBreakablePosition( ...@@ -317,7 +329,7 @@ inline int LazyLineBreakIterator::NextBreakablePosition(
} }
} }
} }
if (i == next_break && !is_last_space) if (i == next_break && (break_after_space || !is_last_space))
return i; return i;
} }
} }
...@@ -325,6 +337,15 @@ inline int LazyLineBreakIterator::NextBreakablePosition( ...@@ -325,6 +337,15 @@ inline int LazyLineBreakIterator::NextBreakablePosition(
return len; return len;
} }
template <typename CharacterType, LineBreakType lineBreakType>
inline int LazyLineBreakIterator::NextBreakablePosition(
int pos,
const CharacterType* str) const {
if (!break_after_space_)
return NextBreakablePosition<CharacterType, lineBreakType, false>(pos, str);
return NextBreakablePosition<CharacterType, lineBreakType, true>(pos, str);
}
template <LineBreakType lineBreakType> template <LineBreakType lineBreakType>
inline int LazyLineBreakIterator::NextBreakablePosition(int pos) const { inline int LazyLineBreakIterator::NextBreakablePosition(int pos) const {
if (string_.Is8Bit()) { if (string_.Is8Bit()) {
......
...@@ -210,6 +210,17 @@ class PLATFORM_EXPORT LazyLineBreakIterator final { ...@@ -210,6 +210,17 @@ class PLATFORM_EXPORT LazyLineBreakIterator final {
LineBreakType BreakType() const { return break_type_; } LineBreakType BreakType() const { return break_type_; }
void SetBreakType(LineBreakType break_type) { break_type_ = break_type; } void SetBreakType(LineBreakType break_type) { break_type_ = break_type; }
// By default, this class breaks before spaces. This is a specialized
// optimization for CSS, where leading/trailing spaces in each line are
// removed, and thus breaking before spaces can save computing hanging spaces.
//
// When 'white-space:pre-wrap', or when in editing, leaging/trailing spaces
// need to be preserved, and this optimization needs to be disabled. This mode
// is compatible with UAX#14/ICU. http://unicode.org/reports/tr14/
void SetBreakAfterSpace(bool break_after_space) {
break_after_space_ = break_after_space;
}
inline bool IsBreakable(int pos, inline bool IsBreakable(int pos,
int& next_breakable, int& next_breakable,
LineBreakType line_break_type) const { LineBreakType line_break_type) const {
...@@ -243,6 +254,8 @@ class PLATFORM_EXPORT LazyLineBreakIterator final { ...@@ -243,6 +254,8 @@ class PLATFORM_EXPORT LazyLineBreakIterator final {
cached_prior_context_length_ = 0; cached_prior_context_length_ = 0;
} }
template <typename CharacterType, LineBreakType, bool>
int NextBreakablePosition(int pos, const CharacterType* str) const;
template <typename CharacterType, LineBreakType> template <typename CharacterType, LineBreakType>
int NextBreakablePosition(int pos, const CharacterType* str) const; int NextBreakablePosition(int pos, const CharacterType* str) const;
template <LineBreakType> template <LineBreakType>
...@@ -258,6 +271,7 @@ class PLATFORM_EXPORT LazyLineBreakIterator final { ...@@ -258,6 +271,7 @@ class PLATFORM_EXPORT LazyLineBreakIterator final {
mutable const UChar* cached_prior_context_; mutable const UChar* cached_prior_context_;
mutable unsigned cached_prior_context_length_; mutable unsigned cached_prior_context_length_;
LineBreakType break_type_; LineBreakType break_type_;
bool break_after_space_ = false;
}; };
// Iterates over "extended grapheme clusters", as defined in UAX #29. // Iterates over "extended grapheme clusters", as defined in UAX #29.
......
...@@ -18,6 +18,7 @@ class TextBreakIteratorTest : public ::testing::Test { ...@@ -18,6 +18,7 @@ class TextBreakIteratorTest : public ::testing::Test {
// The expected break positions must be specified UTF-16 character boundaries. // The expected break positions must be specified UTF-16 character boundaries.
void MatchLineBreaks(LineBreakType line_break_type, void MatchLineBreaks(LineBreakType line_break_type,
bool break_after_space,
const Vector<int> expected_break_positions) { const Vector<int> expected_break_positions) {
if (test_string_.Is8Bit()) { if (test_string_.Is8Bit()) {
test_string_ = String::Make16BitFrom8BitSource(test_string_.Characters8(), test_string_ = String::Make16BitFrom8BitSource(test_string_.Characters8(),
...@@ -25,6 +26,7 @@ class TextBreakIteratorTest : public ::testing::Test { ...@@ -25,6 +26,7 @@ class TextBreakIteratorTest : public ::testing::Test {
} }
LazyLineBreakIterator lazy_break_iterator(test_string_); LazyLineBreakIterator lazy_break_iterator(test_string_);
lazy_break_iterator.SetBreakType(line_break_type); lazy_break_iterator.SetBreakType(line_break_type);
lazy_break_iterator.SetBreakAfterSpace(break_after_space);
TestIsBreakable(expected_break_positions, lazy_break_iterator); TestIsBreakable(expected_break_positions, lazy_break_iterator);
TestNextBreakOpportunity(expected_break_positions, lazy_break_iterator); TestNextBreakOpportunity(expected_break_positions, lazy_break_iterator);
} }
...@@ -69,15 +71,22 @@ class TextBreakIteratorTest : public ::testing::Test { ...@@ -69,15 +71,22 @@ class TextBreakIteratorTest : public ::testing::Test {
Vector<int> breaks; \ Vector<int> breaks; \
breaks.Append(kBreaksArray, sizeof(kBreaksArray) / sizeof(*kBreaksArray)); breaks.Append(kBreaksArray, sizeof(kBreaksArray) / sizeof(*kBreaksArray));
#define MATCH_LINE_BREAKS(LINEBREAKTYPE, ...) \ #define MATCH_LINE_BREAKS(LINEBREAKTYPE, ...) \
{ \ { \
DECLARE_BREAKSVECTOR(__VA_ARGS__); \ DECLARE_BREAKSVECTOR(__VA_ARGS__); \
MatchLineBreaks(LINEBREAKTYPE, breaks); \ MatchLineBreaks(LINEBREAKTYPE, false, breaks); \
}
#define MATCH_BREAK_AFTER_SPACE(LINEBREAKTYPE, ...) \
{ \
DECLARE_BREAKSVECTOR(__VA_ARGS__); \
MatchLineBreaks(LINEBREAKTYPE, true, breaks); \
} }
TEST_F(TextBreakIteratorTest, Basic) { TEST_F(TextBreakIteratorTest, Basic) {
SetTestString("a b c"); SetTestString("a b c");
MATCH_LINE_BREAKS(LineBreakType::kNormal, {1, 3, 5}); MATCH_LINE_BREAKS(LineBreakType::kNormal, {1, 3, 4, 6});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {2, 5, 6});
} }
TEST_F(TextBreakIteratorTest, LatinPunctuation) { TEST_F(TextBreakIteratorTest, LatinPunctuation) {
...@@ -86,6 +95,8 @@ TEST_F(TextBreakIteratorTest, LatinPunctuation) { ...@@ -86,6 +95,8 @@ TEST_F(TextBreakIteratorTest, LatinPunctuation) {
MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {2, 4, 6, 8}); MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {2, 4, 6, 8});
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5, 6, 7, 8}); MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5, 6, 7, 8});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {4, 8}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {4, 8});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {5, 8});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {2, 5, 6, 8});
} }
TEST_F(TextBreakIteratorTest, Chinese) { TEST_F(TextBreakIteratorTest, Chinese) {
...@@ -94,6 +105,8 @@ TEST_F(TextBreakIteratorTest, Chinese) { ...@@ -94,6 +105,8 @@ TEST_F(TextBreakIteratorTest, Chinese) {
MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {1, 2, 3, 4, 5}); MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {1, 2, 3, 4, 5});
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5}); MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {5}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {5});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {1, 2, 3, 4, 5});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {1, 2, 3, 4, 5});
} }
TEST_F(TextBreakIteratorTest, ChineseMixed) { TEST_F(TextBreakIteratorTest, ChineseMixed) {
...@@ -103,6 +116,19 @@ TEST_F(TextBreakIteratorTest, ChineseMixed) { ...@@ -103,6 +116,19 @@ TEST_F(TextBreakIteratorTest, ChineseMixed) {
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter,
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {1, 4, 9, 10}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {1, 4, 9, 10});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {1, 4, 5, 7, 9, 10});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {1, 4, 5, 6, 7, 9, 10});
}
TEST_F(TextBreakIteratorTest, ChineseSpaces) {
SetTestString("標 萬 a 國");
MATCH_LINE_BREAKS(LineBreakType::kNormal, {1, 2, 4, 5, 7, 8, 10});
MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {1, 2, 4, 5, 7, 8, 10});
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter,
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {1, 2, 4, 5, 7, 8, 10});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {3, 6, 9, 10});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {3, 6, 9, 10});
} }
TEST_F(TextBreakIteratorTest, KeepEmojiZWJFamilyIsolate) { TEST_F(TextBreakIteratorTest, KeepEmojiZWJFamilyIsolate) {
...@@ -111,6 +137,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiZWJFamilyIsolate) { ...@@ -111,6 +137,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiZWJFamilyIsolate) {
MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {11}); MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {11});
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {11}); MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {11});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {11}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {11});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {11});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {11});
} }
TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequenceIsolate) { TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequenceIsolate) {
...@@ -119,6 +147,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequenceIsolate) { ...@@ -119,6 +147,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequenceIsolate) {
MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {3}); MATCH_LINE_BREAKS(LineBreakType::kBreakAll, {3});
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {3}); MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, {3});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {3});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {3});
} }
TEST_F(TextBreakIteratorTest, KeepEmojiZWJSequence) { TEST_F(TextBreakIteratorTest, KeepEmojiZWJSequence) {
...@@ -129,6 +159,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiZWJSequence) { ...@@ -129,6 +159,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiZWJSequence) {
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter,
{1, 2, 3, 4, 15, 16, 17, 18, 19}); {1, 2, 3, 4, 15, 16, 17, 18, 19});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3, 15, 19}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3, 15, 19});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {4, 16, 19});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {1, 2, 4, 16, 17, 18, 19});
} }
TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequence) { TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequence) {
...@@ -138,6 +170,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequence) { ...@@ -138,6 +170,8 @@ TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequence) {
MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter, MATCH_LINE_BREAKS(LineBreakType::kBreakCharacter,
{1, 2, 3, 4, 7, 8, 9, 10, 11}); {1, 2, 3, 4, 7, 8, 9, 10, 11});
MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3, 7, 11}); MATCH_LINE_BREAKS(LineBreakType::kKeepAll, {3, 7, 11});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kNormal, {4, 8, 11});
MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {1, 2, 4, 8, 9, 10, 11});
} }
} // namespace blink } // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment