1 /* Copyright 2015 The Chromium Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file. */
5 // Single iframe for NTP tiles.
11 * The different types of events that are logged from the NTP. This enum is
12 * used to transfer information from the NTP JavaScript to the renderer and is
13 * not used as a UMA enum histogram's logged value.
14 * Note: Keep in sync with common/ntp_logging_events.h
19 // The suggestion is coming from the server. Unused here.
20 NTP_SERVER_SIDE_SUGGESTION: 0,
21 // The suggestion is coming from the client.
22 NTP_CLIENT_SIDE_SUGGESTION: 1,
23 // Indicates a tile was rendered, no matter if it's a thumbnail, a gray tile
24 // or an external tile.
26 // The tile uses a local thumbnail image.
27 NTP_THUMBNAIL_TILE: 3,
28 // Used when no thumbnail is specified and a gray tile with the domain is used
29 // as the main tile. Unused here.
31 // The visuals of that tile are handled externally by the page itself.
34 // There was an error in loading both the thumbnail image and the fallback
35 // (if it was provided), resulting in a gray tile.
36 NTP_THUMBNAIL_ERROR: 6,
37 // Used a gray tile with the domain as the fallback for a failed thumbnail.
39 NTP_GRAY_TILE_FALLBACK: 7,
40 // The visuals of that tile's fallback are handled externally. Unused here.
41 NTP_EXTERNAL_TILE_FALLBACK: 8,
42 // The user moused over an NTP tile.
48 * Total number of tiles to show at any time. If the host page doesn't send
49 * enough tiles, we fill them blank.
52 var NUMBER_OF_TILES = 8;
56 * The origin of this request.
59 var DOMAIN_ORIGIN = '{{ORIGIN}}';
63 * Counter for DOM elements that we are waiting to finish loading.
66 var loadedCounter = 1;
70 * DOM element containing the tiles we are going to present next.
71 * Works as a double-buffer that is shown when we receive a "show" postMessage.
78 * Log an event on the NTP.
79 * @param {number} eventType Event from LOG_TYPE.
81 var logEvent = function(eventType) {
82 chrome.embeddedSearch.newTabPage.logEvent(eventType);
87 * Down counts the DOM elements that we are waiting for the page to load.
88 * When we get to 0, we send a message to the parent window.
89 * This is usually used as an EventListener of onload/onerror.
91 var countLoad = function() {
93 if (loadedCounter <= 0) {
94 window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN);
101 * Handles postMessages coming from the host page to the iframe.
102 * We try to keep the logic here to a minimum and just dispatch to the relevant
105 var handlePostMessage = function(event) {
106 var cmd = event.data.cmd;
110 } else if (cmd == 'show') {
114 console.error('Unknown command: ' + event.data);
120 * Called when the host page has finished sending us tile information and
121 * we are ready to show the new tiles and drop the old ones.
123 var showTiles = function() {
124 // Store the tiles on the current closure.
127 // Create empty tiles until we have NUMBER_OF_TILES.
128 while (cur.childNodes.length < NUMBER_OF_TILES) {
132 var parent = document.querySelector('#most-visited');
134 // Mark old tile DIV for removal after the transition animation is done.
135 var old = parent.querySelector('#mv-tiles');
137 old.id = 'mv-tiles-old';
138 cur.addEventListener('webkitTransitionEnd', function(ev) {
139 if (ev.target === cur) {
140 parent.removeChild(old);
147 parent.appendChild(cur);
148 // We want the CSS transition to trigger, so need to add to the DOM before
149 // setting the style.
150 setTimeout(function() {
151 cur.style.opacity = 1.0;
154 // Make sure the tiles variable contain the next tileset we may use.
155 tiles = document.createElement('div');
160 * Called when the host page wants to add a suggestion tile.
161 * For Most Visited, it grabs the data from Chrome and pass on.
162 * For host page generated it just passes the data.
163 * @param {object} args Data for the tile to be rendered.
165 var addTile = function(args) {
167 var data = chrome.embeddedSearch.searchBox.getMostVisitedItemData(args.rid);
168 tiles.appendChild(renderTile(data));
169 logEvent(LOG_TYPE.NTP_CLIENT_SIDE_SUGGESTION);
171 tiles.appendChild(renderTile(null));
177 * Called when the user decided to add a tile to the blacklist.
178 * It sets of the animation for the blacklist and sends the blacklisted id
180 * @param {Element} tile DOM node of the tile we want to remove.
182 var blacklistTile = function(tile) {
183 tile.classList.add('blacklisted');
185 tile.addEventListener('webkitTransitionEnd', function() {
188 window.parent.postMessage({cmd: 'tileBlacklisted',
189 rid: Number(tile.getAttribute('data-rid'))},
196 * Renders a MostVisited tile to the DOM.
197 * @param {object} data Object containing rid, url, title, favicon, thumbnail.
198 * data is null if you want to construct an empty tile.
200 var renderTile = function(data) {
201 var tile = document.createElement('a');
204 tile.className = 'mv-empty-tile';
208 logEvent(LOG_TYPE.NTP_TILE);
210 tile.className = 'mv-tile';
211 tile.setAttribute('data-rid', data.rid);
212 tile.innerHTML = '<div class="mv-favicon"></div>' +
213 '<div class="mv-title"></div><div class="mv-thumb"></div>' +
214 '<div title="' + configData['removeThumbnailTooltip'] +
215 '" class="mv-x"></div>';
217 tile.href = data.url;
218 tile.title = data.title;
219 tile.addEventListener('keypress', function(ev) {
220 if (ev.keyCode == 127) { // DELETE
222 ev.stopPropagation();
226 // TODO(fserb): remove this or at least change to mouseenter.
227 tile.addEventListener('mouseover', function() {
228 logEvent(LOG_TYPE.NTP_MOUSEOVER);
231 var title = tile.querySelector('.mv-title');
232 title.innerText = data.title;
233 title.style.direction = data.direction || 'ltr';
235 var thumb = tile.querySelector('.mv-thumb');
236 if (data.thumbnailUrl) {
237 var img = document.createElement('img');
238 img.title = data.title;
239 img.src = data.thumbnailUrl;
241 img.addEventListener('load', countLoad);
242 img.addEventListener('error', countLoad);
243 img.addEventListener('error', function(ev) {
244 thumb.classList.add('failed-img');
245 thumb.removeChild(img);
246 logEvent(LOG_TYPE.NTP_THUMBNAIL_ERROR);
248 thumb.appendChild(img);
249 logEvent(LOG_TYPE.NTP_THUMBNAIL_TILE);
251 thumb.classList.add('failed-img');
254 var favicon = tile.querySelector('.mv-favicon');
255 if (data.faviconUrl) {
256 var fi = document.createElement('img');
257 fi.src = '../' + data.faviconUrl;
258 // Set the title to empty so screen readers won't say the image name.
261 fi.addEventListener('load', countLoad);
262 fi.addEventListener('error', countLoad);
263 fi.addEventListener('error', function(ev) {
264 favicon.classList.add('failed-favicon');
266 favicon.appendChild(fi);
268 favicon.classList.add('failed-favicon');
271 var mvx = tile.querySelector('.mv-x');
272 mvx.addEventListener('click', function(ev) {
274 ev.stopPropagation();
283 * Do some initialization and parses the query arguments passed to the iframe.
285 var init = function() {
286 // Creates a new DOM element to hold the tiles.
287 tiles = document.createElement('div');
289 // Parse query arguments.
290 var query = window.location.search.substring(1).split('&');
292 for (var i = 0; i < query.length; ++i) {
293 var val = query[i].split('=');
294 if (val[0] == '') continue;
295 args[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
299 if (args['rtl'] == '1') {
300 var html = document.querySelector('html');
304 window.addEventListener('message', handlePostMessage);
308 window.addEventListener('DOMContentLoaded', init);