Commit b163dffb authored by dbeam's avatar dbeam Committed by Commit bot

Teach i18nTemplate.process() to handle <link rel=import> and <template>

This is required to support Polymerized web UI until we figure out a
more flicker-resistant solution (see: crbug.com/506009)

BUG=425626
R=arv@chromium.org
TBR=yoshiki@chromium.org

Review URL: https://codereview.chromium.org/1229573003

Cr-Commit-Position: refs/heads/master@{#337980}
parent 4d17f2da
......@@ -29,7 +29,7 @@
'../../../../../ui/webui/resources/js/cr/ui/touch_handler.js',
'../../../../../ui/webui/resources/js/cr/ui/tree.js',
'../../../../../ui/webui/resources/js/event_tracker.js',
'../../../../../ui/webui/resources/js/i18n_template_no_process.js',
'../../../../../ui/webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
'../../../../../ui/webui/resources/js/load_time_data.js',
'../../../../../ui/webui/resources/js/util.js',
'../../../../../chrome/browser/resources/bookmark_manager/js/bmm.js',
......
<!doctype html>
<html>
<head>
<title i18n-content="title"></title>
<link rel="import" href="data:text/html,
<link rel=import href='data:text/html,<div i18n-content=nested>'>">
</head>
<body i18n-values="type:type">
<span i18n-values=".innerHTML:content;.style.display:display">&lt;3</span>
<template>
<template>
<div i18n-content="nested"></div>
</template>
</template>
<script>
function setUpPage() {
loadTimeData.data = {
'content': "doesn't matter; you can't see me!",
'display': 'none',
'title': 'BUY NOW!',
'type': 'ectomorph',
'nested': 'real deep',
};
i18nTemplate.process(document, loadTimeData);
}
function testI18nProcess_Attributes() {
assertNotEqual(0, document.title.length);
assertTrue(document.body.hasAttribute('type'));
assertTrue(document.querySelector('span').textContent.length > 5);
assertEquals('none', document.querySelector('span').style.display);
}
function testI18nProcess_Imports() {
var outerImportDoc = document.querySelector('link').import;
console.log(outerImportDoc.documentElement.outerHTML);
var innerImportDoc = outerImportDoc.querySelector('link').import;
assertNotEqual(0, innerImportDoc.querySelector('div').textContent.length);
}
function testI18nProcess_Templates() {
var outerDocFrag = document.querySelector('template').content;
var innerDocFrag = outerDocFrag.querySelector('template').content;
assertNotEqual(0, innerDocFrag.querySelector('div').textContent.length);
}
</script>
</body>
</html>
......@@ -71,6 +71,12 @@ IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, EventTargetTest) {
RunTest(base::FilePath(FILE_PATH_LITERAL("event_target_test.html")));
}
IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, I18nProcessTest) {
AddLibrary(IDR_WEBUI_JS_LOAD_TIME_DATA);
AddLibrary(IDR_WEBUI_JS_I18N_TEMPLATE_NO_PROCESS);
RunTest(base::FilePath(FILE_PATH_LITERAL("i18n_process_test.html")));
}
IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, ListTest) {
AddLibrary(IDR_WEBUI_JS_CR);
AddLibrary(IDR_WEBUI_JS_CR_EVENT_TARGET);
......
......@@ -9,7 +9,7 @@
'depends': [
'<@(cws_widget_container)',
'../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate',
'../../../../ui/webui/resources/js/i18n_template_no_process.js',
'../../../../ui/webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
'../../../../ui/webui/resources/js/load_time_data.js',
],
'externs': [
......
......@@ -48,6 +48,10 @@
'target_name': 'passwords_private',
'includes': ['../externs_js.gypi'],
},
{
'target_name': 'pending_compiler_externs',
'includes': ['../externs_js.gypi'],
},
{
'target_name': 'search_engines_private',
'includes': ['../externs_js.gypi'],
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/** @externs */
/**
* @type {?Document}
* @see w3c_dom2.js
* @see http://www.w3.org/TR/html-imports/#interface-import
*/
HTMLLinkElement.prototype.import;
......@@ -12,7 +12,7 @@
'../../../../../ui/webui/resources/js/load_time_data.js',
'../../../../../ui/webui/resources/js/cr.js',
'../../../../../ui/webui/resources/js/util.js',
'../../../../../ui/webui/resources/js/i18n_template_no_process.js',
'../../../../../ui/webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
'../../../../../ui/webui/resources/js/event_tracker.js',
'../../../../../ui/webui/resources/js/cr/ui.js',
'../../../../../ui/webui/resources/js/cr/event_target.js',
......
......@@ -57,7 +57,7 @@
'../../../webui/resources/js/cr/ui/list_selection_controller.js',
'../../../webui/resources/js/cr/ui/list.js',
'../../../webui/resources/js/cr/ui/grid.js',
'../../../webui/resources/js/i18n_template_no_process.js',
'../../../webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
'../../file_manager/common/js/volume_manager_common.js',
'../../file_manager/common/js/lru_cache.js',
'../../file_manager/common/js/async_util.js',
......
......@@ -63,7 +63,7 @@
# Referenced in common/js/util.js.
# TODO(yawano): Remove dependency to cr/ui/dialogs from common/js/util.js.
'../../../webui/resources/js/cr/ui/dialogs.js',
'../../../webui/resources/js/i18n_template_no_process.js',
'../../../webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
'../../file_manager/common/js/async_util.js',
'../../file_manager/common/js/file_type.js',
'../../file_manager/common/js/util.js',
......
......@@ -46,6 +46,7 @@
'target_name': 'i18n_template_no_process',
'variables': {
'depends': ['compiled_resources.gyp:load_time_data'],
'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'],
},
'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'],
},
......@@ -53,6 +54,7 @@
'target_name': 'i18n_template',
'variables': {
'depends': ['compiled_resources.gyp:load_time_data'],
'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'],
},
'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'],
},
......
......@@ -42,12 +42,20 @@
},
{
'target_name': 'i18n_template_no_process',
'dependencies': ['load_time_data'],
'dependencies': [
'load_time_data',
'<(EXTERNS_GYP):pending_compiler_externs',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'i18n_template',
'dependencies': ['load_time_data'],
'dependencies': [
'load_time_data',
# Ideally, <include> would automatically import externs as well, but
# it current doesn't and that sounds hard. Let's just kill <include>.
'<(EXTERNS_GYP):pending_compiler_externs',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
],
......
......@@ -26,6 +26,7 @@
*/
var i18nTemplate = (function() {
'use strict';
/**
* This provides the handlers for the templating engine. The key is used as
* the attribute name and the value is the function that gets called for every
......@@ -111,21 +112,32 @@ var i18nTemplate = (function() {
};
var attributeNames = Object.keys(handlers);
// Chrome for iOS must use Apple's UIWebView, which (as of April 2015) does
// not have native shadow DOM support. If shadow DOM is supported (or
// polyfilled), search for i18n attributes using the /deep/ selector;
// otherwise, do not attempt to search within the shadow DOM.
var selector =
(window.document.body && window.document.body.createShadowRoot) ?
'html /deep/ [' + attributeNames.join('],[') + ']' :
'[' + attributeNames.join('],[') + ']';
var selector = '[' + attributeNames.join('],[') + ']';
/**
* Processes a DOM tree with the {@code dictionary} map.
* @param {Document|Element} root The root of the DOM tree to process.
* @param {Document|DocumentFragment|Element} root The root of the DOM tree to
* process.
* @param {LoadTimeData} dictionary The dictionary to draw from.
*/
function process(root, dictionary) {
var importLinks = root.querySelectorAll('link[rel=import]');
for (var i = 0; i < importLinks.length; ++i) {
var importLink = /** @type {!HTMLLinkElement} */(importLinks[i]);
if (!importLink.import) {
// Happens when a <link rel=import> is inside a <template>.
// TODO(dbeam): should we log an error if we detect that here?
continue;
}
process(importLink.import, dictionary);
}
var templates = root.querySelectorAll('template');
for (var i = 0; i < templates.length; ++i) {
var template = /** @type {HTMLTemplateElement} */(templates[i]);
process(template.content, dictionary);
}
var elements = root.querySelectorAll(selector);
for (var element, i = 0; element = elements[i]; i++) {
for (var j = 0; j < attributeNames.length; j++) {
......@@ -135,8 +147,9 @@ var i18nTemplate = (function() {
handlers[name](element, attribute, dictionary);
}
}
var doc = root instanceof Document ? root : root.ownerDocument;
if (doc)
if (doc && doc.documentElement)
doc.documentElement.classList.add('i18n-processed');
}
......
......@@ -150,6 +150,12 @@ function assertDeepEquals(expected, observed, opt_message) {
console.error('Failed to find test cases.');
cleanTestRun = false;
}
try {
if (window.setUpPage)
window.setUpPage();
} catch(err) {
cleanTestRun = false;
}
continueTesting();
}
......
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