Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. */ | |
| 4 | |
| 5 // Single iframe for NTP tiles. | |
| 6 (function() { | |
| 7 'use strict'; | |
| 8 | |
| 9 | |
| 10 /** | |
| 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 | |
| 15 * @enum {number} | |
| 16 * @const | |
| 17 */ | |
| 18 var LOG_TYPE = { | |
| 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. | |
| 25 NTP_TILE: 2, | |
| 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. | |
| 30 NTP_GRAY_TILE: 4, | |
| 31 // The visuals of that tile are handled externally by the page itself. | |
| 32 // Unused here. | |
| 33 NTP_EXTERNAL_TILE: 5, | |
| 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. | |
| 38 // Unused here. | |
| 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 or title. | |
|
huangs
2015/03/13 03:49:52
Can remove "or title".
fserb
2015/03/13 17:22:44
Done.
| |
| 43 NTP_MOUSEOVER: 9 | |
| 44 }; | |
| 45 | |
| 46 | |
| 47 /** | |
| 48 * Total number of tiles to show at any time. If the host page doesn't send | |
| 49 * enough tiles, we fill them blank. | |
| 50 * @const {number} | |
| 51 */ | |
| 52 var NUMBER_OF_TILES = 8; | |
| 53 | |
| 54 | |
| 55 /** | |
| 56 * The origin of this request. | |
| 57 * @const {string} | |
| 58 */ | |
| 59 var DOMAIN_ORIGIN = '{{ORIGIN}}'; | |
| 60 | |
| 61 | |
| 62 /** | |
| 63 * Counter for DOM elements that we are waiting to finish loading. | |
| 64 * @type {number} | |
| 65 */ | |
| 66 var loadedCounter = 1; | |
| 67 | |
| 68 | |
| 69 /** | |
| 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. | |
| 72 * @type {Element} | |
| 73 */ | |
| 74 var tiles = null; | |
| 75 | |
| 76 | |
| 77 /** | |
| 78 * Log an event on the NTP. | |
| 79 * @param {number} eventType Event from NTP_LOGGING_EVENT_TYPE. | |
|
huangs
2015/03/13 03:49:52
NTP_LOGGING_EVENT_TYPE => LOG_TYPE
fserb
2015/03/13 17:22:45
Done.
| |
| 80 */ | |
| 81 var logEvent = function(eventType) { | |
| 82 chrome.embeddedSearch.newTabPage.logEvent(eventType); | |
| 83 }; | |
| 84 | |
| 85 | |
| 86 /** | |
| 87 * Down count the DOM elements that we are waiting for the page to load. | |
|
huangs
2015/03/13 03:49:52
NIT: "Down counts" -- if a function comment starts
fserb
2015/03/13 17:22:45
Done.
| |
| 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. | |
| 90 */ | |
| 91 var countLoad = function() { | |
| 92 loadedCounter -= 1; | |
| 93 if (loadedCounter <= 0) { | |
| 94 window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN); | |
| 95 loadedCounter = 1; | |
| 96 } | |
| 97 }; | |
| 98 | |
| 99 | |
| 100 /** | |
| 101 * Handle postMessages coming from the host page to the iframe. | |
|
huangs
2015/03/13 03:49:52
NIT: Handles
fserb
2015/03/13 17:22:45
Done.
| |
| 102 * We try to keep the logic here to a minimum and just dispatch to the relevant | |
| 103 * functions. | |
| 104 **/ | |
| 105 var handlePostMessage = function(event) { | |
| 106 var cmd = event.data.cmd; | |
| 107 | |
| 108 if (cmd == 'tile') { | |
| 109 addTile(event.data); | |
| 110 } else if (cmd == 'show') { | |
| 111 showTiles(); | |
| 112 countLoad(); | |
| 113 } else { | |
| 114 console.error('Unknown command: ' + event.data); | |
| 115 } | |
| 116 }; | |
| 117 | |
| 118 | |
| 119 /** | |
| 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. | |
| 122 */ | |
| 123 var showTiles = function() { | |
| 124 // Store the tiles on the current closure. | |
| 125 var cur = tiles; | |
| 126 | |
| 127 // Create empty tiles until we have NUMBER_OF_TILES. | |
| 128 while (cur.childNodes.length < NUMBER_OF_TILES) { | |
| 129 addTile({}); | |
| 130 } | |
| 131 | |
| 132 var parent = document.querySelector('#most-visited'); | |
| 133 | |
| 134 // Mark old tile DIV for removal after the transition animation is done. | |
| 135 var old = parent.querySelector('#mv-tiles'); | |
| 136 if (old) { | |
| 137 old.id = 'mv-tiles-old'; | |
| 138 cur.addEventListener('webkitTransitionEnd', function(ev) { | |
| 139 if (ev.target === cur) { | |
| 140 parent.removeChild(old); | |
| 141 } | |
| 142 }); | |
| 143 } | |
| 144 | |
| 145 // Add new tileset. | |
| 146 cur.id = 'mv-tiles'; | |
| 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() { | |
|
huangs
2015/03/13 03:49:52
Would requestAnimationFrame() do the same thing?
fserb
2015/03/13 17:22:45
not the same thing.
| |
| 151 cur.style.opacity = 1.0; | |
| 152 }, 0); | |
| 153 | |
| 154 // Make sure the tiles variable contain the next tileset we may use. | |
| 155 tiles = document.createElement('div'); | |
| 156 }; | |
| 157 | |
| 158 | |
| 159 /** | |
| 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. | |
| 164 */ | |
| 165 var addTile = function(args) { | |
| 166 if (args.rid) { | |
| 167 var data = chrome.embeddedSearch.searchBox.getMostVisitedItemData(args.rid); | |
| 168 tiles.appendChild(renderTile(data)); | |
| 169 logEvent(LOG_TYPE.NTP_CLIENT_SIDE_SUGGESTION); | |
| 170 } else { | |
| 171 tiles.appendChild(renderTile(null)); | |
|
huangs
2015/03/13 03:49:52
Would you need to log NTP_SERVER_SIDE_SUGGESTION ?
fserb
2015/03/13 17:22:45
When I handle it, yes.
| |
| 172 } | |
| 173 }; | |
| 174 | |
| 175 | |
| 176 /** | |
| 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 | |
| 179 * to the host page. | |
| 180 * @param {Element} tile DOM node of the tile we want to remove. | |
| 181 */ | |
| 182 var blacklistTile = function(tile) { | |
| 183 tile.classList.add('blacklisted'); | |
| 184 var sent = false; | |
| 185 tile.addEventListener('webkitTransitionEnd', function() { | |
| 186 if (sent) return; | |
| 187 sent = true; | |
| 188 window.parent.postMessage({cmd: 'tileBlacklisted', | |
| 189 rid: Number(tile.getAttribute('data-rid'))}, | |
| 190 DOMAIN_ORIGIN); | |
| 191 }); | |
| 192 }; | |
| 193 | |
| 194 | |
| 195 /** | |
| 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. | |
| 199 */ | |
| 200 var renderTile = function(data) { | |
| 201 var tile = document.createElement('a'); | |
| 202 | |
| 203 if (data == null) { | |
| 204 tile.className = 'mv-empty-tile'; | |
| 205 return tile; | |
| 206 } | |
| 207 | |
| 208 logEvent(LOG_TYPE.NTP_TILE); | |
| 209 | |
| 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>'; | |
| 216 | |
| 217 tile.href = data.url; | |
| 218 tile.title = data.title; | |
| 219 tile.addEventListener('keypress', function(ev) { | |
| 220 if (ev.keyCode == 127) { // DELETE | |
| 221 blacklistTile(tile); | |
| 222 ev.stopPropagation(); | |
| 223 return false; | |
| 224 } | |
| 225 }); | |
| 226 // TODO(fserb): remove this or at least change to mouseenter. | |
| 227 tile.addEventListener('mouseover', function() { | |
| 228 logEvent(LOG_TYPE.NTP_MOUSEOVER); | |
| 229 }); | |
| 230 | |
| 231 var title = tile.querySelector('.mv-title'); | |
| 232 title.innerText = data.title; | |
| 233 title.style.direction = data.direction || 'ltr'; | |
| 234 | |
| 235 var thumb = tile.querySelectorAll('.mv-thumb')[0]; | |
|
huangs
2015/03/13 03:49:52
querySelector
fserb
2015/03/13 17:22:44
Done.
| |
| 236 | |
|
huangs
2015/03/13 03:49:52
NIT: Don't need this new line.
fserb
2015/03/13 17:22:45
Done.
| |
| 237 if (data.thumbnailUrl) { | |
| 238 var img = document.createElement('img'); | |
| 239 img.title = data.title; | |
| 240 img.src = data.thumbnailUrl; | |
| 241 loadedCounter += 1; | |
| 242 img.addEventListener('load', countLoad); | |
| 243 img.addEventListener('error', countLoad); | |
| 244 img.addEventListener('error', function(ev) { | |
| 245 thumb.classList.add('failed-img'); | |
| 246 thumb.removeChild(img); | |
| 247 logEvent(LOG_TYPE.NTP_THUMBNAIL_ERROR); | |
| 248 }); | |
| 249 thumb.appendChild(img); | |
| 250 logEvent(LOG_TYPE.NTP_THUMBNAIL_TILE); | |
| 251 } else { | |
| 252 thumb.classList.add('failed-img'); | |
| 253 } | |
| 254 | |
| 255 var favicon = tile.querySelectorAll('.mv-favicon')[0]; | |
|
huangs
2015/03/13 03:49:52
querySelector
fserb
2015/03/13 17:22:45
Done.
| |
| 256 if (data.faviconUrl) { | |
| 257 var fi = document.createElement('img'); | |
| 258 fi.src = '../' + data.faviconUrl; | |
| 259 // We set the title to empty, so it doesn't say the image name on chromevox. | |
|
huangs
2015/03/13 03:49:52
NIT: // Set title to empty so screen readers won't
fserb
2015/03/13 17:22:45
Done.
| |
| 260 fi.title = ''; | |
| 261 loadedCounter += 1; | |
| 262 fi.addEventListener('load', countLoad); | |
| 263 fi.addEventListener('error', countLoad); | |
| 264 fi.addEventListener('error', function(ev) { | |
| 265 favicon.classList.add('failed-favicon'); | |
| 266 }); | |
| 267 favicon.appendChild(fi); | |
| 268 } else { | |
| 269 favicon.classList.add('failed-favicon'); | |
| 270 } | |
| 271 | |
| 272 var mvx = tile.querySelectorAll('.mv-x')[0]; | |
|
huangs
2015/03/13 03:49:52
querySelector
fserb
2015/03/13 17:22:44
Done.
| |
| 273 mvx.addEventListener('click', function(ev) { | |
| 274 blacklistTile(tile); | |
| 275 ev.stopPropagation(); | |
| 276 return false; | |
| 277 }); | |
| 278 | |
| 279 return tile; | |
| 280 }; | |
| 281 | |
| 282 | |
| 283 /** | |
| 284 * Do some initialization and parses the query arguments passed to the iframe. | |
| 285 */ | |
| 286 var init = function() { | |
| 287 // Creates a new DOM element to hold the tiles. | |
| 288 tiles = document.createElement('div'); | |
| 289 | |
| 290 // Parse query arguments. | |
| 291 var query = window.location.search.substring(1).split('&'); | |
| 292 var args = {}; | |
| 293 for (var i = 0; i < query.length; ++i) { | |
| 294 var val = query[i].split('='); | |
| 295 if (val[0] == '') continue; | |
| 296 args[decodeURIComponent(val[0])] = decodeURIComponent(val[1]); | |
| 297 } | |
| 298 | |
| 299 // Enable RTL. | |
| 300 if (args['rtl'] == '1') { | |
| 301 var html = document.querySelector('html'); | |
| 302 html.dir = 'rtl'; | |
| 303 } | |
| 304 | |
| 305 window.addEventListener('message', handlePostMessage); | |
| 306 }; | |
| 307 | |
| 308 | |
| 309 window.addEventListener('DOMContentLoaded', init); | |
| 310 })(); | |
| OLD | NEW |