Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / local_ntp / most_visited_util.js
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 <include src="window_disposition_util.js">
12
13
14 /**
15  * The different types of events that are logged from the NTP.  This enum is
16  * used to transfer information from the NTP javascript to the renderer and is
17  * not used as a UMA enum histogram's logged value.
18  * Note: Keep in sync with common/ntp_logging_events.h
19  * @enum {number}
20  * @const
21  */
22 var NTP_LOGGING_EVENT_TYPE = {
23   // The suggestion is coming from the server.
24   NTP_SERVER_SIDE_SUGGESTION: 0,
25   // The suggestion is coming from the client.
26   NTP_CLIENT_SIDE_SUGGESTION: 1,
27   // Indicates a tile was rendered, no matter if it's a thumbnail, a gray tile
28   // or an external tile.
29   NTP_TILE: 2,
30   // The tile uses a local thumbnail image.
31   NTP_THUMBNAIL_TILE: 3,
32   // Used when no thumbnail is specified and a gray tile with the domain is used
33   // as the main tile.
34   NTP_GRAY_TILE: 4,
35   // The visuals of that tile are handled externally by the page itself.
36   NTP_EXTERNAL_TILE: 5,
37   // There was an error in loading both the thumbnail image and the fallback
38   // (if it was provided), resulting in a grey tile.
39   NTP_THUMBNAIL_ERROR: 6,
40   // Used a gray tile with the domain as the fallback for a failed thumbnail.
41   NTP_GRAY_TILE_FALLBACK: 7,
42   // The visuals of that tile's fallback are handled externally.
43   NTP_EXTERNAL_TILE_FALLBACK: 8,
44   // The user moused over an NTP tile or title.
45   NTP_MOUSEOVER: 9
46 };
47
48 /**
49  * Type of the impression provider for a generic client-provided suggestion.
50  * @type {string}
51  * @const
52  */
53 var CLIENT_PROVIDER_NAME = 'client';
54
55 /**
56  * Type of the impression provider for a generic server-provided suggestion.
57  * @type {string}
58  * @const
59  */
60 var SERVER_PROVIDER_NAME = 'server';
61
62 /**
63  * The origin of this request.
64  * @const {string}
65  */
66 var DOMAIN_ORIGIN = '{{ORIGIN}}';
67
68 /**
69  * Parses query parameters from Location.
70  * @param {string} location The URL to generate the CSS url for.
71  * @return {Object} Dictionary containing name value pairs for URL.
72  */
73 function parseQueryParams(location) {
74   var params = Object.create(null);
75   var query = location.search.substring(1);
76   var vars = query.split('&');
77   for (var i = 0; i < vars.length; i++) {
78     var pair = vars[i].split('=');
79     var k = decodeURIComponent(pair[0]);
80     if (k in params) {
81       // Duplicate parameters are not allowed to prevent attackers who can
82       // append things to |location| from getting their parameter values to
83       // override legitimate ones.
84       return Object.create(null);
85     } else {
86       params[k] = decodeURIComponent(pair[1]);
87     }
88   }
89   return params;
90 }
91
92
93 /**
94  * Creates a new most visited link element.
95  * @param {Object} params URL parameters containing styles for the link.
96  * @param {string} href The destination for the link.
97  * @param {string} title The title for the link.
98  * @param {string|undefined} text The text for the link or none.
99  * @param {string|undefined} direction The text direction.
100  * @param {string|undefined} provider A provider name (max 8 alphanumeric
101  *     characters) used for logging. Undefined if suggestion is not coming from
102  *     the server.
103  * @return {HTMLAnchorElement} A new link element.
104  */
105 function createMostVisitedLink(params, href, title, text, direction, provider) {
106   var styles = getMostVisitedStyles(params, !!text);
107   var link = document.createElement('a');
108   link.style.color = styles.color;
109   link.style.fontSize = styles.fontSize + 'px';
110   if (styles.fontFamily)
111     link.style.fontFamily = styles.fontFamily;
112   if (styles.textAlign)
113     link.style.textAlign = styles.textAlign;
114   if (styles.textFadePos) {
115     var dir = /^rtl$/i.test(direction) ? 'to left' : 'to right';
116     // The fading length in pixels is passed by the caller.
117     var mask = 'linear-gradient(' + dir + ', rgba(0,0,0,1), rgba(0,0,0,1) ' +
118         styles.textFadePos + 'px, rgba(0,0,0,0))';
119     link.style.textOverflow = 'clip';
120     link.style.webkitMask = mask;
121   }
122
123   link.href = href;
124   link.title = title;
125   link.target = '_top';
126   // Include links in the tab order.  The tabIndex is necessary for
127   // accessibility.
128   link.tabIndex = '0';
129   if (text)
130     link.textContent = text;
131   link.addEventListener('mouseover', function() {
132     var ntpApiHandle = chrome.embeddedSearch.newTabPage;
133     ntpApiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_MOUSEOVER);
134   });
135   link.addEventListener('focus', function() {
136     window.parent.postMessage('linkFocused', DOMAIN_ORIGIN);
137   });
138   link.addEventListener('blur', function() {
139     window.parent.postMessage('linkBlurred', DOMAIN_ORIGIN);
140   });
141
142   // Webkit's security policy prevents some Most Visited thumbnails from
143   // working (those with schemes different from http and https). Therefore,
144   // navigateContentWindow is being used in order to get all schemes working.
145   var navigateFunction = function handleNavigation(e) {
146     var isServerSuggestion = 'url' in params;
147
148     // Ping are only populated for server-side suggestions, never for MV.
149     if (isServerSuggestion && params.ping) {
150       generatePing(DOMAIN_ORIGIN + params.ping);
151     }
152
153     var ntpApiHandle = chrome.embeddedSearch.newTabPage;
154     if ('pos' in params && isFinite(params.pos)) {
155       ntpApiHandle.logMostVisitedNavigation(parseInt(params.pos, 10),
156                                             provider || '');
157     }
158
159     if (!isServerSuggestion) {
160       e.preventDefault();
161       ntpApiHandle.navigateContentWindow(href, getDispositionFromEvent(e));
162     }
163     // Else follow <a> normally, so transition type would be LINK.
164   };
165
166   link.addEventListener('click', navigateFunction);
167   link.addEventListener('keydown', function(event) {
168     if (event.keyCode == 46 /* DELETE */ ||
169         event.keyCode == 8 /* BACKSPACE */) {
170       event.preventDefault();
171       window.parent.postMessage('tileBlacklisted,' + params.pos, DOMAIN_ORIGIN);
172     } else if (event.keyCode == 13 /* ENTER */ ||
173                event.keyCode == 32 /* SPACE */) {
174       navigateFunction(event);
175     }
176   });
177
178   return link;
179 }
180
181
182 /**
183  * Returns the color to display string with, depending on whether title is
184  * displayed, the current theme, and URL parameters.
185  * @param {Object.<string, string>} params URL parameters specifying style.
186  * @param {boolean} isTitle if the style is for the Most Visited Title.
187  * @return {string} The color to use, in "rgba(#,#,#,#)" format.
188  */
189 function getTextColor(params, isTitle) {
190   // 'RRGGBBAA' color format overrides everything.
191   if ('c' in params && params.c.match(/^[0-9A-Fa-f]{8}$/)) {
192     // Extract the 4 pairs of hex digits, map to number, then form rgba().
193     var t = params.c.match(/(..)(..)(..)(..)/).slice(1).map(function(s) {
194       return parseInt(s, 16);
195     });
196     return 'rgba(' + t[0] + ',' + t[1] + ',' + t[2] + ',' + t[3] / 255 + ')';
197   }
198
199   // For backward compatibility with server-side NTP, look at themes directly
200   // and use param.c for non-title or as fallback.
201   var apiHandle = chrome.embeddedSearch.newTabPage;
202   var themeInfo = apiHandle.themeBackgroundInfo;
203   var c = '#777';
204   if (isTitle && themeInfo && !themeInfo.usingDefaultTheme) {
205     // Read from theme directly
206     c = convertArrayToRGBAColor(themeInfo.textColorRgba) || c;
207   } else if ('c' in params) {
208     c = convertToHexColor(parseInt(params.c, 16)) || c;
209   }
210   return c;
211 }
212
213
214 /**
215  * Decodes most visited styles from URL parameters.
216  * - c: A hexadecimal number interpreted as a hex color code.
217  * - f: font-family.
218  * - fs: font-size as a number in pixels.
219  * - ta: text-align property, as a string.
220  * - tf: specifying a text fade starting position, in pixels.
221  * @param {Object.<string, string>} params URL parameters specifying style.
222  * @param {boolean} isTitle if the style is for the Most Visited Title.
223  * @return {Object} Styles suitable for CSS interpolation.
224  */
225 function getMostVisitedStyles(params, isTitle) {
226   var styles = {
227     color: getTextColor(params, isTitle),  // Handles 'c' in params.
228     fontFamily: '',
229     fontSize: 11
230   };
231   if ('f' in params && /^[-0-9a-zA-Z ,]+$/.test(params.f))
232     styles.fontFamily = params.f;
233   if ('fs' in params && isFinite(parseInt(params.fs, 10)))
234     styles.fontSize = parseInt(params.fs, 10);
235   if ('ta' in params && /^[-0-9a-zA-Z ,]+$/.test(params.ta))
236     styles.textAlign = params.ta;
237   if ('tf' in params) {
238     var tf = parseInt(params.tf, 10);
239     if (isFinite(tf))
240       styles.textFadePos = tf;
241   }
242   return styles;
243 }
244
245
246 /**
247  * @param {string} location A location containing URL parameters.
248  * @param {function(Object, Object)} fill A function called with styles and
249  *     data to fill.
250  */
251 function fillMostVisited(location, fill) {
252   var params = parseQueryParams(location);
253   params.rid = parseInt(params.rid, 10);
254   if (!isFinite(params.rid) && !params.url)
255     return;
256   // Log whether the suggestion was obtained from the server or the client.
257   chrome.embeddedSearch.newTabPage.logEvent(params.url ?
258       NTP_LOGGING_EVENT_TYPE.NTP_SERVER_SIDE_SUGGESTION :
259       NTP_LOGGING_EVENT_TYPE.NTP_CLIENT_SIDE_SUGGESTION);
260   var data;
261   if (params.url) {
262     // Means that the suggestion data comes from the server. Create data object.
263     data = {
264       url: params.url,
265       thumbnailUrl: params.tu || '',
266       title: params.ti || '',
267       direction: params.di || '',
268       domain: params.dom || '',
269       provider: params.pr || SERVER_PROVIDER_NAME
270     };
271     // Log the fact that suggestion was obtained from the server.
272     var ntpApiHandle = chrome.embeddedSearch.newTabPage;
273     ntpApiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_SERVER_SIDE_SUGGESTION);
274   } else {
275     var apiHandle = chrome.embeddedSearch.searchBox;
276     data = apiHandle.getMostVisitedItemData(params.rid);
277     if (!data)
278       return;
279     // Allow server-side provider override.
280     data.provider = params.pr || CLIENT_PROVIDER_NAME;
281   }
282   if (isFinite(params.dummy) && parseInt(params.dummy, 10)) {
283     data.dummy = true;
284   }
285   if (/^javascript:/i.test(data.url) ||
286       /^javascript:/i.test(data.thumbnailUrl) ||
287       !/^[a-z0-9]{0,8}$/i.test(data.provider))
288     return;
289   if (data.direction)
290     document.body.dir = data.direction;
291   fill(params, data);
292 }
293
294
295 /**
296  * Sends a POST request to ping url.
297  * @param {string} url URL to be pinged.
298  */
299 function generatePing(url) {
300   if (navigator.sendBeacon) {
301     navigator.sendBeacon(url);
302   } else {
303     // if sendBeacon is not enabled, we fallback for "a ping".
304     var a = document.createElement('a');
305     a.href = '#';
306     a.ping = url;
307     a.click();
308   }
309 }