1// Copyright 2013 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
6/**
7 * @fileoverview Utilities for rendering most visited thumbnails and titles.
8 */
9
10<include src="instant_iframe_validation.js">
11
12/**
13 * Enum for the different types of events that are logged from the NTP.
14 * @enum {number}
15 * @const
16 */
17var NTP_LOGGING_EVENT_TYPE = {
18  // The user moused over an NTP tile or title.
19  NTP_MOUSEOVER: 0,
20  // The page attempted to load a thumbnail image.
21  NTP_THUMBNAIL_ATTEMPT: 1,
22  // There was an error in loading both the thumbnail image and the fallback
23  // (if it was provided), resulting in a grey tile.
24  NTP_THUMBNAIL_ERROR: 2,
25  // The page attempted to load a thumbnail URL while a fallback thumbnail was
26  // provided.
27  NTP_FALLBACK_THUMBNAIL_REQUESTED: 3,
28  // The primary thumbnail image failed to load and caused us to use the
29  // secondary thumbnail as a fallback.
30  NTP_FALLBACK_THUMBNAIL_USED: 4,
31  // The suggestion is coming from the server.
32  NTP_SERVER_SIDE_SUGGESTION: 5,
33  // The suggestion is coming from the client.
34  NTP_CLIENT_SIDE_SUGGESTION: 6,
35  // The visuals of that tile are handled externally by the page itself.
36  NTP_EXTERNAL_TILE: 7
37};
38
39/**
40 * Type of the impression provider for a generic client-provided suggestion.
41 * @type {string}
42 * @const
43 */
44var CLIENT_PROVIDER_NAME = 'client';
45
46/**
47 * Type of the impression provider for a generic server-provided suggestion.
48 * @type {string}
49 * @const
50 */
51var SERVER_PROVIDER_NAME = 'server';
52
53/**
54 * Parses query parameters from Location.
55 * @param {string} location The URL to generate the CSS url for.
56 * @return {Object} Dictionary containing name value pairs for URL.
57 */
58function parseQueryParams(location) {
59  var params = Object.create(null);
60  var query = location.search.substring(1);
61  var vars = query.split('&');
62  for (var i = 0; i < vars.length; i++) {
63    var pair = vars[i].split('=');
64    var k = decodeURIComponent(pair[0]);
65    if (k in params) {
66      // Duplicate parameters are not allowed to prevent attackers who can
67      // append things to |location| from getting their parameter values to
68      // override legitimate ones.
69      return Object.create(null);
70    } else {
71      params[k] = decodeURIComponent(pair[1]);
72    }
73  }
74  return params;
75}
76
77
78/**
79 * Creates a new most visited link element.
80 * @param {Object} params URL parameters containing styles for the link.
81 * @param {string} href The destination for the link.
82 * @param {string} title The title for the link.
83 * @param {string|undefined} text The text for the link or none.
84 * @param {string|undefined} ping If specified, a location relative to the
85 *     referrer of this iframe, to ping when the link is clicked. Only works if
86 *     the referrer is HTTPS.
87 * @param {string|undefined} provider A provider name (max 8 alphanumeric
88 *     characters) used for logging. Undefined if suggestion is not coming from
89 *     the server.
90 * @return {HTMLAnchorElement} A new link element.
91 */
92function createMostVisitedLink(params, href, title, text, ping, provider) {
93  var styles = getMostVisitedStyles(params, !!text);
94  var link = document.createElement('a');
95  link.style.color = styles.color;
96  link.style.fontSize = styles.fontSize + 'px';
97  if (styles.fontFamily)
98    link.style.fontFamily = styles.fontFamily;
99  link.href = href;
100  if ('pos' in params && isFinite(params.pos)) {
101    link.ping = '/log.html?pos=' + params.pos;
102    if (provider)
103      link.ping += '&pr=' + provider;
104    // If a ping parameter was specified, add it to the list of pings, relative
105    // to the referrer of this iframe, which is the default search provider.
106    if (ping) {
107      var parentUrl = document.createElement('a');
108      parentUrl.href = document.referrer;
109      if (parentUrl.protocol == 'https:') {
110        link.ping += ' ' + parentUrl.origin + '/' + ping;
111      }
112    }
113  }
114  link.title = title;
115  link.target = '_top';
116  // Exclude links from the tab order.  The tabIndex is added to the thumbnail
117  // parent container instead.
118  link.tabIndex = '-1';
119  if (text)
120    link.textContent = text;
121  link.addEventListener('mouseover', function() {
122    var ntpApiHandle = chrome.embeddedSearch.newTabPage;
123    ntpApiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_MOUSEOVER);
124  });
125  return link;
126}
127
128
129/**
130 * Decodes most visited styles from URL parameters.
131 * - f: font-family
132 * - fs: font-size as a number in pixels.
133 * - c: A hexadecimal number interpreted as a hex color code.
134 * @param {Object.<string, string>} params URL parameters specifying style.
135 * @param {boolean} isTitle if the style is for the Most Visited Title.
136 * @return {Object} Styles suitable for CSS interpolation.
137 */
138function getMostVisitedStyles(params, isTitle) {
139  var styles = {
140    color: '#777',
141    fontFamily: '',
142    fontSize: 11
143  };
144  var apiHandle = chrome.embeddedSearch.newTabPage;
145  var themeInfo = apiHandle.themeBackgroundInfo;
146  if (isTitle && themeInfo && !themeInfo.usingDefaultTheme) {
147    styles.color = convertArrayToRGBAColor(themeInfo.textColorRgba) ||
148        styles.color;
149  } else if ('c' in params) {
150    styles.color = convertToHexColor(parseInt(params.c, 16)) || styles.color;
151  }
152  if ('f' in params && /^[-0-9a-zA-Z ,]+$/.test(params.f))
153    styles.fontFamily = params.f;
154  if ('fs' in params && isFinite(parseInt(params.fs, 10)))
155    styles.fontSize = parseInt(params.fs, 10);
156  return styles;
157}
158
159
160/**
161 * @param {string} location A location containing URL parameters.
162 * @param {function(Object, Object)} fill A function called with styles and
163 *     data to fill.
164 */
165function fillMostVisited(location, fill) {
166  var params = parseQueryParams(document.location);
167  params.rid = parseInt(params.rid, 10);
168  if (!isFinite(params.rid) && !params.url)
169    return;
170  // Log whether the suggestion was obtained from the server or the client.
171  chrome.embeddedSearch.newTabPage.logEvent(params.url ?
172      NTP_LOGGING_EVENT_TYPE.NTP_SERVER_SIDE_SUGGESTION :
173      NTP_LOGGING_EVENT_TYPE.NTP_CLIENT_SIDE_SUGGESTION);
174  var data = {};
175  if (params.url) {
176    // Means that the suggestion data comes from the server. Create data object.
177    data.url = params.url;
178    data.thumbnailUrl = params.tu || '';
179    data.thumbnailUrl2 = params.tu2 || '';
180    data.title = params.ti || '';
181    data.direction = params.di || '';
182    data.domain = params.dom || '';
183    data.ping = params.ping || '';
184    data.provider = params.pr || SERVER_PROVIDER_NAME;
185
186    // Log the fact that suggestion was obtained from the server.
187    var ntpApiHandle = chrome.embeddedSearch.newTabPage;
188    ntpApiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_SERVER_SIDE_SUGGESTION);
189  } else {
190    var apiHandle = chrome.embeddedSearch.searchBox;
191    data = apiHandle.getMostVisitedItemData(params.rid);
192    if (!data)
193      return;
194    data.provider = CLIENT_PROVIDER_NAME;
195    delete data.ping;
196  }
197  if (/^javascript:/i.test(data.url) ||
198      /^javascript:/i.test(data.thumbnailUrl) ||
199      /^javascript:/i.test(data.thumbnailUrl2) ||
200      !/^[a-z0-9]{0,8}$/i.test(data.provider))
201    return;
202  if (data.direction)
203    document.body.dir = data.direction;
204  fill(params, data);
205}
206