mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	This patch adds the following messages to the autocomplete item: 1. For address and credit card autofill, use the `FormAutofill:FillForm` message. 2. For login autofill, use the `PasswordManager:OnFieldAutoComplete` message. 3. For generated password autofill, use the `PasswordManager:FillGeneratedPassword` message. After adding the above three messages, every autocomplete item that performs an action upon being clicked has a corresponding `fillMessageName`. Here is how the new architecture works: 1. Whenever a user selects an autocomplete entry, the `AutoCompleteChild` sends the `AutoComplete:SelectEntry` message to the parent process. 2. `AutoCompleteParent` extracts `fillMessageName` from the message and uses the prefix of the message name to determine which actor should process this message. 3. The `OnFieldAutoCompleteEntrySelected` method of the actor is called. The actor in the parent process determines what action to take. Differential Revision: https://phabricator.services.mozilla.com/D209353
		
			
				
	
	
		
			162 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* vim: set ts=4 sts=4 sw=4 et tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
/**
 | 
						|
 * This autocomplete result combines 3 arrays of entries, fixedEntries and
 | 
						|
 * externalEntries.
 | 
						|
 * Entries are Form History entries, they can be removed.
 | 
						|
 * Fixed entries are "appended" to entries, they are used for datalist items,
 | 
						|
 * search suggestions and extra items from integrations.
 | 
						|
 * External entries are meant for integrations, like Firefox Relay.
 | 
						|
 * Internally entries and fixed entries are kept separated so we can
 | 
						|
 * reuse and filter them.
 | 
						|
 *
 | 
						|
 * @implements {nsIAutoCompleteResult}
 | 
						|
 */
 | 
						|
export class FormHistoryAutoCompleteResult {
 | 
						|
  constructor(input, entries, inputName, searchString) {
 | 
						|
    this.input = input;
 | 
						|
    this.entries = entries;
 | 
						|
    this.inputName = inputName;
 | 
						|
    this.searchString = searchString;
 | 
						|
  }
 | 
						|
 | 
						|
  QueryInterface = ChromeUtils.generateQI([
 | 
						|
    "nsIAutoCompleteResult",
 | 
						|
    "nsISupportsWeakReference",
 | 
						|
  ]);
 | 
						|
 | 
						|
  // private
 | 
						|
  input = null;
 | 
						|
  entries = null;
 | 
						|
  inputName = null;
 | 
						|
  #fixedEntries = [];
 | 
						|
  externalEntries = [];
 | 
						|
 | 
						|
  set fixedEntries(value) {
 | 
						|
    this.#fixedEntries = value;
 | 
						|
    this.removeDuplicateHistoryEntries();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Remove items from history list that are already present in fixed list.
 | 
						|
   * We do this rather than the opposite ( i.e. remove items from fixed list)
 | 
						|
   * to reflect the order that is specified in the fixed list.
 | 
						|
   */
 | 
						|
  removeDuplicateHistoryEntries() {
 | 
						|
    this.entries = this.entries.filter(entry =>
 | 
						|
      this.#fixedEntries.every(
 | 
						|
        fixed => entry.text != (fixed.label || fixed.value)
 | 
						|
      )
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  getAt(index) {
 | 
						|
    for (const group of [
 | 
						|
      this.entries,
 | 
						|
      this.#fixedEntries,
 | 
						|
      this.externalEntries,
 | 
						|
    ]) {
 | 
						|
      if (index < group.length) {
 | 
						|
        return group[index];
 | 
						|
      }
 | 
						|
      index -= group.length;
 | 
						|
    }
 | 
						|
 | 
						|
    throw Components.Exception(
 | 
						|
      "Index out of range.",
 | 
						|
      Cr.NS_ERROR_ILLEGAL_VALUE
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  // Allow autoCompleteSearch to get at the JS object so it can
 | 
						|
  // modify some readonly properties for internal use.
 | 
						|
  get wrappedJSObject() {
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
 | 
						|
  // Interfaces from idl...
 | 
						|
  searchString = "";
 | 
						|
  errorDescription = "";
 | 
						|
 | 
						|
  get defaultIndex() {
 | 
						|
    return this.matchCount ? 0 : -1;
 | 
						|
  }
 | 
						|
 | 
						|
  get searchResult() {
 | 
						|
    return this.matchCount
 | 
						|
      ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
 | 
						|
      : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
 | 
						|
  }
 | 
						|
 | 
						|
  get matchCount() {
 | 
						|
    return (
 | 
						|
      this.entries.length +
 | 
						|
      this.#fixedEntries.length +
 | 
						|
      this.externalEntries.length
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  getValueAt(index) {
 | 
						|
    const item = this.getAt(index);
 | 
						|
    return item.text || item.value;
 | 
						|
  }
 | 
						|
 | 
						|
  getLabelAt(index) {
 | 
						|
    const item = this.getAt(index);
 | 
						|
    return item.text || item.label || item.value;
 | 
						|
  }
 | 
						|
 | 
						|
  getCommentAt(index) {
 | 
						|
    return this.getAt(index).comment ?? "";
 | 
						|
  }
 | 
						|
 | 
						|
  getStyleAt(index) {
 | 
						|
    const itemStyle = this.getAt(index).style;
 | 
						|
    if (itemStyle) {
 | 
						|
      return itemStyle;
 | 
						|
    }
 | 
						|
 | 
						|
    if (index >= 0) {
 | 
						|
      if (index < this.entries.length) {
 | 
						|
        return "fromhistory";
 | 
						|
      }
 | 
						|
 | 
						|
      if (index > 0 && index == this.entries.length) {
 | 
						|
        return "datalist-first";
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return "";
 | 
						|
  }
 | 
						|
 | 
						|
  getImageAt(_index) {
 | 
						|
    return "";
 | 
						|
  }
 | 
						|
 | 
						|
  getFinalCompleteValueAt(index) {
 | 
						|
    return this.getValueAt(index);
 | 
						|
  }
 | 
						|
 | 
						|
  isRemovableAt(index) {
 | 
						|
    return this.#isFormHistoryEntry(index) || this.getAt(index).removable;
 | 
						|
  }
 | 
						|
 | 
						|
  removeValueAt(index) {
 | 
						|
    if (this.#isFormHistoryEntry(index)) {
 | 
						|
      const [removedEntry] = this.entries.splice(index, 1);
 | 
						|
      const actor =
 | 
						|
        this.input.ownerGlobal.windowGlobalChild.getActor("FormHistory");
 | 
						|
      actor.sendAsyncMessage("FormHistory:RemoveEntry", {
 | 
						|
        inputName: this.inputName,
 | 
						|
        value: removedEntry.text,
 | 
						|
        guid: removedEntry.guid,
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #isFormHistoryEntry(index) {
 | 
						|
    return index >= 0 && index < this.entries.length;
 | 
						|
  }
 | 
						|
}
 |