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

Side by Side Diff: chrome/renderer/resources/extensions/searchbox_api.js

Issue 12386019: Instant: Use only one hidden WebContents per profile. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 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 var chrome; 5 var chrome;
6
6 if (!chrome) 7 if (!chrome)
7 chrome = {}; 8 chrome = {};
8 9
9 if (!chrome.embeddedSearch) { 10 if (!chrome.searchBox) {
10 chrome.embeddedSearch = new function() { 11 chrome.searchBox = new function() {
12 native function GetQuery();
13 native function GetVerbatim();
14 native function GetSelectionStart();
15 native function GetSelectionEnd();
16 native function GetX();
17 native function GetY();
18 native function GetWidth();
19 native function GetHeight();
20 native function SetSuggestion();
11 21
12 // ========================================================================= 22 this.__defineGetter__('value', GetQuery);
13 // Private functions 23 this.__defineGetter__('verbatim', GetVerbatim);
14 // ========================================================================= 24 this.__defineGetter__('selectionStart', GetSelectionStart);
15 native function GetFont(); 25 this.__defineGetter__('selectionEnd', GetSelectionEnd);
16 native function NavigateContentWindow(); 26 this.__defineGetter__('x', GetX);
27 this.__defineGetter__('y', GetY);
28 this.__defineGetter__('width', GetWidth);
29 this.__defineGetter__('height', GetHeight);
17 30
18 function escapeHTML(text) { 31 this.setSuggestions = function(obj) {
19 return text.replace(/[<>&"']/g, function(match) { 32 SetSuggestion(obj);
20 switch (match) {
21 case '<': return '&lt;';
22 case '>': return '&gt;';
23 case '&': return '&amp;';
24 case '"': return '&quot;';
25 case "'": return '&apos;';
26 }
27 });
28 }
29
30 var safeObjects = {};
31
32 // Returns the |restrictedText| wrapped in a ShadowDOM.
33 function SafeWrap(restrictedText, width, height, opt_fontSize,
34 opt_direction) {
35 var node = document.createElement('div');
36 var nodeShadow = safeObjects.createShadowRoot.apply(node);
37 nodeShadow.applyAuthorStyles = true;
38 nodeShadow.innerHTML =
39 '<div style="' +
40 'width: ' + width + 'px !important;' +
41 'height: ' + height + 'px !important;' +
42 'font-family: \'' + GetFont() + '\', \'Arial\' !important;' +
43 (opt_fontSize ?
44 'font-size: ' + opt_fontSize + 'px !important;' : '') +
45 'overflow: hidden !important;' +
46 'text-overflow: ellipsis !important;' +
47 'white-space: nowrap !important"' +
48 (opt_direction ? ' dir="' + opt_direction + '"' : '') +
49 '>' +
50 restrictedText +
51 '</div>';
52 safeObjects.defineProperty(node, 'webkitShadowRoot', { value: null });
53 return node;
54 }
55
56 chrome.embeddedSearchOnWindowReady = function() {
57 // |embeddedSearchOnWindowReady| is used for initializing window context
58 // and should be called only once per context.
59 safeObjects.createShadowRoot = Element.prototype.webkitCreateShadowRoot;
60 safeObjects.defineProperty = Object.defineProperty;
61 delete window.chrome.embeddedSearchOnWindowReady;
62 }; 33 };
63 34
64 // ========================================================================= 35 this.onchange = null;
65 // Exported functions 36 this.onsubmit = null;
66 // ========================================================================= 37 this.oncancel = null;
67 this.navigateContentWindow = function(destination, disposition) { 38 this.onresize = null;
68 return NavigateContentWindow(destination, disposition);
69 };
70
71 this.searchBox = new function() {
72
73 // =======================================================================
74 // Constants
75 // =======================================================================
76 var MAX_CLIENT_SUGGESTIONS_TO_DEDUPE = 6;
77 var MAX_ALLOWED_DEDUPE_ATTEMPTS = 5;
78
79 var HTTP_REGEX = /^https?:\/\//;
80
81 var WWW_REGEX = /^www\./;
82
83 // =======================================================================
84 // Private functions
85 // =======================================================================
86 native function GetQuery();
87 native function GetVerbatim();
88 native function GetSelectionStart();
89 native function GetSelectionEnd();
90 native function GetStartMargin();
91 native function GetRightToLeft();
92 native function GetAutocompleteResults();
93 native function GetDisplayInstantResults();
94 native function GetFontSize();
95 native function IsKeyCaptureEnabled();
96 native function SetSuggestions();
97 native function SetQuerySuggestion();
98 native function SetQuerySuggestionFromAutocompleteResult();
99 native function SetQuery();
100 native function SetQueryFromAutocompleteResult();
101 native function ShowOverlay();
102 native function FocusOmnibox();
103 native function StartCapturingKeyStrokes();
104 native function StopCapturingKeyStrokes();
105
106 function SafeWrapSuggestion(restrictedText) {
107 return SafeWrap(restrictedText, window.innerWidth - 155, 22);
108 }
109
110 // Wraps the AutocompleteResult query and URL into ShadowDOM nodes so that
111 // the JS cannot access them and deletes the raw values.
112 function GetAutocompleteResultsWrapper() {
113 var autocompleteResults = DedupeAutocompleteResults(
114 GetAutocompleteResults());
115 var userInput = GetQuery();
116 for (var i = 0, result; result = autocompleteResults[i]; ++i) {
117 var title = escapeHTML(result.contents);
118 var url = escapeHTML(CleanUrl(result.destination_url, userInput));
119 var combinedHtml = '<span class=chrome_url>' + url + '</span>';
120 // TODO(dcblack): Rename these titleElement, urlElement, and
121 // combinedElement for optimal correctness.
122 if (title) {
123 result.titleNode = SafeWrapSuggestion(title);
124 combinedHtml += '<span class=chrome_separator> &ndash; </span>' +
125 '<span class=chrome_title>' + title + '</span>';
126 }
127 result.urlNode = SafeWrapSuggestion(url);
128 result.combinedNode = SafeWrapSuggestion(combinedHtml);
129 delete result.contents;
130 delete result.destination_url;
131 }
132 return autocompleteResults;
133 }
134
135 // TODO(dcblack): Do this in C++ instead of JS.
136 function CleanUrl(url, userInput) {
137 if (url.indexOf(userInput) == 0) {
138 return url;
139 }
140 url = url.replace(HTTP_REGEX, '');
141 if (url.indexOf(userInput) == 0) {
142 return url;
143 }
144 return url.replace(WWW_REGEX, '');
145 }
146
147 // TODO(dcblack): Do this in C++ instead of JS.
148 function CanonicalizeUrl(url) {
149 return url.replace(HTTP_REGEX, '').replace(WWW_REGEX, '');
150 }
151
152 // Removes duplicates from AutocompleteResults.
153 // TODO(dcblack): Do this in C++ instead of JS.
154 function DedupeAutocompleteResults(autocompleteResults) {
155 var urlToResultMap = {};
156 for (var i = 0, result; result = autocompleteResults[i]; ++i) {
157 var url = CanonicalizeUrl(result.destination_url);
158 if (url in urlToResultMap) {
159 var oldRelevance = urlToResultMap[url].rankingData.relevance;
160 var newRelevance = result.rankingData.relevance;
161 if (newRelevance > oldRelevance) {
162 urlToResultMap[url] = result;
163 }
164 } else {
165 urlToResultMap[url] = result;
166 }
167 }
168 var dedupedResults = [];
169 for (url in urlToResultMap) {
170 dedupedResults.push(urlToResultMap[url]);
171 }
172 return dedupedResults;
173 }
174
175 var lastPrefixQueriedForDuplicates = '';
176 var numDedupeAttempts = 0;
177
178 function DedupeClientSuggestions(clientSuggestions) {
179 var userInput = GetQuery();
180 if (userInput == lastPrefixQueriedForDuplicates) {
181 numDedupeAttempts += 1;
182 if (numDedupeAttempts > MAX_ALLOWED_DEDUPE_ATTEMPTS) {
183 // Request blocked for privacy reasons.
184 // TODO(dcblack): This check is insufficient. We should have a
185 // check such that it's only callable once per onnativesuggestions,
186 // not once per prefix. Also, there is a timing problem where if
187 // the user types quickly then the client will (correctly) attempt
188 // to render stale results, and end up calling dedupe multiple times
189 // when getValue shows the same prefix. A better solution would be
190 // to have the client send up rid ranges to dedupe against and have
191 // the binary keep around all the old suggestions ever given to this
192 // overlay. I suspect such an approach would clean up this code
193 // quite a bit.
194 return false;
195 }
196 } else {
197 lastPrefixQueriedForDuplicates = userInput;
198 numDedupeAttempts = 1;
199 }
200
201 var autocompleteResults = DedupeAutocompleteResults(
202 GetAutocompleteResults());
203 var nativeUrls = {};
204 for (var i = 0, result; result = autocompleteResults[i]; ++i) {
205 var nativeUrl = CanonicalizeUrl(result.destination_url);
206 nativeUrls[nativeUrl] = result.rid;
207 }
208 for (var i = 0; clientSuggestions[i] &&
209 i < MAX_CLIENT_SUGGESTIONS_TO_DEDUPE; ++i) {
210 var result = clientSuggestions[i];
211 if (result.url) {
212 var clientUrl = CanonicalizeUrl(result.url);
213 if (clientUrl in nativeUrls) {
214 result.duplicateOf = nativeUrls[clientUrl];
215 }
216 }
217 }
218 return true;
219 }
220
221 // =======================================================================
222 // Exported functions
223 // =======================================================================
224 this.__defineGetter__('value', GetQuery);
225 this.__defineGetter__('verbatim', GetVerbatim);
226 this.__defineGetter__('selectionStart', GetSelectionStart);
227 this.__defineGetter__('selectionEnd', GetSelectionEnd);
228 this.__defineGetter__('startMargin', GetStartMargin);
229 this.__defineGetter__('rtl', GetRightToLeft);
230 this.__defineGetter__('nativeSuggestions', GetAutocompleteResultsWrapper);
231 this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled);
232 this.__defineGetter__('displayInstantResults', GetDisplayInstantResults);
233 this.__defineGetter__('font', GetFont);
234 this.__defineGetter__('fontSize', GetFontSize);
235
236 this.setSuggestions = function(text) {
237 SetSuggestions(text);
238 };
239 this.setAutocompleteText = function(text, behavior) {
240 SetQuerySuggestion(text, behavior);
241 };
242 this.setRestrictedAutocompleteText = function(resultId) {
243 SetQuerySuggestionFromAutocompleteResult(resultId);
244 };
245 this.setValue = function(text, type) {
246 SetQuery(text, type);
247 };
248 this.setRestrictedValue = function(resultId) {
249 SetQueryFromAutocompleteResult(resultId);
250 };
251 // TODO(jered): Remove the deprecated "reason" argument.
252 this.showOverlay = function(reason, height) {
253 ShowOverlay(reason, height);
254 };
255 // TODO(jered): Remove this when GWS knows about showOverlay().
256 this.show = this.showOverlay;
257 this.markDuplicateSuggestions = function(clientSuggestions) {
258 return DedupeClientSuggestions(clientSuggestions);
259 };
260 this.focus = function() {
261 FocusOmnibox();
262 };
263 this.startCapturingKeyStrokes = function() {
264 StartCapturingKeyStrokes();
265 };
266 this.stopCapturingKeyStrokes = function() {
267 StopCapturingKeyStrokes();
268 };
269 this.onchange = null;
270 this.onsubmit = null;
271 this.oncancel = null;
272 this.onresize = null;
273 this.onkeypress = null;
274 this.onkeycapturechange = null;
275 this.oncontextchange = null;
276 this.onmarginchange = null;
277 this.onnativesuggestions = null;
278
279 // DEPRECATED. These methods are from the legacy searchbox API.
280 // TODO(jered): Delete these.
281 native function GetX();
282 native function GetY();
283 native function GetWidth();
284 native function GetHeight();
285 this.__defineGetter__('x', GetX);
286 this.__defineGetter__('y', GetY);
287 this.__defineGetter__('width', GetWidth);
288 this.__defineGetter__('height', GetHeight);
289 };
290
291 this.newTabPage = new function() {
292
293 // =======================================================================
294 // Private functions
295 // =======================================================================
296 native function GetMostVisitedItems();
297 native function GetThemeBackgroundInfo();
298 native function DeleteMostVisitedItem();
299 native function UndoAllMostVisitedDeletions();
300 native function UndoMostVisitedDeletion();
301
302 function SafeWrapMostVisited(restrictedText, width, opt_direction) {
303 return SafeWrap(restrictedText, width, 18, 11, opt_direction);
304 }
305
306 function GetMostVisitedItemsWrapper() {
307 var mostVisitedItems = GetMostVisitedItems();
308 for (var i = 0, item; item = mostVisitedItems[i]; ++i) {
309 var title = escapeHTML(item.title);
310 var domain = escapeHTML(item.domain);
311 item.titleElement = SafeWrapMostVisited(title, 140, item.direction);
312 item.domainElement = SafeWrapMostVisited(domain, 123);
313 delete item.title;
314 delete item.domain;
315 delete item.direction;
316 }
317 return mostVisitedItems;
318 }
319
320 // =======================================================================
321 // Exported functions
322 // =======================================================================
323 this.__defineGetter__('mostVisited', GetMostVisitedItemsWrapper);
324 this.__defineGetter__('themeBackgroundInfo', GetThemeBackgroundInfo);
325
326 this.deleteMostVisitedItem = function(restrictId) {
327 DeleteMostVisitedItem(restrictId);
328 };
329 this.undoAllMostVisitedDeletions = function() {
330 UndoAllMostVisitedDeletions();
331 };
332 this.undoMostVisitedDeletion = function(restrictId) {
333 UndoMostVisitedDeletion(restrictId);
334 };
335
336 this.onmostvisitedchange = null;
337 this.onthemechange = null;
338 };
339
340 // Export legacy searchbox API.
341 // TODO: Remove this when Instant Extended is fully launched.
342 chrome.searchBox = this.searchBox;
343 }; 39 };
344 } 40 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698