layout: capitalize string for TextTransformCase::Capitalize in fn rendered_text_collection_steps (#37486)

Previously, `rendered_text_collection_steps` ignores
`TextTransformCase::Capitalize` due to limitation of iterator. Now we
handle the case outside.

Testing: Added a new test as not covered by existing wpt-test, except
for the indirectly related WebDriver test.
`./mach test-wpt -r
tests\wpt\tests\webdriver\tests\classic\get_element_text\get.py
--product servodriver`

Fixes: #37469

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
Euclid Ye 2025-06-16 22:12:07 +08:00 committed by GitHub
parent 910cc0b687
commit f60e9cdff5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 87 additions and 16 deletions

View file

@ -672,7 +672,7 @@ where
/// Given a string and whether the start of the string represents a word boundary, create a copy of
/// the string with letters after word boundaries capitalized.
fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
pub(crate) fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
let mut output_string = String::new();
output_string.reserve(string.len());

View file

@ -37,11 +37,12 @@ use style::values::generics::font::LineHeight;
use style::values::generics::position::AspectRatio;
use style::values::specified::GenericGridTemplateComponent;
use style::values::specified::box_::DisplayInside;
use style::values::specified::text::TextTransformCase;
use style_traits::{ParsingMode, ToCss};
use crate::ArcRefCell;
use crate::dom::NodeExt;
use crate::flow::inline::construct::{TextTransformation, WhitespaceCollapse};
use crate::flow::inline::construct::{TextTransformation, WhitespaceCollapse, capitalize_string};
use crate::fragment_tree::{
BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo,
};
@ -777,11 +778,17 @@ fn rendered_text_collection_steps(
// rules are slightly modified: collapsible spaces at the end of lines are always
// collapsed, but they are only removed if the line is the last line of the block,
// or it ends with a br element. Soft hyphens should be preserved.
let mut transformed_text: String = TextTransformation::new(
with_white_space_rules_applied,
style.clone_text_transform().case(),
)
.collect();
let text_transform = style.clone_text_transform().case();
let mut transformed_text: String =
TextTransformation::new(with_white_space_rules_applied, text_transform)
.collect();
// Since iterator for capitalize not doing anything, we must handle it outside here
// FIXME: This assumes the element always start at a word boundary. But can fail:
// a<span style="text-transform: capitalize">b</span>c
if TextTransformCase::Capitalize == text_transform {
transformed_text = capitalize_string(&transformed_text, true);
}
let is_preformatted_element =
white_space_collapse == WhiteSpaceCollapseValue::Preserve;

View file

@ -612692,6 +612692,13 @@
]
]
},
"text-transform-capitalize-036.html": [
"a47dc35b949b56c734a0090c9c7a4932c745c4cf",
[
null,
{}
]
],
"text-transform-upperlower-107.html": [
"791edd14c0e144a945b4766a338725bca13da6bd",
[

View file

@ -0,0 +1,3 @@
[text-transform-capitalize-036.html]
[text-transform: capitalize test for not starting at word boundary]
expected: FAIL

View file

@ -5,15 +5,6 @@
[test_no_such_element_with_shadow_root]
expected: FAIL
[test_transform_capitalize[space\]]
expected: FAIL
[test_transform_capitalize[dash\]]
expected: FAIL
[test_transform_capitalize[underscore\]]
expected: FAIL
[test_shadow_root_slot[custom outside\]]
expected: FAIL

View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>text-transform: capitalize innerText WPT tests</title>
<link rel="author" href="mailto:yezhizhenjiakang@gmail.com" title="Euclid Ye">
<link rel="help" href="https://drafts.csswg.org/css-text/#propdef-text-transform">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="div1" style="text-transform: capitalize;">hello world</div>
<div id="div2" style="text-transform: capitalize;">foo-bar</div>
<div id="div3" style="text-transform: capitalize;">john's apple</div>
<!-- Test case 4 nested elements -->
<div id="div4" style="text-transform: capitalize;">
hello <span>world</span>
</div>
<div id="div5" style="text-transform: capitalize;">foo_bar</div>
<!-- Test case 6 not starting at word boundary -->
a<span id="span1" style="text-transform: capitalize;">b</span>c
<script>
test(function () {
var div = document.getElementById("div1");
assert_equals(div.innerText, "Hello World",
"innerText for 'hello world' should be 'Hello World'");
}, "text-transform: capitalize test for 'hello world'");
test(function () {
var div = document.getElementById("div2");
assert_equals(div.innerText, "Foo-Bar",
"innerText for 'foo-bar' should be 'Foo-Bar'");
}, "text-transform: capitalize test for 'foo-bar'");
test(function () {
var div = document.getElementById("div3");
assert_equals(div.innerText, "John's Apple",
"innerText for \"john's apple\" should be \"John's Apple\"");
}, "text-transform: capitalize test for \"john's apple\"");
// Test for nested elements: the text inside the span should also be affected.
test(function () {
var div = document.getElementById("div4");
assert_equals(div.innerText, "Hello World",
"innerText for nested 'hello <span>world</span>' should be 'Hello World'");
}, "text-transform: capitalize test for nested elements");
// Test for underscore
test(function () {
var div = document.getElementById("div5");
assert_equals(div.innerText.trim(), "Foo_bar",
"innerText for 'foo_bar' should be 'Foo_bar'");
}, "text-transform: capitalize test for underscore");
test(function () {
var div = document.getElementById("span1");
assert_equals(div.innerText, "b",
"innerText for span in 'a<span style='text-transform: capitalize;'>b</span>c' should be 'b'");
}, "text-transform: capitalize test for not starting at word boundary");
</script>
</body>
</html>