Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: chrome/browser/resources/local_ntp/most_visited_single.js

Issue 997203003: Adds a new NTP endpoint for the single frame of the fast NTP (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/local_ntp/most_visited_single.js
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
new file mode 100644
index 0000000000000000000000000000000000000000..2d1dda1044e91b7b946111ca8aaebcc86f307324
--- /dev/null
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -0,0 +1,293 @@
+/* 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. */
+
+ // Single iframe for NTP tiles.
+(function() {
+'use strict';
+
+<include src="../../../../ui/webui/resources/js/util.js">
+
+/**
+ * The different types of events that are logged from the NTP. This enum is
+ * used to transfer information from the NTP javascript to the renderer and is
Mathieu 2015/03/12 15:53:26 nit: JavaScript
fserb 2015/03/12 16:05:34 OMG
+ * not used as a UMA enum histogram's logged value.
+ * Note: Keep in sync with common/ntp_logging_events.h
+ * @enum {number}
+ * @const
+ */
+var LOGTYPE = {
huangs 2015/03/12 06:16:56 NIT: LOG_TYPE?
fserb 2015/03/12 16:05:33 Done.
+ // The suggestion is coming from the server.
+ NTP_SERVER_SIDE_SUGGESTION: 0,
huangs 2015/03/12 06:16:56 The following are unused: NTP_SERVER_SIDE_SUGGES
fserb 2015/03/12 16:05:33 Done.
+ // The suggestion is coming from the client.
+ NTP_CLIENT_SIDE_SUGGESTION: 1,
+ // Indicates a tile was rendered, no matter if it's a thumbnail, a gray tile
+ // or an external tile.
+ NTP_TILE: 2,
+ // The tile uses a local thumbnail image.
+ NTP_THUMBNAIL_TILE: 3,
+ // Used when no thumbnail is specified and a gray tile with the domain is used
+ // as the main tile.
+ NTP_GRAY_TILE: 4,
+ // The visuals of that tile are handled externally by the page itself.
+ NTP_EXTERNAL_TILE: 5,
+ // There was an error in loading both the thumbnail image and the fallback
+ // (if it was provided), resulting in a grey tile.
huangs 2015/03/12 06:16:56 NIT: grey
fserb 2015/03/12 16:05:34 Done.
+ NTP_THUMBNAIL_ERROR: 6,
+ // Used a gray tile with the domain as the fallback for a failed thumbnail.
+ NTP_GRAY_TILE_FALLBACK: 7,
+ // The visuals of that tile's fallback are handled externally.
+ NTP_EXTERNAL_TILE_FALLBACK: 8,
+ // The user moused over an NTP tile or title.
+ NTP_MOUSEOVER: 9
+};
+
+/**
+ * The origin of this request.
+ * @const {string}
+ */
+var DOMAIN_ORIGIN = '{{ORIGIN}}';
+
+/**
+ * Counter for DOM elements that we are waiting to finish loading.
+ * @type {number}
+ */
+var loadedCounter = 1;
+
+/**
+ * DOM element containing the tiles we are going to present next.
+ * Works as a double-buffer that is shown when we receive a "show" postMessage.
+ * @type {DOM}
huangs 2015/03/12 06:16:56 Note that {DOMNode} appears below, which is incons
fserb 2015/03/12 16:05:34 Done.
+ */
+var tiles = null;
+
+/**
+ * Log an event on the NTP
huangs 2015/03/12 06:16:56 NIT: "." at end.
fserb 2015/03/12 16:05:33 Done.
+ * @param {number} eventName Event from NTP_LOGGING_EVENT_TYPE.
huangs 2015/03/12 06:16:56 NIT: |eventType| instead of |eventName|? Also NTP
fserb 2015/03/12 16:05:33 Done.
+ */
+var logEvent = function(eventName) {
+ chrome.embeddedSearch.newTabPage.logEvent(eventName);
+};
+
Mathieu 2015/03/12 15:53:26 nit: either choose 2 lines between functions or 1
fserb 2015/03/12 16:05:34 I decided on two lines.
+
+/**
+ * Down count the DOM elements that we are waiting for the page to load.
+ * When we get to 0, we send a message to the parent window.
+ * This is usually used as an EventListener of onload/onerror.
+ */
+var countLoad = function() {
+ loadedCounter--;
+ if (loadedCounter <= 0) {
+ window.parent.postMessage({cmd: 'loaded' }, DOMAIN_ORIGIN);
huangs 2015/03/12 06:16:56 NIT: extra space before "}"?
fserb 2015/03/12 16:05:33 Done.
+ loadedCounter = 1;
+ }
+};
+
+
+/**
+ * Handle postMessages coming from the host page to the iframe.
+ * We try to keep the logic here to a minimum and just dispatch to the relevant
+ * functions.
+ **/
+var handlePostMessage = function(event) {
+ var cmd = event.data.cmd;
+ var args = event.data;
huangs 2015/03/12 06:16:56 |args| is used only once, and in the "else" case y
fserb 2015/03/12 16:05:33 Done.
+
+ if (cmd == 'tile') {
+ addTile(args);
+ } else if (cmd == 'show') {
+ showTiles();
+ countLoad();
+ } else {
+ console.error('Unknown command: ' + event.data);
+ }
+};
+
+/**
+ * Called when the host page has finished sending us tile information and
+ * we are ready to show the new tiles and drop the old ones.
+ */
+var showTiles = function() {
+ // store the tiles on the current closure.
huangs 2015/03/12 06:16:56 Nit: Capitalize "Store".
fserb 2015/03/12 16:05:34 Done.
+ var cur = tiles;
+
+ // Create empty tiles until we have 8.
+ while (cur.childNodes.length < 8) {
huangs 2015/03/12 06:16:56 Magic number 8.
fserb 2015/03/12 16:05:34 Done.
+ addTile({});
+ }
+
+ var parent = $('most-visited');
huangs 2015/03/12 06:16:56 Why not document.querySelector('#most-visited')?
fserb 2015/03/12 16:05:34 Done.
+
+ // Mark old tile DIV for removal after the transition animation is done.
+ var old = parent.querySelector('#mv-tiles');
+ if (old) {
+ old.id = 'mv-tiles-old';
+ cur.addEventListener('webkitTransitionEnd', function(ev) {
+ if (ev.target === cur) {
+ parent.removeChild(old);
+ }
+ });
+ }
+
+ // Add new tileset.
+ cur.id = 'mv-tiles';
+ parent.appendChild(cur);
+ // We want the CSS transition to trigger, so need to add to the DOM before
+ // setting the style.
+ setTimeout(function() {
+ cur.style.opacity = 1.0;
+ }, 0);
+
+ // Make sure the tiles variable contain the next tileset we may use.
+ tiles = document.createElement('div');
+};
+
+
+/**
+ * Called when the host page wants to add a suggestion tile.
+ * For Most Visited, it grabs the data from Chrome and pass on.
+ * For host page generated it just passes the data.
+ * @param {object} args Data for the tile to be rendered.
+ */
+var addTile = function(args) {
+ if (args.rid) {
+ var data = chrome.embeddedSearch.searchBox.getMostVisitedItemData(args.rid);
+ tiles.appendChild(renderTile(data));
+ logEvent(LOGTYPE.NTP_CLIENT_SIDE_SUGGESTION);
+ } else {
+ tiles.appendChild(renderTile(null));
+ }
+};
+
+
+/**
+ * Called when the user decided to add a tile to the blacklist.
+ * It sets of the animation for the blacklist and sends the blacklisted id
+ * to the host page.
+ * @param {DOMNode} tile DOM node of the tile we want to remove.
+ */
+var blacklistTile = function(tile) {
+ tile.classList.add('blacklisted');
+ var sent = false;
+ tile.addEventListener('webkitTransitionEnd', function() {
+ if (sent) return;
+ sent = true;
+ window.parent.postMessage({cmd: 'tileBlacklisted',
+ rid: Number(tile.getAttribute('rid'))},
huangs 2015/03/12 06:16:56 HTML custom attributes should have '-', so 'data-r
fserb 2015/03/12 16:05:34 Done.
+ DOMAIN_ORIGIN);
+ });
+};
+
+
+/**
+ * Renders a MostVisited tile to the DOM.
+ * @param {object} data Object containing rid, url, title, favicon, thumbnail.
+ * data is null if you want to construct an empty tile.
+ */
+var renderTile = function(data) {
+ var tile = document.createElement('a');
+ tile.className = 'mv-tile';
+
+ if (data == null) {
+ tile.className = 'mv-empty-tile';
+ return tile;
+ }
+
+ logEvent(LOGTYPE.NTP_TILE);
+
+ tile.setAttribute('rid', data.rid);
huangs 2015/03/12 06:16:56 HTML custom attributes should have '-', so 'data-r
fserb 2015/03/12 16:05:34 Done.
+ tile.innerHTML = '<div class="mv-favicon"></div>' +
+ '<div class="mv-title"></div><div class="mv-thumb"></div>' +
+ '<div title="Don\'t show on this page" class="mv-x"></div>';
+
+ tile.href = data.url;
+ tile.title = data.title;
+ tile.onkeypress = function(ev) {
huangs 2015/03/12 06:16:56 Use tile.addEventListener('keypress', function() {
fserb 2015/03/12 16:05:33 Done.
+ if (ev.keyCode == 127) { // DELETE
+ blacklistTile(tile);
+ ev.stopPropagation();
+ return false;
+ }
+ };
+ tile.addEventListener('mouseover', function() {
+ logEvent(LOGTYPE.NTP_MOUSEOVER);
huangs 2015/03/12 06:16:56 I'm a bit concerned with using mouseover; could yo
fserb 2015/03/12 16:05:33 Acknowledged.
+ });
+
+ var title = tile.querySelectorAll('.mv-title')[0];
huangs 2015/03/12 06:16:55 title.querySelector('.mv-title') is cleaner (takes
fserb 2015/03/12 16:05:34 Done.
+ title.innerHTML = data.title;
huangs 2015/03/12 06:16:55 Is data.title already escape entities? Otherwise
fserb 2015/03/12 16:05:34 Done.
+ title.style.direction = data.direction || 'ltr';
+
+ var thumb = tile.querySelectorAll('.mv-thumb')[0];
+
+ var img = document.createElement('img');
huangs 2015/03/12 06:16:55 Move these 2 lines inside "if" since it's unused o
fserb 2015/03/12 16:05:33 Done.
+ img.title = data.title;
+ if (data.thumbnailUrl) {
+ img.src = data.thumbnailUrl;
+ loadedCounter += 1;
huangs 2015/03/12 06:16:55 Nit: Inconsistent with "loadedCounter--" you used
fserb 2015/03/12 16:05:34 Done.
+ img.addEventListener('load', countLoad);
+ img.addEventListener('error', countLoad);
+ img.addEventListener('error', function(ev) {
+ thumb.classList.add('failed-img');
+ thumb.removeChild(img);
+ logEvent(LOGTYPE.NTP_THUMBNAIL_ERROR);
+ });
+ thumb.appendChild(img);
+ logEvent(LOGTYPE.NTP_THUMBNAIL_TILE);
+ } else {
+ thumb.classList.add('failed-img');
+ }
+
+ var favicon = tile.querySelectorAll('.mv-favicon')[0];
+ if (data.faviconUrl) {
+ var fi = document.createElement('img');
+ fi.src = '../' + data.faviconUrl;
+ fi.title = '';
huangs 2015/03/12 06:16:56 Wouldn't |ti.title| be empty already? Please comm
fserb 2015/03/12 16:05:33 Done.
Mathieu 2015/03/12 17:39:18 I don't see the results of this comment.
+ loadedCounter += 1;
+ fi.addEventListener('load', countLoad);
+ fi.addEventListener('error', countLoad);
+ fi.addEventListener('error', function(ev) {
+ favicon.classList.add('failed-favicon');
+ });
+ favicon.appendChild(fi);
+ } else {
+ favicon.classList.add('failed-favicon');
+ }
+
+ var mvx = tile.querySelectorAll('.mv-x')[0];
+ mvx.onclick = function(ev) {
huangs 2015/03/12 06:16:55 mvx.addEventListener('click', ...) ?
fserb 2015/03/12 16:05:34 Done.
+ blacklistTile(tile);
+ ev.stopPropagation();
+ return false;
+ };
+
+ return tile;
+};
+
+
+/**
+ * Do some initialization and parses the query arguments passed to the iframe.
+ */
+var init = function() {
+ // Creates a new DOM element to hold the tiles.
+ tiles = document.createElement('div');
+
+ // Parse query arguments.
Mathieu 2015/03/12 15:53:26 are we only doing this for RTL? consider early bre
fserb 2015/03/12 16:05:34 We are only using it for RTL for now, but I expect
Mathieu 2015/03/12 17:39:18 Then consider moving to a private function, it cle
+ var query = window.location.search.substring(1).split('&');
+ var args = {};
+ for (var i = 0; i < query.length; ++i) {
+ var val = query[i].split('=');
+ if (val[0] == '') continue;
+ args[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
+ }
+
+ // Enable RTL.
+ if (args['rtl'] == '1') {
+ var html = document.querySelector('html');
+ html.dir = 'rtl';
+ }
+
+ window.addEventListener('message', handlePostMessage);
+};
+
+window.addEventListener('DOMContentLoaded', init);
+})();

Powered by Google App Engine
This is Rietveld 408576698