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: 37909
  • Created:
  • Creator: Mozdevuser22
  • Is current revision? No
  • Comment 193 words added

Revision Content

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.

Sometimes, you may want to build your own autocomplete source. To build a source 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 use JavaScript. How to Build an XPCOM Component in Javascript will step you through the process. 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="[{&#34;value&#34;:&#34;mark&#34;,&#34;comment&#34;:&#34;cool dude&#34;},{&#34;value&#34;:&#34;mary&#34;,&#34;comment&#34;:&#34;nice lady&#34;},{&#34;value&#34;:&#34;jimmy&#34;,&#34;comment&#34;:&#34;very uncool guy&#34;},{&#34;value&#34;:&#34;jimbo&#34;,&#34;comment&#34;: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; }

Introduced in Gecko 2 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)
The components registration was changed (see  more info here: https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0),
So you need to make the following 2 additional changes:
Change 1 - Add "NSGetFactory", after "NSGetModule", in the end of JS file, as follows:
function NSGetFactory(cid) {
  if (cid.toString().toUpperCase() != CLASS_ID.toString().toUpperCase())
      throw Components.results.NS_ERROR_FACTORY_NOT_REGISTERED;

  return SimpleAutoCompleteSearch;
}
Change 2 - explictly register the compienent by adding the following 2 lines in the "chrome.manifest":
component {6224daa1-71a2-4d1a-ad90-01ca1c08e3237} components/[YourFileName].js
contract @mozilla.org/autocomplete/search;1?name=simple-autocomplete {6224daa1-71a2-4d1a-ad90-01ca1c08e323}

 

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

Revision Source

<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="eval">&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>
<p>Sometimes, you may want to build your own autocomplete source. To build a source 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 use JavaScript. <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. 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>&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>&lt;panel id="richPopup" type="autocomplete-richlistbox" /&gt;
&lt;textbox id="text1" type="autocomplete" autocompletesearch="simple-autocomplete" showcommentcolumn="true" autocompletepopup="richPopup" autocompletesearchparam="[{&amp;#34;value&amp;#34;:&amp;#34;mark&amp;#34;,&amp;#34;comment&amp;#34;:&amp;#34;cool dude&amp;#34;},{&amp;#34;value&amp;#34;:&amp;#34;mary&amp;#34;,&amp;#34;comment&amp;#34;:&amp;#34;nice lady&amp;#34;},{&amp;#34;value&amp;#34;:&amp;#34;jimmy&amp;#34;,&amp;#34;comment&amp;#34;:&amp;#34;very uncool guy&amp;#34;},{&amp;#34;value&amp;#34;:&amp;#34;jimbo&amp;#34;,&amp;#34;comment&amp;#34;: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>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"><span style="font-size: x-large; "><strong>Introduced in Gecko 2 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)</strong></span></div>

<div class="geckoVersionNote">The components registration was changed (see  more info here: <a href="/en/XPCOM/XPCOM_changes_in_Gecko_2.0" title="https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0">https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0</a>),</div>
<div class="geckoVersionNote">So you need to make the following 2 additional changes:</div>

<div class="geckoVersionNote"><u><strong>Change 1 - Add "<em>NSGetFactory</em>", after "<em>NSGetModule</em>", in the end of JS file, as follows:</strong></u></div>

<div class="geckoVersionNote"><code>function NSGetFactory(cid) {</code></div>
<div class="geckoVersionNote"><code>  if (cid.toString().toUpperCase() != CLASS_ID.toString().toUpperCase())</code></div>
<div class="geckoVersionNote"><code>      throw Components.results.NS_ERROR_FACTORY_NOT_REGISTERED;</code></div>
<div class="geckoVersionNote"><code><br>
</code></div>
<div class="geckoVersionNote"><code>  return SimpleAutoCompleteSearch;</code></div>
<div class="geckoVersionNote"><code>}</code></div>

<div class="geckoVersionNote"><u><strong>Change 2 - explictly register the compienent by adding the following 2 lines in the "chrome.manifest":</strong></u></div>

<div class="geckoVersionNote">
<div><code>component {<span class="Apple-style-span" style="font-family: 'Courier New', 'Andale Mono', monospace; line-height: normal; white-space: pre; ">6224daa1-71a2-4d1a-ad90-01ca1c08e323</span>7} components/[YourFileName].js</code></div>
<div><code>contract @mozilla.org/autocomplete/search;1?name=<span class="Apple-style-span" style="font-family: 'Courier New', 'Andale Mono', monospace; line-height: normal; white-space: pre; ">simple-autocomplete</span> {<span class="Apple-style-span" style="font-family: 'Courier New', 'Andale Mono', monospace; line-height: normal; white-space: pre; ">6224daa1-71a2-4d1a-ad90-01ca1c08e323</span>}</code></div>

</div>

<p> </p>
<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>
Revert to this revision