searchbox_api.js revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 3// found in the LICENSE file. 4 5var chrome; 6if (!chrome) 7 chrome = {}; 8 9if (!chrome.embeddedSearch) { 10 chrome.embeddedSearch = new function() { 11 12 // ========================================================================= 13 // Private functions 14 // ========================================================================= 15 native function GetFont(); 16 // DEPRECATED. TODO(sreeram): Remove once google.com no longer uses this. 17 native function NavigateContentWindow(); 18 19 // ========================================================================= 20 // Exported functions 21 // ========================================================================= 22 // DEPRECATED. TODO(sreeram): Remove once google.com no longer uses this. 23 this.navigateContentWindow = function(destination, disposition) { 24 return NavigateContentWindow(destination, disposition); 25 }; 26 27 this.searchBox = new function() { 28 29 // ======================================================================= 30 // Constants 31 // ======================================================================= 32 var MAX_CLIENT_SUGGESTIONS_TO_DEDUPE = 6; 33 var MAX_ALLOWED_DEDUPE_ATTEMPTS = 5; 34 35 var HTTP_REGEX = /^https?:\/\//; 36 37 var WWW_REGEX = /^www\./; 38 39 // ======================================================================= 40 // Private functions 41 // ======================================================================= 42 native function GetQuery(); 43 native function GetVerbatim(); 44 native function GetSelectionStart(); 45 native function GetSelectionEnd(); 46 native function GetStartMargin(); 47 native function GetRightToLeft(); 48 native function GetAutocompleteResults(); 49 native function GetDisplayInstantResults(); 50 native function GetFontSize(); 51 native function IsFocused(); 52 native function IsKeyCaptureEnabled(); 53 native function SetQuery(); 54 native function SetQueryFromAutocompleteResult(); 55 native function SetSuggestion(); 56 native function SetSuggestionFromAutocompleteResult(); 57 native function SetSuggestions(); 58 native function ShowOverlay(); 59 native function FocusOmnibox(); 60 native function StartCapturingKeyStrokes(); 61 native function StopCapturingKeyStrokes(); 62 native function NavigateSearchBox(); 63 native function ShowBars(); 64 native function HideBars(); 65 native function GetSuggestionData(); 66 native function GetMostVisitedItemData(); 67 68 function GetAutocompleteResultsWrapper() { 69 var autocompleteResults = DedupeAutocompleteResults( 70 GetAutocompleteResults()); 71 for (var i = 0, result; result = autocompleteResults[i]; ++i) { 72 result.destination_url = null; 73 result.contents = null; 74 result.description = null; 75 } 76 return autocompleteResults; 77 } 78 79 // TODO(dcblack): Do this in C++ instead of JS. 80 function CanonicalizeUrl(url) { 81 return url.replace(HTTP_REGEX, '').replace(WWW_REGEX, ''); 82 } 83 84 // Removes duplicates from AutocompleteResults. 85 // TODO(dcblack): Do this in C++ instead of JS. 86 function DedupeAutocompleteResults(autocompleteResults) { 87 var urlToResultMap = {}; 88 for (var i = 0, result; result = autocompleteResults[i]; ++i) { 89 var url = CanonicalizeUrl(result.destination_url); 90 if (url in urlToResultMap) { 91 var oldRelevance = urlToResultMap[url].rankingData.relevance; 92 var newRelevance = result.rankingData.relevance; 93 if (newRelevance > oldRelevance) { 94 urlToResultMap[url] = result; 95 } 96 } else { 97 urlToResultMap[url] = result; 98 } 99 } 100 var dedupedResults = []; 101 for (url in urlToResultMap) { 102 dedupedResults.push(urlToResultMap[url]); 103 } 104 return dedupedResults; 105 } 106 107 var lastPrefixQueriedForDuplicates = ''; 108 var numDedupeAttempts = 0; 109 110 function DedupeClientSuggestions(clientSuggestions) { 111 var userInput = GetQuery(); 112 if (userInput == lastPrefixQueriedForDuplicates) { 113 numDedupeAttempts += 1; 114 if (numDedupeAttempts > MAX_ALLOWED_DEDUPE_ATTEMPTS) { 115 // Request blocked for privacy reasons. 116 // TODO(dcblack): This check is insufficient. We should have a 117 // check such that it's only callable once per onnativesuggestions, 118 // not once per prefix. Also, there is a timing problem where if 119 // the user types quickly then the client will (correctly) attempt 120 // to render stale results, and end up calling dedupe multiple times 121 // when getValue shows the same prefix. A better solution would be 122 // to have the client send up rid ranges to dedupe against and have 123 // the binary keep around all the old suggestions ever given to this 124 // overlay. I suspect such an approach would clean up this code 125 // quite a bit. 126 return false; 127 } 128 } else { 129 lastPrefixQueriedForDuplicates = userInput; 130 numDedupeAttempts = 1; 131 } 132 133 var autocompleteResults = DedupeAutocompleteResults( 134 GetAutocompleteResults()); 135 var nativeUrls = {}; 136 for (var i = 0, result; result = autocompleteResults[i]; ++i) { 137 var nativeUrl = CanonicalizeUrl(result.destination_url); 138 nativeUrls[nativeUrl] = result.rid; 139 } 140 for (var i = 0; clientSuggestions[i] && 141 i < MAX_CLIENT_SUGGESTIONS_TO_DEDUPE; ++i) { 142 var result = clientSuggestions[i]; 143 if (result.url) { 144 var clientUrl = CanonicalizeUrl(result.url); 145 if (clientUrl in nativeUrls) { 146 result.duplicateOf = nativeUrls[clientUrl]; 147 } 148 } 149 } 150 return true; 151 } 152 153 // ======================================================================= 154 // Exported functions 155 // ======================================================================= 156 this.__defineGetter__('value', GetQuery); 157 this.__defineGetter__('verbatim', GetVerbatim); 158 this.__defineGetter__('selectionStart', GetSelectionStart); 159 this.__defineGetter__('selectionEnd', GetSelectionEnd); 160 this.__defineGetter__('startMargin', GetStartMargin); 161 this.__defineGetter__('rtl', GetRightToLeft); 162 this.__defineGetter__('nativeSuggestions', GetAutocompleteResultsWrapper); 163 this.__defineGetter__('isFocused', IsFocused); 164 this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled); 165 this.__defineGetter__('displayInstantResults', GetDisplayInstantResults); 166 this.__defineGetter__('font', GetFont); 167 this.__defineGetter__('fontSize', GetFontSize); 168 169 // This method is restricted to chrome-search://suggestion pages by 170 // checking the invoking context's origin in searchbox_extension.cc. 171 this.getSuggestionData = function(restrictedId) { 172 return GetSuggestionData(restrictedId); 173 }; 174 175 // This method is restricted to chrome-search://most-visited pages by 176 // checking the invoking context's origin in searchbox_extension.cc. 177 this.getMostVisitedItemData = function(restrictedId) { 178 return GetMostVisitedItemData(restrictedId); 179 }; 180 181 this.setSuggestions = function(text) { 182 SetSuggestions(text); 183 }; 184 this.setAutocompleteText = function(text, behavior) { 185 SetSuggestion(text, behavior); 186 }; 187 this.setRestrictedAutocompleteText = function(autocompleteResultId) { 188 SetSuggestionFromAutocompleteResult(autocompleteResultId); 189 }; 190 this.setValue = function(text, type) { 191 SetQuery(text, type); 192 }; 193 // Must access nativeSuggestions before calling setRestrictedValue. 194 this.setRestrictedValue = function(autocompleteResultId) { 195 SetQueryFromAutocompleteResult(autocompleteResultId); 196 }; 197 this.showOverlay = function(height) { 198 ShowOverlay(height); 199 }; 200 this.markDuplicateSuggestions = function(clientSuggestions) { 201 return DedupeClientSuggestions(clientSuggestions); 202 }; 203 this.focus = function() { 204 FocusOmnibox(); 205 }; 206 this.startCapturingKeyStrokes = function() { 207 StartCapturingKeyStrokes(); 208 }; 209 this.stopCapturingKeyStrokes = function() { 210 StopCapturingKeyStrokes(); 211 }; 212 this.navigateContentWindow = function(destination, disposition) { 213 NavigateSearchBox(destination, disposition); 214 }; 215 this.showBars = function() { 216 ShowBars(); 217 }; 218 this.hideBars = function() { 219 HideBars(); 220 }; 221 this.onchange = null; 222 this.onsubmit = null; 223 this.oncancel = null; 224 this.onresize = null; 225 this.onkeypress = null; 226 this.onkeycapturechange = null; 227 this.onmarginchange = null; 228 this.onnativesuggestions = null; 229 this.onbarshidden = null; 230 this.onfocuschange = null; 231 this.ontogglevoicesearch = null; 232 233 // DEPRECATED. These methods are from the legacy searchbox API. 234 // TODO(jered): Delete these. 235 native function GetX(); 236 native function GetY(); 237 native function GetWidth(); 238 native function GetHeight(); 239 this.__defineGetter__('x', GetX); 240 this.__defineGetter__('y', GetY); 241 this.__defineGetter__('width', GetWidth); 242 this.__defineGetter__('height', GetHeight); 243 }; 244 245 this.newTabPage = new function() { 246 247 // ======================================================================= 248 // Private functions 249 // ======================================================================= 250 native function GetMostVisitedItems(); 251 native function GetThemeBackgroundInfo(); 252 native function DeleteMostVisitedItem(); 253 native function UndoAllMostVisitedDeletions(); 254 native function UndoMostVisitedDeletion(); 255 native function NavigateNewTabPage(); 256 native function IsInputInProgress(); 257 258 function GetMostVisitedItemsWrapper() { 259 var mostVisitedItems = GetMostVisitedItems(); 260 for (var i = 0, item; item = mostVisitedItems[i]; ++i) { 261 // These properties are private data and should not be returned to 262 // the page. They are only accessible via getMostVisitedItemData(). 263 item.url = null; 264 item.title = null; 265 item.domain = null; 266 item.direction = null; 267 } 268 return mostVisitedItems; 269 } 270 271 // ======================================================================= 272 // Exported functions 273 // ======================================================================= 274 this.__defineGetter__('mostVisited', GetMostVisitedItemsWrapper); 275 this.__defineGetter__('themeBackgroundInfo', GetThemeBackgroundInfo); 276 this.__defineGetter__('isInputInProgress', IsInputInProgress); 277 278 this.deleteMostVisitedItem = function(restrictedId) { 279 DeleteMostVisitedItem(restrictedId); 280 }; 281 this.undoAllMostVisitedDeletions = function() { 282 UndoAllMostVisitedDeletions(); 283 }; 284 this.undoMostVisitedDeletion = function(restrictedId) { 285 UndoMostVisitedDeletion(restrictedId); 286 }; 287 this.navigateContentWindow = function(destination, disposition) { 288 NavigateNewTabPage(destination, disposition); 289 } 290 291 this.onmostvisitedchange = null; 292 this.onthemechange = null; 293 this.oninputstart = null; 294 this.oninputcancel = null; 295 }; 296 297 // Export legacy searchbox API. 298 // TODO: Remove this when Instant Extended is fully launched. 299 chrome.searchBox = this.searchBox; 300 }; 301} 302