toolkit/components/search/nsSearchSuggestions.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/search/nsSearchSuggestions.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,584 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +const SEARCH_RESPONSE_SUGGESTION_JSON = "application/x-suggestions+json";
     1.9 +
    1.10 +const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled";
    1.11 +const XPCOM_SHUTDOWN_TOPIC              = "xpcom-shutdown";
    1.12 +const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
    1.13 +
    1.14 +const Cc = Components.classes;
    1.15 +const Ci = Components.interfaces;
    1.16 +const Cr = Components.results;
    1.17 +const Cu = Components.utils;
    1.18 +
    1.19 +const HTTP_OK                    = 200;
    1.20 +const HTTP_INTERNAL_SERVER_ERROR = 500;
    1.21 +const HTTP_BAD_GATEWAY           = 502;
    1.22 +const HTTP_SERVICE_UNAVAILABLE   = 503;
    1.23 +
    1.24 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.25 +Cu.import("resource://gre/modules/nsFormAutoCompleteResult.jsm");
    1.26 +Cu.import("resource://gre/modules/Services.jsm");
    1.27 +
    1.28 +/**
    1.29 + * SuggestAutoComplete is a base class that implements nsIAutoCompleteSearch
    1.30 + * and can collect results for a given search by using the search URL supplied
    1.31 + * by the subclass. We do it this way since the AutoCompleteController in
    1.32 + * Mozilla requires a unique XPCOM Service for every search provider, even if
    1.33 + * the logic for two providers is identical.
    1.34 + * @constructor
    1.35 + */
    1.36 +function SuggestAutoComplete() {
    1.37 +  this._init();
    1.38 +}
    1.39 +SuggestAutoComplete.prototype = {
    1.40 +
    1.41 +  _init: function() {
    1.42 +    this._addObservers();
    1.43 +    this._suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
    1.44 +  },
    1.45 +
    1.46 +  get _suggestionLabel() {
    1.47 +    delete this._suggestionLabel;
    1.48 +    let bundle = Services.strings.createBundle("chrome://global/locale/search/search.properties");
    1.49 +    return this._suggestionLabel = bundle.GetStringFromName("suggestion_label");
    1.50 +  },
    1.51 +
    1.52 +  /**
    1.53 +   * Search suggestions will be shown if this._suggestEnabled is true.
    1.54 +   */
    1.55 +  _suggestEnabled: null,
    1.56 +
    1.57 +  /*************************************************************************
    1.58 +   * Server request backoff implementation fields below
    1.59 +   * These allow us to throttle requests if the server is getting hammered.
    1.60 +   **************************************************************************/
    1.61 +
    1.62 +  /**
    1.63 +   * This is an array that contains the timestamps (in unixtime) of
    1.64 +   * the last few backoff-triggering errors.
    1.65 +   */
    1.66 +  _serverErrorLog: [],
    1.67 +
    1.68 +  /**
    1.69 +   * If we receive this number of backoff errors within the amount of time
    1.70 +   * specified by _serverErrorPeriod, then we initiate backoff.
    1.71 +   */
    1.72 +  _maxErrorsBeforeBackoff: 3,
    1.73 +
    1.74 +  /**
    1.75 +   * If we receive enough consecutive errors (where "enough" is defined by
    1.76 +   * _maxErrorsBeforeBackoff above) within this time period,
    1.77 +   * we trigger the backoff behavior.
    1.78 +   */
    1.79 +  _serverErrorPeriod: 600000,  // 10 minutes in milliseconds
    1.80 +
    1.81 +  /**
    1.82 +   * If we get another backoff error immediately after timeout, we increase the
    1.83 +   * backoff to (2 x old period) + this value.
    1.84 +   */
    1.85 +  _serverErrorTimeoutIncrement: 600000,  // 10 minutes in milliseconds
    1.86 +
    1.87 +  /**
    1.88 +   * The current amount of time to wait before trying a server request
    1.89 +   * after receiving a backoff error.
    1.90 +   */
    1.91 +  _serverErrorTimeout: 0,
    1.92 +
    1.93 +  /**
    1.94 +   * Time (in unixtime) after which we're allowed to try requesting again.
    1.95 +   */
    1.96 +  _nextRequestTime: 0,
    1.97 +
    1.98 +  /**
    1.99 +   * The last engine we requested against (so that we can tell if the
   1.100 +   * user switched engines).
   1.101 +   */
   1.102 +  _serverErrorEngine: null,
   1.103 +
   1.104 +  /**
   1.105 +   * The XMLHttpRequest object.
   1.106 +   * @private
   1.107 +   */
   1.108 +  _request: null,
   1.109 +
   1.110 +  /**
   1.111 +   * The object implementing nsIAutoCompleteObserver that we notify when
   1.112 +   * we have found results
   1.113 +   * @private
   1.114 +   */
   1.115 +  _listener: null,
   1.116 +
   1.117 +  /**
   1.118 +   * If this is true, we'll integrate form history results with the
   1.119 +   * suggest results.
   1.120 +   */
   1.121 +  _includeFormHistory: true,
   1.122 +
   1.123 +  /**
   1.124 +   * True if a request for remote suggestions was sent. This is used to
   1.125 +   * differentiate between the "_request is null because the request has
   1.126 +   * already returned a result" and "_request is null because no request was
   1.127 +   * sent" cases.
   1.128 +   */
   1.129 +  _sentSuggestRequest: false,
   1.130 +
   1.131 +  /**
   1.132 +   * This is the callback for the suggest timeout timer.
   1.133 +   */
   1.134 +  notify: function SAC_notify(timer) {
   1.135 +    // FIXME: bug 387341
   1.136 +    // Need to break the cycle between us and the timer.
   1.137 +    this._formHistoryTimer = null;
   1.138 +
   1.139 +    // If this._listener is null, we've already sent out suggest results, so
   1.140 +    // nothing left to do here.
   1.141 +    if (!this._listener)
   1.142 +      return;
   1.143 +
   1.144 +    // Otherwise, the XMLHTTPRequest for suggest results is taking too long,
   1.145 +    // so send out the form history results and cancel the request.
   1.146 +    this._listener.onSearchResult(this, this._formHistoryResult);
   1.147 +    this._reset();
   1.148 +  },
   1.149 +
   1.150 +  /**
   1.151 +   * This determines how long (in ms) we should wait before giving up on
   1.152 +   * the suggestions and just showing local form history results.
   1.153 +   */
   1.154 +  _suggestionTimeout: 500,
   1.155 +
   1.156 +  /**
   1.157 +   * This is the callback for that the form history service uses to
   1.158 +   * send us results.
   1.159 +   */
   1.160 +  onSearchResult: function SAC_onSearchResult(search, result) {
   1.161 +    this._formHistoryResult = result;
   1.162 +
   1.163 +    if (this._request) {
   1.164 +      // We still have a pending request, wait a bit to give it a chance to
   1.165 +      // finish.
   1.166 +      this._formHistoryTimer = Cc["@mozilla.org/timer;1"].
   1.167 +                               createInstance(Ci.nsITimer);
   1.168 +      this._formHistoryTimer.initWithCallback(this, this._suggestionTimeout,
   1.169 +                                              Ci.nsITimer.TYPE_ONE_SHOT);
   1.170 +    } else if (!this._sentSuggestRequest) {
   1.171 +      // We didn't send a request, so just send back the form history results.
   1.172 +      this._listener.onSearchResult(this, this._formHistoryResult);
   1.173 +      this._reset();
   1.174 +    }
   1.175 +  },
   1.176 +
   1.177 +  /**
   1.178 +   * This is the URI that the last suggest request was sent to.
   1.179 +   */
   1.180 +  _suggestURI: null,
   1.181 +
   1.182 +  /**
   1.183 +   * Autocomplete results from the form history service get stored here.
   1.184 +   */
   1.185 +  _formHistoryResult: null,
   1.186 +
   1.187 +  /**
   1.188 +   * This holds the suggest server timeout timer, if applicable.
   1.189 +   */
   1.190 +  _formHistoryTimer: null,
   1.191 +
   1.192 +  /**
   1.193 +   * Maximum number of history items displayed. This is capped at 7
   1.194 +   * because the primary consumer (Firefox search bar) displays 10 rows
   1.195 +   * by default, and so we want to leave some space for suggestions
   1.196 +   * to be visible.
   1.197 +   */
   1.198 +  _historyLimit: 7,
   1.199 +
   1.200 +  /**
   1.201 +   * This clears all the per-request state.
   1.202 +   */
   1.203 +  _reset: function SAC_reset() {
   1.204 +    // Don't let go of our listener and form history result if the timer is
   1.205 +    // still pending, the timer will call _reset() when it fires.
   1.206 +    if (!this._formHistoryTimer) {
   1.207 +      this._listener = null;
   1.208 +      this._formHistoryResult = null;
   1.209 +    }
   1.210 +    this._request = null;
   1.211 +  },
   1.212 +
   1.213 +  /**
   1.214 +   * This sends an autocompletion request to the form history service,
   1.215 +   * which will call onSearchResults with the results of the query.
   1.216 +   */
   1.217 +  _startHistorySearch: function SAC_SHSearch(searchString, searchParam) {
   1.218 +    var formHistory =
   1.219 +      Cc["@mozilla.org/autocomplete/search;1?name=form-history"].
   1.220 +      createInstance(Ci.nsIAutoCompleteSearch);
   1.221 +    formHistory.startSearch(searchString, searchParam, this._formHistoryResult, this);
   1.222 +  },
   1.223 +
   1.224 +  /**
   1.225 +   * Makes a note of the fact that we've received a backoff-triggering
   1.226 +   * response, so that we can adjust the backoff behavior appropriately.
   1.227 +   */
   1.228 +  _noteServerError: function SAC__noteServeError() {
   1.229 +    var currentTime = Date.now();
   1.230 +
   1.231 +    this._serverErrorLog.push(currentTime);
   1.232 +    if (this._serverErrorLog.length > this._maxErrorsBeforeBackoff)
   1.233 +      this._serverErrorLog.shift();
   1.234 +
   1.235 +    if ((this._serverErrorLog.length == this._maxErrorsBeforeBackoff) &&
   1.236 +        ((currentTime - this._serverErrorLog[0]) < this._serverErrorPeriod)) {
   1.237 +      // increase timeout, and then don't request until timeout is over
   1.238 +      this._serverErrorTimeout = (this._serverErrorTimeout * 2) +
   1.239 +                                 this._serverErrorTimeoutIncrement;
   1.240 +      this._nextRequestTime = currentTime + this._serverErrorTimeout;
   1.241 +    }
   1.242 +  },
   1.243 +
   1.244 +  /**
   1.245 +   * Resets the backoff behavior; called when we get a successful response.
   1.246 +   */
   1.247 +  _clearServerErrors: function SAC__clearServerErrors() {
   1.248 +    this._serverErrorLog = [];
   1.249 +    this._serverErrorTimeout = 0;
   1.250 +    this._nextRequestTime = 0;
   1.251 +  },
   1.252 +
   1.253 +  /**
   1.254 +   * This checks whether we should send a server request (i.e. we're not
   1.255 +   * in a error-triggered backoff period.
   1.256 +   *
   1.257 +   * @private
   1.258 +   */
   1.259 +  _okToRequest: function SAC__okToRequest() {
   1.260 +    return Date.now() > this._nextRequestTime;
   1.261 +  },
   1.262 +
   1.263 +  /**
   1.264 +   * This checks to see if the new search engine is different
   1.265 +   * from the previous one, and if so clears any error state that might
   1.266 +   * have accumulated for the old engine.
   1.267 +   *
   1.268 +   * @param engine The engine that the suggestion request would be sent to.
   1.269 +   * @private
   1.270 +   */
   1.271 +  _checkForEngineSwitch: function SAC__checkForEngineSwitch(engine) {
   1.272 +    if (engine == this._serverErrorEngine)
   1.273 +      return;
   1.274 +
   1.275 +    // must've switched search providers, clear old errors
   1.276 +    this._serverErrorEngine = engine;
   1.277 +    this._clearServerErrors();
   1.278 +  },
   1.279 +
   1.280 +  /**
   1.281 +   * This returns true if the status code of the HTTP response
   1.282 +   * represents a backoff-triggering error.
   1.283 +   *
   1.284 +   * @param status  The status code from the HTTP response
   1.285 +   * @private
   1.286 +   */
   1.287 +  _isBackoffError: function SAC__isBackoffError(status) {
   1.288 +    return ((status == HTTP_INTERNAL_SERVER_ERROR) ||
   1.289 +            (status == HTTP_BAD_GATEWAY) ||
   1.290 +            (status == HTTP_SERVICE_UNAVAILABLE));
   1.291 +  },
   1.292 +
   1.293 +  /**
   1.294 +   * Called when the 'readyState' of the XMLHttpRequest changes. We only care
   1.295 +   * about state 4 (COMPLETED) - handle the response data.
   1.296 +   * @private
   1.297 +   */
   1.298 +  onReadyStateChange: function() {
   1.299 +    // xxx use the real const here
   1.300 +    if (!this._request || this._request.readyState != 4)
   1.301 +      return;
   1.302 +
   1.303 +    try {
   1.304 +      var status = this._request.status;
   1.305 +    } catch (e) {
   1.306 +      // The XML HttpRequest can throw NS_ERROR_NOT_AVAILABLE.
   1.307 +      return;
   1.308 +    }
   1.309 +
   1.310 +    if (this._isBackoffError(status)) {
   1.311 +      this._noteServerError();
   1.312 +      return;
   1.313 +    }
   1.314 +
   1.315 +    var responseText = this._request.responseText;
   1.316 +    if (status != HTTP_OK || responseText == "")
   1.317 +      return;
   1.318 +
   1.319 +    this._clearServerErrors();
   1.320 +
   1.321 +    try {
   1.322 +      var serverResults = JSON.parse(responseText);
   1.323 +    } catch(ex) {
   1.324 +      Components.utils.reportError("Failed to parse JSON from " + this._suggestURI.spec + ": " + ex);
   1.325 +      return;
   1.326 +    }
   1.327 +
   1.328 +    var searchString = serverResults[0] || "";
   1.329 +    var results = serverResults[1] || [];
   1.330 +
   1.331 +    var comments = [];  // "comments" column values for suggestions
   1.332 +    var historyResults = [];
   1.333 +    var historyComments = [];
   1.334 +
   1.335 +    // If form history is enabled and has results, add them to the list.
   1.336 +    if (this._includeFormHistory && this._formHistoryResult &&
   1.337 +        (this._formHistoryResult.searchResult ==
   1.338 +         Ci.nsIAutoCompleteResult.RESULT_SUCCESS)) {
   1.339 +      var maxHistoryItems = Math.min(this._formHistoryResult.matchCount, this._historyLimit);
   1.340 +      for (var i = 0; i < maxHistoryItems; ++i) {
   1.341 +        var term = this._formHistoryResult.getValueAt(i);
   1.342 +
   1.343 +        // we don't want things to appear in both history and suggestions
   1.344 +        var dupIndex = results.indexOf(term);
   1.345 +        if (dupIndex != -1)
   1.346 +          results.splice(dupIndex, 1);
   1.347 +
   1.348 +        historyResults.push(term);
   1.349 +        historyComments.push("");
   1.350 +      }
   1.351 +    }
   1.352 +
   1.353 +    // fill out the comment column for the suggestions
   1.354 +    for (var i = 0; i < results.length; ++i)
   1.355 +      comments.push("");
   1.356 +
   1.357 +    // if we have any suggestions, put a label at the top
   1.358 +    if (comments.length > 0)
   1.359 +      comments[0] = this._suggestionLabel;
   1.360 +
   1.361 +    // now put the history results above the suggestions
   1.362 +    var finalResults = historyResults.concat(results);
   1.363 +    var finalComments = historyComments.concat(comments);
   1.364 +
   1.365 +    // Notify the FE of our new results
   1.366 +    this.onResultsReady(searchString, finalResults, finalComments,
   1.367 +                        this._formHistoryResult);
   1.368 +
   1.369 +    // Reset our state for next time.
   1.370 +    this._reset();
   1.371 +  },
   1.372 +
   1.373 +  /**
   1.374 +   * Notifies the front end of new results.
   1.375 +   * @param searchString  the user's query string
   1.376 +   * @param results       an array of results to the search
   1.377 +   * @param comments      an array of metadata corresponding to the results
   1.378 +   * @private
   1.379 +   */
   1.380 +  onResultsReady: function(searchString, results, comments,
   1.381 +                           formHistoryResult) {
   1.382 +    if (this._listener) {
   1.383 +      var result = new FormAutoCompleteResult(
   1.384 +          searchString,
   1.385 +          Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
   1.386 +          0,
   1.387 +          "",
   1.388 +          results,
   1.389 +          results,
   1.390 +          comments,
   1.391 +          formHistoryResult);
   1.392 +
   1.393 +      this._listener.onSearchResult(this, result);
   1.394 +
   1.395 +      // Null out listener to make sure we don't notify it twice, in case our
   1.396 +      // timer callback still hasn't run.
   1.397 +      this._listener = null;
   1.398 +    }
   1.399 +  },
   1.400 +
   1.401 +  /**
   1.402 +   * Initiates the search result gathering process. Part of
   1.403 +   * nsIAutoCompleteSearch implementation.
   1.404 +   *
   1.405 +   * @param searchString    the user's query string
   1.406 +   * @param searchParam     unused, "an extra parameter"; even though
   1.407 +   *                        this parameter and the next are unused, pass
   1.408 +   *                        them through in case the form history
   1.409 +   *                        service wants them
   1.410 +   * @param previousResult  unused, a client-cached store of the previous
   1.411 +   *                        generated resultset for faster searching.
   1.412 +   * @param listener        object implementing nsIAutoCompleteObserver which
   1.413 +   *                        we notify when results are ready.
   1.414 +   */
   1.415 +  startSearch: function(searchString, searchParam, previousResult, listener) {
   1.416 +    // Don't reuse a previous form history result when it no longer applies.
   1.417 +    if (!previousResult)
   1.418 +      this._formHistoryResult = null;
   1.419 +
   1.420 +    var formHistorySearchParam = searchParam.split("|")[0];
   1.421 +
   1.422 +    // Receive the information about the privacy mode of the window to which
   1.423 +    // this search box belongs.  The front-end's search.xml bindings passes this
   1.424 +    // information in the searchParam parameter.  The alternative would have
   1.425 +    // been to modify nsIAutoCompleteSearch to add an argument to startSearch
   1.426 +    // and patch all of autocomplete to be aware of this, but the searchParam
   1.427 +    // argument is already an opaque argument, so this solution is hopefully
   1.428 +    // less hackish (although still gross.)
   1.429 +    var privacyMode = (searchParam.split("|")[1] == "private");
   1.430 +
   1.431 +    // Start search immediately if possible, otherwise once the search
   1.432 +    // service is initialized
   1.433 +    if (Services.search.isInitialized) {
   1.434 +      this._triggerSearch(searchString, formHistorySearchParam, listener, privacyMode);
   1.435 +      return;
   1.436 +    }
   1.437 +
   1.438 +    Services.search.init((function startSearch_cb(aResult) {
   1.439 +      if (!Components.isSuccessCode(aResult)) {
   1.440 +        Cu.reportError("Could not initialize search service, bailing out: " + aResult);
   1.441 +        return;
   1.442 +      }
   1.443 +      this._triggerSearch(searchString, formHistorySearchParam, listener, privacyMode);
   1.444 +    }).bind(this));
   1.445 +  },
   1.446 +
   1.447 +  /**
   1.448 +   * Actual implementation of search.
   1.449 +   */
   1.450 +  _triggerSearch: function(searchString, searchParam, listener, privacyMode) {
   1.451 +    // If there's an existing request, stop it. There is no smart filtering
   1.452 +    // here as there is when looking through history/form data because the
   1.453 +    // result set returned by the server is different for every typed value -
   1.454 +    // "ocean breathes" does not return a subset of the results returned for
   1.455 +    // "ocean", for example. This does nothing if there is no current request.
   1.456 +    this.stopSearch();
   1.457 +
   1.458 +    this._listener = listener;
   1.459 +
   1.460 +    var engine = Services.search.currentEngine;
   1.461 +
   1.462 +    this._checkForEngineSwitch(engine);
   1.463 +
   1.464 +    if (!searchString ||
   1.465 +        !this._suggestEnabled ||
   1.466 +        !engine.supportsResponseType(SEARCH_RESPONSE_SUGGESTION_JSON) ||
   1.467 +        !this._okToRequest()) {
   1.468 +      // We have an empty search string (user pressed down arrow to see
   1.469 +      // history), or search suggestions are disabled, or the current engine
   1.470 +      // has no suggest functionality, or we're in backoff mode; so just use
   1.471 +      // local history.
   1.472 +      this._sentSuggestRequest = false;
   1.473 +      this._startHistorySearch(searchString, searchParam);
   1.474 +      return;
   1.475 +    }
   1.476 +
   1.477 +    // Actually do the search
   1.478 +    this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
   1.479 +                    createInstance(Ci.nsIXMLHttpRequest);
   1.480 +    var submission = engine.getSubmission(searchString,
   1.481 +                                          SEARCH_RESPONSE_SUGGESTION_JSON);
   1.482 +    this._suggestURI = submission.uri;
   1.483 +    var method = (submission.postData ? "POST" : "GET");
   1.484 +    this._request.open(method, this._suggestURI.spec, true);
   1.485 +    this._request.channel.notificationCallbacks = new AuthPromptOverride();
   1.486 +    if (this._request.channel instanceof Ci.nsIPrivateBrowsingChannel) {
   1.487 +      this._request.channel.setPrivate(privacyMode);
   1.488 +    }
   1.489 +
   1.490 +    var self = this;
   1.491 +    function onReadyStateChange() {
   1.492 +      self.onReadyStateChange();
   1.493 +    }
   1.494 +    this._request.onreadystatechange = onReadyStateChange;
   1.495 +    this._request.send(submission.postData);
   1.496 +
   1.497 +    if (this._includeFormHistory) {
   1.498 +      this._sentSuggestRequest = true;
   1.499 +      this._startHistorySearch(searchString, searchParam);
   1.500 +    }
   1.501 +  },
   1.502 +
   1.503 +  /**
   1.504 +   * Ends the search result gathering process. Part of nsIAutoCompleteSearch
   1.505 +   * implementation.
   1.506 +   */
   1.507 +  stopSearch: function() {
   1.508 +    if (this._request) {
   1.509 +      this._request.abort();
   1.510 +      this._reset();
   1.511 +    }
   1.512 +  },
   1.513 +
   1.514 +  /**
   1.515 +   * nsIObserver
   1.516 +   */
   1.517 +  observe: function SAC_observe(aSubject, aTopic, aData) {
   1.518 +    switch (aTopic) {
   1.519 +      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
   1.520 +        this._suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
   1.521 +        break;
   1.522 +      case XPCOM_SHUTDOWN_TOPIC:
   1.523 +        this._removeObservers();
   1.524 +        break;
   1.525 +    }
   1.526 +  },
   1.527 +
   1.528 +  _addObservers: function SAC_addObservers() {
   1.529 +    Services.prefs.addObserver(BROWSER_SUGGEST_PREF, this, false);
   1.530 +
   1.531 +    Services.obs.addObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
   1.532 +  },
   1.533 +
   1.534 +  _removeObservers: function SAC_removeObservers() {
   1.535 +    Services.prefs.removeObserver(BROWSER_SUGGEST_PREF, this);
   1.536 +
   1.537 +    Services.obs.removeObserver(this, XPCOM_SHUTDOWN_TOPIC);
   1.538 +  },
   1.539 +
   1.540 +  // nsISupports
   1.541 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch,
   1.542 +                                         Ci.nsIAutoCompleteObserver])
   1.543 +};
   1.544 +
   1.545 +function AuthPromptOverride() {
   1.546 +}
   1.547 +AuthPromptOverride.prototype = {
   1.548 +  // nsIAuthPromptProvider
   1.549 +  getAuthPrompt: function (reason, iid) {
   1.550 +    // Return a no-op nsIAuthPrompt2 implementation.
   1.551 +    return {
   1.552 +      promptAuth: function () {
   1.553 +        throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.554 +      },
   1.555 +      asyncPromptAuth: function () {
   1.556 +        throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.557 +      }
   1.558 +    };
   1.559 +  },
   1.560 +
   1.561 +  // nsIInterfaceRequestor
   1.562 +  getInterface: function SSLL_getInterface(iid) {
   1.563 +    return this.QueryInterface(iid);
   1.564 +  },
   1.565 +
   1.566 +  // nsISupports
   1.567 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPromptProvider,
   1.568 +                                         Ci.nsIInterfaceRequestor])
   1.569 +};
   1.570 +/**
   1.571 + * SearchSuggestAutoComplete is a service implementation that handles suggest
   1.572 + * results specific to web searches.
   1.573 + * @constructor
   1.574 + */
   1.575 +function SearchSuggestAutoComplete() {
   1.576 +  // This calls _init() in the parent class (SuggestAutoComplete) via the
   1.577 +  // prototype, below.
   1.578 +  this._init();
   1.579 +}
   1.580 +SearchSuggestAutoComplete.prototype = {
   1.581 +  classID: Components.ID("{aa892eb4-ffbf-477d-9f9a-06c995ae9f27}"),
   1.582 +  __proto__: SuggestAutoComplete.prototype,
   1.583 +  serviceURL: ""
   1.584 +};
   1.585 +
   1.586 +var component = [SearchSuggestAutoComplete];
   1.587 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);

mercurial