mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
[PALEMOON] Localize a private copy of the pre-richlistbox autocomplete ksmodule
This commit is contained in:
parent
cc8e59b113
commit
51bef58e0b
3 changed files with 298 additions and 0 deletions
|
|
@ -35,6 +35,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
["OS", "resource://gre/modules/osfile.jsm"],
|
||||
["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
|
||||
["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
|
||||
["AutoCompletePopup", "resource:///modules/AutoCompletePopup.jsm"],
|
||||
["DateTimePickerHelper", "resource://gre/modules/DateTimePickerHelper.jsm"],
|
||||
].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
|
||||
|
||||
|
|
@ -405,6 +406,8 @@ BrowserGlue.prototype = {
|
|||
#endif
|
||||
FormValidationHandler.init();
|
||||
|
||||
AutoCompletePopup.init();
|
||||
|
||||
LoginManagerParent.init();
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
|
|
@ -515,6 +518,7 @@ BrowserGlue.prototype = {
|
|||
webrtcUI.uninit();
|
||||
#endif
|
||||
FormValidationHandler.uninit();
|
||||
AutoCompletePopup.uninit();
|
||||
this._dispose();
|
||||
},
|
||||
|
||||
|
|
|
|||
293
application/palemoon/modules/AutoCompletePopup.jsm
Normal file
293
application/palemoon/modules/AutoCompletePopup.jsm
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "AutoCompletePopup" ];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// nsITreeView implementation that feeds the autocomplete popup
|
||||
// with the search data.
|
||||
var AutoCompleteTreeView = {
|
||||
// nsISupports
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView,
|
||||
Ci.nsIAutoCompleteController]),
|
||||
|
||||
// Private variables
|
||||
treeBox: null,
|
||||
results: [],
|
||||
|
||||
// nsITreeView
|
||||
selection: null,
|
||||
|
||||
get rowCount() { return this.results.length; },
|
||||
setTree: function(treeBox) { this.treeBox = treeBox; },
|
||||
getCellText: function(idx, column) { return this.results[idx].value },
|
||||
isContainer: function(idx) { return false; },
|
||||
getCellValue: function(idx, column) { return false },
|
||||
isContainerOpen: function(idx) { return false; },
|
||||
isContainerEmpty: function(idx) { return false; },
|
||||
isSeparator: function(idx) { return false; },
|
||||
isSorted: function() { return false; },
|
||||
isEditable: function(idx, column) { return false; },
|
||||
canDrop: function(idx, orientation, dt) { return false; },
|
||||
getLevel: function(idx) { return 0; },
|
||||
getParentIndex: function(idx) { return -1; },
|
||||
hasNextSibling: function(idx, after) { return idx < this.results.length - 1 },
|
||||
toggleOpenState: function(idx) { },
|
||||
getCellProperties: function(idx, column) { return this.results[idx].style || ""; },
|
||||
getRowProperties: function(idx) { return ""; },
|
||||
getImageSrc: function(idx, column) { return null; },
|
||||
getProgressMode : function(idx, column) { },
|
||||
cycleHeader: function(column) { },
|
||||
cycleCell: function(idx, column) { },
|
||||
selectionChanged: function() { },
|
||||
performAction: function(action) { },
|
||||
performActionOnCell: function(action, index, column) { },
|
||||
getColumnProperties: function(column) { return ""; },
|
||||
|
||||
// nsIAutoCompleteController
|
||||
get matchCount() {
|
||||
return this.rowCount;
|
||||
},
|
||||
|
||||
handleEnter: function(aIsPopupSelection) {
|
||||
AutoCompletePopup.handleEnter(aIsPopupSelection);
|
||||
},
|
||||
|
||||
stopSearch: function() {},
|
||||
|
||||
// Internal JS-only API
|
||||
clearResults: function() {
|
||||
this.results = [];
|
||||
},
|
||||
|
||||
setResults: function(results) {
|
||||
this.results = results;
|
||||
},
|
||||
};
|
||||
|
||||
this.AutoCompletePopup = {
|
||||
MESSAGES: [
|
||||
"FormAutoComplete:SelectBy",
|
||||
"FormAutoComplete:GetSelectedIndex",
|
||||
"FormAutoComplete:SetSelectedIndex",
|
||||
"FormAutoComplete:MaybeOpenPopup",
|
||||
"FormAutoComplete:ClosePopup",
|
||||
"FormAutoComplete:Disconnect",
|
||||
"FormAutoComplete:RemoveEntry",
|
||||
"FormAutoComplete:Invalidate",
|
||||
],
|
||||
|
||||
init: function() {
|
||||
for (let msg of this.MESSAGES) {
|
||||
Services.mm.addMessageListener(msg, this);
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
for (let msg of this.MESSAGES) {
|
||||
Services.mm.removeMessageListener(msg, this);
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.type) {
|
||||
case "popupshowing": {
|
||||
this.sendMessageToBrowser("FormAutoComplete:PopupOpened");
|
||||
break;
|
||||
}
|
||||
|
||||
case "popuphidden": {
|
||||
this.sendMessageToBrowser("FormAutoComplete:PopupClosed");
|
||||
this.openedPopup = null;
|
||||
this.weakBrowser = null;
|
||||
evt.target.removeEventListener("popuphidden", this);
|
||||
evt.target.removeEventListener("popupshowing", this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Along with being called internally by the receiveMessage handler,
|
||||
// this function is also called directly by the login manager, which
|
||||
// uses a single message to fill in the autocomplete results. See
|
||||
// "RemoteLogins:autoCompleteLogins".
|
||||
showPopupWithResults: function({ browser, rect, dir, results }) {
|
||||
if (!results.length || this.openedPopup) {
|
||||
// We shouldn't ever be showing an empty popup, and if we
|
||||
// already have a popup open, the old one needs to close before
|
||||
// we consider opening a new one.
|
||||
return;
|
||||
}
|
||||
|
||||
let window = browser.ownerDocument.defaultView;
|
||||
let tabbrowser = window.gBrowser;
|
||||
if (Services.focus.activeWindow != window ||
|
||||
tabbrowser.selectedBrowser != browser) {
|
||||
// We were sent a message from a window or tab that went into the
|
||||
// background, so we'll ignore it for now.
|
||||
return;
|
||||
}
|
||||
|
||||
this.weakBrowser = Cu.getWeakReference(browser);
|
||||
this.openedPopup = browser.autoCompletePopup;
|
||||
this.openedPopup.hidden = false;
|
||||
// don't allow the popup to become overly narrow
|
||||
this.openedPopup.setAttribute("width", Math.max(100, rect.width));
|
||||
this.openedPopup.style.direction = dir;
|
||||
|
||||
AutoCompleteTreeView.setResults(results);
|
||||
this.openedPopup.view = AutoCompleteTreeView;
|
||||
this.openedPopup.selectedIndex = -1;
|
||||
this.openedPopup.invalidate();
|
||||
|
||||
if (results.length) {
|
||||
// Reset fields that were set from the last time the search popup was open
|
||||
this.openedPopup.mInput = null;
|
||||
this.openedPopup.showCommentColumn = false;
|
||||
this.openedPopup.showImageColumn = false;
|
||||
this.openedPopup.addEventListener("popuphidden", this);
|
||||
this.openedPopup.addEventListener("popupshowing", this);
|
||||
this.openedPopup.openPopupAtScreenRect("after_start", rect.left, rect.top,
|
||||
rect.width, rect.height, false,
|
||||
false);
|
||||
} else {
|
||||
this.closePopup();
|
||||
}
|
||||
},
|
||||
|
||||
invalidate(results) {
|
||||
if (!this.openedPopup) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!results.length) {
|
||||
this.closePopup();
|
||||
} else {
|
||||
AutoCompleteTreeView.setResults(results);
|
||||
// We need to re-set the view in order for the
|
||||
// tree to know the view has changed.
|
||||
this.openedPopup.view = AutoCompleteTreeView;
|
||||
this.openedPopup.invalidate();
|
||||
}
|
||||
},
|
||||
|
||||
closePopup() {
|
||||
if (this.openedPopup) {
|
||||
// Note that hidePopup() closes the popup immediately,
|
||||
// so popuphiding or popuphidden events will be fired
|
||||
// and handled during this call.
|
||||
this.openedPopup.hidePopup();
|
||||
}
|
||||
AutoCompleteTreeView.clearResults();
|
||||
},
|
||||
|
||||
removeLogin(login) {
|
||||
Services.logins.removeLogin(login);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
if (!message.target.autoCompletePopup) {
|
||||
// Returning false to pacify ESLint, but this return value is
|
||||
// ignored by the messaging infrastructure.
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (message.name) {
|
||||
case "FormAutoComplete:SelectBy": {
|
||||
this.openedPopup.selectBy(message.data.reverse, message.data.page);
|
||||
break;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:GetSelectedIndex": {
|
||||
if (this.openedPopup) {
|
||||
return this.openedPopup.selectedIndex;
|
||||
}
|
||||
// If the popup was closed, then the selection
|
||||
// has not changed.
|
||||
return -1;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:SetSelectedIndex": {
|
||||
let { index } = message.data;
|
||||
if (this.openedPopup) {
|
||||
this.openedPopup.selectedIndex = index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:MaybeOpenPopup": {
|
||||
let { results, rect, dir } = message.data;
|
||||
this.showPopupWithResults({ browser: message.target, rect, dir,
|
||||
results });
|
||||
break;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:Invalidate": {
|
||||
let { results } = message.data;
|
||||
this.invalidate(results);
|
||||
break;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:ClosePopup": {
|
||||
this.closePopup();
|
||||
break;
|
||||
}
|
||||
|
||||
case "FormAutoComplete:Disconnect": {
|
||||
// The controller stopped controlling the current input, so clear
|
||||
// any cached data. This is necessary cause otherwise we'd clear data
|
||||
// only when starting a new search, but the next input could not support
|
||||
// autocomplete and it would end up inheriting the existing data.
|
||||
AutoCompleteTreeView.clearResults();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Returning false to pacify ESLint, but this return value is
|
||||
// ignored by the messaging infrastructure.
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Despite its name, this handleEnter is only called when the user clicks on
|
||||
* one of the items in the popup since the popup is rendered in the parent process.
|
||||
* The real controller's handleEnter is called directly in the content process
|
||||
* for other methods of completing a selection (e.g. using the tab or enter
|
||||
* keys) since the field with focus is in that process.
|
||||
*/
|
||||
handleEnter(aIsPopupSelection) {
|
||||
if (this.openedPopup) {
|
||||
this.sendMessageToBrowser("FormAutoComplete:HandleEnter", {
|
||||
selectedIndex: this.openedPopup.selectedIndex,
|
||||
isPopupSelection: aIsPopupSelection,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If a browser exists that AutoCompletePopup knows about,
|
||||
* sends it a message. Otherwise, this is a no-op.
|
||||
*
|
||||
* @param {string} msgName
|
||||
* The name of the message to send.
|
||||
* @param {object} data
|
||||
* The optional data to send with the message.
|
||||
*/
|
||||
sendMessageToBrowser(msgName, data) {
|
||||
let browser = this.weakBrowser ? this.weakBrowser.get()
|
||||
: null;
|
||||
if (browser) {
|
||||
browser.messageManager.sendAsyncMessage(msgName, data);
|
||||
}
|
||||
},
|
||||
|
||||
stopSearch: function() {}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
EXTRA_JS_MODULES += [ 'promise.js' ]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'AutoCompletePopup.jsm',
|
||||
'BrowserNewTabPreloader.jsm',
|
||||
'CharsetMenu.jsm',
|
||||
'FormSubmitObserver.jsm',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue