function search()
{
  this.startSearch = function startSearch(searchString, searchParam, previousResult, listener)
  {
    // …
  };

  this.stopSearch = function stopSearch()
  {
    // …
  };

  this.QueryInterface = function QueryInterface(aIID)
  {
    if (!aIID.equals(Components.interfaces.nsIAutoCompleteSearch) &&    
        !aIID.equals(Components.interfaces.nsISupports))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  };
}

function results(list, search, type)
{
  var data = list || [];

  this.defaultIndex = 0;
  this.errorDescription = undefined;
  this.matchCount = data.length;
  this.searchResult = data.length == 0 ? Components.interfaces.nsIAutoCompleteResult.RESULT_NOMATCH : type;
  this.searchString = search;

  this.getCommentAt = function getCommentAt(idx)
  {
    return "";
  };

  this.getStyleAt = function getStyleAt(idx)
  {
    return "";
  };

  this.getValueAt = function getValueAt(idx)
  {
    return data[idx];
  };

  this.removeValueAt = function removeValueAt(idx)
  {
    data = data.splice(idx, 1);
  };

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

// all the rest of this is just the magic incantation for creating a module.
function NSGetModule()
{
  var components = [
    {
      name: "Autocomplete Search",
      classID: Components.ID("{32f17767-735f-46de-a11d-a16b48324261}"), // make sure you create your own uuid, they can't be reused
      contractID: "@mozilla.org/autocomplete/search;1?name=foo",
      factory: makeFactory(search)
    },
    {
      name: "Autocomplete Results",
      classID: Components.ID("{8f430d1e-ee24-4260-a88c-7f534d9887dc}"), // make sure you create your own uuid, they can't be reused
      contractID: "@example.com/autocomplete/results;1",
      factory: makeFactory(results)
    }
  ];

  function makeFactory(obj)
  {
    return {
      createInstance: function createInstance(outer, iid)
      {
        if (outer != null)
          throw Components.results.NS_ERROR_NO_AGGREGATION;

        return (new obj).QueryInterface(iid);
      }
    };
  }

  return {
    registerSelf: function(compMgr, fileSpec, location, type)
    {
      compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

      for each (component in components)
        compMgr.registerFactoryLocation(component.classID,
                                        component.name,
                                        component.contractID,
                                        fileSpec,
                                        location,
                                        type);
    },

    unregisterSelf: function(compMgr, location, type)
    {
      compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

      for each (component in components)
        compMgr.unregisterFactoryLocation(component.classID, location);
    },

    getClassObject: function(compMgr, cid, iid)
    {
      if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

      for each (component in components)
        if (cid.equals(component.classID))
          return component.factory;

      throw Components.results.NS_ERROR_NO_INTERFACE;
    },

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

