gecko-dev/toolkit/modules/AutoCompletePopupContent.jsm

154 строки
4.0 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
var EXPORTED_SYMBOLS = ["AutoCompletePopup"];
/* eslint no-unused-vars: ["error", {args: "none"}] */
ChromeUtils.defineModuleGetter(
this,
"BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm"
);
const MESSAGES = [
"FormAutoComplete:HandleEnter",
"FormAutoComplete:PopupClosed",
"FormAutoComplete:PopupOpened",
"FormAutoComplete:RequestFocus",
];
class AutoCompletePopup {
constructor(mm) {
this.mm = mm;
for (let messageName of MESSAGES) {
mm.addMessageListener(messageName, this);
}
this._input = null;
this._popupOpen = false;
}
receiveMessage(message) {
switch (message.name) {
case "FormAutoComplete:HandleEnter": {
this.selectedIndex = message.data.selectedIndex;
let controller = Cc[
"@mozilla.org/autocomplete/controller;1"
].getService(Ci.nsIAutoCompleteController);
controller.handleEnter(message.data.isPopupSelection);
break;
}
case "FormAutoComplete:PopupClosed": {
this._popupOpen = false;
break;
}
case "FormAutoComplete:PopupOpened": {
this._popupOpen = true;
break;
}
case "FormAutoComplete:RequestFocus": {
if (this._input) {
this._input.focus();
}
break;
}
}
}
get input() {
return this._input;
}
get overrideValue() {
return null;
}
set selectedIndex(index) {
this.mm.sendAsyncMessage("FormAutoComplete:SetSelectedIndex", { index });
}
get selectedIndex() {
// selectedIndex getter must be synchronous because we need the
// correct value when the controller is in controller::HandleEnter.
// We can't easily just let the parent inform us the new value every
// time it changes because not every action that can change the
// selectedIndex is trivial to catch (e.g. moving the mouse over the
// list).
return this.mm.sendSyncMessage("FormAutoComplete:GetSelectedIndex", {});
}
get popupOpen() {
return this._popupOpen;
}
openAutocompletePopup(input, element) {
if (this._popupOpen || !input) {
return;
}
let rect = BrowserUtils.getElementBoundingScreenRect(element);
let window = element.ownerGlobal;
let dir = window.getComputedStyle(element).direction;
let results = this.getResultsFromController(input);
this.mm.sendAsyncMessage("FormAutoComplete:MaybeOpenPopup", {
results,
rect,
dir,
});
this._input = input;
}
closePopup() {
// We set this here instead of just waiting for the
// PopupClosed message to do it so that we don't end
// up in a state where the content thinks that a popup
// is open when it isn't (or soon won't be).
this._popupOpen = false;
this.mm.sendAsyncMessage("FormAutoComplete:ClosePopup", {});
}
invalidate() {
if (this._popupOpen) {
let results = this.getResultsFromController(this._input);
this.mm.sendAsyncMessage("FormAutoComplete:Invalidate", { results });
}
}
selectBy(reverse, page) {
this._index = this.mm.sendSyncMessage("FormAutoComplete:SelectBy", {
reverse,
page,
});
}
getResultsFromController(inputField) {
let results = [];
if (!inputField) {
return results;
}
let controller = inputField.controller;
if (!(controller instanceof Ci.nsIAutoCompleteController)) {
return results;
}
for (let i = 0; i < controller.matchCount; ++i) {
let result = {};
result.value = controller.getValueAt(i);
result.label = controller.getLabelAt(i);
result.comment = controller.getCommentAt(i);
result.style = controller.getStyleAt(i);
result.image = controller.getImageAt(i);
results.push(result);
}
return results;
}
}