How to implement a custom autocomplete search component

  • Revision slug: How_to_implement_custom_autocomplete_search_component
  • Revision title: How to implement a custom autocomplete search component
  • Revision id: 37921
  • Created:
  • Creator: madarche
  • Is current revision? No
  • Comment Added another reference; 6 words added

Revision Content

The default functionalities

The XUL textbox element supports an autocomplete mechanism that is used to create a textbox with a popup containing a list of possible completions for what the user has started to type. There are actually two autocomplete mechanisms:

  • An older mechanism that is part of the "xpfe" codebase and is used in older applications such as Thunderbird and SeaMonkey.
  • A newer and slightly simplified mechanism that is used in "toolkit" applications such as Firefox and XULRunner.
  • Thunderbird 2.x and SeaMonkey 1.1.x support the toolkit interfaces, although they do not use the same autocomplete widget.

To use autocomplete with a XUL textbox, all you need to do is set some attributes:

<textbox type="autocomplete" autocompletesearch="history"/>

The type="autocomplete" turns on the autocomplete mechanism and the autocompletesearch="history" sets the source for the autocomplete. There are more options and features, see XUL:textbox_(Firefox_autocomplete) for more details. The toolkit mechanism has built-in support for several autocomplete sources, including:

  • history: Search the browser's URL history (Firefox: 1.0+; SeaMonkey: 1.1+)
  • form-history: Search the values that the user has entered into form fields. (Firefox: 1.0+; SeaMonkey: 2.0+)
  • file: Search file names (Firefox: 2.0+; Thunderbird: 2.0+; SeaMonkey: 1.1+; GTK builds only). The component uses the autocompletesearchparam attribute or searchParam property to allow the developer to define the default directory otherwise only paths beginning with / or ~/ will be autocompleted.

Building your custom autocomplete search component

Sometimes, you may want to build your own autocomplete source. To do so you need the following:

  • Create an XPCOM component that implements the nsIAutoCompleteSearch interface.
  • Make sure the contract ID of your XPCOM component follows the following form: "@mozilla.org/autocomplete/search;1?name=xxx", where "xxx" is the name of your autocomplete source.
  • Implement the list of matched strings using the nsIAutoCompleteResult interface.

The simplest way to make an XPCOM component is to build an XPCOM JavaScript Component (this cannot be done with a JavaScript module). How to Build an XPCOM Component in Javascript will step you through the process. Because creating an XPCOM component in order to build a custom autocomplete source can be enough to discourage some developers, below is an example JavaScript XPCOM component called "simple-autocomplete" that implements the necessary interfaces.

The "simple-autocomplete" autocomplete source was built to show how to create an XPCOM component from scratch, but it can also be used "as-is" for simple autocomplete lists. The component uses the autocompletesearchparam attribute or searchParam property to allow the developer to define a set of data to use for autocompletion. Use "simple-autocomplete" with a textbox like this:

<textbox id="text1" type="autocomplete" autocompletesearch="simple-autocomplete" showcommentcolumn="true"
         autocompletesearchparam='[{"value":"mark","comment":"cool dude"},{"value":"mary","comment":"nice lady"},{"value":"jimmy","comment":"very uncool guy"},{"value":"jimbo","comment":null}]' />

Note the format of the JSON. The comment property is optional and may be omitted from the JSON. Be sure to include it if you use the showcommentcolumn="true" textbox attribute.

In case you do not like the standard tree layout of the autocomplete popup, you can also trigger the use of a richlistbox by linking a panel with the type attribute set to "autocomplete-richlistbox" like this:

<panel id="richPopup" type="autocomplete-richlistbox" />

<textbox id="text1" type="autocomplete" autocompletesearch="simple-autocomplete"
         showcommentcolumn="true" autocompletepopup="richPopup"
         autocompletesearchparam='[{"value":"mark","comment":"cool dude"},{"value":"mary","comment":"nice lady"},{"value":"jimmy","comment":"very uncool guy"},{"value":"jimbo","comment":null}]' />

   

Copy the following code into a .js file and save it to whatever "components" folder is appropriate.

const Ci = Components.interfaces;

const CLASS_ID = Components.ID("6224daa1-71a2-4d1a-ad90-01ca1c08e323");
const CLASS_NAME = "Simple AutoComplete";
const CONTRACT_ID = "@mozilla.org/autocomplete/search;1?name=simple-autocomplete";

// Implements nsIAutoCompleteResult
function SimpleAutoCompleteResult(searchString, searchResult,
                                  defaultIndex, errorDescription,
                                  results, comments) {
  this._searchString = searchString;
  this._searchResult = searchResult;
  this._defaultIndex = defaultIndex;
  this._errorDescription = errorDescription;
  this._results = results;
  this._comments = comments;
}

SimpleAutoCompleteResult.prototype = {
  _searchString: "",
  _searchResult: 0,
  _defaultIndex: 0,
  _errorDescription: "",
  _results: [],
  _comments: [],

  /**
   * The original search string
   */
  get searchString() {
    return this._searchString;
  },

  /**
   * The result code of this result object, either:
   *         RESULT_IGNORED   (invalid searchString)
   *         RESULT_FAILURE   (failure)
   *         RESULT_NOMATCH   (no matches found)
   *         RESULT_SUCCESS   (matches found)
   */
  get searchResult() {
    return this._searchResult;
  },

  /**
   * Index of the default item that should be entered if none is selected
   */
  get defaultIndex() {
    return this._defaultIndex;
  },

  /**
   * A string describing the cause of a search failure
   */
  get errorDescription() {
    return this._errorDescription;
  },

  /**
   * The number of matches
   */
  get matchCount() {
    return this._results.length;
  },

  /**
   * Get the value of the result at the given index
   */
  getValueAt: function(index) {
    return this._results[index];
  },

  /**
   * Get the comment of the result at the given index
   */
  getCommentAt: function(index) {
    return this._comments[index];
  },

  /**
   * Get the style hint for the result at the given index
   */
  getStyleAt: function(index) {
    if (!this._comments[index])
      return null;  // not a category label, so no special styling

    if (index == 0)
      return "suggestfirst";  // category label on first line of results

    return "suggesthint";   // category label on any other line of results
  },

  /**
   * Get the image for the result at the given index
   * The return value is expected to be an URI to the image to display
   */
  getImageAt : function (index) {
    return "";
  },

  /**
   * Remove the value at the given index from the autocomplete results.
   * If removeFromDb is set to true, the value should be removed from
   * persistent storage as well.
   */
  removeValueAt: function(index, removeFromDb) {
    this._results.splice(index, 1);
    this._comments.splice(index, 1);
  },

  QueryInterface: function(aIID) {
    if (!aIID.equals(Ci.nsIAutoCompleteResult) && !aIID.equals(Ci.nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};


// Implements nsIAutoCompleteSearch
function SimpleAutoCompleteSearch() {
}

SimpleAutoCompleteSearch.prototype = {
  /*
   * Search for a given string and notify a listener (either synchronously
   * or asynchronously) of the result
   *
   * @param searchString - The string to search for
   * @param searchParam - An extra parameter
   * @param previousResult - A previous result to use for faster searchinig
   * @param listener - A listener to notify when the search is complete
   */
  startSearch: function(searchString, searchParam, result, listener) {
    // This autocomplete source assumes the developer attached a JSON string
    // to the the "autocompletesearchparam" attribute or "searchParam" property
    // of the <textbox> element. The JSON is converted into an array and used
    // as the source of match data. Any values that match the search string
    // are moved into temporary arrays and passed to the AutoCompleteResult
    if (searchParam.length > 0) {
      var nativeJSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
      var searchResults = nativeJSON.decode(searchParam);
      var results = [];
      var comments = [];
      for (i=0; i<searchResults.length; i++) {
        if (searchResults[i].value.indexOf(searchString) == 0) {
          results.push(searchResults[i].value);
          if (searchResults[i].comment)
            comments.push(searchResults[i].comment);
          else
            comments.push(null);
        }
      }
      var newResult = new SimpleAutoCompleteResult(searchString, Ci.nsIAutoCompleteResult.RESULT_SUCCESS, 0, "", results, comments);
      listener.onSearchResult(this, newResult);
    }
  },

  /*
   * Stop an asynchronous search that is in progress
   */
  stopSearch: function() {
  },
    
  QueryInterface: function(aIID) {
    if (!aIID.equals(Ci.nsIAutoCompleteSearch) && !aIID.equals(Ci.nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

// Factory
var SimpleAutoCompleteSearchFactory = {
  singleton: null,
  createInstance: function (aOuter, aIID) {
    if (aOuter != null)
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    if (this.singleton == null)
      this.singleton = new SimpleAutoCompleteSearch();
    return this.singleton.QueryInterface(aIID);
  }
};

// Module
var SimpleAutoCompleteSearchModule = {
  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
  },

  unregisterSelf: function(aCompMgr, aLocation, aType) {
    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  },
  
  getClassObject: function(aCompMgr, aCID, aIID) {
    if (!aIID.equals(Components.interfaces.nsIFactory))
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    if (aCID.equals(CLASS_ID))
      return SimpleAutoCompleteSearchFactory;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  canUnload: function(aCompMgr) { return true; }
};

// Module initialization
function NSGetModule(aCompMgr, aFileSpec) { return SimpleAutoCompleteSearchModule; }

{{ gecko_callout_heading("2.0") }}

Starting in Gecko 2.0, component registration has been changed, so you need to make the following changes:

  1. Add an NSGetFactory() function:

    function NSGetFactory(cid) {
      if (cid.toString().toUpperCase() != CLASS_ID.toString().toUpperCase()) {
        throw Components.results.NS_ERROR_FACTORY_NOT_REGISTERED;
      }

      return SimpleAutoCompleteSearchFactory;
    }
  2. You need to explicitly register the component by adding these lines into your chrome.manifest file:

     component {6224daa1-71a2-4d1a-ad90-01ca1c08e323} components/[YourFileName].js
    contract @mozilla.org/autocomplete/search;1?name=simple-autocomplete {6224daa1-71a2-4d1a-ad90-01ca1c08e323}
  3. You need to add the following method:
    getLabelAt: function(index) { return this._results[index]; }
    to
    SimpleAutoCompleteResult
Deleting Cache file, in order that changes in .JS file will be reflected:
For Microsoft Xp users: If you are using xulrunner, be sure to delete the following files, after making changes in your JS file :
C:\Documents and Settings\[USERNAME]\Application Data\[FULL USERNAME]\[XUL APP NAME]\Profiles\[XXX].default\compreg.dat
C:\Documents and Settings\[USERNAME]\Application Data\[FULL USERNAME]\[XUL APP NAME]\Profiles\[XXX].default\xpti.dat

See also

  • Textbox (Toolkit autocomplete)
  • {{ interface("nsIAutoCompleteSearch") }}
  • {{ interface("nsIAutoCompleteResult") }}
  • {{ interface("nsIAutoCompleteInput") }}

Revision Source

<h2>The default functionalities</h2>
<p>The XUL <a href="/En/XUL/Textbox_(Toolkit_autocomplete)" title="en/XUL/textbox_(Firefox_autocomplete)">textbox</a> element supports an autocomplete mechanism that is used to create a textbox with a popup containing a list of possible completions for what the user has started to type. There are actually two autocomplete mechanisms:</p>
<ul> <li>An older mechanism that is part of the "xpfe" codebase and is used in older applications such as Thunderbird and SeaMonkey.</li> <li>A newer and slightly simplified mechanism that is used in "toolkit" applications such as Firefox and XULRunner.</li> <li>Thunderbird 2.x and SeaMonkey 1.1.x support the toolkit interfaces, although they do not use the same autocomplete widget.</li>
</ul>
<p>To use autocomplete with a XUL textbox, all you need to do is set some attributes:</p>
<pre class="brush: xml">&lt;textbox type="autocomplete" autocompletesearch="history"/&gt;
</pre>
<p>The <code>type="autocomplete"</code> turns on the autocomplete mechanism and the <code>autocompletesearch="history"</code> sets the source for the autocomplete. There are more options and features, see <a href="/En/XUL/Textbox_(Toolkit_autocomplete)" title="en/XUL/textbox_(Firefox_autocomplete)">XUL:textbox_(Firefox_autocomplete)</a> for more details. The toolkit mechanism has built-in support for several autocomplete sources, including:</p>
<ul> <li><code>history</code>: Search the browser's URL history (Firefox: 1.0+; SeaMonkey: 1.1+)</li> <li><code>form-history</code>: Search the values that the user has entered into form fields. (Firefox: 1.0+; SeaMonkey: 2.0+)</li> <li><code>file</code>: Search file names (Firefox: 2.0+; Thunderbird: 2.0+; SeaMonkey: 1.1+; GTK builds only). The component uses the <code>autocompletesearchparam</code> attribute or <code>searchParam</code> property to allow the developer to define the default directory otherwise only paths beginning with <code>/</code> or <code>~/</code> will be autocompleted.</li>
</ul>
<h2>Building your custom autocomplete search component</h2>
<p>Sometimes, you may want to build your own autocomplete source. To do so you need the following:</p>
<ul> <li>Create an XPCOM component that implements the <code>nsIAutoCompleteSearch</code> interface.</li> <li>Make sure the contract ID of your XPCOM component follows the following form: <code>"@mozilla.org/autocomplete/search;1?name=xxx"</code>, where <code>"xxx"</code> is the name of your autocomplete source.</li> <li>Implement the list of matched strings using the <code>nsIAutoCompleteResult</code> interface.</li>
</ul>
<p>The simplest way to make an XPCOM component is to build an XPCOM JavaScript Component (this cannot be done with a <a href="/en/JavaScript_code_modules" title="https://developer.mozilla.org/en/JavaScript_code_modules/">JavaScript module</a>). <a href="/en/How_to_Build_an_XPCOM_Component_in_Javascript" title="en/How_to_Build_an_XPCOM_Component_in_Javascript">How to Build an XPCOM Component in Javascript</a> will step you through the process. Because creating an XPCOM component in order to build a custom autocomplete source can be enough to discourage some developers, below is an example JavaScript XPCOM component called "simple-autocomplete" that implements the necessary interfaces.</p>
<p>The "simple-autocomplete" autocomplete source was built to show how to create an XPCOM component from scratch, but it can also be used "as-is" for simple autocomplete lists. The component uses the <code>autocompletesearchparam</code> attribute or <code>searchParam</code> property to allow the developer to define a set of data to use for autocompletion. Use "simple-autocomplete" with a <code>textbox</code> like this:</p>
<pre class="brush: xml">&lt;textbox id="text1" type="autocomplete" autocompletesearch="simple-autocomplete" showcommentcolumn="true"
         autocompletesearchparam='[{"value":"mark","comment":"cool dude"},{"value":"mary","comment":"nice lady"},{"value":"jimmy","comment":"very uncool guy"},{"value":"jimbo","comment":null}]' /&gt;</pre>
<p>Note the format of the JSON. The <code>comment</code> property is optional and may be omitted from the JSON. Be sure to include it if you use the <code>showcommentcolumn="true"</code> textbox attribute.</p>
<p>In case you do not like the standard tree layout of the autocomplete popup, you can also trigger the use of a <code>richlistbox</code> by linking a <code>panel</code> with the <code>type</code> attribute set to "<code>autocomplete-richlistbox</code>" like this:</p>
<pre class="brush: xml">&lt;panel id="richPopup" type="autocomplete-richlistbox" /&gt;

&lt;textbox id="text1" type="autocomplete" autocompletesearch="simple-autocomplete"
         showcommentcolumn="true" autocompletepopup="richPopup"
         autocompletesearchparam='[{"value":"mark","comment":"cool dude"},{"value":"mary","comment":"nice lady"},{"value":"jimmy","comment":"very uncool guy"},{"value":"jimbo","comment":null}]' /&gt;</pre>
<p>   </p>
<p>Copy the following code into a .js file and save it to whatever "components" folder is appropriate.</p>
<pre class="brush: js">const Ci = Components.interfaces;

const CLASS_ID = Components.ID("6224daa1-71a2-4d1a-ad90-01ca1c08e323");
const CLASS_NAME = "Simple AutoComplete";
const CONTRACT_ID = "@mozilla.org/autocomplete/search;1?name=simple-autocomplete";

// Implements nsIAutoCompleteResult
function SimpleAutoCompleteResult(searchString, searchResult,
                                  defaultIndex, errorDescription,
                                  results, comments) {
  this._searchString = searchString;
  this._searchResult = searchResult;
  this._defaultIndex = defaultIndex;
  this._errorDescription = errorDescription;
  this._results = results;
  this._comments = comments;
}

SimpleAutoCompleteResult.prototype = {
  _searchString: "",
  _searchResult: 0,
  _defaultIndex: 0,
  _errorDescription: "",
  _results: [],
  _comments: [],

  /**
   * The original search string
   */
  get searchString() {
    return this._searchString;
  },

  /**
   * The result code of this result object, either:
   *         RESULT_IGNORED   (invalid searchString)
   *         RESULT_FAILURE   (failure)
   *         RESULT_NOMATCH   (no matches found)
   *         RESULT_SUCCESS   (matches found)
   */
  get searchResult() {
    return this._searchResult;
  },

  /**
   * Index of the default item that should be entered if none is selected
   */
  get defaultIndex() {
    return this._defaultIndex;
  },

  /**
   * A string describing the cause of a search failure
   */
  get errorDescription() {
    return this._errorDescription;
  },

  /**
   * The number of matches
   */
  get matchCount() {
    return this._results.length;
  },

  /**
   * Get the value of the result at the given index
   */
  getValueAt: function(index) {
    return this._results[index];
  },

  /**
   * Get the comment of the result at the given index
   */
  getCommentAt: function(index) {
    return this._comments[index];
  },

  /**
   * Get the style hint for the result at the given index
   */
  getStyleAt: function(index) {
    if (!this._comments[index])
      return null;  // not a category label, so no special styling

    if (index == 0)
      return "suggestfirst";  // category label on first line of results

    return "suggesthint";   // category label on any other line of results
  },

  /**
   * Get the image for the result at the given index
   * The return value is expected to be an URI to the image to display
   */
  getImageAt : function (index) {
    return "";
  },

  /**
   * Remove the value at the given index from the autocomplete results.
   * If removeFromDb is set to true, the value should be removed from
   * persistent storage as well.
   */
  removeValueAt: function(index, removeFromDb) {
    this._results.splice(index, 1);
    this._comments.splice(index, 1);
  },

  QueryInterface: function(aIID) {
    if (!aIID.equals(Ci.nsIAutoCompleteResult) &amp;&amp; !aIID.equals(Ci.nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};


// Implements nsIAutoCompleteSearch
function SimpleAutoCompleteSearch() {
}

SimpleAutoCompleteSearch.prototype = {
  /*
   * Search for a given string and notify a listener (either synchronously
   * or asynchronously) of the result
   *
   * @param searchString - The string to search for
   * @param searchParam - An extra parameter
   * @param previousResult - A previous result to use for faster searchinig
   * @param listener - A listener to notify when the search is complete
   */
  startSearch: function(searchString, searchParam, result, listener) {
    // This autocomplete source assumes the developer attached a JSON string
    // to the the "autocompletesearchparam" attribute or "searchParam" property
    // of the &lt;textbox&gt; element. The JSON is converted into an array and used
    // as the source of match data. Any values that match the search string
    // are moved into temporary arrays and passed to the AutoCompleteResult
    if (searchParam.length &gt; 0) {
      var nativeJSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
      var searchResults = nativeJSON.decode(searchParam);
      var results = [];
      var comments = [];
      for (i=0; i&lt;searchResults.length; i++) {
        if (searchResults[i].value.indexOf(searchString) == 0) {
          results.push(searchResults[i].value);
          if (searchResults[i].comment)
            comments.push(searchResults[i].comment);
          else
            comments.push(null);
        }
      }
      var newResult = new SimpleAutoCompleteResult(searchString, Ci.nsIAutoCompleteResult.RESULT_SUCCESS, 0, "", results, comments);
      listener.onSearchResult(this, newResult);
    }
  },

  /*
   * Stop an asynchronous search that is in progress
   */
  stopSearch: function() {
  },
    
  QueryInterface: function(aIID) {
    if (!aIID.equals(Ci.nsIAutoCompleteSearch) &amp;&amp; !aIID.equals(Ci.nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

// Factory
var SimpleAutoCompleteSearchFactory = {
  singleton: null,
  createInstance: function (aOuter, aIID) {
    if (aOuter != null)
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    if (this.singleton == null)
      this.singleton = new SimpleAutoCompleteSearch();
    return this.singleton.QueryInterface(aIID);
  }
};

// Module
var SimpleAutoCompleteSearchModule = {
  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
  },

  unregisterSelf: function(aCompMgr, aLocation, aType) {
    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  },
  
  getClassObject: function(aCompMgr, aCID, aIID) {
    if (!aIID.equals(Components.interfaces.nsIFactory))
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    if (aCID.equals(CLASS_ID))
      return SimpleAutoCompleteSearchFactory;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  canUnload: function(aCompMgr) { return true; }
};

// Module initialization
function NSGetModule(aCompMgr, aFileSpec) { return SimpleAutoCompleteSearchModule; }

</pre>
<div class="geckoVersionNote">
<p>{{ gecko_callout_heading("2.0") }}</p>
<p>Starting in Gecko 2.0, component registration <a href="/en/XPCOM/XPCOM_changes_in_Gecko_2.0" title="en/XPCOM/XPCOM changes in Gecko 2.0">has been changed</a>, so you need to make the following changes:</p>
<ol> <li>Add an NSGetFactory() function:<br> <br> <code>function NSGetFactory(cid) {<br>   if (cid.toString().toUpperCase() != CLASS_ID.toString().toUpperCase()) {<br>     throw Components.results.NS_ERROR_FACTORY_NOT_REGISTERED;<br>   }<br> <br>   return SimpleAutoCompleteSearchFactory;<br> }</code></li> <li>You need to explicitly register the component by adding these lines into your <code>chrome.manifest</code> file:<br> <br> <code> component {6224daa1-71a2-4d1a-ad90-01ca1c08e323} components/[YourFileName].js<br> contract @mozilla.org/autocomplete/search;1?name=simple-autocomplete {6224daa1-71a2-4d1a-ad90-01ca1c08e323}</code></li> <li> <div>You need to add the following method:</div> <div><code>getLabelAt: function(index) { return this._results[index]; }</code></div> <div>to<br> <code>SimpleAutoCompleteResult</code></div> </li>
</ol>
</div>
<div class="note"><strong>Deleting Cache file, in order that changes in .JS file will be reflected:</strong><br>
For Microsoft Xp users: If you are using xulrunner, be sure to delete the following files, after making changes in your JS file :<br>
C:\Documents and Settings\[USERNAME]\Application Data\[FULL USERNAME]\[XUL APP NAME]\Profiles\[XXX].default\compreg.dat<br>
C:\Documents and Settings\[USERNAME]\Application Data\[FULL USERNAME]\[XUL APP NAME]\Profiles\[XXX].default\xpti.dat</div>
<h2>See also</h2>
<ul> <li><a href="/En/XUL/Textbox_(Toolkit_autocomplete)" title="https://developer.mozilla.org/En/XUL/Textbox_(Toolkit_autocomplete)">Textbox (Toolkit autocomplete)</a></li> <li>{{ interface("nsIAutoCompleteSearch") }}</li> <li>{{ interface("nsIAutoCompleteResult") }}</li> <li>{{ interface("nsIAutoCompleteInput") }}</li>
</ul>
Revert to this revision