Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | |
michael@0 | 6 | const Cc = Components.classes; |
michael@0 | 7 | const Ci = Components.interfaces; |
michael@0 | 8 | const Cr = Components.results; |
michael@0 | 9 | |
michael@0 | 10 | Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 11 | Components.utils.import("resource://gre/modules/Services.jsm"); |
michael@0 | 12 | |
michael@0 | 13 | XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", |
michael@0 | 14 | "resource://gre/modules/BrowserUtils.jsm"); |
michael@0 | 15 | XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", |
michael@0 | 16 | "resource://gre/modules/Deprecated.jsm"); |
michael@0 | 17 | XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", |
michael@0 | 18 | "resource://gre/modules/FormHistory.jsm"); |
michael@0 | 19 | |
michael@0 | 20 | function FormAutoComplete() { |
michael@0 | 21 | this.init(); |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | /** |
michael@0 | 25 | * FormAutoComplete |
michael@0 | 26 | * |
michael@0 | 27 | * Implements the nsIFormAutoComplete interface in the main process. |
michael@0 | 28 | */ |
michael@0 | 29 | FormAutoComplete.prototype = { |
michael@0 | 30 | classID : Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"), |
michael@0 | 31 | QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]), |
michael@0 | 32 | |
michael@0 | 33 | _prefBranch : null, |
michael@0 | 34 | _debug : true, // mirrors browser.formfill.debug |
michael@0 | 35 | _enabled : true, // mirrors browser.formfill.enable preference |
michael@0 | 36 | _agedWeight : 2, |
michael@0 | 37 | _bucketSize : 1, |
michael@0 | 38 | _maxTimeGroupings : 25, |
michael@0 | 39 | _timeGroupingSize : 7 * 24 * 60 * 60 * 1000 * 1000, |
michael@0 | 40 | _expireDays : null, |
michael@0 | 41 | _boundaryWeight : 25, |
michael@0 | 42 | _prefixWeight : 5, |
michael@0 | 43 | |
michael@0 | 44 | // Only one query is performed at a time, which will be stored in _pendingQuery |
michael@0 | 45 | // while the query is being performed. It will be cleared when the query finishes, |
michael@0 | 46 | // is cancelled, or an error occurs. If a new query occurs while one is already |
michael@0 | 47 | // pending, the existing one is cancelled. The pending query will be an |
michael@0 | 48 | // mozIStoragePendingStatement object. |
michael@0 | 49 | _pendingQuery : null, |
michael@0 | 50 | |
michael@0 | 51 | init : function() { |
michael@0 | 52 | // Preferences. Add observer so we get notified of changes. |
michael@0 | 53 | this._prefBranch = Services.prefs.getBranch("browser.formfill."); |
michael@0 | 54 | this._prefBranch.addObserver("", this.observer, true); |
michael@0 | 55 | this.observer._self = this; |
michael@0 | 56 | |
michael@0 | 57 | this._debug = this._prefBranch.getBoolPref("debug"); |
michael@0 | 58 | this._enabled = this._prefBranch.getBoolPref("enable"); |
michael@0 | 59 | this._agedWeight = this._prefBranch.getIntPref("agedWeight"); |
michael@0 | 60 | this._bucketSize = this._prefBranch.getIntPref("bucketSize"); |
michael@0 | 61 | this._maxTimeGroupings = this._prefBranch.getIntPref("maxTimeGroupings"); |
michael@0 | 62 | this._timeGroupingSize = this._prefBranch.getIntPref("timeGroupingSize") * 1000 * 1000; |
michael@0 | 63 | this._expireDays = this._prefBranch.getIntPref("expire_days"); |
michael@0 | 64 | }, |
michael@0 | 65 | |
michael@0 | 66 | observer : { |
michael@0 | 67 | _self : null, |
michael@0 | 68 | |
michael@0 | 69 | QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, |
michael@0 | 70 | Ci.nsISupportsWeakReference]), |
michael@0 | 71 | |
michael@0 | 72 | observe : function (subject, topic, data) { |
michael@0 | 73 | let self = this._self; |
michael@0 | 74 | if (topic == "nsPref:changed") { |
michael@0 | 75 | let prefName = data; |
michael@0 | 76 | self.log("got change to " + prefName + " preference"); |
michael@0 | 77 | |
michael@0 | 78 | switch (prefName) { |
michael@0 | 79 | case "agedWeight": |
michael@0 | 80 | self._agedWeight = self._prefBranch.getIntPref(prefName); |
michael@0 | 81 | break; |
michael@0 | 82 | case "debug": |
michael@0 | 83 | self._debug = self._prefBranch.getBoolPref(prefName); |
michael@0 | 84 | break; |
michael@0 | 85 | case "enable": |
michael@0 | 86 | self._enabled = self._prefBranch.getBoolPref(prefName); |
michael@0 | 87 | break; |
michael@0 | 88 | case "maxTimeGroupings": |
michael@0 | 89 | self._maxTimeGroupings = self._prefBranch.getIntPref(prefName); |
michael@0 | 90 | break; |
michael@0 | 91 | case "timeGroupingSize": |
michael@0 | 92 | self._timeGroupingSize = self._prefBranch.getIntPref(prefName) * 1000 * 1000; |
michael@0 | 93 | break; |
michael@0 | 94 | case "bucketSize": |
michael@0 | 95 | self._bucketSize = self._prefBranch.getIntPref(prefName); |
michael@0 | 96 | break; |
michael@0 | 97 | case "boundaryWeight": |
michael@0 | 98 | self._boundaryWeight = self._prefBranch.getIntPref(prefName); |
michael@0 | 99 | break; |
michael@0 | 100 | case "prefixWeight": |
michael@0 | 101 | self._prefixWeight = self._prefBranch.getIntPref(prefName); |
michael@0 | 102 | break; |
michael@0 | 103 | default: |
michael@0 | 104 | self.log("Oops! Pref not handled, change ignored."); |
michael@0 | 105 | } |
michael@0 | 106 | } |
michael@0 | 107 | } |
michael@0 | 108 | }, |
michael@0 | 109 | |
michael@0 | 110 | |
michael@0 | 111 | /* |
michael@0 | 112 | * log |
michael@0 | 113 | * |
michael@0 | 114 | * Internal function for logging debug messages to the Error Console |
michael@0 | 115 | * window |
michael@0 | 116 | */ |
michael@0 | 117 | log : function (message) { |
michael@0 | 118 | if (!this._debug) |
michael@0 | 119 | return; |
michael@0 | 120 | dump("FormAutoComplete: " + message + "\n"); |
michael@0 | 121 | Services.console.logStringMessage("FormAutoComplete: " + message); |
michael@0 | 122 | }, |
michael@0 | 123 | |
michael@0 | 124 | |
michael@0 | 125 | autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) { |
michael@0 | 126 | Deprecated.warning("nsIFormAutoComplete::autoCompleteSearch is deprecated", "https://bugzilla.mozilla.org/show_bug.cgi?id=697377"); |
michael@0 | 127 | |
michael@0 | 128 | let result = null; |
michael@0 | 129 | let listener = { |
michael@0 | 130 | onSearchCompletion: function (r) result = r |
michael@0 | 131 | }; |
michael@0 | 132 | this._autoCompleteSearchShared(aInputName, aUntrimmedSearchString, aField, aPreviousResult, listener); |
michael@0 | 133 | |
michael@0 | 134 | // Just wait for the result to to be available. |
michael@0 | 135 | let thread = Components.classes["@mozilla.org/thread-manager;1"].getService().currentThread; |
michael@0 | 136 | while (!result && this._pendingQuery) { |
michael@0 | 137 | thread.processNextEvent(true); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | return result; |
michael@0 | 141 | }, |
michael@0 | 142 | |
michael@0 | 143 | autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) { |
michael@0 | 144 | this._autoCompleteSearchShared(aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener); |
michael@0 | 145 | }, |
michael@0 | 146 | |
michael@0 | 147 | /* |
michael@0 | 148 | * autoCompleteSearchShared |
michael@0 | 149 | * |
michael@0 | 150 | * aInputName -- |name| attribute from the form input being autocompleted. |
michael@0 | 151 | * aUntrimmedSearchString -- current value of the input |
michael@0 | 152 | * aField -- nsIDOMHTMLInputElement being autocompleted (may be null if from chrome) |
michael@0 | 153 | * aPreviousResult -- previous search result, if any. |
michael@0 | 154 | * aListener -- nsIFormAutoCompleteObserver that listens for the nsIAutoCompleteResult |
michael@0 | 155 | * that may be returned asynchronously. |
michael@0 | 156 | */ |
michael@0 | 157 | _autoCompleteSearchShared : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) { |
michael@0 | 158 | function sortBytotalScore (a, b) { |
michael@0 | 159 | return b.totalScore - a.totalScore; |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | let result = null; |
michael@0 | 163 | if (!this._enabled) { |
michael@0 | 164 | result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString); |
michael@0 | 165 | if (aListener) { |
michael@0 | 166 | aListener.onSearchCompletion(result); |
michael@0 | 167 | } |
michael@0 | 168 | return; |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | // don't allow form inputs (aField != null) to get results from search bar history |
michael@0 | 172 | if (aInputName == 'searchbar-history' && aField) { |
michael@0 | 173 | this.log('autoCompleteSearch for input name "' + aInputName + '" is denied'); |
michael@0 | 174 | result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString); |
michael@0 | 175 | if (aListener) { |
michael@0 | 176 | aListener.onSearchCompletion(result); |
michael@0 | 177 | } |
michael@0 | 178 | return; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | this.log("AutoCompleteSearch invoked. Search is: " + aUntrimmedSearchString); |
michael@0 | 182 | let searchString = aUntrimmedSearchString.trim().toLowerCase(); |
michael@0 | 183 | |
michael@0 | 184 | // reuse previous results if: |
michael@0 | 185 | // a) length greater than one character (others searches are special cases) AND |
michael@0 | 186 | // b) the the new results will be a subset of the previous results |
michael@0 | 187 | if (aPreviousResult && aPreviousResult.searchString.trim().length > 1 && |
michael@0 | 188 | searchString.indexOf(aPreviousResult.searchString.trim().toLowerCase()) >= 0) { |
michael@0 | 189 | this.log("Using previous autocomplete result"); |
michael@0 | 190 | result = aPreviousResult; |
michael@0 | 191 | result.wrappedJSObject.searchString = aUntrimmedSearchString; |
michael@0 | 192 | |
michael@0 | 193 | let searchTokens = searchString.split(/\s+/); |
michael@0 | 194 | // We have a list of results for a shorter search string, so just |
michael@0 | 195 | // filter them further based on the new search string and add to a new array. |
michael@0 | 196 | let entries = result.wrappedJSObject.entries; |
michael@0 | 197 | let filteredEntries = []; |
michael@0 | 198 | for (let i = 0; i < entries.length; i++) { |
michael@0 | 199 | let entry = entries[i]; |
michael@0 | 200 | // Remove results that do not contain the token |
michael@0 | 201 | // XXX bug 394604 -- .toLowerCase can be wrong for some intl chars |
michael@0 | 202 | if(searchTokens.some(function (tok) entry.textLowerCase.indexOf(tok) < 0)) |
michael@0 | 203 | continue; |
michael@0 | 204 | this._calculateScore(entry, searchString, searchTokens); |
michael@0 | 205 | this.log("Reusing autocomplete entry '" + entry.text + |
michael@0 | 206 | "' (" + entry.frecency +" / " + entry.totalScore + ")"); |
michael@0 | 207 | filteredEntries.push(entry); |
michael@0 | 208 | } |
michael@0 | 209 | filteredEntries.sort(sortBytotalScore); |
michael@0 | 210 | result.wrappedJSObject.entries = filteredEntries; |
michael@0 | 211 | |
michael@0 | 212 | if (aListener) { |
michael@0 | 213 | aListener.onSearchCompletion(result); |
michael@0 | 214 | } |
michael@0 | 215 | } else { |
michael@0 | 216 | this.log("Creating new autocomplete search result."); |
michael@0 | 217 | |
michael@0 | 218 | // Start with an empty list. |
michael@0 | 219 | result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString); |
michael@0 | 220 | |
michael@0 | 221 | let processEntry = function(aEntries) { |
michael@0 | 222 | if (aField && aField.maxLength > -1) { |
michael@0 | 223 | result.entries = |
michael@0 | 224 | aEntries.filter(function (el) { return el.text.length <= aField.maxLength; }); |
michael@0 | 225 | } else { |
michael@0 | 226 | result.entries = aEntries; |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | if (aListener) { |
michael@0 | 230 | aListener.onSearchCompletion(result); |
michael@0 | 231 | } |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | this.getAutoCompleteValues(aInputName, searchString, processEntry); |
michael@0 | 235 | } |
michael@0 | 236 | }, |
michael@0 | 237 | |
michael@0 | 238 | stopAutoCompleteSearch : function () { |
michael@0 | 239 | if (this._pendingQuery) { |
michael@0 | 240 | this._pendingQuery.cancel(); |
michael@0 | 241 | this._pendingQuery = null; |
michael@0 | 242 | } |
michael@0 | 243 | }, |
michael@0 | 244 | |
michael@0 | 245 | /* |
michael@0 | 246 | * Get the values for an autocomplete list given a search string. |
michael@0 | 247 | * |
michael@0 | 248 | * fieldName - fieldname field within form history (the form input name) |
michael@0 | 249 | * searchString - string to search for |
michael@0 | 250 | * callback - called when the values are available. Passed an array of objects, |
michael@0 | 251 | * containing properties for each result. The callback is only called |
michael@0 | 252 | * when successful. |
michael@0 | 253 | */ |
michael@0 | 254 | getAutoCompleteValues : function (fieldName, searchString, callback) { |
michael@0 | 255 | let params = { |
michael@0 | 256 | agedWeight: this._agedWeight, |
michael@0 | 257 | bucketSize: this._bucketSize, |
michael@0 | 258 | expiryDate: 1000 * (Date.now() - this._expireDays * 24 * 60 * 60 * 1000), |
michael@0 | 259 | fieldname: fieldName, |
michael@0 | 260 | maxTimeGroupings: this._maxTimeGroupings, |
michael@0 | 261 | timeGroupingSize: this._timeGroupingSize, |
michael@0 | 262 | prefixWeight: this._prefixWeight, |
michael@0 | 263 | boundaryWeight: this._boundaryWeight |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | this.stopAutoCompleteSearch(); |
michael@0 | 267 | |
michael@0 | 268 | let results = []; |
michael@0 | 269 | let processResults = { |
michael@0 | 270 | handleResult: aResult => { |
michael@0 | 271 | results.push(aResult); |
michael@0 | 272 | }, |
michael@0 | 273 | handleError: aError => { |
michael@0 | 274 | this.log("getAutocompleteValues failed: " + aError.message); |
michael@0 | 275 | }, |
michael@0 | 276 | handleCompletion: aReason => { |
michael@0 | 277 | this._pendingQuery = null; |
michael@0 | 278 | if (!aReason) { |
michael@0 | 279 | callback(results); |
michael@0 | 280 | } |
michael@0 | 281 | } |
michael@0 | 282 | }; |
michael@0 | 283 | |
michael@0 | 284 | this._pendingQuery = FormHistory.getAutoCompleteResults(searchString, params, processResults); |
michael@0 | 285 | }, |
michael@0 | 286 | |
michael@0 | 287 | /* |
michael@0 | 288 | * _calculateScore |
michael@0 | 289 | * |
michael@0 | 290 | * entry -- an nsIAutoCompleteResult entry |
michael@0 | 291 | * aSearchString -- current value of the input (lowercase) |
michael@0 | 292 | * searchTokens -- array of tokens of the search string |
michael@0 | 293 | * |
michael@0 | 294 | * Returns: an int |
michael@0 | 295 | */ |
michael@0 | 296 | _calculateScore : function (entry, aSearchString, searchTokens) { |
michael@0 | 297 | let boundaryCalc = 0; |
michael@0 | 298 | // for each word, calculate word boundary weights |
michael@0 | 299 | for each (let token in searchTokens) { |
michael@0 | 300 | boundaryCalc += (entry.textLowerCase.indexOf(token) == 0); |
michael@0 | 301 | boundaryCalc += (entry.textLowerCase.indexOf(" " + token) >= 0); |
michael@0 | 302 | } |
michael@0 | 303 | boundaryCalc = boundaryCalc * this._boundaryWeight; |
michael@0 | 304 | // now add more weight if we have a traditional prefix match and |
michael@0 | 305 | // multiply boundary bonuses by boundary weight |
michael@0 | 306 | boundaryCalc += this._prefixWeight * |
michael@0 | 307 | (entry.textLowerCase. |
michael@0 | 308 | indexOf(aSearchString) == 0); |
michael@0 | 309 | entry.totalScore = Math.round(entry.frecency * Math.max(1, boundaryCalc)); |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | }; // end of FormAutoComplete implementation |
michael@0 | 313 | |
michael@0 | 314 | /** |
michael@0 | 315 | * FormAutoCompleteChild |
michael@0 | 316 | * |
michael@0 | 317 | * Implements the nsIFormAutoComplete interface in a child content process, |
michael@0 | 318 | * and forwards the auto-complete requests to the parent process which |
michael@0 | 319 | * also implements a nsIFormAutoComplete interface and has |
michael@0 | 320 | * direct access to the FormHistory database. |
michael@0 | 321 | */ |
michael@0 | 322 | function FormAutoCompleteChild() { |
michael@0 | 323 | this.init(); |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | FormAutoCompleteChild.prototype = { |
michael@0 | 327 | classID : Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"), |
michael@0 | 328 | QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]), |
michael@0 | 329 | |
michael@0 | 330 | _debug: false, |
michael@0 | 331 | _enabled: true, |
michael@0 | 332 | |
michael@0 | 333 | /* |
michael@0 | 334 | * init |
michael@0 | 335 | * |
michael@0 | 336 | * Initializes the content-process side of the FormAutoComplete component, |
michael@0 | 337 | * and add a listener for the message that the parent process sends when |
michael@0 | 338 | * a result is produced. |
michael@0 | 339 | */ |
michael@0 | 340 | init: function() { |
michael@0 | 341 | this._debug = Services.prefs.getBoolPref("browser.formfill.debug"); |
michael@0 | 342 | this._enabled = Services.prefs.getBoolPref("browser.formfill.enable"); |
michael@0 | 343 | this.log("init"); |
michael@0 | 344 | }, |
michael@0 | 345 | |
michael@0 | 346 | /* |
michael@0 | 347 | * log |
michael@0 | 348 | * |
michael@0 | 349 | * Internal function for logging debug messages |
michael@0 | 350 | */ |
michael@0 | 351 | log : function (message) { |
michael@0 | 352 | if (!this._debug) |
michael@0 | 353 | return; |
michael@0 | 354 | dump("FormAutoCompleteChild: " + message + "\n"); |
michael@0 | 355 | }, |
michael@0 | 356 | |
michael@0 | 357 | autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) { |
michael@0 | 358 | // This function is deprecated |
michael@0 | 359 | }, |
michael@0 | 360 | |
michael@0 | 361 | autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) { |
michael@0 | 362 | this.log("autoCompleteSearchAsync"); |
michael@0 | 363 | |
michael@0 | 364 | this._pendingListener = aListener; |
michael@0 | 365 | |
michael@0 | 366 | let rect = BrowserUtils.getElementBoundingScreenRect(aField); |
michael@0 | 367 | |
michael@0 | 368 | let window = aField.ownerDocument.defaultView; |
michael@0 | 369 | let topLevelDocshell = window.QueryInterface(Ci.nsIInterfaceRequestor) |
michael@0 | 370 | .getInterface(Ci.nsIDocShell) |
michael@0 | 371 | .sameTypeRootTreeItem |
michael@0 | 372 | .QueryInterface(Ci.nsIDocShell); |
michael@0 | 373 | |
michael@0 | 374 | let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor) |
michael@0 | 375 | .getInterface(Ci.nsIContentFrameMessageManager); |
michael@0 | 376 | |
michael@0 | 377 | mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", { |
michael@0 | 378 | inputName: aInputName, |
michael@0 | 379 | untrimmedSearchString: aUntrimmedSearchString, |
michael@0 | 380 | left: rect.left, |
michael@0 | 381 | top: rect.top, |
michael@0 | 382 | width: rect.width, |
michael@0 | 383 | height: rect.height |
michael@0 | 384 | }); |
michael@0 | 385 | |
michael@0 | 386 | mm.addMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", |
michael@0 | 387 | function searchFinished(message) { |
michael@0 | 388 | mm.removeMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", searchFinished); |
michael@0 | 389 | let result = new FormAutoCompleteResult( |
michael@0 | 390 | null, |
michael@0 | 391 | [{text: res} for (res of message.data.results)], |
michael@0 | 392 | null, |
michael@0 | 393 | null |
michael@0 | 394 | ); |
michael@0 | 395 | if (aListener) { |
michael@0 | 396 | aListener.onSearchCompletion(result); |
michael@0 | 397 | } |
michael@0 | 398 | } |
michael@0 | 399 | ); |
michael@0 | 400 | |
michael@0 | 401 | this.log("autoCompleteSearchAsync message was sent"); |
michael@0 | 402 | }, |
michael@0 | 403 | |
michael@0 | 404 | stopAutoCompleteSearch : function () { |
michael@0 | 405 | this.log("stopAutoCompleteSearch"); |
michael@0 | 406 | }, |
michael@0 | 407 | }; // end of FormAutoCompleteChild implementation |
michael@0 | 408 | |
michael@0 | 409 | // nsIAutoCompleteResult implementation |
michael@0 | 410 | function FormAutoCompleteResult (formHistory, entries, fieldName, searchString) { |
michael@0 | 411 | this.formHistory = formHistory; |
michael@0 | 412 | this.entries = entries; |
michael@0 | 413 | this.fieldName = fieldName; |
michael@0 | 414 | this.searchString = searchString; |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | FormAutoCompleteResult.prototype = { |
michael@0 | 418 | QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult, |
michael@0 | 419 | Ci.nsISupportsWeakReference]), |
michael@0 | 420 | |
michael@0 | 421 | // private |
michael@0 | 422 | formHistory : null, |
michael@0 | 423 | entries : null, |
michael@0 | 424 | fieldName : null, |
michael@0 | 425 | |
michael@0 | 426 | _checkIndexBounds : function (index) { |
michael@0 | 427 | if (index < 0 || index >= this.entries.length) |
michael@0 | 428 | throw Components.Exception("Index out of range.", Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 429 | }, |
michael@0 | 430 | |
michael@0 | 431 | // Allow autoCompleteSearch to get at the JS object so it can |
michael@0 | 432 | // modify some readonly properties for internal use. |
michael@0 | 433 | get wrappedJSObject() { |
michael@0 | 434 | return this; |
michael@0 | 435 | }, |
michael@0 | 436 | |
michael@0 | 437 | // Interfaces from idl... |
michael@0 | 438 | searchString : null, |
michael@0 | 439 | errorDescription : "", |
michael@0 | 440 | get defaultIndex() { |
michael@0 | 441 | if (entries.length == 0) |
michael@0 | 442 | return -1; |
michael@0 | 443 | else |
michael@0 | 444 | return 0; |
michael@0 | 445 | }, |
michael@0 | 446 | get searchResult() { |
michael@0 | 447 | if (this.entries.length == 0) |
michael@0 | 448 | return Ci.nsIAutoCompleteResult.RESULT_NOMATCH; |
michael@0 | 449 | return Ci.nsIAutoCompleteResult.RESULT_SUCCESS; |
michael@0 | 450 | }, |
michael@0 | 451 | get matchCount() { |
michael@0 | 452 | return this.entries.length; |
michael@0 | 453 | }, |
michael@0 | 454 | |
michael@0 | 455 | getValueAt : function (index) { |
michael@0 | 456 | this._checkIndexBounds(index); |
michael@0 | 457 | return this.entries[index].text; |
michael@0 | 458 | }, |
michael@0 | 459 | |
michael@0 | 460 | getLabelAt: function(index) { |
michael@0 | 461 | return getValueAt(index); |
michael@0 | 462 | }, |
michael@0 | 463 | |
michael@0 | 464 | getCommentAt : function (index) { |
michael@0 | 465 | this._checkIndexBounds(index); |
michael@0 | 466 | return ""; |
michael@0 | 467 | }, |
michael@0 | 468 | |
michael@0 | 469 | getStyleAt : function (index) { |
michael@0 | 470 | this._checkIndexBounds(index); |
michael@0 | 471 | return ""; |
michael@0 | 472 | }, |
michael@0 | 473 | |
michael@0 | 474 | getImageAt : function (index) { |
michael@0 | 475 | this._checkIndexBounds(index); |
michael@0 | 476 | return ""; |
michael@0 | 477 | }, |
michael@0 | 478 | |
michael@0 | 479 | getFinalCompleteValueAt : function (index) { |
michael@0 | 480 | return this.getValueAt(index); |
michael@0 | 481 | }, |
michael@0 | 482 | |
michael@0 | 483 | removeValueAt : function (index, removeFromDB) { |
michael@0 | 484 | this._checkIndexBounds(index); |
michael@0 | 485 | |
michael@0 | 486 | let [removedEntry] = this.entries.splice(index, 1); |
michael@0 | 487 | |
michael@0 | 488 | if (removeFromDB) { |
michael@0 | 489 | this.formHistory.update({ op: "remove", |
michael@0 | 490 | fieldname: this.fieldName, |
michael@0 | 491 | value: removedEntry.text }); |
michael@0 | 492 | } |
michael@0 | 493 | } |
michael@0 | 494 | }; |
michael@0 | 495 | |
michael@0 | 496 | |
michael@0 | 497 | let remote = Services.appinfo.browserTabsRemote; |
michael@0 | 498 | if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT && remote) { |
michael@0 | 499 | // Register the stub FormAutoComplete module in the child which will |
michael@0 | 500 | // forward messages to the parent through the process message manager. |
michael@0 | 501 | let component = [FormAutoCompleteChild]; |
michael@0 | 502 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |
michael@0 | 503 | } else { |
michael@0 | 504 | let component = [FormAutoComplete]; |
michael@0 | 505 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |
michael@0 | 506 | } |