如何为firefox工具栏做一个自定义自动完成文本框

How to do a custom auto-complete textbox for firefox toolbar

本文关键字:自定义 一个 文本 firefox 工具栏      更新时间:2023-09-26

更新:我找到了一个解决方案

我必须这样做:

在扩展名dir中,我创建了一个名为"components"的新目录。

在该目录中,我创建了一个带有自定义自动完成名称的js文件,在本例中为"simpleautocomplete.js"。这个js文件的内容是:

/*
 * https://developer.mozilla.org/en/How_to_implement_custom_autocomplete_search_component
 */
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";
try{
    Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
} catch (x) {
}

// 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: [],
    getLabelAt: function(index) { return this._results[index]; },
  /**
   * 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 = {
    classID: CLASS_ID,
    classDescription: CLASS_NAME,
    contractID: CONTRACT_ID,
  /*
   * 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;
  },
  _QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIAutoCompleteSearch])
};
// 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; }
if (XPCOMUtils.generateNSGetFactory){
    var NSGetFactory = XPCOMUtils.generateNSGetFactory([SimpleAutoCompleteSearch]);
}

我将这段代码添加到chrome.manifest:

component {6224daa1-71a2-4d1a-ad90-01ca1c08e323} components/simpleautocomplete.js
contract @mozilla.org/autocomplete/search;1?name=simple-autocomplete {6224daa1-71a2-4d1a-ad90-01ca1c08e323}

在xul文件中我添加了:

<textbox type="autocomplete" autocompletesearch="simple-autocomplete"   
autocompletesearchparam='[{"value":"param1"},{"value":"param2"}]' /> 

重要提示:设置autocompletesearchparam属性时,赋值必须在两个单引号内,而不是双引号内。只有里面的值必须像上面的xul一样用双引号括起来。

就是这样。

当我测试我的扩展并在自动完成文本框中输入'p'时,单词"param1"answers";param2"poped-up .

我还可以在js文件中设置如下参数:

var searchTextField = document.getElementById("searchTextField");
var param1 = "Param1", param2 = "Param2";
paramsToSet = "[";
paramsToSet += "{'"value'" : '"" + param1 + "'"},";
paramsToSet += "{'"value'" : '"" + param2 + "'"},";
paramsToSet = paramsToSet.substring(0, paramsToSet.length-1); // to remove the last ","
paramsToSet += "]";
paramsToSet = paramsToSet.toLowerCase(); // important!
searchTextField.setAttribute("autocompletesearchparam", paramsToSet);

note1:请注意,自动完成文本框只接受小写字母的文本。

note2:如果设置文本框的参数是动态的,需要一些时间,例如-当客户端在文本框中输入,输入的文本被发送到服务器,所以包含参数的响应将附加到文本框,这个过程需要一些时间(大约一秒或一半),然后弹出自动完成不会弹出,因为它的autocompletesearchparam是空的,当客户端开始键入。在这种情况下,当你在文本框中设置了如下参数时,可以强制弹出自动完成:

searchTextField.open = true;    

我自己找到了解决办法,希望能对别人有所帮助。

我必须这样做:

我在扩展名dir中创建了一个名为components的新目录。

在该目录中,我创建了一个带有自定义自动完成名称的js文件,在本例中为"simpleautocomplete.js"。这个js文件的内容是:

/*
 * https://developer.mozilla.org/en/How_to_implement_custom_autocomplete_search_component
 */
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";
try{
    Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
} catch (x) {
}

// 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: [],
    getLabelAt: function(index) { return this._results[index]; },
  /**
   * 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 = {
    classID: CLASS_ID,
    classDescription: CLASS_NAME,
    contractID: CONTRACT_ID,
  /*
   * 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;
  },
  _QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIAutoCompleteSearch])
};
// 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; }
if (XPCOMUtils.generateNSGetFactory){
    var NSGetFactory = XPCOMUtils.generateNSGetFactory([SimpleAutoCompleteSearch]);
}

我将这段代码添加到chrome.manifest:

component {6224daa1-71a2-4d1a-ad90-01ca1c08e323} components/simpleautocomplete.js
contract @mozilla.org/autocomplete/search;1?name=simple-autocomplete {6224daa1-71a2-4d1a-ad90-01ca1c08e323}

在xul文件中我添加了:

<textbox type="autocomplete" autocompletesearch="simple-autocomplete"   
autocompletesearchparam='[{"value":"param1"},{"value":"param2"}]' /> 
重要提示:当设置autocompletesearchparam属性时,赋值必须在两个单引号内,而不是双引号内。只有里面的值必须像上面的xul一样用双引号括起来。

就是这样。

当我测试我的扩展并在自动完成文本框中输入"p"时,弹出了"param1"answers"param2"两个词。

我还可以在js文件中设置如下参数:

var searchTextField = document.getElementById("searchTextField");
var param1 = "Param1", param2 = "Param2";
paramsToSet = "[";
paramsToSet += "{'"value'" : '"" + param1 + "'"},";
paramsToSet += "{'"value'" : '"" + param2 + "'"},";
paramsToSet = paramsToSet.substring(0, paramsToSet.length-1); // to remove the last ","
paramsToSet += "]";
paramsToSet = paramsToSet.toLowerCase(); // important!
searchTextField.setAttribute("autocompletesearchparam", paramsToSet);

note1:请注意,自动完成文本框只接受小写字母的文本。

note2:如果设置文本框的参数是动态的并且需要一些时间,例如-当客户端在文本框中输入并且输入的文本被发送到服务器时,因此包含参数的响应将附加到文本框中,并且这个过程需要一些时间(大约一秒或一半),那么弹出的自动完成将不会弹出,因为它的autocompletesearchparam在客户端开始键入时是空的。在这种情况下,当你在文本框中设置了如下参数时,可以强制弹出自动完成:

searchTextField.open = true;