Auto merge of #13414 - flacerdk:master, r=mbrubeck

Implement `word-break: keep-all` (#9673)

<!-- Please describe your changes on the following line: -->
Implement the `keep-all` value for the `word-break` property, as specified in [CSS](https://drafts.csswg.org/css-text-3/#word-break-property).

The relevant CSSWG tests (in `tests/wpt/css-tests/css-text-3_dev/html/word-break-keep-all-*.htm`) do not currently pass. As far as I can tell, this is because the tests use some JavaScript code that is not working properly. (But then, it seems that most tests in this directory are failing at the moment. I'm not sure what can be done here for now.)

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #9673.

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13414)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-09-29 19:20:55 -05:00 committed by GitHub
commit c9442346d3
15 changed files with 282 additions and 13 deletions

View file

@ -148,6 +148,8 @@ bitflags! {
const DISABLE_KERNING_SHAPING_FLAG = 0x04, const DISABLE_KERNING_SHAPING_FLAG = 0x04,
#[doc = "Text direction is right-to-left."] #[doc = "Text direction is right-to-left."]
const RTL_FLAG = 0x08, const RTL_FLAG = 0x08,
#[doc = "Set if word-break is set to keep-all."]
const KEEP_ALL_FLAG = 0x10,
} }
} }

View file

@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au; use app_units::Au;
use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, RunMetrics}; use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, KEEP_ALL_FLAG};
use font::ShapingOptions; use font::{RunMetrics, ShapingOptions};
use platform::font_template::FontTemplateData; use platform::font_template::FontTemplateData;
use range::Range; use range::Range;
use std::cell::Cell; use std::cell::Cell;
@ -206,11 +206,14 @@ impl<'a> TextRun {
// Split off any trailing whitespace into a separate glyph run. // Split off any trailing whitespace into a separate glyph run.
let mut whitespace = slice.end..slice.end; let mut whitespace = slice.end..slice.end;
if let Some((i, _)) = word.char_indices().rev() if let Some((i, _)) = word.char_indices().rev()
.take_while(|&(_, c)| char_is_whitespace(c)).last() { .take_while(|&(_, c)| char_is_whitespace(c)).last() {
whitespace.start = slice.start + i; whitespace.start = slice.start + i;
slice.end = whitespace.start; slice.end = whitespace.start;
} } else if idx != text.len() && options.flags.contains(KEEP_ALL_FLAG) {
// If there's no whitespace and word-break is set to
// keep-all, try increasing the slice.
continue;
}
if slice.len() > 0 { if slice.len() > 0 {
glyphs.push(GlyphRun { glyphs.push(GlyphRun {
glyph_store: font.shape_text(&text[slice.clone()], options), glyph_store: font.shape_text(&text[slice.clone()], options),

View file

@ -1637,8 +1637,8 @@ impl Fragment {
} }
match self.style().get_inheritedtext().word_break { match self.style().get_inheritedtext().word_break {
word_break::T::normal => { word_break::T::normal | word_break::T::keep_all => {
// Break at normal word boundaries. // Break at normal word boundaries. keep-all forbids soft wrap opportunities.
let natural_word_breaking_strategy = let natural_word_breaking_strategy =
text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range); text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
self.calculate_split_position_using_breaking_strategy( self.calculate_split_position_using_breaking_strategy(

View file

@ -10,7 +10,7 @@ use app_units::Au;
use fragment::{Fragment, REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES, ScannedTextFlags}; use fragment::{Fragment, REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES, ScannedTextFlags};
use fragment::{SELECTED, ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; use fragment::{SELECTED, ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG}; use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG};
use gfx::font::{RTL_FLAG, RunMetrics, ShapingFlags, ShapingOptions}; use gfx::font::{KEEP_ALL_FLAG, RTL_FLAG, RunMetrics, ShapingFlags, ShapingOptions};
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::text::glyph::ByteIndex; use gfx::text::glyph::ByteIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
@ -24,7 +24,7 @@ use std::collections::LinkedList;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::{line_height, text_orientation, text_rendering, text_transform}; use style::computed_values::{line_height, text_orientation, text_rendering, text_transform};
use style::computed_values::white_space; use style::computed_values::{word_break, white_space};
use style::logical_geometry::{LogicalSize, WritingMode}; use style::logical_geometry::{LogicalSize, WritingMode};
use style::properties::ServoComputedValues; use style::properties::ServoComputedValues;
use style::properties::style_structs; use style::properties::style_structs;
@ -151,6 +151,7 @@ impl TextRunScanner {
let letter_spacing; let letter_spacing;
let word_spacing; let word_spacing;
let text_rendering; let text_rendering;
let word_break;
{ {
let in_fragment = self.clump.front().unwrap(); let in_fragment = self.clump.front().unwrap();
let font_style = in_fragment.style().get_font_arc(); let font_style = in_fragment.style().get_font_arc();
@ -169,6 +170,7 @@ impl TextRunScanner {
.map(|lop| lop.to_hash_key()) .map(|lop| lop.to_hash_key())
.unwrap_or((Au(0), NotNaN::new(0.0).unwrap())); .unwrap_or((Au(0), NotNaN::new(0.0).unwrap()));
text_rendering = inherited_text_style.text_rendering; text_rendering = inherited_text_style.text_rendering;
word_break = inherited_text_style.word_break;
} }
// First, transform/compress text of all the nodes. // First, transform/compress text of all the nodes.
@ -289,6 +291,9 @@ impl TextRunScanner {
flags.insert(IGNORE_LIGATURES_SHAPING_FLAG); flags.insert(IGNORE_LIGATURES_SHAPING_FLAG);
flags.insert(DISABLE_KERNING_SHAPING_FLAG) flags.insert(DISABLE_KERNING_SHAPING_FLAG)
} }
if word_break == word_break::T::keep_all {
flags.insert(KEEP_ALL_FLAG);
}
let options = ShapingOptions { let options = ShapingOptions {
letter_spacing: letter_spacing, letter_spacing: letter_spacing,
word_spacing: word_spacing, word_spacing: word_spacing,

View file

@ -385,8 +385,7 @@ ${helpers.single_keyword("overflow-wrap",
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support. // TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
${helpers.single_keyword("word-break", ${helpers.single_keyword("word-break",
"normal break-all", "normal break-all keep-all",
extra_gecko_values="keep-all",
gecko_constant_prefix="NS_STYLE_WORDBREAK", gecko_constant_prefix="NS_STYLE_WORDBREAK",
animatable=False)} animatable=False)}

View file

@ -5970,6 +5970,54 @@
"url": "/_mozilla/css/width_nonreplaced_block_simple_a.html" "url": "/_mozilla/css/width_nonreplaced_block_simple_a.html"
} }
], ],
"css/word-break-keep-all-005.htm": [
{
"path": "css/word-break-keep-all-005.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-005.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-005.htm"
}
],
"css/word-break-keep-all-006.htm": [
{
"path": "css/word-break-keep-all-006.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-006.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-006.htm"
}
],
"css/word-break-keep-all-007.htm": [
{
"path": "css/word-break-keep-all-007.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-007.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-007.htm"
}
],
"css/word-break-keep-all-008.htm": [
{
"path": "css/word-break-keep-all-008.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-008.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-008.htm"
}
],
"css/word-spacing.html": [ "css/word-spacing.html": [
{ {
"path": "css/word-spacing.html", "path": "css/word-spacing.html",
@ -19632,6 +19680,54 @@
"url": "/_mozilla/css/width_nonreplaced_block_simple_a.html" "url": "/_mozilla/css/width_nonreplaced_block_simple_a.html"
} }
], ],
"css/word-break-keep-all-005.htm": [
{
"path": "css/word-break-keep-all-005.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-005.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-005.htm"
}
],
"css/word-break-keep-all-006.htm": [
{
"path": "css/word-break-keep-all-006.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-006.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-006.htm"
}
],
"css/word-break-keep-all-007.htm": [
{
"path": "css/word-break-keep-all-007.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-007.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-007.htm"
}
],
"css/word-break-keep-all-008.htm": [
{
"path": "css/word-break-keep-all-008.htm",
"references": [
[
"/_mozilla/css/word-break-keep-all-ref-008.htm",
"=="
]
],
"url": "/_mozilla/css/word-break-keep-all-008.htm"
}
],
"css/word-spacing.html": [ "css/word-spacing.html": [
{ {
"path": "css/word-spacing.html", "path": "css/word-spacing.html",

View file

@ -0,0 +1,3 @@
[word-break-keep-all-008.htm]
type: reftest
expected: FAIL

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, latin</title>
<meta content="word-break: keep-all means breaking is forbidden within 'words'." name="assert">
<link href="https://drafts.csswg.org/css-text-3/#word-break-property" rel="help">
<link href="word-break-keep-all-ref-005.htm" rel="match">
<link href="mailto:ishida@w3.org" rel="author" title="Richard Ishida">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div class="test"><div id="testdiv"><span id="testspan">Latin latin latin latin</span></div></div>
<div class="ref"><span>Latin latin latin<br>latin</span></div>
<script>
var sentenceWidth = document.getElementById('testspan').getBoundingClientRect().width;
document.getElementById('testdiv').style.width = String(sentenceWidth - 5)+'px'
</script>
</body></html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, japanese</title>
<meta content="word-break: keep-all means breaking is forbidden within 'words'. In this style, sequences of CJK characters do not break." name="assert">
<link href="https://drafts.csswg.org/css-text-3/#word-break-property" rel="help">
<link href="word-break-keep-all-ref-006.htm" rel="match">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif ; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="ja" class="test"><div id="testdiv"><span id="testspan">日本語 日本語 日本語</span></div></div>
<div lang="ja" class="ref"><span>日本語 日本語<br>日本語</span></div>
<script>
var sentenceWidth = document.getElementById('testspan').getBoundingClientRect().width;
document.getElementById('testdiv').style.width = String(sentenceWidth - 5)+'px'
</script>
</body></html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, korean</title>
<meta content="word-break: keep-all means breaking is forbidden within 'words'. In this style, sequences of CJK characters do not break." name="assert">
<link href="https://drafts.csswg.org/css-text-3/#word-break-property" rel="help">
<link href="word-break-keep-all-ref-007.htm" rel="match">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="ko" class="test"><div id="testdiv"><span id="testspan">한글이 한글이 한글이</span></div></div>
<div lang="ko" class="ref"><span>한글이 한글이<br>한글이</span></div>
<script>
var sentenceWidth = document.getElementById('testspan').getBoundingClientRect().width;
document.getElementById('testdiv').style.width = String(sentenceWidth - 5)+'px'
</script>
</body></html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, thai</title>
<meta content="word-break: keep-all means breaking is forbidden within 'words', except where opportunities exist due to dictionary-based breaking (such as in Thai)." name="assert">
<link href="https://drafts.csswg.org/css-text-3/#word-break-property" rel="help">
<link href="word-break-keep-all-ref-008.htm" rel="match">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="th" class="test"><div id="testdiv"><span id="testspan">แและ แและแและ</span></div></div>
<div lang="th" class="ref"><span>แและ แและ<br>แและ</span></div>
<script>
var sentenceWidth = document.getElementById('testspan').getBoundingClientRect().width;
document.getElementById('testdiv').style.width = String(sentenceWidth - 5)+'px'
</script>
</body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, latin</title>
<link href="mailto:ishida@w3.org" rel="author" title="Richard Ishida">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div class="ref"><span>Latin latin latin<br>latin</span></div>
<div class="ref"><span>Latin latin latin<br>latin</span></div>
</body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, japanese</title>
<link href="mailto:ishida@w3.org" rel="author" title="Richard Ishida">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="ja" class="ref"><span>日本語 日本語<br>日本語</span></div>
<div lang="ja" class="ref"><span>日本語 日本語<br>日本語</span></div>
</body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, korean</title>
<link href="mailto:ishida@w3.org" rel="author" title="Richard Ishida">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="ko" class="ref"><span>한글이 한글이<br>한글이</span></div>
<div lang="ko" class="ref"><span>한글이 한글이<br>한글이</span></div>
</body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<title>word-break: keep-all, thai</title>
<link href="mailto:ishida@w3.org" rel="author" title="Richard Ishida">
<style type="text/css">
.test { word-break: keep-all; }
/* the CSS below is not part of the test */
.test, .ref { border: 1px solid orange; margin: 20px; padding: 10px; width: 390px; font: 36px/1 sans-serif; }
</style>
</head>
<body>
<div id="instructions">Test passes if the two orange boxes are the same.</div>
<div lang="th" class="ref"><span>แและ แและ<br>แและ</span></div>
<div lang="th" class="ref"><span>แและ แและ<br>แและ</span></div>
</body></html>