Commit 5bdc2511 authored by Tibor Goldschwendt's avatar Tibor Goldschwendt Committed by Commit Bot

[webui][ntp] Support local images with ntp-img

That way we can assign both local and external URLs and ntp-img will
figure out whether to load the image directly or employ the
chrome://image source.

+ Use this new functionality for the middle slot promo.

Change-Id: I4855023ff5bdb946b14d2f2f2bf46f1bc29d2e7a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2404153Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Auto-Submit: Tibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806280}
parent d71ad353
......@@ -3,42 +3,62 @@
// found in the LICENSE file.
/**
* @fileoverview <ntp-img> is specialized <img> that lets you embed external
* images via its external-src attribute. Usage:
* @fileoverview <ntp-img> is a specialized <img> that facilitates embedding
* images into WebUIs via its auto-src attribute. <ntp-img> automatically
* determines if the image is local (e.g. data: or chrome://) or external (e.g.
* https://), and embeds the image directly or via the chrome://image data
* source accordingly. Usage:
*
* 1. In C++ register |SanitizedImageSource| for your WebUI.
*
* 2. In HTML instantiate
*
* <img is="ntp-img" external-src="https://foo.com/bar.png"></img>
* <img is="ntp-img" auto-src="https://foo.com/bar.png"></img>
*
* NOTE: Internally, <ntp-img> uses the chrome://image data source. This means
* the external image will be transcoded to PNG.
* NOTE: Since <ntp-img> may use the chrome://image data source some images may
* be transcoded to PNG.
*/
/** @type {string} */
const EXTERNAL_SRC = 'external-src';
const AUTO_SRC = 'auto-src';
export class ImgElement extends HTMLImageElement {
static get observedAttributes() {
return [EXTERNAL_SRC];
return [AUTO_SRC];
}
/** @override */
attributeChangedCallback(name, oldValue, newValue) {
if (name === EXTERNAL_SRC) {
this.src = newValue ? 'chrome://image?' + newValue : '';
if (name !== AUTO_SRC) {
return;
}
let url = null;
try {
url = new URL(newValue || '');
} catch (_) {
}
if (!url || url.protocol === 'chrome-untrusted:') {
// Loading chrome-untrusted:// directly kills the renderer process.
// Loading chrome-untrusted:// via the chrome://image data source
// results in a broken image.
this.removeAttribute('src');
} else if (url.protocol === 'data:' || url.protocol === 'chrome:') {
this.src = url.href;
} else {
this.src = 'chrome://image?' + url.href;
}
}
/** @param {string} src */
set externalSrc(src) {
this.setAttribute(EXTERNAL_SRC, src);
set autoSrc(src) {
this.setAttribute(AUTO_SRC, src);
}
/** @return {string} */
get externalSrc() {
return this.getAttribute(EXTERNAL_SRC);
get autoSrc() {
return this.getAttribute(AUTO_SRC);
}
}
......
......@@ -6,7 +6,9 @@ import 'chrome://resources/cr_elements/hidden_style_css.m.js';
import 'chrome://resources/cr_elements/shared_vars_css.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy} from './browser_proxy.js';
import {ImgElement} from './img.js';
import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
// Element that requests and renders the middle-slot promo. The element is
......@@ -37,10 +39,8 @@ class MiddleSlotPromoElement extends PolymerElement {
promo.middleSlotParts.forEach(({image, link, text}) => {
let el;
if (image) {
el = document.createElement('img');
el.src = image.imageUrl.url.startsWith('data:') ?
image.imageUrl.url :
`chrome://image?${image.imageUrl.url}`;
el = new ImgElement();
el.autoSrc = image.imageUrl.url;
if (image.target) {
const anchor = this.createAnchor_(image.target);
if (anchor) {
......
......@@ -16,7 +16,7 @@
<cr-grid id="tiles" columns="3">
<template id="tileList" is="dom-repeat" items="[[tiles]]">
<div class="tile-item" title="[[item.label]]">
<img is="ntp-img" external-src="[[item.imageUrl]]"></img>
<img is="ntp-img" auto-src="[[item.imageUrl]]"></img>
<span>[[item.value]]</span>
</div>
</template>
......
......@@ -16,23 +16,30 @@ suite('NewTabPageImgTest', () => {
document.body.appendChild(img);
});
test('setting externalSrc sets src', () => {
// Act.
img.externalSrc = 'foo.com/img.png';
// Assert.
assertEquals('foo.com/img.png', img.externalSrc);
assertEquals('foo.com/img.png', img.getAttribute('external-src'));
assertEquals('chrome://image/?foo.com/img.png', img.src);
});
test('setting external-src sets src', () => {
// Act.
img.setAttribute('external-src', 'foo.com/img.png');
// Assert.
assertEquals('foo.com/img.png', img.externalSrc);
assertEquals('foo.com/img.png', img.getAttribute('external-src'));
assertEquals('chrome://image/?foo.com/img.png', img.src);
[['https://foo.com/img.png', 'chrome://image/?https://foo.com/img.png'],
['chrome://foo/img.png', 'chrome://foo/img.png'],
['data:imge/png;base64,abc', 'data:imge/png;base64,abc'],
['', ''],
['chrome-untrusted://foo/img.png', ''],
].forEach(([autoSrc, src]) => {
test(`setting autoSrc to '${autoSrc}' sets src to '${src}'`, () => {
// Act.
img.autoSrc = autoSrc;
// Assert.
assertEquals(autoSrc, img.autoSrc);
assertEquals(autoSrc, img.getAttribute('auto-src'));
assertEquals(src, img.src);
});
test(`setting auto-src to '${autoSrc}' sets src to '${src}'`, () => {
// Act.
img.setAttribute('auto-src', autoSrc);
// Assert.
assertEquals(autoSrc, img.autoSrc);
assertEquals(autoSrc, img.getAttribute('auto-src'));
assertEquals(src, img.src);
});
});
});
......@@ -67,15 +67,13 @@ suite('NewTabPageMiddleSlotPromoTest', () => {
assertEquals(6, parts.length);
const [image, imageWithLink, imageWithCommand, text, link, command] = parts;
assertEquals('chrome://image/?https://image', image.src);
assertEquals('https://image', image.autoSrc);
assertEquals('https://link/', imageWithLink.href);
assertEquals(
'chrome://image/?https://image', imageWithLink.children[0].src);
assertEquals('https://image', imageWithLink.children[0].autoSrc);
assertEquals('', imageWithCommand.href);
assertEquals(
'chrome://image/?https://image', imageWithCommand.children[0].src);
assertEquals('https://image', imageWithCommand.children[0].autoSrc);
assertEquals('text', text.innerText);
assertEquals('red', text.style.color);
......
......@@ -28,6 +28,6 @@ suite('NewTabPageModulesDummyModuleTest', () => {
'4IP40Q18w6aDF4oS4WRnUj0MlCCKPK-vLHqSd4r-RfS6Jx' +
'gblG5WJuRYpkJkoTzLMS0qv3Sxhf9wdaKkn3vHnyy6oe7Ah' +
'5y0=w170-h85-p-k-no-nd-mv',
tiles[2].querySelector('img').externalSrc);
tiles[2].querySelector('img').autoSrc);
});
});
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