Commit 140a71fd authored by rbpotter's avatar rbpotter Committed by Commit Bot

Web UI: Allow multiple templates in JS files when replacing expressions

Allow multiple HTML _template strings in JS files when running i18n
template replacements. This is necessary to correctly replace
expressions for bundled JS files generated from Polymer 3 pages, as the
bundled files will generally contain many element definitions, each
including HTML template string. This will be used when optimized
Web UI pages are migrated to use Polymer 3/JS modules (e.g.
chrome://extensions).

Bug: 1004967
Change-Id: I9b5990fc26daf272980ee8d4f5aac688aceffb27
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1834846
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703031}
parent 087a0cd2
...@@ -21,18 +21,18 @@ const char kHtmlTemplateStart[] = "_template: html`"; ...@@ -21,18 +21,18 @@ const char kHtmlTemplateStart[] = "_template: html`";
const size_t kHtmlTemplateStartSize = base::size(kHtmlTemplateStart) - 1; const size_t kHtmlTemplateStartSize = base::size(kHtmlTemplateStart) - 1;
// Currently only legacy _template: html`...`, syntax is supported. // Currently only legacy _template: html`...`, syntax is supported.
enum HtmlTemplateType { NONE = 0, LEGACY = 1 }; enum HtmlTemplateType { INVALID = 0, NONE = 1, LEGACY = 2 };
struct TemplatePosition { struct TemplatePosition {
HtmlTemplateType type; HtmlTemplateType type;
base::StringPiece::size_type position; base::StringPiece::size_type position;
}; };
TemplatePosition FindHtmlTemplateStart(const base::StringPiece& source) { struct HtmlTemplate {
base::StringPiece::size_type found = source.find(kHtmlTemplateStart); base::StringPiece::size_type start;
HtmlTemplateType type = found == base::StringPiece::npos ? NONE : LEGACY; base::StringPiece::size_type length;
return {type, found + kHtmlTemplateStartSize}; HtmlTemplateType type;
} };
TemplatePosition FindHtmlTemplateEnd(const base::StringPiece& source) { TemplatePosition FindHtmlTemplateEnd(const base::StringPiece& source) {
enum State { OPEN, IN_ESCAPE, IN_TICK }; enum State { OPEN, IN_ESCAPE, IN_TICK };
...@@ -62,6 +62,36 @@ TemplatePosition FindHtmlTemplateEnd(const base::StringPiece& source) { ...@@ -62,6 +62,36 @@ TemplatePosition FindHtmlTemplateEnd(const base::StringPiece& source) {
return {NONE, base::StringPiece::npos}; return {NONE, base::StringPiece::npos};
} }
HtmlTemplate FindHtmlTemplate(const base::StringPiece& source) {
HtmlTemplate out;
base::StringPiece::size_type found = source.find(kHtmlTemplateStart);
// No template found, return early.
if (found == base::StringPiece::npos) {
out.type = NONE;
return out;
}
out.start = found + kHtmlTemplateStartSize;
TemplatePosition end = FindHtmlTemplateEnd(source.substr(out.start));
// Template is not terminated.
if (end.type == NONE) {
out.type = INVALID;
return out;
}
out.length = end.position;
// Check for a nested template
if (source.substr(out.start, out.length).find(kHtmlTemplateStart) !=
base::StringPiece::npos) {
out.type = INVALID;
return out;
}
out.type = LEGACY;
return out;
}
// Escape quotes and backslashes ('"\). // Escape quotes and backslashes ('"\).
std::string PolymerParameterEscape(const std::string& in_string) { std::string PolymerParameterEscape(const std::string& in_string) {
std::string out; std::string out;
...@@ -200,30 +230,30 @@ void TemplateReplacementsFromDictionaryValue( ...@@ -200,30 +230,30 @@ void TemplateReplacementsFromDictionaryValue(
bool ReplaceTemplateExpressionsInJS(base::StringPiece source, bool ReplaceTemplateExpressionsInJS(base::StringPiece source,
const TemplateReplacements& replacements, const TemplateReplacements& replacements,
std::string* formatted) { std::string* formatted) {
// Replacement is only done in JS for the contents of the HTML _template CHECK(formatted->empty());
// string. base::StringPiece remaining = source;
TemplatePosition start_result = FindHtmlTemplateStart(source); while (true) {
if (start_result.type == NONE) { // Replacement is only done in JS for the contents of HTML _template
*formatted = source.as_string(); // strings.
return true; HtmlTemplate current_template = FindHtmlTemplate(remaining);
}
// Only one template allowed per file. // If there was an error finding a template, return false.
TemplatePosition second_start_result = if (current_template.type == INVALID)
FindHtmlTemplateStart(source.substr(start_result.position));
if (second_start_result.type != NONE)
return false; return false;
TemplatePosition end_result = // If there are no more templates, copy the remaining JS to the output and
FindHtmlTemplateEnd(source.substr(start_result.position)); // return true.
if (current_template.type == NONE) {
formatted->append(remaining.as_string());
return true;
}
// Template must be properly terminated. // Copy the JS before the template to the output.
if (start_result.type != end_result.type) formatted->append(remaining.substr(0, current_template.start).as_string());
return false;
// Retrieve the HTML portion of the source. // Retrieve the HTML portion of the source.
base::StringPiece html_template = base::StringPiece html_template =
source.substr(start_result.position, end_result.position); remaining.substr(current_template.start, current_template.length);
// Perform replacements with JS escaping. // Perform replacements with JS escaping.
std::string formatted_html; std::string formatted_html;
...@@ -232,10 +262,13 @@ bool ReplaceTemplateExpressionsInJS(base::StringPiece source, ...@@ -232,10 +262,13 @@ bool ReplaceTemplateExpressionsInJS(base::StringPiece source,
return false; return false;
} }
// Re-assemble the JS file. // Append the formatted HTML template.
*formatted = formatted->append(formatted_html);
source.substr(0, start_result.position).as_string() + formatted_html +
source.substr(start_result.position + end_result.position).as_string(); // Increment to the end of the current template.
remaining =
remaining.substr(current_template.start + current_template.length);
}
return true; return true;
} }
......
...@@ -275,17 +275,6 @@ TEST(TemplateExpressionsTest, JSReplacementsError) { ...@@ -275,17 +275,6 @@ TEST(TemplateExpressionsTest, JSReplacementsError) {
// All these cases should fail. // All these cases should fail.
const TestCase kTestCases[] = { const TestCase kTestCases[] = {
// 2 HTML template strings are not allowed.
{"Polymer({\n"
" _template: html`\n"
" <span>Hello</span>\n"
" `,\n"
" _template: html`\n"
" <div>World</div>\n"
" `,\n"
" is: 'foo-element',\n"
"});",
""},
// Nested templates not allowed. // Nested templates not allowed.
{"Polymer({\n" {"Polymer({\n"
" _template: html`\n" " _template: html`\n"
...@@ -349,4 +338,54 @@ TEST(TemplateExpressionsTest, JSReplacementsError) { ...@@ -349,4 +338,54 @@ TEST(TemplateExpressionsTest, JSReplacementsError) {
} }
} }
TEST(TemplateExpressionsTest, JSMultipleTemplates) {
TemplateReplacements substitutions;
substitutions["test"] = "word";
substitutions["5"] = "number";
const TestCase kTestCases[] = {
// Only the second template has substitutions
{"Polymer({\n"
" _template: html`<div>Hello</div>`,\n"
" is: 'foo-element',\n"
"});"
"Polymer({\n"
" _template: html`<div>$i18n{5}$i18n{test}</div>`,\n"
" is: 'bar-element',\n"
"});",
"Polymer({\n"
" _template: html`<div>Hello</div>`,\n"
" is: 'foo-element',\n"
"});"
"Polymer({\n"
" _template: html`<div>numberword</div>`,\n"
" is: 'bar-element',\n"
"});"},
// 2 templates, both with substitutions.
{"Polymer({\n"
" _template: html`<div>$i18n{test}</div>`,\n"
" is: 'foo-element',\n"
"});"
"Polymer({\n"
" _template: html`<div>$i18n{5}</div>`,\n"
" is: 'bar-element',\n"
"});",
"Polymer({\n"
" _template: html`<div>word</div>`,\n"
" is: 'foo-element',\n"
"});"
"Polymer({\n"
" _template: html`<div>number</div>`,\n"
" is: 'bar-element',\n"
"});"}};
std::string formatted;
for (const TestCase test_case : kTestCases) {
ASSERT_TRUE(ReplaceTemplateExpressionsInJS(test_case.js_in, substitutions,
&formatted));
EXPECT_EQ(test_case.expected_out, formatted);
formatted.clear();
}
}
} // namespace ui } // namespace ui
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