Commit 502e56bf authored by Oriol Brufau's avatar Oriol Brufau Committed by Commit Bot

[css-logical] Implement flow-relative values for 'float', 'clear' and 'resize' properties

They are implemented behind the CSSLogical runtime flag.

Intent to Implement:
https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/48OwfwZrbvI/A1XZFGkzAwAJ

Spec: https://drafts.csswg.org/css-logical/#directional-keywords

BUG=850004

TEST=external/wpt/css/css-logical/logical-values-float-clear.html
TEST=external/wpt/css/css-logical/logical-values-resize.html

The tests still have some failures because sideways writing modes have
not been implemented yet (http://crbug.com/680331).

Change-Id: Ieede2703368a44f3ce9996e917857226795ebaea
Reviewed-on: https://chromium-review.googlesource.com/1163667Reviewed-by: default avatarAnders Ruud <andruud@chromium.org>
Reviewed-by: default avatarYoav Weiss <yoav@yoav.ws>
Commit-Queue: Oriol Brufau <obrufau@igalia.com>
Cr-Commit-Position: refs/heads/master@{#581904}
parent 2abccfab
......@@ -9,7 +9,7 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("border-*-color", {type: "color"}));
</script>
......@@ -9,7 +9,7 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("border-*", {type: ["length", "border-style", "color"]}));
</script>
......@@ -9,7 +9,7 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("border-*-style", {type: "border-style"}));
</script>
......@@ -9,8 +9,8 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("border-*-width", {
type: "length",
prerequisites: {"border-*-style": "solid"},
......
......@@ -9,8 +9,8 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("inset-*", {
type: "length",
prerequisites: {"position": "relative"},
......
......@@ -9,7 +9,7 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("margin-*", {type: "length"}));
</script>
......@@ -9,7 +9,7 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
runTests(createBoxPropertyGroup("padding-*", {type: "length"}));
</script>
......@@ -9,8 +9,8 @@
<div id="log"></div>
<script src="./resources/test-box-properties.js"></script>
<script>
<script type="module">
import {runTests, createSizingPropertyGroup} from "./resources/test-box-properties.js";
runTests(createSizingPropertyGroup(""));
runTests(createSizingPropertyGroup("max-"));
runTests(createSizingPropertyGroup("min-"));
......
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Logical Values: Flow-Relative Values for the 'float' and 'clear' Properties</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
<link rel="help" href="https://drafts.csswg.org/css-logical/#float-clear">
<meta name="assert" content="This test checks the flow-relative values for 'float' and 'clear' in different writing modes." />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script type="module">
import {runTests} from "./resources/test-logical-values.js";
const values = ["inline-start", "inline-end"];
runTests("clear", values);
runTests("float", values);
</script>
This is a testharness.js-based test.
PASS Test that 'resize: block' is supported.
PASS Test 'resize: block' with 'writing-mode: horizontal-tb; direction: ltr; '.
PASS Test 'resize: block' with 'writing-mode: horizontal-tb; direction: rtl; '.
PASS Test 'resize: block' with 'writing-mode: vertical-rl; direction: rtl; '.
FAIL Test 'resize: block' with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: computed value, resize expected "horizontal" but got "vertical"
PASS Test 'resize: block' with 'writing-mode: vertical-rl; direction: ltr; '.
FAIL Test 'resize: block' with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: computed value, resize expected "horizontal" but got "vertical"
PASS Test 'resize: block' with 'writing-mode: vertical-lr; direction: rtl; '.
FAIL Test 'resize: block' with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: computed value, resize expected "horizontal" but got "vertical"
PASS Test 'resize: block' with 'writing-mode: vertical-lr; direction: ltr; '.
FAIL Test 'resize: block' with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: computed value, resize expected "horizontal" but got "vertical"
PASS Test that 'resize: inline' is supported.
PASS Test 'resize: inline' with 'writing-mode: horizontal-tb; direction: ltr; '.
PASS Test 'resize: inline' with 'writing-mode: horizontal-tb; direction: rtl; '.
PASS Test 'resize: inline' with 'writing-mode: vertical-rl; direction: rtl; '.
FAIL Test 'resize: inline' with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: computed value, resize expected "vertical" but got "horizontal"
PASS Test 'resize: inline' with 'writing-mode: vertical-rl; direction: ltr; '.
FAIL Test 'resize: inline' with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: computed value, resize expected "vertical" but got "horizontal"
PASS Test 'resize: inline' with 'writing-mode: vertical-lr; direction: rtl; '.
FAIL Test 'resize: inline' with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: computed value, resize expected "vertical" but got "horizontal"
PASS Test 'resize: inline' with 'writing-mode: vertical-lr; direction: ltr; '.
FAIL Test 'resize: inline' with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: computed value, resize expected "vertical" but got "horizontal"
Harness: the test ran to completion.
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Logical Values: Flow-Relative Values for the 'resize' Property</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
<link rel="help" href="https://drafts.csswg.org/css-logical/#resize">
<meta name="assert" content="This test checks the flow-relative values for 'resize' in different writing modes." />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script type="module">
import {runTests} from "./resources/test-logical-values.js";
runTests("resize", ["block", "inline"]);
</script>
import {
testElement,
writingModes,
testCSSValues,
testComputedValues,
makeDeclaration
} from "./test-shared.js";
/**
* Tests flow-relative values for a CSS property in different writing modes.
*
* @param {string} property
* The CSS property to be tested.
* @param {string[]} values
* An array with the flow-relative values to be tested.
*/
export function runTests(property, values) {
for (const value of values) {
test(function() {
const {style} = testElement;
style.cssText = "";
style.setProperty(property, value);
testCSSValues("logical values in inline style", style, [[property, value]]);
}, `Test that '${property}: ${value}' is supported.`);
const camelCase = value.replace(/-(.)/g, (match, $1) => $1.toUpperCase());
for (const writingMode of writingModes) {
for (const style of writingMode.styles) {
const writingModeDecl = makeDeclaration(style);
test(function() {
const physicalSide = writingMode[camelCase];
let expected;
if (physicalSide === writingMode.lineLeft) {
expected = "left";
} else if (physicalSide === writingMode.lineRight) {
expected = "right";
} else {
expected = physicalSide;
}
testComputedValues(`computed value`,
`.test { ${writingModeDecl} }`,
[[property, expected]]);
}, `Test '${property}: ${value}' with '${writingModeDecl}'.`);
}
}
}
}
const sheet = document.head.appendChild(document.createElement("style"));
// Specify size for outer <div> to avoid unconstrained-size warnings
// when writing-mode of the inner test <div> is vertical-*
const wrapper = document.body.appendChild(document.createElement("div"));
wrapper.style.cssText = "width:100px; height: 100px;";
export const testElement = wrapper.appendChild(document.createElement("div"));
testElement.id = testElement.className = "test";
// Six unique overall writing modes for property-mapping purposes.
export const writingModes = [
{
styles: [
{"writing-mode": "horizontal-tb", "direction": "ltr"},
],
blockStart: "top", blockEnd: "bottom", inlineStart: "left", inlineEnd: "right",
over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
block: "vertical", inline: "horizontal" },
{
styles: [
{"writing-mode": "horizontal-tb", "direction": "rtl"},
],
blockStart: "top", blockEnd: "bottom", inlineStart: "right", inlineEnd: "left",
over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
block: "vertical", inline: "horizontal" },
{
styles: [
{"writing-mode": "vertical-rl", "direction": "rtl"},
{"writing-mode": "sideways-rl", "direction": "rtl"},
],
blockStart: "right", blockEnd: "left", inlineStart: "bottom", inlineEnd: "top",
over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
block: "horizontal", inline: "vertical" },
{
styles: [
{"writing-mode": "vertical-rl", "direction": "ltr"},
{"writing-mode": "sideways-rl", "direction": "ltr"},
],
blockStart: "right", blockEnd: "left", inlineStart: "top", inlineEnd: "bottom",
over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
block: "horizontal", inline: "vertical" },
{
styles: [
{"writing-mode": "vertical-lr", "direction": "rtl"},
],
blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top",
over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
block: "horizontal", inline: "vertical" },
{
styles: [
{"writing-mode": "sideways-lr", "direction": "ltr"},
],
blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top",
over: "left", under: "right", lineLeft: "bottom", lineRight: "top",
block: "horizontal", inline: "vertical" },
{
styles: [
{"writing-mode": "vertical-lr", "direction": "ltr"},
],
blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom",
over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
block: "horizontal", inline: "vertical" },
{
styles: [
{"writing-mode": "sideways-lr", "direction": "rtl"},
],
blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom",
over: "left", under: "right", lineLeft: "bottom", lineRight: "top",
block: "horizontal", inline: "vertical" },
];
export function testCSSValues(testName, style, expectedValues) {
for (const [property, value] of expectedValues) {
assert_equals(style.getPropertyValue(property), value, `${testName}, ${property}`);
}
}
export function testComputedValues(testName, rules, expectedValues) {
sheet.textContent = rules;
const cs = getComputedStyle(testElement);
testCSSValues(testName, cs, expectedValues);
sheet.textContent = "";
}
export function makeDeclaration(object = {}, replacement = "*") {
let decl = "";
for (const [property, value] of Object.entries(object)) {
decl += `${property.replace("*", replacement)}: ${value}; `;
}
return decl;
}
......@@ -79,6 +79,30 @@ el.style.setProperty('text-underline-position', 'right under')
el.style.getPropertyValue('text-underline-position') is
getComputedStyle(el).getPropertyValue('text-underline-position') is auto
el.style.setProperty('clear', 'inline-start')
el.style.getPropertyValue('clear') is
getComputedStyle(el).getPropertyValue('clear') is none
el.style.setProperty('clear', 'inline-end')
el.style.getPropertyValue('clear') is
getComputedStyle(el).getPropertyValue('clear') is none
el.style.setProperty('float', 'inline-start')
el.style.getPropertyValue('float') is
getComputedStyle(el).getPropertyValue('float') is none
el.style.setProperty('float', 'inline-end')
el.style.getPropertyValue('float') is
getComputedStyle(el).getPropertyValue('float') is none
el.style.setProperty('resize', 'block')
el.style.getPropertyValue('resize') is
getComputedStyle(el).getPropertyValue('resize') is none
el.style.setProperty('resize', 'inline')
el.style.getPropertyValue('resize') is
getComputedStyle(el).getPropertyValue('resize') is none
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -79,6 +79,30 @@ el.style.setProperty('text-underline-position', 'right under')
el.style.getPropertyValue('text-underline-position') is under right
getComputedStyle(el).getPropertyValue('text-underline-position') is under right
el.style.setProperty('clear', 'inline-start')
el.style.getPropertyValue('clear') is inline-start
getComputedStyle(el).getPropertyValue('clear') is left
el.style.setProperty('clear', 'inline-end')
el.style.getPropertyValue('clear') is inline-end
getComputedStyle(el).getPropertyValue('clear') is right
el.style.setProperty('float', 'inline-start')
el.style.getPropertyValue('float') is inline-start
getComputedStyle(el).getPropertyValue('float') is left
el.style.setProperty('float', 'inline-end')
el.style.getPropertyValue('float') is inline-end
getComputedStyle(el).getPropertyValue('float') is right
el.style.setProperty('resize', 'block')
el.style.getPropertyValue('resize') is block
getComputedStyle(el).getPropertyValue('resize') is vertical
el.style.setProperty('resize', 'inline')
el.style.getPropertyValue('resize') is inline
getComputedStyle(el).getPropertyValue('resize') is horizontal
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -43,6 +43,13 @@ var properties = [
['text-underline-position', 'right'],
['text-underline-position', 'under left'],
['text-underline-position', 'right under'],
['clear', 'inline-start'],
['clear', 'inline-end'],
['float', 'inline-start'],
['float', 'inline-end'],
['resize', 'block'],
['resize', 'inline'],
];
properties.forEach(function(args) {
......
......@@ -1393,6 +1393,7 @@
keywords: ["none", "left", "right", "both"],
typedom_types: ["Keyword"],
default_value: "none",
style_builder_custom_functions: ["value"],
},
{
name: "clip",
......@@ -1691,6 +1692,7 @@
default_value: "none",
name_for_methods: "Floating",
type_name: "EFloat",
style_builder_custom_functions: ["value"],
},
{
name: "flood-color",
......
......@@ -265,6 +265,8 @@
"-webkit-auto",
"left",
"right",
"inline-start",
"inline-end",
"center",
"justify",
"-webkit-left",
......
......@@ -583,7 +583,10 @@ bool CSSParserFastPaths::IsValidKeywordPropertyAndValue(
return value_id == CSSValueTop || value_id == CSSValueBottom;
case CSSPropertyClear:
return value_id == CSSValueNone || value_id == CSSValueLeft ||
value_id == CSSValueRight || value_id == CSSValueBoth;
value_id == CSSValueRight || value_id == CSSValueBoth ||
(RuntimeEnabledFeatures::CSSLogicalEnabled() &&
(value_id == CSSValueInlineStart ||
value_id == CSSValueInlineEnd));
case CSSPropertyClipRule:
case CSSPropertyFillRule:
return value_id == CSSValueNonzero || value_id == CSSValueEvenodd;
......@@ -611,6 +614,9 @@ bool CSSParserFastPaths::IsValidKeywordPropertyAndValue(
return value_id == CSSValueShow || value_id == CSSValueHide;
case CSSPropertyFloat:
return value_id == CSSValueLeft || value_id == CSSValueRight ||
(RuntimeEnabledFeatures::CSSLogicalEnabled() &&
(value_id == CSSValueInlineStart ||
value_id == CSSValueInlineEnd)) ||
value_id == CSSValueNone;
case CSSPropertyImageRendering:
return value_id == CSSValueAuto ||
......@@ -668,6 +674,8 @@ bool CSSParserFastPaths::IsValidKeywordPropertyAndValue(
case CSSPropertyResize:
return value_id == CSSValueNone || value_id == CSSValueBoth ||
value_id == CSSValueHorizontal || value_id == CSSValueVertical ||
(RuntimeEnabledFeatures::CSSLogicalEnabled() &&
(value_id == CSSValueBlock || value_id == CSSValueInline)) ||
value_id == CSSValueAuto;
case CSSPropertyScrollBehavior:
DCHECK(RuntimeEnabledFeatures::CSSOMSmoothScrollEnabled());
......
......@@ -16,5 +16,27 @@ const CSSValue* Clear::CSSValueFromComputedStyleInternal(
return CSSIdentifierValue::Create(style.Clear());
}
void Clear::ApplyValue(StyleResolverState& state, const CSSValue& value) const {
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
EClear c;
CSSValueID id = identifier_value.GetValueID();
switch (id) {
case CSSValueInlineStart:
case CSSValueInlineEnd:
if ((id == CSSValueInlineStart) ==
(state.Style()->Direction() == TextDirection::kLtr)) {
c = EClear::kLeft;
} else {
c = EClear::kRight;
}
break;
default:
c = identifier_value.ConvertTo<EClear>();
break;
}
state.Style()->SetClear(c);
}
} // namespace CSSLonghand
} // namespace blink
......@@ -20,5 +20,27 @@ const CSSValue* Float::CSSValueFromComputedStyleInternal(
return CSSIdentifierValue::Create(style.Floating());
}
void Float::ApplyValue(StyleResolverState& state, const CSSValue& value) const {
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
EFloat f;
CSSValueID id = identifier_value.GetValueID();
switch (id) {
case CSSValueInlineStart:
case CSSValueInlineEnd:
if ((id == CSSValueInlineStart) ==
(state.Style()->Direction() == TextDirection::kLtr)) {
f = EFloat::kLeft;
} else {
f = EFloat::kRight;
}
break;
default:
f = identifier_value.ConvertTo<EFloat>();
break;
}
state.Style()->SetFloating(f);
}
} // namespace CSSLonghand
} // namespace blink
......@@ -25,14 +25,27 @@ void Resize::ApplyValue(StyleResolverState& state,
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
EResize r = EResize::kNone;
if (identifier_value.GetValueID() == CSSValueAuto) {
if (Settings* settings = state.GetDocument().GetSettings()) {
r = settings->GetTextAreasAreResizable() ? EResize::kBoth
: EResize::kNone;
}
UseCounter::Count(state.GetDocument(), WebFeature::kCSSResizeAuto);
} else {
r = identifier_value.ConvertTo<EResize>();
CSSValueID id = identifier_value.GetValueID();
switch (id) {
case CSSValueAuto:
if (Settings* settings = state.GetDocument().GetSettings()) {
r = settings->GetTextAreasAreResizable() ? EResize::kBoth
: EResize::kNone;
}
UseCounter::Count(state.GetDocument(), WebFeature::kCSSResizeAuto);
break;
case CSSValueBlock:
case CSSValueInline:
if ((id == CSSValueBlock) ==
IsHorizontalWritingMode(state.Style()->GetWritingMode())) {
r = EResize::kVertical;
} else {
r = EResize::kHorizontal;
}
break;
default:
r = identifier_value.ConvertTo<EResize>();
break;
}
state.Style()->SetResize(r);
}
......
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