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 |