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 @@ ...@@ -17,9 +17,9 @@
<span id="green_part" part="partp">This text</span> <span id="green_part" part="partp">This text</span>
</template> </template>
<script>installCustomElement("custom-element-middle", "custom-element-middle-template");</script> <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> <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: The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer> <custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span> <span id="part" part="partp">This text</span>
</template> </template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script> <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: The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer> <custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span> <span id="part" part="partp">This text</span>
</template> </template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script> <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: The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer> <custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<span id="part" part="partp">This text</span> <span id="part" part="partp">This text</span>
</template> </template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script> <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: The following text should be green:
<div id="elem"><custom-element-outer id="c-e-outer"></custom-element-outer></div> <div id="elem"><custom-element-outer id="c-e-outer"></custom-element-outer></div>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script> <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
<template id="custom-element-outer-template"> <template id="custom-element-outer-template">
<style>#c-e-inner::part(partp) { color: red; }</style> <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> </template>
The following text should be green: The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer> <custom-element-outer id="c-e-outer"></custom-element-outer>
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<span id="green_part" part="partp">This text</span> <span id="green_part" part="partp">This text</span>
</template> </template>
<script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script> <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: The following text should be green:
<custom-element-outer id="c-e-outer"></custom-element-outer> <custom-element-outer id="c-e-outer"></custom-element-outer>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -394,8 +394,8 @@ TEST_F(ElementTest, PartAttribute) { ...@@ -394,8 +394,8 @@ TEST_F(ElementTest, PartAttribute) {
TEST_F(ElementTest, PartmapAttribute) { TEST_F(ElementTest, PartmapAttribute) {
Document& document = GetDocument(); Document& document = GetDocument();
SetBodyContent(R"HTML( SetBodyContent(R"HTML(
<span id='has_one_mapping' partmap='partname1 partname2'></span> <span id='has_one_mapping' partmap='partname1: partname2'></span>
<span id='has_two_mappings' partmap='partname1 partname2, partname3 partname4'></span> <span id='has_two_mappings' partmap='partname1: partname2, partname3: partname4'></span>
<span id='has_no_mapping'></span> <span id='has_no_mapping'></span>
)HTML"); )HTML");
...@@ -432,7 +432,7 @@ TEST_F(ElementTest, PartmapAttribute) { ...@@ -432,7 +432,7 @@ TEST_F(ElementTest, PartmapAttribute) {
EXPECT_FALSE(has_no_mapping->PartNamesMap()); EXPECT_FALSE(has_no_mapping->PartNamesMap());
// Now update the attribute value and make sure it's reflected. // 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(); const NamesMap* part_names_map = has_no_mapping->PartNamesMap();
ASSERT_TRUE(part_names_map); ASSERT_TRUE(part_names_map);
ASSERT_EQ(1UL, part_names_map->size()); ASSERT_EQ(1UL, part_names_map->size());
......
...@@ -47,24 +47,32 @@ void NamesMap::Add(const AtomicString& key, const AtomicString& value) { ...@@ -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. // 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 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 { enum State {
kPreKey, // Searching for the start of a key: kPreKey, // Searching for the start of a key:
// space, comma -> kPreKey+ // space, comma, colon* -> kPreKey+
// else -> kKey // else -> kKey
kKey, // Searching for the end of a key: kKey, // Searching for the end of a key:
// space, comma -> kPreValue // comma -> kPreKey+
// colon -> kPreValue+
// space -> kPostKey+
// else -> kKey+ // else -> kKey+
kPostKey, // Searching for a delimiter:
// comma -> kPreKey+
// colon -> kPreValue+
// space, else* -> kPostKey+
kPreValue, // Searching for the start of a value: kPreValue, // Searching for the start of a value:
// space -> kPreValue+
// comma -> kPreKey+ // comma -> kPreKey+
// colon*, space -> kPreValue+
// else -> kValue+ // else -> kValue+
kValue, // Searching for the end of a value: kValue, // Searching for the end of a value:
// comma -> kPreKey+ // comma -> kPreKey+
// space -> kPostValue+ // colon*, space -> kPostValue+
// else -> kValue+ // else -> kValue+
kPostValue, // Searching for the comma after the value: kPostValue, // Searching for the comma after the value:
// comma -> kPreKey+ // comma -> kPreKey+
// else -> kPostValue+ // colon*, else -> kPostValue+
}; };
template <typename CharacterType> template <typename CharacterType>
...@@ -80,24 +88,55 @@ void NamesMap::Set(const AtomicString& source, ...@@ -80,24 +88,55 @@ void NamesMap::Set(const AtomicString& source,
State state = kPreKey; State state = kPreKey;
AtomicString key; AtomicString key;
while (cur < length) { 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) { switch (state) {
case kPreKey: case kPreKey:
// Skip any number of spaces and commas. When we find something else, it // Skip any number of spaces, commas and colons. When we find something
// is the start of a key. // else, it is the start of a key.
if (!IsHTMLSpaceOrComma<CharacterType>(characters[cur])) { if ((IsHTMLSpaceOrComma<CharacterType>(characters[cur]) ||
IsColon<CharacterType>(characters[cur]))) {
break;
}
start = cur; start = cur;
state = kKey; state = kKey;
} FALLTHROUGH;
break;
case kKey: case kKey:
// At a space or comma, we have found the end of the key. // At a comma this was a key without a value, the implicit value is the
if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) { // 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); key = AtomicString(characters + start, cur - start);
state = kPreValue; state = kPreValue;
} else { // 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;
}
break; 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;
} }
FALLTHROUGH; // Spaces should be consumed. We consume other characters too
// although the input is invalid
break;
case kPreValue: case kPreValue:
// At a comma this was a key without a value, the implicit value is the // At a comma this was a key without a value, the implicit value is the
// same as the key. // same as the key.
...@@ -106,21 +145,27 @@ void NamesMap::Set(const AtomicString& source, ...@@ -106,21 +145,27 @@ void NamesMap::Set(const AtomicString& source,
state = kPreKey; state = kPreKey;
// If we reach a non-space character, we have found the start of the // If we reach a non-space character, we have found the start of the
// value. // value.
} else if (IsNotHTMLSpace<CharacterType>(characters[cur])) { } else if (IsColon<CharacterType>(characters[cur]) ||
IsHTMLSpace<CharacterType>(characters[cur])) {
break;
} else {
start = cur; start = cur;
state = kValue; state = kValue;
} }
break; break;
case kValue: case kValue:
// At a comma or space, we have found the end of the value. // At a comma, we have found the end of the value and expect
if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) { // the next key.
Add(key, AtomicString(characters + start, cur - start));
if (IsComma<CharacterType>(characters[cur])) { if (IsComma<CharacterType>(characters[cur])) {
Add(key, AtomicString(characters + start, cur - start));
state = kPreKey; state = kPreKey;
} else { // 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));
state = kPostValue; state = kPostValue;
} }
}
break; break;
case kPostValue: case kPostValue:
// At a comma, we start looking for the next key. // At a comma, we start looking for the next key.
...@@ -141,6 +186,7 @@ void NamesMap::Set(const AtomicString& source, ...@@ -141,6 +186,7 @@ void NamesMap::Set(const AtomicString& source,
// The string ends with a key. // The string ends with a key.
key = AtomicString(characters + start, cur - start); key = AtomicString(characters + start, cur - start);
FALLTHROUGH; FALLTHROUGH;
case kPostKey:
case kPreValue: case kPreValue:
// The string ends after a key but with nothing else useful. // The string ends after a key but with nothing else useful.
Add(key, key); Add(key, key);
......
...@@ -29,26 +29,38 @@ TEST(NamesMapTest, Set) { ...@@ -29,26 +29,38 @@ TEST(NamesMapTest, Set) {
Vector<std::pair<String, ExpectedMap>> test_cases({ Vector<std::pair<String, ExpectedMap>> test_cases({
// Various valid values. // Various valid values.
{"foo", {{"foo", "foo"}}}, {"foo", {{"foo", "foo"}}},
{"foo bar", {{"foo", "bar"}}}, {"foo: bar", {{"foo", "bar"}}},
{"foo bar, foo buz", {{"foo", "bar buz"}}}, {"foo : bar", {{"foo", "bar"}}},
{"foo bar, buz", {{"foo", "bar"}, {"buz", "buz"}}}, {"foo: bar, foo: buz", {{"foo", "bar buz"}}},
{"foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo: bar, buz", {{"foo", "bar"}, {"buz", "buz"}}},
{"foo, buz bar", {{"foo", "foo"}, {"buz", "bar"}}}, {"foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo, buz: bar", {{"foo", "foo"}, {"buz", "bar"}}},
// This is an error but qux should be ignored. // This is an error but qux should be ignored.
{"foo bar qux, buz bar", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo: bar qux, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
{"foo bar, buz bar qux", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo: bar, buz: bar qux", {{"foo", "bar"}, {"buz", "bar"}}},
// This is an error but the extra comma should be ignored. // This is an error but the extra commas and colons should be ignored.
{",foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo:", {{"foo", "foo"}}},
{"foo bar,, buz bar", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo:,", {{"foo", "foo"}}},
{"foo bar, buz bar,", {{"foo", "bar"}, {"buz", "bar"}}}, {"foo :", {{"foo", "foo"}}},
{"foo bar, buz bar,,", {{"foo", "bar"}, {"buz", "bar"}}}, {"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. // 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; NamesMap map;
......
...@@ -102,6 +102,11 @@ inline bool IsComma(CharType character) { ...@@ -102,6 +102,11 @@ inline bool IsComma(CharType character) {
return character == ','; return character == ',';
} }
template <typename CharType>
inline bool IsColon(CharType character) {
return character == ':';
}
template <typename CharType> template <typename CharType>
inline bool IsHTMLSpaceOrComma(CharType character) { inline bool IsHTMLSpaceOrComma(CharType character) {
return IsComma(character) || IsHTMLSpace(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