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

Side by Side Diff: chrome/browser/resources/local_ntp/local_ntp_fast.js

Issue 997223003: New fast NTP that uses a single iframe (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 unified diff | Download patch
« no previous file with comments | « chrome/browser/resources/local_ntp/local_ntp_fast.html ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 5
6 /** 6 /**
7 * @fileoverview The local InstantExtended NTP. 7 * @fileoverview The local InstantExtended NTP.
8 */ 8 */
9 9
10 10
11 /** 11 /**
12 * Controls rendering the new tab page for InstantExtended. 12 * Controls rendering the new tab page for InstantExtended.
13 * @return {Object} A limited interface for testing the local NTP. 13 * @return {Object} A limited interface for testing the local NTP.
14 */ 14 */
15 function LocalNTP() { 15 function LocalNTP() {
16 <include src="../../../../ui/webui/resources/js/assert.js"> 16 'use strict';
17 <include src="local_ntp_design.js">
18 <include src="local_ntp_util.js">
19 <include src="window_disposition_util.js">
20 17
18 <include src="../../../../ui/webui/resources/js/util.js">
19 <include src='local_ntp_design.js'>
21 20
22 /** 21 /**
23 * Enum for classnames. 22 * Enum for classnames.
24 * @enum {string} 23 * @enum {string}
25 * @const 24 * @const
26 */ 25 */
27 var CLASSES = { 26 var CLASSES = {
28 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme 27 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme
29 BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation
30 BLACKLIST_BUTTON: 'mv-x',
31 BLACKLIST_BUTTON_INNER: 'mv-x-inner',
32 DARK: 'dark', 28 DARK: 'dark',
33 DEFAULT_THEME: 'default-theme', 29 DEFAULT_THEME: 'default-theme',
34 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', 30 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide',
35 DOT: 'dot',
36 FAKEBOX_DISABLE: 'fakebox-disable', // Makes fakebox non-interactive 31 FAKEBOX_DISABLE: 'fakebox-disable', // Makes fakebox non-interactive
37 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox 32 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox
38 // Applies drag focus style to the fakebox 33 // Applies drag focus style to the fakebox
39 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused', 34 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused',
40 FAVICON: 'mv-favicon',
41 FAVICON_FALLBACK: 'mv-favicon-fallback',
42 FOCUSED: 'mv-focused',
43 HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation
44 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo', 35 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo',
45 HIDE_NOTIFICATION: 'mv-notice-hide', 36 HIDE_NOTIFICATION: 'mv-notice-hide',
46 // Vertically centers the most visited section for a non-Google provided page. 37 // Vertically centers the most visited section for a non-Google provided page.
47 NON_GOOGLE_PAGE: 'non-google-page', 38 NON_GOOGLE_PAGE: 'non-google-page',
48 PAGE: 'mv-page', // page tiles 39 RTL: 'rtl' // Right-to-left language text.
49 PAGE_READY: 'mv-page-ready', // page tile when ready
50 RTL: 'rtl', // Right-to-left language text.
51 THUMBNAIL: 'mv-thumb',
52 THUMBNAIL_FALLBACK: 'mv-thumb-fallback',
53 THUMBNAIL_MASK: 'mv-mask',
54 TILE: 'mv-tile',
55 TILE_INNER: 'mv-tile-inner',
56 TITLE: 'mv-title'
57 }; 40 };
58 41
59 42
60 /** 43 /**
61 * Enum for HTML element ids. 44 * Enum for HTML element ids.
62 * @enum {string} 45 * @enum {string}
63 * @const 46 * @const
64 */ 47 */
65 var IDS = { 48 var IDS = {
66 ATTRIBUTION: 'attribution', 49 ATTRIBUTION: 'attribution',
(...skipping 29 matching lines...) Expand all
96 * @const 79 * @const
97 */ 80 */
98 var NTP_DISPOSE_STATE = { 81 var NTP_DISPOSE_STATE = {
99 NONE: 0, // Preserve the NTP appearance and functionality 82 NONE: 0, // Preserve the NTP appearance and functionality
100 DISABLE_FAKEBOX: 1, 83 DISABLE_FAKEBOX: 1,
101 HIDE_FAKEBOX_AND_LOGO: 2 84 HIDE_FAKEBOX_AND_LOGO: 2
102 }; 85 };
103 86
104 87
105 /** 88 /**
106 * The JavaScript button event value for a middle click.
107 * @type {number}
108 * @const
109 */
110 var MIDDLE_MOUSE_BUTTON = 1;
111
112
113 /**
114 * The container for the tile elements.
115 * @type {Element}
116 */
117 var tilesContainer;
118
119
120 /**
121 * The notification displayed when a page is blacklisted. 89 * The notification displayed when a page is blacklisted.
122 * @type {Element} 90 * @type {Element}
123 */ 91 */
124 var notification; 92 var notification;
125 93
126 94
127 /** 95 /**
128 * The container for the theme attribution. 96 * The container for the theme attribution.
129 * @type {Element} 97 * @type {Element}
130 */ 98 */
131 var attribution; 99 var attribution;
132 100
133 101
134 /** 102 /**
135 * The "fakebox" - an input field that looks like a regular searchbox. When it 103 * The "fakebox" - an input field that looks like a regular searchbox. When it
136 * is focused, any text the user types goes directly into the omnibox. 104 * is focused, any text the user types goes directly into the omnibox.
137 * @type {Element} 105 * @type {Element}
138 */ 106 */
139 var fakebox; 107 var fakebox;
140 108
141 109
142 /** 110 /**
143 * The container for NTP elements. 111 * The container for NTP elements.
144 * @type {Element} 112 * @type {Element}
145 */ 113 */
146 var ntpContents; 114 var ntpContents;
147 115
148 116
149 /** 117 /**
150 * The array of rendered tiles, ordered by appearance. 118 * The last blacklisted tile rid if any, which by definition should not be
151 * @type {!Array<Tile>} 119 * filler.
152 */ 120 * @type {?number}
153 var tiles = [];
154
155
156 /**
157 * The last blacklisted tile if any, which by definition should not be filler.
158 * @type {?Tile}
159 */ 121 */
160 var lastBlacklistedTile = null; 122 var lastBlacklistedTile = null;
161 123
162 124
163 /** 125 /**
164 * The iframe element which is currently keyboard focused, or null.
165 * @type {?Element}
166 */
167 var focusedIframe = null;
168
169
170 /**
171 * True if a page has been blacklisted and we're waiting on the
172 * onmostvisitedchange callback. See renderAllTiles() for how this is used.
173 * @type {boolean}
174 */
175 var isBlacklisting = false;
176
177
178 /**
179 * Current number of tiles columns shown based on the window width, including 126 * Current number of tiles columns shown based on the window width, including
180 * those that just contain filler. 127 * those that just contain filler.
181 * @type {number} 128 * @type {number}
182 */ 129 */
183 var numColumnsShown = 0; 130 var numColumnsShown = 0;
184 131
185 132
186 /** 133 /**
187 * A flag to indicate Most Visited changed caused by user action. If true, then
188 * in renderAllTiles() tiles remain visible so no flickering occurs.
189 * @type {boolean}
190 */
191 var userInitiatedMostVisitedChange = false;
192
193
194 /**
195 * The browser embeddedSearch.newTabPage object. 134 * The browser embeddedSearch.newTabPage object.
196 * @type {Object} 135 * @type {Object}
197 */ 136 */
198 var ntpApiHandle; 137 var ntpApiHandle;
199 138
200 139
201 /** 140 /**
202 * The browser embeddedSearch.searchBox object. 141 * The browser embeddedSearch.searchBox object.
203 * @type {Object} 142 * @type {Object}
204 */ 143 */
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 /** 177 /**
239 * Minimum total padding to give to the left and right of the most visited 178 * Minimum total padding to give to the left and right of the most visited
240 * section. Used to determine how many tiles to show. 179 * section. Used to determine how many tiles to show.
241 * @type {number} 180 * @type {number}
242 * @const 181 * @const
243 */ 182 */
244 var MIN_TOTAL_HORIZONTAL_PADDING = 200; 183 var MIN_TOTAL_HORIZONTAL_PADDING = 200;
245 184
246 185
247 /** 186 /**
248 * The filename for a most visited iframe src which shows a page title.
249 * @type {string}
250 * @const
251 */
252 var MOST_VISITED_TITLE_IFRAME = 'title.html';
253
254
255 /**
256 * The filename for a most visited iframe src which shows a thumbnail image.
257 * @type {string}
258 * @const
259 */
260 var MOST_VISITED_THUMBNAIL_IFRAME = 'thumbnail.html';
261
262
263 /**
264 * The color of the title in RRGGBBAA format. 187 * The color of the title in RRGGBBAA format.
265 * @type {?string} 188 * @type {?string}
266 */ 189 */
267 var titleColor = null; 190 var titleColor = null;
268 191
269 192
270 /** 193 /**
271 * Hide most visited tiles for at most this many milliseconds while painting.
272 * @type {number}
273 * @const
274 */
275 var MOST_VISITED_PAINT_TIMEOUT_MSEC = 500;
276
277
278 /**
279 * A Tile is either a rendering of a Most Visited page or "filler" used to
280 * pad out the section when not enough pages exist.
281 *
282 * @param {Element} elem The element for rendering the tile.
283 * @param {Element=} opt_innerElem The element for contents of tile.
284 * @param {Element=} opt_titleElem The element for rendering the title.
285 * @param {Element=} opt_thumbnailElem The element for rendering the thumbnail.
286 * @param {number=} opt_rid The RID for the corresponding Most Visited page.
287 * Should only be left unspecified when creating a filler tile.
288 * @constructor
289 */
290 function Tile(elem, opt_innerElem, opt_titleElem, opt_thumbnailElem, opt_rid) {
291 /** @type {Element} */
292 this.elem = elem;
293
294 /** @type {Element|undefined} */
295 this.innerElem = opt_innerElem;
296
297 /** @type {Element|undefined} */
298 this.titleElem = opt_titleElem;
299
300 /** @type {Element|undefined} */
301 this.thumbnailElem = opt_thumbnailElem;
302
303 /** @type {number|undefined} */
304 this.rid = opt_rid;
305 }
306
307
308 /**
309 * Heuristic to determine whether a theme should be considered to be dark, so 194 * Heuristic to determine whether a theme should be considered to be dark, so
310 * the colors of various UI elements can be adjusted. 195 * the colors of various UI elements can be adjusted.
311 * @param {ThemeBackgroundInfo|undefined} info Theme background information. 196 * @param {ThemeBackgroundInfo|undefined} info Theme background information.
312 * @return {boolean} Whether the theme is dark. 197 * @return {boolean} Whether the theme is dark.
313 * @private 198 * @private
314 */ 199 */
315 function getIsThemeDark(info) { 200 function getIsThemeDark(info) {
316 if (!info) 201 if (!info)
317 return false; 202 return false;
318 // Heuristic: light text implies dark theme. 203 // Heuristic: light text implies dark theme.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 setCustomThemeStyle(info); 248 setCustomThemeStyle(info);
364 } 249 }
365 250
366 251
367 /** 252 /**
368 * Updates the NTP based on the current theme, then rerenders all tiles. 253 * Updates the NTP based on the current theme, then rerenders all tiles.
369 * @private 254 * @private
370 */ 255 */
371 function onThemeChange() { 256 function onThemeChange() {
372 renderTheme(); 257 renderTheme();
373 tilesContainer.innerHTML = '';
374 renderAllTiles();
375 } 258 }
376 259
377 260
378 /** 261 /**
379 * Updates the NTP style according to theme. 262 * Updates the NTP style according to theme.
380 * @param {Object=} opt_themeInfo The information about the theme. If it is 263 * @param {Object=} opt_themeInfo The information about the theme. If it is
381 * omitted the style will be reverted to the default. 264 * omitted the style will be reverted to the default.
382 * @private 265 * @private
383 */ 266 */
384 function setCustomThemeStyle(opt_themeInfo) { 267 function setCustomThemeStyle(opt_themeInfo) {
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
482 function convertToRGBAColor(color) { 365 function convertToRGBAColor(color) {
483 return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + 366 return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' +
484 color[3] / 255 + ')'; 367 color[3] / 255 + ')';
485 } 368 }
486 369
487 370
488 /** 371 /**
489 * Called when page data change. 372 * Called when page data change.
490 */ 373 */
491 function onMostVisitedChange() { 374 function onMostVisitedChange() {
492 renderAllTiles(); 375 reloadTiles();
493 }
494
495
496 /**
497 * Rerenders all tiles based on Most Visited page data.
498 */
499 function renderAllTiles() {
500 if (isBlacklisting) {
501 // Trigger the blacklist animation, which then triggers reloadAllTiles().
502 var lastBlacklistedTileElem = lastBlacklistedTile.elem;
503 lastBlacklistedTileElem.addEventListener(
504 'webkitTransitionEnd', blacklistAnimationDone);
505 lastBlacklistedTileElem.classList.add(CLASSES.BLACKLIST);
506 } else {
507 reloadAllTiles();
508 }
509 }
510
511
512 /**
513 * Handles the end of the blacklist animation by showing the notification and
514 * re-rendering the new set of tiles.
515 */
516 function blacklistAnimationDone() {
517 showNotification();
518 isBlacklisting = false;
519 tilesContainer.classList.remove(CLASSES.HIDE_BLACKLIST_BUTTON);
520 lastBlacklistedTile.elem.removeEventListener(
521 'webkitTransitionEnd', blacklistAnimationDone);
522 // Need to call explicitly to re-render the tiles, since the initial
523 // renderAllTiles() issued by the blacklist function only triggered the
524 // animation.
525 reloadAllTiles();
526 } 376 }
527 377
528 378
529 /** 379 /**
530 * Fetches new data, creates, and renders tiles. 380 * Fetches new data, creates, and renders tiles.
531 */ 381 */
532 function reloadAllTiles() { 382 function reloadTiles() {
533 var pages = ntpApiHandle.mostVisited; 383 var pages = ntpApiHandle.mostVisited;
384 var iframe = $('mv-single').contentWindow;
534 385
535 tiles = []; 386 tiles = [];
536 for (var i = 0; i < MAX_NUM_TILES_TO_SHOW; ++i) 387 for (var i = 0; i < Math.min(MAX_NUM_TILES_TO_SHOW, pages.length); ++i) {
537 tiles.push(createTile(pages[i], i)); 388 iframe.postMessage({cmd: 'tile', rid: pages[i].rid}, '*');
538 389 }
539 tilesContainer.innerHTML = ''; 390 iframe.postMessage({cmd: 'show'}, '*');
540 renderAndShowTiles();
541 } 391 }
542 392
543 393
544 /**
545 * Binds onload events for a tile's internal iframe elements.
546 * @param {Tile} tile The main tile to bind events to.
547 * @param {Barrier} tileVisibilityBarrier A barrier to make all tiles visible
548 * the moment all tiles are loaded.
549 */
550 function bindTileOnloadEvents(tile, tileVisibilityBarrier) {
551 if (tile.titleElem) {
552 tileVisibilityBarrier.add();
553 tile.titleElem.onload = function() {
554 tileVisibilityBarrier.remove();
555 };
556 }
557 if (tile.thumbnailElem) {
558 tileVisibilityBarrier.add();
559 tile.thumbnailElem.onload = function() {
560 tile.elem.classList.add(CLASSES.PAGE_READY);
561 tileVisibilityBarrier.remove();
562 };
563 }
564 }
565
566
567 /**
568 * Renders the current list of visible tiles to DOM, and hides tiles that are
569 * already in the DOM but should not be seen.
570 */
571 function renderAndShowTiles() {
572 var numExisting = tilesContainer.querySelectorAll('.' + CLASSES.TILE).length;
573 // Only add visible tiles to the DOM, to avoid creating invisible tiles that
574 // produce meaningless impression metrics. However, if a tile becomes
575 // invisible then we leave it in DOM to prevent reload if it's shown again.
576 var numDesired = Math.min(tiles.length, numColumnsShown * NUM_ROWS);
577
578 // If we need to render new tiles, manage the visibility to hide intermediate
579 // load states of the iframes.
580 if (numExisting < numDesired) {
581 var showAll = function() {
582 for (var i = 0; i < numDesired; ++i) {
583 if (tiles[i].titleElem || tiles[i].thumbnailElem)
584 tiles[i].elem.classList.add(CLASSES.PAGE_READY);
585 }
586 };
587 var tileVisibilityBarrier = new Barrier(showAll);
588
589 if (!userInitiatedMostVisitedChange) {
590 // Make titleContainer invisible, but still taking up space.
591 // titleContainer becomes visible again (1) on timeout, or (2) when all
592 // tiles finish loading (using tileVisibilityBarrier).
593 window.setTimeout(function() {
594 tileVisibilityBarrier.cancel();
595 showAll();
596 }, MOST_VISITED_PAINT_TIMEOUT_MSEC);
597 }
598 userInitiatedMostVisitedChange = false;
599
600 for (var i = numExisting; i < numDesired; ++i) {
601 bindTileOnloadEvents(tiles[i], tileVisibilityBarrier);
602 tilesContainer.appendChild(tiles[i].elem);
603 }
604 }
605
606 // Show only the desired tiles. Note that .hidden does not work for
607 // inline-block elements like tiles[i].elem.
608 for (var i = 0; i < numDesired; ++i)
609 tiles[i].elem.style.display = 'inline-block';
610 // If |numDesired| < |numExisting| then hide extra tiles (e.g., this occurs
611 // when window is downsized).
612 for (; i < numExisting; ++i)
613 tiles[i].elem.style.display = 'none';
614 }
615
616
617 /**
618 * Builds a URL to display a most visited tile title in an iframe.
619 * @param {number} rid The restricted ID.
620 * @param {number} position The position of the iframe in the UI.
621 * @return {string} An URL to display the most visited title in an iframe.
622 */
623 function getMostVisitedTitleIframeUrl(rid, position) {
624 var url = 'chrome-search://most-visited/' +
625 encodeURIComponent(MOST_VISITED_TITLE_IFRAME);
626 var params = [
627 'rid=' + encodeURIComponent(rid),
628 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily),
629 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize),
630 'c=' + encodeURIComponent(titleColor),
631 'pos=' + encodeURIComponent(position)];
632 if (NTP_DESIGN.titleTextAlign)
633 params.push('ta=' + encodeURIComponent(NTP_DESIGN.titleTextAlign));
634 if (NTP_DESIGN.titleTextFade)
635 params.push('tf=' + encodeURIComponent(NTP_DESIGN.titleTextFade));
636 return url + '?' + params.join('&');
637 }
638
639
640 /**
641 * Builds a URL to display a most visited tile thumbnail in an iframe.
642 * @param {number} rid The restricted ID.
643 * @param {number} position The position of the iframe in the UI.
644 * @return {string} An URL to display the most visited thumbnail in an iframe.
645 */
646 function getMostVisitedThumbnailIframeUrl(rid, position) {
647 var url = 'chrome-search://most-visited/' +
648 encodeURIComponent(MOST_VISITED_THUMBNAIL_IFRAME);
649 var params = [
650 'rid=' + encodeURIComponent(rid),
651 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily),
652 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize),
653 'c=' + encodeURIComponent(NTP_DESIGN.thumbnailTextColor),
654 'pos=' + encodeURIComponent(position)];
655 if (NTP_DESIGN.thumbnailFallback)
656 params.push('etfb=1');
657 return url + '?' + params.join('&');
658 }
659
660
661 /**
662 * Creates a Tile with the specified page data. If no data is provided, a
663 * filler Tile is created.
664 * @param {?Object} page The page data.
665 * @param {number} position The position of the tile.
666 * @return {Tile} The new Tile.
667 */
668 function createTile(page, position) {
669 var tileElem = document.createElement('div');
670 tileElem.classList.add(CLASSES.TILE);
671 // Prevent tile from being selected (and highlighted) when areas outside the
672 // iframes are clicked.
673 tileElem.addEventListener('mousedown', function(e) {
674 e.preventDefault();
675 });
676
677 if (!page) {
678 return new Tile(tileElem);
679 }
680
681 var rid = page.rid;
682 tileElem.classList.add(CLASSES.PAGE);
683
684 var navigateFunction = function(e) {
685 e.preventDefault();
686 ntpApiHandle.navigateContentWindow(rid, getDispositionFromEvent(e));
687 };
688
689 // The click handler for navigating to the page identified by the RID.
690 tileElem.addEventListener('click', navigateFunction);
691
692 // Container of tile contents.
693 var innerElem = createAndAppendElement(tileElem, 'div', CLASSES.TILE_INNER);
694
695 // The iframe which renders the page title.
696 var titleElem = document.createElement('iframe');
697 // Enable tab navigation on the iframe, which will move the selection to the
698 // link element (which also has a tabindex).
699 titleElem.tabIndex = '0';
700
701 // Make the iframe presentational for accessibility so screen readers perceive
702 // the iframe content as just part of the same page.
703 titleElem.setAttribute('role', 'presentation');
704
705 // Why iframes have IDs:
706 //
707 // On navigating back to the NTP we see several onmostvisitedchange() events
708 // in series with incrementing RIDs. After the first event, a set of iframes
709 // begins loading RIDs n, n+1, ..., n+k-1; after the second event, these get
710 // destroyed and a new set begins loading RIDs n+k, n+k+1, ..., n+2k-1.
711 // Now due to crbug.com/68841, Chrome incorrectly loads the content for the
712 // first set of iframes into the most recent set of iframes.
713 //
714 // Giving iframes distinct ids seems to cause some invalidation and prevent
715 // associating the incorrect data.
716 //
717 // TODO(jered): Find and fix the root (probably Blink) bug.
718
719 // Keep this ID here. See comment above.
720 titleElem.id = 'title-' + rid;
721 titleElem.className = CLASSES.TITLE;
722 titleElem.src = getMostVisitedTitleIframeUrl(rid, position);
723 innerElem.appendChild(titleElem);
724
725 // A fallback element for missing thumbnails.
726 if (NTP_DESIGN.thumbnailFallback) {
727 var fallbackElem = createAndAppendElement(
728 innerElem, 'div', CLASSES.THUMBNAIL_FALLBACK);
729 if (NTP_DESIGN.thumbnailFallback === THUMBNAIL_FALLBACK.DOT)
730 createAndAppendElement(fallbackElem, 'div', CLASSES.DOT);
731 }
732
733 // The iframe which renders either a thumbnail or domain element.
734 var thumbnailElem = document.createElement('iframe');
735 thumbnailElem.tabIndex = '-1';
736 thumbnailElem.setAttribute('aria-hidden', 'true');
737 // Keep this ID here. See comment above.
738 thumbnailElem.id = 'thumb-' + rid;
739 thumbnailElem.className = CLASSES.THUMBNAIL;
740 thumbnailElem.src = getMostVisitedThumbnailIframeUrl(rid, position);
741 innerElem.appendChild(thumbnailElem);
742
743 // The button used to blacklist this page.
744 var blacklistButton = createAndAppendElement(
745 innerElem, 'div', CLASSES.BLACKLIST_BUTTON);
746 createAndAppendElement(
747 blacklistButton, 'div', CLASSES.BLACKLIST_BUTTON_INNER);
748 var blacklistFunction = generateBlacklistFunction(rid);
749 blacklistButton.addEventListener('click', blacklistFunction);
750 blacklistButton.title = configData.translatedStrings.removeThumbnailTooltip;
751
752 // A helper mask on top of the tile that is used to create hover border
753 // and/or to darken the thumbnail on focus.
754 var maskElement = createAndAppendElement(
755 innerElem, 'div', CLASSES.THUMBNAIL_MASK);
756
757 // The page favicon, or a fallback.
758 var favicon = createAndAppendElement(innerElem, 'div', CLASSES.FAVICON);
759 if (page.faviconUrl) {
760 favicon.style.backgroundImage = 'url(' + page.faviconUrl + ')';
761 } else {
762 favicon.classList.add(CLASSES.FAVICON_FALLBACK);
763 }
764 return new Tile(tileElem, innerElem, titleElem, thumbnailElem, rid);
765 }
766
767
768 /**
769 * Generates a function to be called when the page with the corresponding RID
770 * is blacklisted.
771 * @param {number} rid The RID of the page being blacklisted.
772 * @return {function(Event=)} A function which handles the blacklisting of the
773 * page by updating state variables and notifying Chrome.
774 */
775 function generateBlacklistFunction(rid) {
776 return function(e) {
777 // Prevent navigation when the page is being blacklisted.
778 if (e)
779 e.stopPropagation();
780
781 userInitiatedMostVisitedChange = true;
782 isBlacklisting = true;
783 tilesContainer.classList.add(CLASSES.HIDE_BLACKLIST_BUTTON);
784 lastBlacklistedTile = getTileByRid(rid);
785 ntpApiHandle.deleteMostVisitedItem(rid);
786 };
787 }
788
789
790 /** 394 /**
791 * Shows the blacklist notification and triggers a delay to hide it. 395 * Shows the blacklist notification and triggers a delay to hide it.
792 */ 396 */
793 function showNotification() { 397 function showNotification() {
794 notification.classList.remove(CLASSES.HIDE_NOTIFICATION); 398 notification.classList.remove(CLASSES.HIDE_NOTIFICATION);
795 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION); 399 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
796 notification.scrollTop; 400 notification.scrollTop;
797 notification.classList.add(CLASSES.DELAYED_HIDE_NOTIFICATION); 401 notification.classList.add(CLASSES.DELAYED_HIDE_NOTIFICATION);
798 } 402 }
799 403
800 404
801 /** 405 /**
802 * Hides the blacklist notification. 406 * Hides the blacklist notification.
803 */ 407 */
804 function hideNotification() { 408 function hideNotification() {
805 notification.classList.add(CLASSES.HIDE_NOTIFICATION); 409 notification.classList.add(CLASSES.HIDE_NOTIFICATION);
806 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION); 410 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
807 } 411 }
808 412
809 413
810 /** 414 /**
811 * Handles a click on the notification undo link by hiding the notification and 415 * Handles a click on the notification undo link by hiding the notification and
812 * informing Chrome. 416 * informing Chrome.
813 */ 417 */
814 function onUndo() { 418 function onUndo() {
815 userInitiatedMostVisitedChange = true;
816 hideNotification(); 419 hideNotification();
817 var lastBlacklistedRID = lastBlacklistedTile.rid; 420 if (lastBlacklistedTile != null) {
818 if (typeof lastBlacklistedRID != 'undefined') 421 ntpApiHandle.undoMostVisitedDeletion(lastBlacklistedTile);
819 ntpApiHandle.undoMostVisitedDeletion(lastBlacklistedRID); 422 }
820 } 423 }
821 424
822 425
823 /** 426 /**
824 * Handles a click on the restore all notification link by hiding the 427 * Handles a click on the restore all notification link by hiding the
825 * notification and informing Chrome. 428 * notification and informing Chrome.
826 */ 429 */
827 function onRestoreAll() { 430 function onRestoreAll() {
828 userInitiatedMostVisitedChange = true;
829 hideNotification(); 431 hideNotification();
830 ntpApiHandle.undoAllMostVisitedDeletions(); 432 ntpApiHandle.undoAllMostVisitedDeletions();
831 } 433 }
832 434
833 435
834 /** 436 /**
835 * Recomputes the number of tile columns, and width of various contents based 437 * Recomputes the number of tile columns, and width of various contents based
836 * on the width of the window. 438 * on the width of the window.
837 * @return {boolean} Whether the number of tile columns has changed. 439 * @return {boolean} Whether the number of tile columns has changed.
838 */ 440 */
(...skipping 10 matching lines...) Expand all
849 if (newNumColumns < MIN_NUM_COLUMNS) 451 if (newNumColumns < MIN_NUM_COLUMNS)
850 newNumColumns = MIN_NUM_COLUMNS; 452 newNumColumns = MIN_NUM_COLUMNS;
851 else if (newNumColumns > MAX_NUM_COLUMNS) 453 else if (newNumColumns > MAX_NUM_COLUMNS)
852 newNumColumns = MAX_NUM_COLUMNS; 454 newNumColumns = MAX_NUM_COLUMNS;
853 455
854 if (numColumnsShown === newNumColumns) 456 if (numColumnsShown === newNumColumns)
855 return false; 457 return false;
856 458
857 numColumnsShown = newNumColumns; 459 numColumnsShown = newNumColumns;
858 var tilesContainerWidth = numColumnsShown * tileRequiredWidth; 460 var tilesContainerWidth = numColumnsShown * tileRequiredWidth;
859 tilesContainer.style.width = tilesContainerWidth + 'px'; 461 $(IDS.TILES).style.width = tilesContainerWidth + 'px';
860 if (fakebox) { 462 if (fakebox) {
861 // -2 to account for border. 463 // -2 to account for border.
862 var fakeboxWidth = (tilesContainerWidth - NTP_DESIGN.tileMargin - 2); 464 var fakeboxWidth = (tilesContainerWidth - NTP_DESIGN.tileMargin - 2);
863 fakebox.style.width = fakeboxWidth + 'px'; 465 fakebox.style.width = fakeboxWidth + 'px';
864 } 466 }
865 return true; 467 return true;
866 } 468 }
867 469
868 470
869 /** 471 /**
870 * Resizes elements because the number of tile columns may need to change in 472 * Resizes elements because the number of tile columns may need to change in
871 * response to resizing. Also shows or hides extra tiles tiles according to the 473 * response to resizing. Also shows or hides extra tiles tiles according to the
872 * new width of the page. 474 * new width of the page.
873 */ 475 */
874 function onResize() { 476 function onResize() {
875 if (updateContentWidth()) { 477 updateContentWidth();
876 // Render without clearing tiles.
877 renderAndShowTiles();
878 }
879 } 478 }
880 479
881 480
882 /**
883 * Returns the tile corresponding to the specified page RID.
884 * @param {number} rid The page RID being looked up.
885 * @return {Tile} The corresponding tile.
886 */
887 function getTileByRid(rid) {
888 for (var i = 0, length = tiles.length; i < length; ++i) {
889 var tile = tiles[i];
890 if (tile.rid == rid)
891 return tile;
892 }
893 return null;
894 }
895
896
897 /** 481 /**
898 * Handles new input by disposing the NTP, according to where the input was 482 * Handles new input by disposing the NTP, according to where the input was
899 * entered. 483 * entered.
900 */ 484 */
901 function onInputStart() { 485 function onInputStart() {
902 if (fakebox && isFakeboxFocused()) { 486 if (fakebox && isFakeboxFocused()) {
903 setFakeboxFocus(false); 487 setFakeboxFocus(false);
904 setFakeboxDragFocus(false); 488 setFakeboxDragFocus(false);
905 disposeNtp(true); 489 disposeNtp(true);
906 } else if (!isFakeboxFocused()) { 490 } else if (!isFakeboxFocused()) {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1001 function createAndAppendElement(parent, name, opt_class) { 585 function createAndAppendElement(parent, name, opt_class) {
1002 var child = document.createElement(name); 586 var child = document.createElement(name);
1003 if (opt_class) 587 if (opt_class)
1004 child.classList.add(opt_class); 588 child.classList.add(opt_class);
1005 parent.appendChild(child); 589 parent.appendChild(child);
1006 return child; 590 return child;
1007 } 591 }
1008 592
1009 593
1010 /** 594 /**
1011 * Removes a node from its parent.
1012 * @param {Node} node The node to remove.
1013 */
1014 function removeNode(node) {
1015 node.parentNode.removeChild(node);
1016 }
1017
1018
1019 /**
1020 * @param {!Element} element The element to register the handler for. 595 * @param {!Element} element The element to register the handler for.
1021 * @param {number} keycode The keycode of the key to register. 596 * @param {number} keycode The keycode of the key to register.
1022 * @param {!Function} handler The key handler to register. 597 * @param {!Function} handler The key handler to register.
1023 */ 598 */
1024 function registerKeyHandler(element, keycode, handler) { 599 function registerKeyHandler(element, keycode, handler) {
1025 element.addEventListener('keydown', function(event) { 600 element.addEventListener('keydown', function(event) {
1026 if (event.keyCode == keycode) 601 if (event.keyCode == keycode)
1027 handler(event); 602 handler(event);
1028 }); 603 });
1029 } 604 }
(...skipping 10 matching lines...) Expand all
1040 return null; 615 return null;
1041 } 616 }
1042 617
1043 618
1044 /** 619 /**
1045 * Event handler for the focus changed and blacklist messages on link elements. 620 * Event handler for the focus changed and blacklist messages on link elements.
1046 * Used to toggle visual treatment on the tiles (depending on the message). 621 * Used to toggle visual treatment on the tiles (depending on the message).
1047 * @param {Event} event Event received. 622 * @param {Event} event Event received.
1048 */ 623 */
1049 function handlePostMessage(event) { 624 function handlePostMessage(event) {
1050 if (event.origin !== 'chrome-search://most-visited') 625 var cmd = event.data.cmd;
1051 return; 626 var args = event.data;
627 if (cmd == 'tileBlacklisted') {
628 showNotification();
629 lastBlacklistedTile = args.rid;
1052 630
1053 if (event.data === 'linkFocused') { 631 ntpApiHandle.deleteMostVisitedItem(args.rid);
1054 var activeElement = document.activeElement;
1055 if (activeElement.classList.contains(CLASSES.TITLE)) {
1056 activeElement.classList.add(CLASSES.FOCUSED);
1057 focusedIframe = activeElement;
1058 }
1059 } else if (event.data === 'linkBlurred') {
1060 if (focusedIframe)
1061 focusedIframe.classList.remove(CLASSES.FOCUSED);
1062 focusedIframe = null;
1063 } else if (event.data.indexOf('tileBlacklisted') === 0) {
1064 var tilePosition = event.data.split(',')[1];
1065 if (tilePosition)
1066 generateBlacklistFunction(tiles[parseInt(tilePosition, 10)].rid)();
1067 } 632 }
1068 } 633 }
1069 634
1070 635
1071 /** 636 /**
1072 * Prepares the New Tab Page by adding listeners, rendering the current 637 * Prepares the New Tab Page by adding listeners, rendering the current
1073 * theme, the most visited pages section, and Google-specific elements for a 638 * theme, the most visited pages section, and Google-specific elements for a
1074 * Google-provided page. 639 * Google-provided page.
1075 */ 640 */
1076 function init() { 641 function init() {
1077 tilesContainer = $(IDS.TILES);
1078 notification = $(IDS.NOTIFICATION); 642 notification = $(IDS.NOTIFICATION);
1079 attribution = $(IDS.ATTRIBUTION); 643 attribution = $(IDS.ATTRIBUTION);
1080 ntpContents = $(IDS.NTP_CONTENTS); 644 ntpContents = $(IDS.NTP_CONTENTS);
1081 645
1082 if (configData.isGooglePage) { 646 if (configData.isGooglePage) {
1083 var logo = document.createElement('div'); 647 var logo = document.createElement('div');
1084 logo.id = IDS.LOGO; 648 logo.id = IDS.LOGO;
1085 logo.title = 'Google'; 649 logo.title = 'Google';
1086 650
1087 fakebox = document.createElement('div'); 651 fakebox = document.createElement('div');
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1134 ntpApiHandle.onthemechange = onThemeChange; 698 ntpApiHandle.onthemechange = onThemeChange;
1135 ntpApiHandle.onmostvisitedchange = onMostVisitedChange; 699 ntpApiHandle.onmostvisitedchange = onMostVisitedChange;
1136 700
1137 ntpApiHandle.oninputstart = onInputStart; 701 ntpApiHandle.oninputstart = onInputStart;
1138 ntpApiHandle.oninputcancel = restoreNtp; 702 ntpApiHandle.oninputcancel = restoreNtp;
1139 703
1140 if (ntpApiHandle.isInputInProgress) 704 if (ntpApiHandle.isInputInProgress)
1141 onInputStart(); 705 onInputStart();
1142 706
1143 renderTheme(); 707 renderTheme();
1144 renderAllTiles();
1145 708
1146 searchboxApiHandle = topLevelHandle.searchBox; 709 searchboxApiHandle = topLevelHandle.searchBox;
1147 710
1148 if (fakebox) { 711 if (fakebox) {
1149 // Listener for updating the key capture state. 712 // Listener for updating the key capture state.
1150 document.body.onmousedown = function(event) { 713 document.body.onmousedown = function(event) {
1151 if (isFakeboxClick(event)) 714 if (isFakeboxClick(event))
1152 searchboxApiHandle.startCapturingKeyStrokes(); 715 searchboxApiHandle.startCapturingKeyStrokes();
1153 else if (isFakeboxFocused()) 716 else if (isFakeboxFocused())
1154 searchboxApiHandle.stopCapturingKeyStrokes(); 717 searchboxApiHandle.stopCapturingKeyStrokes();
(...skipping 28 matching lines...) Expand all
1183 // Update the fakebox style to match the current key capturing state. 746 // Update the fakebox style to match the current key capturing state.
1184 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled); 747 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled);
1185 } 748 }
1186 749
1187 if (searchboxApiHandle.rtl) { 750 if (searchboxApiHandle.rtl) {
1188 $(IDS.NOTIFICATION).dir = 'rtl'; 751 $(IDS.NOTIFICATION).dir = 'rtl';
1189 // Grabbing the root HTML element. 752 // Grabbing the root HTML element.
1190 document.documentElement.setAttribute('dir', 'rtl'); 753 document.documentElement.setAttribute('dir', 'rtl');
1191 // Add class for setting alignments based on language directionality. 754 // Add class for setting alignments based on language directionality.
1192 document.documentElement.classList.add(CLASSES.RTL); 755 document.documentElement.classList.add(CLASSES.RTL);
1193 $(IDS.TILES).dir = 'rtl';
1194 } 756 }
1195 757
758 var iframe = document.createElement('iframe');
759 iframe.id = 'mv-single';
760 var args = [];
761
762 if (searchboxApiHandle.rtl) {
763 args.push('rtl=1');
764 }
765
766 iframe.src = '//most-visited/single.html?' + args.join('&');
767 $(IDS.TILES).appendChild(iframe);
768
769 iframe.onload = function() {
770 reloadTiles();
771 };
772
1196 window.addEventListener('message', handlePostMessage); 773 window.addEventListener('message', handlePostMessage);
1197 } 774 }
1198 775
1199 776
1200 /** 777 /**
1201 * Binds event listeners. 778 * Binds event listeners.
1202 */ 779 */
1203 function listen() { 780 function listen() {
1204 document.addEventListener('DOMContentLoaded', init); 781 document.addEventListener('DOMContentLoaded', init);
1205 } 782 }
1206 783
1207 return { 784 return {
1208 init: init, 785 init: init,
1209 listen: listen 786 listen: listen
1210 }; 787 };
1211 } 788 }
1212 789
1213 if (!window.localNTPUnitTest) { 790 if (!window.localNTPUnitTest) {
1214 LocalNTP().listen(); 791 LocalNTP().listen();
1215 } 792 }
OLDNEW
« no previous file with comments | « chrome/browser/resources/local_ntp/local_ntp_fast.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698