Commit 4cb84b57 authored by Fergal Daly's avatar Fergal Daly Committed by Commit Bot

[css, dom] Support partmap="foo: bar" syntax.

This is the new syntax agreed for partmap.

Bug: 805271
Change-Id: I5e8e34551167b4a56706980dae8054e4b7a6fba2
Reviewed-on: https://chromium-review.googlesource.com/c/1278566
Commit-Queue: Fergal Daly <fergal@chromium.org>
Reviewed-by: default avatarHayato Ito <hayato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599142}
parent afc97ac0
......@@ -17,9 +17,9 @@
<span id="green_part" part="partp">This text</span>
</template>
<script>installCustomElement("custom-element-middle", "custom-element-middle-template");</script>
<template id="custom-element-middle-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded1"></custom-element-inner></template>
<template id="custom-element-middle-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded1"></custom-element-inner></template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"><custom-element-middle id="c-e-middle" partmap="part-forwarded1 part-forwarded2"></custom-element-middle></template>
<template id="custom-element-outer-template"><custom-element-middle id="c-e-middle" partmap="part-forwarded1: part-forwarded2"></custom-element-middle></template>
The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript">
......
......@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span>
</template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript">
......
......@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span>
</template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript">
......
......@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span>
</template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
The following text should be green:
<div id="elem"><custom-element-outer id="c-e-outer"></custom-element-outer></div>
<script type="text/javascript">
......
......@@ -19,7 +19,7 @@
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template">
<style>#c-e-inner::part(partp) { color: red; }</style>
<custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner>
<custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner>
</template>
The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer>
......
......@@ -17,7 +17,7 @@
<span id="green_part" part="partp">This text</span>
</template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
<template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript">
......
......@@ -394,8 +394,8 @@ TEST_F(ElementTest, PartAttribute) {
TEST_F(ElementTest, PartmapAttribute) {
Document& document = GetDocument();
SetBodyContent(R"HTML(
<span id='has_one_mapping' partmap='partname1 partname2'></span>
<span id='has_two_mappings' partmap='partname1 partname2, partname3 partname4'></span>
<span id='has_one_mapping' partmap='partname1: partname2'></span>
<span id='has_two_mappings' partmap='partname1: partname2, partname3: partname4'></span>
<span id='has_no_mapping'></span>
)HTML");
......@@ -432,7 +432,7 @@ TEST_F(ElementTest, PartmapAttribute) {
EXPECT_FALSE(has_no_mapping->PartNamesMap());
// Now update the attribute value and make sure it's reflected.
has_no_mapping->setAttribute("partmap", "partname1 partname2");
has_no_mapping->setAttribute("partmap", "partname1: partname2");
const NamesMap* part_names_map = has_no_mapping->PartNamesMap();
ASSERT_TRUE(part_names_map);
ASSERT_EQ(1UL, part_names_map->size());
......
......@@ -47,24 +47,32 @@ void NamesMap::Add(const AtomicString& key, const AtomicString& value) {
// The states that can occur while parsing the part map and their transitions.
// A "+" indicates that this transition should consume the current character.
// A "*" indicates that this is invalid input, usually the decision here is
// to just do our best and recover gracefully.
enum State {
kPreKey, // Searching for the start of a key:
// space, comma -> kPreKey+
// space, comma, colon* -> kPreKey+
// else -> kKey
kKey, // Searching for the end of a key:
// space, comma -> kPreValue
// comma -> kPreKey+
// colon -> kPreValue+
// space -> kPostKey+
// else -> kKey+
kPostKey, // Searching for a delimiter:
// comma -> kPreKey+
// colon -> kPreValue+
// space, else* -> kPostKey+
kPreValue, // Searching for the start of a value:
// space -> kPreValue+
// comma -> kPreKey+
// colon*, space -> kPreValue+
// else -> kValue+
kValue, // Searching for the end of a value:
// comma -> kPreKey+
// space -> kPostValue+
// colon*, space -> kPostValue+
// else -> kValue+
kPostValue, // Searching for the comma after the value:
// comma -> kPreKey+
// else -> kPostValue+
// colon*, else -> kPostValue+
};
template <typename CharacterType>
......@@ -80,24 +88,55 @@ void NamesMap::Set(const AtomicString& source,
State state = kPreKey;
AtomicString key;
while (cur < length) {
// Almost all cases break, ensuring that some input is consumed and we avoid
// an infinite loop. For the few transitions which should happen without
// consuming input we fall through into the new state's case. This makes it
// easy to see the cases which don't consume input and check that they lead
// to a case which does.
//
// The only state which should set a value for key is kKey, as we leave the
// state.
switch (state) {
case kPreKey:
// Skip any number of spaces and commas. When we find something else, it
// is the start of a key.
if (!IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
start = cur;
state = kKey;
// Skip any number of spaces, commas and colons. When we find something
// else, it is the start of a key.
if ((IsHTMLSpaceOrComma<CharacterType>(characters[cur]) ||
IsColon<CharacterType>(characters[cur]))) {
break;
}
break;
start = cur;
state = kKey;
FALLTHROUGH;
case kKey:
// At a space or comma, we have found the end of the key.
if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
// At a comma this was a key without a value, the implicit value is the
// same as the key.
if (IsComma<CharacterType>(characters[cur])) {
key = AtomicString(characters + start, cur - start);
Add(key, key);
state = kPreKey;
// At a colon, we have found the end of the key and we expect a value.
} else if (IsColon<CharacterType>(characters[cur])) {
key = AtomicString(characters + start, cur - start);
state = kPreValue;
} else {
break;
// At a space, we have found the end of the key.
} else if (IsHTMLSpace<CharacterType>(characters[cur])) {
key = AtomicString(characters + start, cur - start);
state = kPostKey;
}
FALLTHROUGH;
break;
case kPostKey:
// At a comma this was a key without a value, the implicit value is the
// same as the key.
if (IsComma<CharacterType>(characters[cur])) {
Add(key, key);
state = kPreKey;
// At a colon this was a key with a value, we expect a value.
} else if (IsColon<CharacterType>(characters[cur])) {
state = kPreValue;
}
// Spaces should be consumed. We consume other characters too
// although the input is invalid
break;
case kPreValue:
// At a comma this was a key without a value, the implicit value is the
// same as the key.
......@@ -106,20 +145,26 @@ void NamesMap::Set(const AtomicString& source,
state = kPreKey;
// If we reach a non-space character, we have found the start of the
// value.
} else if (IsNotHTMLSpace<CharacterType>(characters[cur])) {
} else if (IsColon<CharacterType>(characters[cur]) ||
IsHTMLSpace<CharacterType>(characters[cur])) {
break;
} else {
start = cur;
state = kValue;
}
break;
case kValue:
// At a comma or space, we have found the end of the value.
if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
// At a comma, we have found the end of the value and expect
// the next key.
if (IsComma<CharacterType>(characters[cur])) {
Add(key, AtomicString(characters + start, cur - start));
state = kPreKey;
// At a space or colon, we have found the end of the value,
// although a colon is invalid here.
} else if (IsHTMLSpace<CharacterType>(characters[cur]) ||
IsColon<CharacterType>(characters[cur])) {
Add(key, AtomicString(characters + start, cur - start));
if (IsComma<CharacterType>(characters[cur])) {
state = kPreKey;
} else {
state = kPostValue;
}
state = kPostValue;
}
break;
case kPostValue:
......@@ -141,6 +186,7 @@ void NamesMap::Set(const AtomicString& source,
// The string ends with a key.
key = AtomicString(characters + start, cur - start);
FALLTHROUGH;
case kPostKey:
case kPreValue:
// The string ends after a key but with nothing else useful.
Add(key, key);
......
......@@ -29,26 +29,38 @@ TEST(NamesMapTest, Set) {
Vector<std::pair<String, ExpectedMap>> test_cases({
// Various valid values.
{"foo", {{"foo", "foo"}}},
{"foo bar", {{"foo", "bar"}}},
{"foo bar, foo buz", {{"foo", "bar buz"}}},
{"foo bar, buz", {{"foo", "bar"}, {"buz", "buz"}}},
{"foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo, buz bar", {{"foo", "foo"}, {"buz", "bar"}}},
{"foo: bar", {{"foo", "bar"}}},
{"foo : bar", {{"foo", "bar"}}},
{"foo: bar, foo: buz", {{"foo", "bar buz"}}},
{"foo: bar, buz", {{"foo", "bar"}, {"buz", "buz"}}},
{"foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo, buz: bar", {{"foo", "foo"}, {"buz", "bar"}}},
// This is an error but qux should be ignored.
{"foo bar qux, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar qux", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar qux, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar qux", {{"foo", "bar"}, {"buz", "bar"}}},
// This is an error but the extra comma should be ignored.
{",foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar,, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar,", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar,,", {{"foo", "bar"}, {"buz", "bar"}}},
// This is an error but the extra commas and colons should be ignored.
{"foo:", {{"foo", "foo"}}},
{"foo:,", {{"foo", "foo"}}},
{"foo :", {{"foo", "foo"}}},
{"foo :,", {{"foo", "foo"}}},
{"foo: bar, buz:", {{"foo", "bar"}, {"buz", "buz"}}},
{"foo: bar, buz :", {{"foo", "bar"}, {"buz", "buz"}}},
{",foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar,, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar,", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar,,", {{"foo", "bar"}, {"buz", "bar"}}},
{":foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar:, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: :bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar:", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar::", {{"foo", "bar"}, {"buz", "bar"}}},
// Spaces in odd places.
{" foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar ", {{"foo", "bar"}, {"buz", "bar"}}},
{" foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo: bar, buz: bar ", {{"foo", "bar"}, {"buz", "bar"}}},
});
NamesMap map;
......
......@@ -102,6 +102,11 @@ inline bool IsComma(CharType character) {
return character == ',';
}
template <typename CharType>
inline bool IsColon(CharType character) {
return character == ':';
}
template <typename CharType>
inline bool IsHTMLSpaceOrComma(CharType character) {
return IsComma(character) || IsHTMLSpace(character);
......
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