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

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

Powered by Google App Engine
This is Rietveld 408576698