Implement white-space:break-spaces value
authorjfernandez@igalia.com <jfernandez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 8 Apr 2019 20:31:05 +0000 (20:31 +0000)
committerjfernandez@igalia.com <jfernandez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 8 Apr 2019 20:31:05 +0000 (20:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177327

Reviewed by Myles Maxfield and Zalan Bujtas.

LayoutTests/imported/w3c:

Imoprted a few aditional tests from the CSS Text WPT suite that are relevant for this change.

* web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html: Added.
* web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html: Added.
* web-platform-tests/css/css-text/overflow-wrap/w3c-import.log:
* web-platform-tests/css/css-text/parsing/white-space-valid-expected.txt:
* web-platform-tests/css/css-text/white-space/break-spaces-003-expected.html: Added.
* web-platform-tests/css/css-text/white-space/break-spaces-003.html: Added.
* web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html: Added.
* web-platform-tests/css/css-text/white-space/break-spaces-009.html: Added.
* web-platform-tests/css/css-text/white-space/w3c-import.log:

Source/WebCore:

Finally the CSS WG decided [1] to move back the 'break-spaces' value to
the 'white-space' property. This makes the parsing logic easier than
the previous approach of using the 'overflow-wrap' property.

This new value prevents the white-space sequence to collapse and gives
breaking opportunities after every preserved white-space.

https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces

Additionally, unlike 'pre-wrap', non-collapsible spaces or tabs at the
end of a line cannot be hung or visually collapsed, since we want them
to be preserved and broken.

[1] https://github.com/w3c/csswg-drafts/pull/2841

Tests: imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html
       imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003.html
       imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html

* css/CSSPrimitiveValueMappings.h:
(WebCore::CSSPrimitiveValue::CSSPrimitiveValue): New mapping for CSSVaueBreakSpaces.
(WebCore::CSSPrimitiveValue::operator WhiteSpace const): New value BreakSpaces for the Whitespace enum.
* css/CSSProperties.json: new 'break-spaces' value for the 'white-space' property.
* css/CSSValueKeywords.in: new break-spaces keyword
* css/parser/CSSParserFastPaths.cpp:
(WebCore::CSSParserFastPaths::isValidKeywordPropertyAndValue):
* rendering/SimpleLineLayout.cpp:  The SimpleLineLayut codepath must handle properly the new behavior of preserved whitespaces.
(WebCore::SimpleLineLayout::LineState::hasTrailingWhitespace const): Require that m_trailingWhitespaceWidth being greater than zero.
(WebCore::SimpleLineLayout::LineState::hasWhitespaceFragments const): New function to detect former whitespae fragments that could be breaking opportunities.
(WebCore::SimpleLineLayout::removeTrailingWhitespace): The 'break-spaces' feature forbids to remove any trailing whitespace.
(WebCore::SimpleLineLayout::firstFragment): We have now leading whitespace sequences comming from the previous line.
(WebCore::SimpleLineLayout::createLineRuns): We should revert some breaking opportunities if others were formerly found in the line.
* rendering/SimpleLineLayoutTextFragmentIterator.cpp:
(WebCore::SimpleLineLayout::TextFragmentIterator::Style::Style): New style fields to determine whether the break-space feature is being used. Also split out the break-all and breal-all values.
* rendering/SimpleLineLayoutTextFragmentIterator.h:
* rendering/line/BreakingContext.h: Different codepath but similar changes to properly handle the new behavior of preserved whitespace.
(WebCore::BreakingContext::BreakingContext): New class field to determine whether there are some whitespace that may prevent the word ot be broken.
(WebCore::BreakingContext::handleText): New line-breaking logic to implement the break-spaces behavior.
(WebCore::BreakingContext::trailingSpacesHang): Cases where the preserved breakspaces should hand or overflow.
* rendering/style/RenderStyle.h:
(WebCore::RenderStyle::collapseWhiteSpace): With break-spaces collapsing whitespaces is not allowed.
(WebCore::RenderStyle::breakOnlyAfterWhiteSpace const): Add the WhiteSpace::BreakSpaces to this group.
* rendering/style/RenderStyleConstants.h: A new constan added.

LayoutTests:

Removed many failure expectations of tests that pass now thanks to this change.

The overflow-wrap-break-word-003.html tests fails in mac and iOS platforms due to an issue related to
how we compute widths using 'ch' units and 'monospace' fonts. I filed bug #196169 to report the specific
case of this test (break-word+pre-wrap) but the root cause it's probably the bug #196353.

I have submitted a PR [1] to the Web Platform Tests to change several tests that failed only if the
monospace font is used, but pass with the Ahem font. Additionally, I've requested another PR [2] to
add new layout tests, with the same case than overflow-wrap-break-word-003.html but using the Ahem
font.

[1] https://github.com/web-platform-tests/wpt/pull/16137
[2] https://github.com/web-platform-tests/wpt/pull/16124

* TestExpectations: Removed many Failure entries on tests that pass now thanks to this change.
* platform/mac/TestExpectations: Added one Failure entry and filed the corresponding bug report.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244036 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/w3c-import.log
LayoutTests/imported/w3c/web-platform-tests/css/css-text/parsing/white-space-valid-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003-expected.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/textarea-break-spaces-001-expected.html
LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/w3c-import.log
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/mac/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/CSSPrimitiveValueMappings.h
Source/WebCore/css/CSSProperties.json
Source/WebCore/css/CSSValueKeywords.in
Source/WebCore/css/parser/CSSParserFastPaths.cpp
Source/WebCore/rendering/SimpleLineLayout.cpp
Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.cpp
Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.h
Source/WebCore/rendering/line/BreakingContext.h
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/RenderStyleConstants.h

index a4e1ba1..3a6e520 100644 (file)
@@ -1,3 +1,27 @@
+2019-04-08  Javier Fernandez  <jfernandez@igalia.com>
+
+        Implement white-space:break-spaces value
+        https://bugs.webkit.org/show_bug.cgi?id=177327
+
+        Reviewed by Myles Maxfield and Zalan Bujtas.
+
+        Removed many failure expectations of tests that pass now thanks to this change.
+
+        The overflow-wrap-break-word-003.html tests fails in mac and iOS platforms due to an issue related to
+        how we compute widths using 'ch' units and 'monospace' fonts. I filed bug #196169 to report the specific
+        case of this test (break-word+pre-wrap) but the root cause it's probably the bug #196353.
+
+        I have submitted a PR [1] to the Web Platform Tests to change several tests that failed only if the
+        monospace font is used, but pass with the Ahem font. Additionally, I've requested another PR [2] to
+        add new layout tests, with the same case than overflow-wrap-break-word-003.html but using the Ahem
+        font.
+
+        [1] https://github.com/web-platform-tests/wpt/pull/16137
+        [2] https://github.com/web-platform-tests/wpt/pull/16124
+
+        * TestExpectations: Removed many Failure entries on tests that pass now thanks to this change.
+        * platform/mac/TestExpectations: Added one Failure entry and filed the corresponding bug report.
+
 2019-04-08  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] JS wrapper may be deleted while animation is yet to dispatch its finish event
index 3f6b21c..5bd9e18 100644 (file)
@@ -1809,8 +1809,6 @@ webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/line-breaking/l
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/line-breaking/line-breaking-011.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/line-breaking/line-breaking-ic-002.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/line-breaking/line-breaking-ic-003.html [ ImageOnlyFailure ]
-webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-002.html [ ImageOnlyFailure ]
-webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/tab-size/tab-size-length-001.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/tab-size/tab-size-length-002.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/text-align/text-align-end-001.html [ ImageOnlyFailure ]
@@ -1907,7 +1905,6 @@ webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/line-break/line
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/line-break/line-break-loose-017a.xht [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/line-break/line-break-loose-014.xht [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/line-break/line-break-loose-015.xht [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-006.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/tab-size/tab-size-spacing-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/tab-size/tab-min-rendered-width-1.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/tab-size/tab-size-inheritance-001.html [ ImageOnlyFailure ]
@@ -1926,36 +1923,25 @@ webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/tra
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/trailing-ideographic-space-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-intrinsic-size-004.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/text-space-collapse-discard-001.xht [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-008.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/tab-stop-threshold-004.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-intrinsic-size-002.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/control-chars-000.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-intrinsic-size-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-intrinsic-size-003.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/trailing-ideographic-space-002.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/tab-stop-threshold-002.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/line-edge-white-space-collapse-001.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-005.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/tab-stop-threshold-005.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/textarea-break-spaces-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/trailing-ideographic-space-003.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-002.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/pre-wrap-014.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-007.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/text-space-trim-trim-inner-001.xht [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/trailing-ideographic-space-004.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-003.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/tab-stop-threshold-006.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/text-space-collapse-preserve-breaks-001.xht [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/line-edge-white-space-collapse-002.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-004.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-006.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-break-all-020.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-break-all-012.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-keep-all-006.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-keep-all-005.html [ ImageOnlyFailure ]
-webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-break-all-013.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/writing-system/writing-system-segment-break-001.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/writing-system/writing-system-line-break-002.html [ ImageOnlyFailure ]
 webkit.org/b/195275 imported/w3c/web-platform-tests/css/css-text/writing-system/writing-system-text-transform-001.html [ ImageOnlyFailure ]
index d591c05..c9e36fc 100644 (file)
@@ -1,3 +1,22 @@
+2019-04-08  Javier Fernandez  <jfernandez@igalia.com>
+
+        Implement white-space:break-spaces value
+        https://bugs.webkit.org/show_bug.cgi?id=177327
+
+        Reviewed by Myles Maxfield and Zalan Bujtas.
+
+        Imoprted a few aditional tests from the CSS Text WPT suite that are relevant for this change.
+
+        * web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html: Added.
+        * web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html: Added.
+        * web-platform-tests/css/css-text/overflow-wrap/w3c-import.log:
+        * web-platform-tests/css/css-text/parsing/white-space-valid-expected.txt:
+        * web-platform-tests/css/css-text/white-space/break-spaces-003-expected.html: Added.
+        * web-platform-tests/css/css-text/white-space/break-spaces-003.html: Added.
+        * web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html: Added.
+        * web-platform-tests/css/css-text/white-space/break-spaces-009.html: Added.
+        * web-platform-tests/css/css-text/white-space/w3c-import.log:
+
 2019-04-05  Caitlin Potter  <caitp@igalia.com>
 
         [JSC] throw if 'ownKeys' Proxy trap result contains duplicate keys
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html
new file mode 100644 (file)
index 0000000..0e0300a
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Reference File</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<style>
+div {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html
new file mode 100644 (file)
index 0000000..9a3a95b
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap:break-word + white-space:break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property " href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-break-word">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<meta name="assert" content="break-word + break-spaces do allow a break
+between the last character of a word and the first space of a sequence of preserved spaces
+if there are no other wrapping opportunities earlier in the line">
+<style>
+div {
+  position: relative;
+  font-family: Ahem;
+  font-size: 25px;
+  line-height: 1em;
+}
+.red {
+  position: absolute;
+  color: green;
+  width: 100px;
+  height: 100px;
+  white-space: pre;
+}
+.test {
+  background: green;
+  color: red;
+  width: 4ch;
+  z-index: -1;
+
+  white-space: break-spaces;
+  overflow-wrap: break-word;
+</style>
+
+<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+<div class="red">XXXX<br> <br>XXXX<br></div>
+<div class="test">XXXX XXXX </div>
index a901a5a..f377492 100644 (file)
@@ -49,6 +49,8 @@ List of files:
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-006.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-007-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-007.html
+/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008-expected.html
+/LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-fit-content-001-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-fit-content-001.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-span-001-expected.html
index f0797d3..5c01ebc 100644 (file)
@@ -3,6 +3,6 @@ PASS e.style['white-space'] = "normal" should set the property value
 PASS e.style['white-space'] = "pre" should set the property value 
 PASS e.style['white-space'] = "nowrap" should set the property value 
 PASS e.style['white-space'] = "pre-wrap" should set the property value 
-FAIL e.style['white-space'] = "break-spaces" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['white-space'] = "break-spaces" should set the property value 
 PASS e.style['white-space'] = "pre-line" should set the property value 
 
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003-expected.html
new file mode 100644 (file)
index 0000000..3247ae5
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Reference File</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<style>
+div {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+</style>
+<body>
+    <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003.html
new file mode 100644 (file)
index 0000000..000388b
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space: break-spaces</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-word">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/pre-wrap-001-ref.html">
+<meta name="assert" content="The word is not broken if there are previous breaking opportunities, honoring the 'white-space: break-spaces' value.">
+<style>
+div {
+   position: relative;
+   font: 25px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 4ch;
+
+  white-space: break-spaces;
+  word-break: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail">XX<span>XX</span><br>X<span>X</span>X<span>X<br>XXXX<br>XXXX</span></div>
+  <div class="test">XX X X</div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html
new file mode 100644 (file)
index 0000000..3247ae5
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Reference File</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<style>
+div {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+</style>
+<body>
+    <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div></div>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html
new file mode 100644 (file)
index 0000000..9ecaa91
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: word-break:break-word + white-space:break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-word">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/white-space-break-spaces-005-ref.html">
+<meta name="assert" content="break-word + break-spaces do allow a break
+between the last character of a word and the first space of a sequence of preserved spaces
+if there are no other wrapping opportunities earlier in the line">
+<style>
+div {
+  position: relative;
+  font: 25px/1 Ahem;
+}
+.red {
+  position: absolute;
+  color: green;
+  width: 100px;
+  height: 100px;
+  white-space: pre;
+}
+.test {
+  background: green;
+  color: red;
+  width: 4ch;
+  z-index: -1;
+
+  white-space: break-spaces;
+  word-break: break-word;
+</style>
+
+<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+<div class="red">XXXX<br> <br>XXXX<br></div>
+<div class="test">XXXX XXXX </div>
index d16fd81..a811dbc 100644 (file)
@@ -28,6 +28,8 @@ List of files:
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-007.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-008-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-008.html
+/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009-expected.html
+/LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/control-chars-000-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/control-chars-000.html
 /LayoutTests/imported/w3c/web-platform-tests/css/css-text/white-space/control-chars-001-expected.html
index 7ba15b3..7a5173b 100644 (file)
@@ -3066,6 +3066,7 @@ webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-line-break-opclns-115.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-line-break-opclns-116.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-break-all-006.html [ ImageOnlyFailure ]
+webkit.org/b/196169 imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html [ ImageOnlyFailure ]
 
 # unsupported
 fast/dynamic/mail-autosize-viewport-unit.html [ Failure ]
index 97a65e7..3326aa3 100644 (file)
@@ -1686,6 +1686,7 @@ webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-line-break-opclns-115.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/i18n/css3-text-line-break-opclns-116.html [ ImageOnlyFailure ]
 webkit.org/b/183258 imported/w3c/web-platform-tests/css/css-text/word-break/word-break-break-all-006.html [ ImageOnlyFailure ]
+webkit.org/b/196169 imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html [ ImageOnlyFailure ]
 
 # Color Well is turned off
 accessibility/color-well.html [ Skip ]
index f8da0a1..84febe2 100644 (file)
@@ -1,3 +1,54 @@
+2019-04-08  Javier Fernandez  <jfernandez@igalia.com>
+
+        Implement white-space:break-spaces value
+        https://bugs.webkit.org/show_bug.cgi?id=177327
+
+        Reviewed by Myles Maxfield and Zalan Bujtas.
+
+        Finally the CSS WG decided [1] to move back the 'break-spaces' value to
+        the 'white-space' property. This makes the parsing logic easier than
+        the previous approach of using the 'overflow-wrap' property.
+
+        This new value prevents the white-space sequence to collapse and gives
+        breaking opportunities after every preserved white-space.
+
+        https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces
+
+        Additionally, unlike 'pre-wrap', non-collapsible spaces or tabs at the
+        end of a line cannot be hung or visually collapsed, since we want them
+        to be preserved and broken.
+
+        [1] https://github.com/w3c/csswg-drafts/pull/2841
+
+        Tests: imported/w3c/web-platform-tests/css/css-text/overflow-wrap/overflow-wrap-break-word-008.html
+               imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-003.html
+               imported/w3c/web-platform-tests/css/css-text/white-space/break-spaces-009.html
+
+        * css/CSSPrimitiveValueMappings.h:
+        (WebCore::CSSPrimitiveValue::CSSPrimitiveValue): New mapping for CSSVaueBreakSpaces.
+        (WebCore::CSSPrimitiveValue::operator WhiteSpace const): New value BreakSpaces for the Whitespace enum.
+        * css/CSSProperties.json: new 'break-spaces' value for the 'white-space' property.
+        * css/CSSValueKeywords.in: new break-spaces keyword
+        * css/parser/CSSParserFastPaths.cpp:
+        (WebCore::CSSParserFastPaths::isValidKeywordPropertyAndValue):
+        * rendering/SimpleLineLayout.cpp:  The SimpleLineLayut codepath must handle properly the new behavior of preserved whitespaces.
+        (WebCore::SimpleLineLayout::LineState::hasTrailingWhitespace const): Require that m_trailingWhitespaceWidth being greater than zero.
+        (WebCore::SimpleLineLayout::LineState::hasWhitespaceFragments const): New function to detect former whitespae fragments that could be breaking opportunities.
+        (WebCore::SimpleLineLayout::removeTrailingWhitespace): The 'break-spaces' feature forbids to remove any trailing whitespace.
+        (WebCore::SimpleLineLayout::firstFragment): We have now leading whitespace sequences comming from the previous line.
+        (WebCore::SimpleLineLayout::createLineRuns): We should revert some breaking opportunities if others were formerly found in the line.
+        * rendering/SimpleLineLayoutTextFragmentIterator.cpp:
+        (WebCore::SimpleLineLayout::TextFragmentIterator::Style::Style): New style fields to determine whether the break-space feature is being used. Also split out the break-all and breal-all values.
+        * rendering/SimpleLineLayoutTextFragmentIterator.h:
+        * rendering/line/BreakingContext.h: Different codepath but similar changes to properly handle the new behavior of preserved whitespace.
+        (WebCore::BreakingContext::BreakingContext): New class field to determine whether there are some whitespace that may prevent the word ot be broken.
+        (WebCore::BreakingContext::handleText): New line-breaking logic to implement the break-spaces behavior.
+        (WebCore::BreakingContext::trailingSpacesHang): Cases where the preserved breakspaces should hand or overflow.
+        * rendering/style/RenderStyle.h:
+        (WebCore::RenderStyle::collapseWhiteSpace): With break-spaces collapsing whitespaces is not allowed.
+        (WebCore::RenderStyle::breakOnlyAfterWhiteSpace const): Add the WhiteSpace::BreakSpaces to this group.
+        * rendering/style/RenderStyleConstants.h: A new constan added.
+
 2019-04-08  Youenn Fablet  <youenn@apple.com>
 
         LibWebRTCMediaEndpoint does not need to hop to the signaling thread to gather stats
index d09f49f..aa65f0d 100644 (file)
@@ -3051,6 +3051,9 @@ template<> inline CSSPrimitiveValue::CSSPrimitiveValue(WhiteSpace e)
     case WhiteSpace::KHTMLNoWrap:
         m_value.valueID = CSSValueWebkitNowrap;
         break;
+    case WhiteSpace::BreakSpaces:
+        m_value.valueID = CSSValueBreakSpaces;
+        break;
     }
 }
 
@@ -3071,6 +3074,8 @@ template<> inline CSSPrimitiveValue::operator WhiteSpace() const
         return WhiteSpace::PreLine;
     case CSSValueNormal:
         return WhiteSpace::Normal;
+    case CSSValueBreakSpaces:
+        return WhiteSpace::BreakSpaces;
     default:
         break;
     }
index 2fe2bac..0941d0e 100644 (file)
                 "pre",
                 "pre-wrap",
                 "pre-line",
-                "nowrap"
+                "nowrap",
+                "break-spaces"
             ],
             "specification": {
                 "category": "css-22",
index 05be12c..2a83be4 100644 (file)
@@ -769,6 +769,11 @@ keep-all
 break-word
 
 //
+// CSS_PROP_WHITE_SPACE
+//
+break-spaces
+
+//
 // CSS_PROP__KHTML_NBSP_MODE
 //
 space
index e7e0824..833a2b5 100644 (file)
@@ -761,8 +761,8 @@ bool CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyID propertyId
         return (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt)
             || valueID == CSSValueLrTb || valueID == CSSValueRlTb || valueID == CSSValueTbRl
             || valueID == CSSValueLr || valueID == CSSValueRl || valueID == CSSValueTb;
-    case CSSPropertyWhiteSpace: // normal | pre | nowrap
-        return valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap;
+    case CSSPropertyWhiteSpace: // normal | pre | nowrap | pre-line | nowrap | break-spacess
+        return valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap || valueID == CSSValueBreakSpaces;
     case CSSPropertyWordBreak: // normal | break-all | keep-all | break-word (this is a custom extension)
         return valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueKeepAll || valueID == CSSValueBreakWord;
     case CSSPropertyWebkitBorderFit:
index 07e7612..8d244ec 100644 (file)
@@ -422,7 +422,8 @@ public:
     float availableWidth() const { return m_availableWidth; }
     float logicalLeftOffset() const { return m_logicalLeftOffset; }
     const TextFragmentIterator::TextFragment& overflowedFragment() const { return m_overflowedFragment; }
-    bool hasTrailingWhitespace() const { return m_lastFragment.type() == TextFragmentIterator::TextFragment::Whitespace; }
+    bool hasTrailingWhitespace() const { return m_lastFragment.type() == TextFragmentIterator::TextFragment::Whitespace && m_lastFragment.length() > 0; }
+    bool hasWhitespaceFragments() const { return m_lastWhitespaceFragment != WTF::nullopt; }
     TextFragmentIterator::TextFragment lastFragment() const { return m_lastFragment; }
     bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; }
     bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
@@ -505,9 +506,10 @@ public:
         if (m_fragments)
             (*m_fragments).append(fragment);
 
-        if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace)
+        if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
             m_trailingWhitespaceWidth += fragment.width();
-        else {
+            m_lastWhitespaceFragment = fragment;
+        } else {
             m_trailingWhitespaceWidth = 0;
             m_lastNonWhitespaceFragment = fragment;
         }
@@ -567,6 +569,7 @@ private:
     TextFragmentIterator::TextFragment m_overflowedFragment;
     TextFragmentIterator::TextFragment m_lastFragment;
     Optional<TextFragmentIterator::TextFragment> m_lastNonWhitespaceFragment;
+    Optional<TextFragmentIterator::TextFragment> m_lastWhitespaceFragment;
     TextFragmentIterator::TextFragment m_lastCompleteFragment;
     float m_uncompletedWidth { 0 };
     float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
@@ -590,7 +593,7 @@ static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& ru
     // Remove collapsed whitespace, or non-collapsed pre-wrap whitespace, unless it's the only content on the line -so removing the whitesapce
     // would produce an empty line.
     const auto& style = textFragmentIterator.style();
-    bool collapseWhitespace = style.collapseWhitespace | preWrap(style);
+    bool collapseWhitespace = style.collapseWhitespace || (!style.breakSpaces && preWrap(style));
     if (!collapseWhitespace)
         return;
     if (preWrap(style) && lineState.isWhitespaceOnly())
@@ -747,6 +750,12 @@ static TextFragmentIterator::TextFragment firstFragment(TextFragmentIterator& te
 
     // Leading whitespace handling.
     auto& style = textFragmentIterator.style();
+    if (style.breakSpaces) {
+        // Leading whitespace created after breaking the previous line.
+        // Breaking before the first space after a word is only allowed in combination with break-all or break-word.
+        if (style.breakFirstWordOnOverflow || previousLine.hasTrailingWhitespace())
+            return overflowedFragment;
+    }
     // Special overflow pre-wrap whitespace handling: skip the overflowed whitespace (even when style says not-collapsible)
     // if we manage to fit at least one character on the previous line.
     auto preWrapIsOn = preWrap(style);
@@ -812,6 +821,11 @@ static bool createLineRuns(LineState& line, const LineState& previousLine, Layou
                     line.setOverflowedFragment(fragment);
                     break;
                 }
+                if (style.breakSpaces && style.breakWordOnOverflow && line.hasWhitespaceFragments() && fragment.length() == 1) {
+                    // Breaking before the first space after a word is not allowed if there are previous breaking opportunities in the line.
+                    textFragmentIterator.revertToEndOfFragment(line.revertToLastCompleteFragment(runs));
+                    break;
+                }
                 // Split the whitespace; left part stays on this line, right is pushed to next line.
                 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line, textFragmentIterator));
                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
index 1c51861..b8e5d01 100644 (file)
@@ -42,8 +42,10 @@ TextFragmentIterator::Style::Style(const RenderStyle& style)
     , collapseWhitespace(style.collapseWhiteSpace())
     , preserveNewline(style.preserveNewline())
     , wrapLines(style.autoWrap())
+    , breakSpaces(style.whiteSpace() == WhiteSpace::BreakSpaces)
     , breakAnyWordOnOverflow(style.wordBreak() == WordBreak::BreakAll && wrapLines)
-    , breakFirstWordOnOverflow(breakAnyWordOnOverflow || (style.breakWords() && (wrapLines || preserveNewline)))
+    , breakWordOnOverflow(style.breakWords() && (wrapLines || preserveNewline))
+    , breakFirstWordOnOverflow(breakAnyWordOnOverflow || breakWordOnOverflow)
     , breakNBSP(wrapLines && style.nbspMode() == NBSPMode::Space)
     , keepAllWordsForCJK(style.wordBreak() == WordBreak::KeepAll)
     , wordSpacing(font.wordSpacing())
index a69f850..13dfdf4 100644 (file)
@@ -111,7 +111,9 @@ public:
         bool collapseWhitespace;
         bool preserveNewline;
         bool wrapLines;
+        bool breakSpaces;
         bool breakAnyWordOnOverflow;
+        bool breakWordOnOverflow;
         bool breakFirstWordOnOverflow;
         bool breakNBSP;
         bool keepAllWordsForCJK;
index b2ffbb7..f355a2b 100644 (file)
@@ -114,6 +114,7 @@ public:
         , m_ignoringSpaces(false)
         , m_currentCharacterIsSpace(false)
         , m_currentCharacterIsWS(false)
+        , m_hasFormerOpportunity(false)
         , m_appliedStartWidth(appliedStartWidth)
         , m_includeEndWidth(true)
         , m_autoWrap(false)
@@ -146,6 +147,7 @@ public:
     void handleEmptyInline();
     void handleReplaced();
     bool handleText(WordMeasurements&, bool& hyphenated, unsigned& consecutiveHyphenatedLines);
+    void trailingSpacesHang(InlineIterator&, RenderObject&, bool canBreakMidWord, bool previousCharacterIsSpace);
     bool canBreakAtThisPosition();
     void commitAndUpdateLineBreakIfNeeded();
     InlineIterator handleEndOfLine();
@@ -215,6 +217,7 @@ private:
     // a run.
     bool m_currentCharacterIsSpace;
     bool m_currentCharacterIsWS;
+    bool m_hasFormerOpportunity;
     bool m_appliedStartWidth;
     bool m_includeEndWidth;
     bool m_autoWrap;
@@ -734,6 +737,8 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
     }
 
     HashSet<const Font*> fallbackFonts;
+    m_hasFormerOpportunity = false;
+    bool canBreakMidWord = breakWords || breakAll;
     UChar lastCharacterFromPreviousRenderText = m_renderTextInfo.lineBreakIterator.lastCharacter();
     UChar lastCharacter = m_renderTextInfo.lineBreakIterator.lastCharacter();
     UChar secondToLastCharacter = m_renderTextInfo.lineBreakIterator.secondToLastCharacter();
@@ -749,8 +754,11 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
 
         // A single preserved leading white-space doesn't fulfill the 'betweenWords' condition, however it's indeed a
         // soft-breaking opportunty so we may want to avoid breaking in the middle of the word.
-        if (m_atStart && m_currentCharacterIsSpace && !previousCharacterIsSpace)
+        if (m_atStart && m_currentCharacterIsSpace && !previousCharacterIsSpace) {
+            m_hasFormerOpportunity = canBreakMidWord;
             breakWords = false;
+            canBreakMidWord = breakAll;
+        }
 
         if (canHangPunctuationAtStart && m_width.isFirstLine() && !m_width.committedWidth() && !wrapW && !inlineLogicalWidth(m_current.renderer(), true, false)) {
             m_width.addUncommittedWidth(-renderText.hangablePunctuationStartWidth(m_current.offset()));
@@ -774,7 +782,7 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
 
         m_currentCharacterIsWS = m_currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
 
-        if ((breakAll || breakWords) && !midWordBreak && (!m_currentCharacterIsSpace || m_atStart || style.whiteSpace() != WhiteSpace::PreWrap)) {
+        if (canBreakMidWord && !midWordBreak && (!m_currentCharacterIsSpace || m_atStart || style.whiteSpace() != WhiteSpace::PreWrap)) {
             wrapW += charWidth;
             bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && U16_IS_TRAIL(renderText.characterAt(m_current.offset() + 1));
             charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace, fallbackFonts, textLayout);
@@ -846,7 +854,7 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
                 // If we break only after white-space, consider the current character
                 // as candidate width for this line.
                 bool lineWasTooWide = false;
-                if (fitsOnLineOrHangsAtEnd() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
+                if (fitsOnLineOrHangsAtEnd() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && (!midWordBreak || m_currWS == WhiteSpace::BreakSpaces)) {
                     float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0);
                     // Check if line is too big even without the extra space
                     // at the end of the line. If it is not, do nothing.
@@ -855,8 +863,12 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
                     // additional whitespace.
                     if (!m_width.fitsOnLineIncludingExtraWidth(charWidth)) {
                         lineWasTooWide = true;
-                        m_lineBreak.moveTo(renderObject, m_current.offset(), m_current.nextBreakablePosition());
-                        m_lineBreaker.skipTrailingWhitespace(m_lineBreak, m_lineInfo);
+                        if (m_currWS == WhiteSpace::BreakSpaces)
+                            trailingSpacesHang(m_lineBreak, renderObject, canBreakMidWord, previousCharacterIsSpace);
+                        else {
+                            m_lineBreak.moveTo(renderObject, m_current.offset(), m_current.nextBreakablePosition());
+                            m_lineBreaker.skipTrailingWhitespace(m_lineBreak, m_lineInfo);
+                        }
                     }
                 }
                 if ((lineWasTooWide || !m_width.fitsOnLine()) && !m_hangsAtEnd) {
@@ -941,14 +953,16 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool
                 wrapW = wrapWidthOffset;
                 // Auto-wrapping text should not wrap in the middle of a word once it has had an
                 // opportunity to break after a word.
+                m_hasFormerOpportunity = canBreakMidWord;
                 breakWords = false;
+                canBreakMidWord = breakAll;
             }
 
             if (midWordBreak && !U16_IS_TRAIL(c) && !(U_GET_GC_MASK(c) & U_GC_M_MASK)) {
                 // Remember this as a breakable position in case
                 // adding the end width forces a break.
                 m_lineBreak.moveTo(renderObject, m_current.offset(), m_current.nextBreakablePosition());
-                midWordBreak &= (breakWords || breakAll);
+                midWordBreak &= canBreakMidWord;
             }
 
             if (betweenWords) {
@@ -1072,6 +1086,22 @@ inline bool textBeginsWithBreakablePosition(RenderText& nextText)
     return c == ' ' || c == '\t' || (c == '\n' && !nextText.preservesNewline());
 }
 
+inline void BreakingContext::trailingSpacesHang(InlineIterator& lineBreak, RenderObject& renderObject, bool canBreakMidWord, bool previousCharacterIsSpace)
+{
+    ASSERT(m_currWS == WhiteSpace::BreakSpaces);
+    // Avoid breaking before the first white-space after a word if there is a
+    // breaking opportunity before.
+    if (m_hasFormerOpportunity)
+        return;
+
+    lineBreak.moveTo(renderObject, m_current.offset(), m_current.nextBreakablePosition());
+
+    // Avoid breaking before the first white-space after a word, unless
+    // overflow-wrap or word-break allow to.
+    if (!previousCharacterIsSpace && !canBreakMidWord)
+        lineBreak.increment();
+}
+
 inline bool BreakingContext::canBreakAtThisPosition()
 {
     // If we are no-wrap and have found a line-breaking opportunity already then we should take it.
index d493ece..94534b2 100644 (file)
@@ -2066,7 +2066,7 @@ inline bool RenderStyle::preserveNewline(WhiteSpace whiteSpace)
 inline bool RenderStyle::collapseWhiteSpace(WhiteSpace ws)
 {
     // Pre and prewrap do not collapse whitespace.
-    return ws != WhiteSpace::Pre && ws != WhiteSpace::PreWrap;
+    return ws != WhiteSpace::Pre && ws != WhiteSpace::PreWrap && ws != WhiteSpace::BreakSpaces;
 }
 
 inline bool RenderStyle::isCollapsibleWhiteSpace(UChar character) const
@@ -2084,7 +2084,7 @@ inline bool RenderStyle::isCollapsibleWhiteSpace(UChar character) const
 
 inline bool RenderStyle::breakOnlyAfterWhiteSpace() const
 {
-    return whiteSpace() == WhiteSpace::PreWrap || lineBreak() == LineBreak::AfterWhiteSpace;
+    return whiteSpace() == WhiteSpace::PreWrap || whiteSpace() == WhiteSpace::BreakSpaces || lineBreak() == LineBreak::AfterWhiteSpace;
 }
 
 inline bool RenderStyle::breakWords() const
index 67c7c04..084667b 100644 (file)
@@ -658,7 +658,8 @@ enum class WhiteSpace : uint8_t {
     PreWrap,
     PreLine,
     NoWrap,
-    KHTMLNoWrap
+    KHTMLNoWrap,
+    BreakSpaces
 };
 
 // The order of this enum must match the order of the text align values in CSSValueKeywords.in.