2016-08-18 17:50:58 +03:00
|
|
|
/* 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";
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
var EXPORTED_SYMBOLS = ["AutoCompleteParent"];
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2019-01-17 21:18:31 +03:00
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
// Stores the browser and actor that has the active popup, used by formfill
|
|
|
|
let currentBrowserWeakRef = null;
|
|
|
|
let currentActor = null;
|
|
|
|
|
|
|
|
let autoCompleteListeners = new Set();
|
|
|
|
|
|
|
|
function compareContext(message) {
|
|
|
|
if (
|
|
|
|
!currentActor ||
|
|
|
|
(currentActor.browsingContext != message.data.browsingContext &&
|
|
|
|
currentActor.browsingContext.top != message.data.browsingContext)
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These are two synchronous messages sent by the child.
|
|
|
|
// The browsingContext within the message data is either the one that has
|
|
|
|
// the active autocomplete popup or the top-level of the one that has
|
|
|
|
// the active autocomplete popup.
|
|
|
|
Services.ppmm.addMessageListener(
|
|
|
|
"FormAutoComplete:GetSelectedIndex",
|
|
|
|
message => {
|
|
|
|
if (compareContext(message)) {
|
|
|
|
let actor = currentActor;
|
|
|
|
if (actor && actor.openedPopup) {
|
|
|
|
return actor.openedPopup.selectedIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Services.ppmm.addMessageListener("FormAutoComplete:SelectBy", message => {
|
|
|
|
if (compareContext(message)) {
|
|
|
|
let actor = currentActor;
|
|
|
|
if (actor && actor.openedPopup) {
|
|
|
|
actor.openedPopup.selectBy(message.data.reverse, message.data.page);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// AutoCompleteResultView is an abstraction around a list of results.
|
|
|
|
// It implements enough of nsIAutoCompleteController and
|
|
|
|
// nsIAutoCompleteInput to make the richlistbox popup work. Since only
|
|
|
|
// one autocomplete popup should be open at a time, this is a singleton.
|
2016-08-20 00:15:56 +03:00
|
|
|
var AutoCompleteResultView = {
|
2016-08-18 17:50:58 +03:00
|
|
|
// nsISupports
|
2017-06-13 01:45:00 +03:00
|
|
|
QueryInterface: ChromeUtils.generateQI([
|
|
|
|
Ci.nsIAutoCompleteController,
|
2017-06-15 03:46:42 +03:00
|
|
|
Ci.nsIAutoCompleteInput,
|
2017-06-13 01:45:00 +03:00
|
|
|
]),
|
2016-08-18 17:50:58 +03:00
|
|
|
|
|
|
|
// Private variables
|
|
|
|
results: [],
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
// The AutoCompleteParent currently showing results or null otherwise.
|
|
|
|
currentActor: null,
|
|
|
|
|
2016-08-18 17:50:58 +03:00
|
|
|
// nsIAutoCompleteController
|
|
|
|
get matchCount() {
|
2016-08-20 00:15:56 +03:00
|
|
|
return this.results.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
getValueAt(index) {
|
|
|
|
return this.results[index].value;
|
|
|
|
},
|
|
|
|
|
2018-10-19 19:08:26 +03:00
|
|
|
getFinalCompleteValueAt(index) {
|
|
|
|
return this.results[index].value;
|
|
|
|
},
|
|
|
|
|
2016-08-20 00:15:56 +03:00
|
|
|
getLabelAt(index) {
|
2019-05-10 20:58:10 +03:00
|
|
|
// Backwardly-used by richlist autocomplete - see getCommentAt.
|
|
|
|
// The label is used for secondary information.
|
|
|
|
return this.results[index].comment;
|
2016-08-20 00:15:56 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
getCommentAt(index) {
|
|
|
|
// The richlist autocomplete popup uses comment for its main
|
|
|
|
// display of an item, which is why we're returning the label
|
|
|
|
// here instead.
|
|
|
|
return this.results[index].label;
|
|
|
|
},
|
|
|
|
|
|
|
|
getStyleAt(index) {
|
|
|
|
return this.results[index].style;
|
|
|
|
},
|
|
|
|
|
|
|
|
getImageAt(index) {
|
|
|
|
return this.results[index].image;
|
2016-08-18 17:50:58 +03:00
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
handleEnter(aIsPopupSelection) {
|
2019-10-09 22:02:57 +03:00
|
|
|
if (this.currentActor) {
|
|
|
|
this.currentActor.handleEnter(aIsPopupSelection);
|
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
stopSearch() {},
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2016-08-20 00:15:56 +03:00
|
|
|
searchString: "",
|
|
|
|
|
|
|
|
// nsIAutoCompleteInput
|
|
|
|
get controller() {
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
get popup() {
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
_focus() {
|
2019-10-09 22:02:57 +03:00
|
|
|
if (this.currentActor) {
|
|
|
|
this.currentActor.requestFocus();
|
|
|
|
}
|
2016-08-20 00:15:56 +03:00
|
|
|
},
|
|
|
|
|
2016-08-18 17:50:58 +03:00
|
|
|
// Internal JS-only API
|
2016-12-30 02:34:54 +03:00
|
|
|
clearResults() {
|
2019-10-09 22:02:57 +03:00
|
|
|
this.currentActor = null;
|
2016-08-18 17:50:58 +03:00
|
|
|
this.results = [];
|
|
|
|
},
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
setResults(actor, results) {
|
|
|
|
this.currentActor = actor;
|
2016-08-18 17:50:58 +03:00
|
|
|
this.results = results;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
class AutoCompleteParent extends JSWindowActorParent {
|
|
|
|
willDestroy() {
|
|
|
|
if (this.openedPopup) {
|
|
|
|
this.openedPopup.closePopup();
|
2016-08-18 17:50:58 +03:00
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2019-12-11 16:37:59 +03:00
|
|
|
static getCurrentActor() {
|
|
|
|
return currentActor;
|
|
|
|
}
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
static getCurrentBrowser() {
|
|
|
|
return currentBrowserWeakRef ? currentBrowserWeakRef.get() : null;
|
|
|
|
}
|
2017-11-10 12:00:33 +03:00
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
static addPopupStateListener(listener) {
|
|
|
|
autoCompleteListeners.add(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
static removePopupStateListener(listener) {
|
|
|
|
autoCompleteListeners.delete(listener);
|
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
handleEvent(evt) {
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
switch (evt.type) {
|
|
|
|
case "popupshowing": {
|
2019-10-09 22:02:57 +03:00
|
|
|
this.sendAsyncMessage("FormAutoComplete:PopupOpened", {});
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "popuphidden": {
|
2018-06-22 13:37:09 +03:00
|
|
|
let selectedIndex = this.openedPopup.selectedIndex;
|
2020-04-29 00:59:34 +03:00
|
|
|
let selectedRowComment =
|
|
|
|
selectedIndex != -1
|
|
|
|
? AutoCompleteResultView.getCommentAt(selectedIndex)
|
|
|
|
: "";
|
2018-06-22 13:37:09 +03:00
|
|
|
let selectedRowStyle =
|
|
|
|
selectedIndex != -1
|
|
|
|
? AutoCompleteResultView.getStyleAt(selectedIndex)
|
|
|
|
: "";
|
2019-10-09 22:02:57 +03:00
|
|
|
this.sendAsyncMessage("FormAutoComplete:PopupClosed", {
|
2020-04-29 00:59:34 +03:00
|
|
|
selectedRowComment,
|
2018-06-22 13:37:09 +03:00
|
|
|
selectedRowStyle,
|
|
|
|
});
|
2016-08-20 00:15:56 +03:00
|
|
|
AutoCompleteResultView.clearResults();
|
|
|
|
// adjustHeight clears the height from the popup so that
|
|
|
|
// we don't have a big shrink effect if we closed with a
|
|
|
|
// large list, and then open on a small one.
|
|
|
|
this.openedPopup.adjustHeight();
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
this.openedPopup = null;
|
2019-10-09 22:02:57 +03:00
|
|
|
currentBrowserWeakRef = null;
|
|
|
|
currentActor = null;
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
evt.target.removeEventListener("popuphidden", this);
|
|
|
|
evt.target.removeEventListener("popupshowing", this);
|
|
|
|
break;
|
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
showPopupWithResults({ rect, dir, results }) {
|
2016-08-18 17:50:58 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
let browser = this.browsingContext.top.embedderElement;
|
2016-08-18 17:50:58 +03:00
|
|
|
let window = browser.ownerGlobal;
|
2017-06-02 02:20:34 +03:00
|
|
|
// Also check window top in case this is a sidebar.
|
2017-11-04 15:40:47 +03:00
|
|
|
if (
|
|
|
|
Services.focus.activeWindow !== window.top &&
|
|
|
|
Services.focus.focusedWindow.top !== window.top
|
|
|
|
) {
|
2016-08-18 17:50:58 +03:00
|
|
|
// We were sent a message from a window or tab that went into the
|
|
|
|
// background, so we'll ignore it for now.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-29 22:35:53 +03:00
|
|
|
// Non-empty result styles
|
|
|
|
let resultStyles = new Set(results.map(r => r.style).filter(r => !!r));
|
2019-10-09 22:02:57 +03:00
|
|
|
currentBrowserWeakRef = Cu.getWeakReference(browser);
|
|
|
|
currentActor = this;
|
2016-08-18 17:50:58 +03:00
|
|
|
this.openedPopup = browser.autoCompletePopup;
|
2017-01-25 06:04:15 +03:00
|
|
|
// the layout varies according to different result type
|
2019-07-29 22:35:53 +03:00
|
|
|
this.openedPopup.setAttribute("resultstyles", [...resultStyles].join(" "));
|
2016-08-18 17:50:58 +03:00
|
|
|
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;
|
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
AutoCompleteResultView.setResults(this, results);
|
2016-08-20 00:15:56 +03:00
|
|
|
this.openedPopup.view = AutoCompleteResultView;
|
2016-08-18 17:50:58 +03:00
|
|
|
this.openedPopup.selectedIndex = -1;
|
|
|
|
|
2020-03-14 00:09:07 +03:00
|
|
|
// Reset fields that were set from the last time the search popup was open
|
|
|
|
this.openedPopup.mInput = AutoCompleteResultView;
|
|
|
|
// Temporarily increase the maxRows as we don't want to show
|
|
|
|
// the scrollbar in login or form autofill popups.
|
|
|
|
if (
|
|
|
|
resultStyles.size &&
|
|
|
|
(resultStyles.has("autofill-profile") || resultStyles.has("loginsFooter"))
|
|
|
|
) {
|
|
|
|
this.openedPopup._normalMaxRows = this.openedPopup.maxRows;
|
2020-04-24 22:25:40 +03:00
|
|
|
this.openedPopup.mInput.maxRows = 10;
|
2016-08-18 17:50:58 +03:00
|
|
|
}
|
2020-03-14 00:09:07 +03:00
|
|
|
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
|
|
|
|
);
|
|
|
|
this.openedPopup.invalidate();
|
2020-03-18 22:03:51 +03:00
|
|
|
this._maybeRecordTelemetryEvents(results);
|
2020-03-14 00:09:07 +03:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:03:51 +03:00
|
|
|
/**
|
|
|
|
* @param {object[]} results - Non-empty array of autocomplete results.
|
|
|
|
*/
|
|
|
|
_maybeRecordTelemetryEvents(results) {
|
2020-03-14 00:09:07 +03:00
|
|
|
let actor = this.browsingContext.currentWindowGlobal.getActor(
|
|
|
|
"LoginManager"
|
|
|
|
);
|
|
|
|
actor.maybeRecordPasswordGenerationShownTelemetryEvent(results);
|
2020-03-18 22:03:51 +03:00
|
|
|
|
|
|
|
// Assume the result with the start time (loginsFooter) is last.
|
|
|
|
let lastResult = results[results.length - 1];
|
|
|
|
if (lastResult.style != "loginsFooter") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The comment field of `loginsFooter` results have many additional pieces of
|
|
|
|
// information for telemetry purposes. After bug 1555209, this information
|
|
|
|
// can be passed to the parent process outside of nsIAutoCompleteResult APIs
|
|
|
|
// so we won't need this hack.
|
|
|
|
let rawExtraData = JSON.parse(lastResult.comment);
|
|
|
|
if (!rawExtraData.searchStartTimeMS) {
|
|
|
|
throw new Error("Invalid autocomplete search start time");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rawExtraData.stringLength > 1) {
|
|
|
|
// To reduce event volume, only record for lengths 0 and 1.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let duration =
|
|
|
|
Services.telemetry.msSystemNow() - rawExtraData.searchStartTimeMS;
|
|
|
|
delete rawExtraData.searchStartTimeMS;
|
|
|
|
|
|
|
|
delete rawExtraData.formHostname;
|
|
|
|
|
|
|
|
// Add counts by result style to rawExtraData.
|
|
|
|
results.reduce((accumulated, r) => {
|
|
|
|
// Keys can be a maximum of 15 characters and values must be strings.
|
2020-05-10 03:49:59 +03:00
|
|
|
// Also treat both "loginWithOrigin" and "login" as "login" as extra_keys
|
|
|
|
// is limited to 10.
|
|
|
|
let truncatedStyle = r.style.substring(
|
|
|
|
0,
|
|
|
|
r.style === "loginWithOrigin" ? 5 : 15
|
|
|
|
);
|
2020-03-18 22:03:51 +03:00
|
|
|
accumulated[truncatedStyle] = (accumulated[truncatedStyle] || 0) + 1;
|
|
|
|
return accumulated;
|
|
|
|
}, rawExtraData);
|
|
|
|
|
|
|
|
// Convert extra values to strings since recordEvent requires that.
|
|
|
|
let extraStrings = Object.fromEntries(
|
|
|
|
Object.entries(rawExtraData).map(([key, val]) => {
|
|
|
|
let stringVal = "";
|
|
|
|
if (typeof val == "boolean") {
|
|
|
|
stringVal += val ? "1" : "0";
|
|
|
|
} else {
|
|
|
|
stringVal += val;
|
|
|
|
}
|
|
|
|
return [key, stringVal];
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
Services.telemetry.recordEvent(
|
|
|
|
"form_autocomplete",
|
|
|
|
"show",
|
|
|
|
"logins",
|
|
|
|
// Convert to a string
|
|
|
|
duration + "",
|
|
|
|
extraStrings
|
|
|
|
);
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
|
|
|
invalidate(results) {
|
|
|
|
if (!this.openedPopup) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!results.length) {
|
|
|
|
this.closePopup();
|
|
|
|
} else {
|
2019-10-09 22:02:57 +03:00
|
|
|
AutoCompleteResultView.setResults(this, results);
|
2016-08-18 17:50:58 +03:00
|
|
|
this.openedPopup.invalidate();
|
2020-03-18 22:03:51 +03:00
|
|
|
this._maybeRecordTelemetryEvents(results);
|
2016-08-18 17:50:58 +03:00
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
|
|
|
closePopup() {
|
|
|
|
if (this.openedPopup) {
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
// Note that hidePopup() closes the popup immediately,
|
|
|
|
// so popuphiding or popuphidden events will be fired
|
|
|
|
// and handled during this call.
|
|
|
|
this.openedPopup.hidePopup();
|
2016-08-18 17:50:58 +03:00
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
receiveMessage(message) {
|
2019-10-09 22:02:57 +03:00
|
|
|
let browser = this.browsingContext.top.embedderElement;
|
|
|
|
if (!browser || !browser.autoCompletePopup) {
|
|
|
|
// If there is no browser or popup, just make sure that the popup has been closed.
|
|
|
|
if (this.openedPopup) {
|
|
|
|
this.openedPopup.closePopup();
|
|
|
|
}
|
|
|
|
|
2016-08-18 17:50:58 +03:00
|
|
|
// Returning false to pacify ESLint, but this return value is
|
|
|
|
// ignored by the messaging infrastructure.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (message.name) {
|
|
|
|
case "FormAutoComplete:SetSelectedIndex": {
|
|
|
|
let { index } = message.data;
|
|
|
|
if (this.openedPopup) {
|
|
|
|
this.openedPopup.selectedIndex = index;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "FormAutoComplete:MaybeOpenPopup": {
|
|
|
|
let { results, rect, dir } = message.data;
|
2017-06-13 01:45:00 +03:00
|
|
|
this.showPopupWithResults({
|
|
|
|
rect,
|
|
|
|
dir,
|
2017-06-15 03:46:42 +03:00
|
|
|
results,
|
2017-06-13 01:45:00 +03:00
|
|
|
});
|
2019-10-09 22:02:57 +03:00
|
|
|
|
|
|
|
this.notifyListeners();
|
2016-08-18 17:50:58 +03:00
|
|
|
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.
|
2016-08-20 00:15:56 +03:00
|
|
|
AutoCompleteResultView.clearResults();
|
2016-08-18 17:50:58 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Returning false to pacify ESLint, but this return value is
|
|
|
|
// ignored by the messaging infrastructure.
|
|
|
|
return false;
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
notifyListeners() {
|
|
|
|
let window = this.browsingContext.top.embedderElement.ownerGlobal;
|
|
|
|
for (let listener of autoCompleteListeners) {
|
|
|
|
try {
|
|
|
|
listener(window);
|
|
|
|
} catch (ex) {
|
|
|
|
Cu.reportError(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
|
|
|
/**
|
2017-01-24 03:06:22 +03:00
|
|
|
* 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.
|
2017-06-13 20:48:52 +03:00
|
|
|
* @param {boolean} aIsPopupSelection
|
2016-08-18 17:50:58 +03:00
|
|
|
*/
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
handleEnter(aIsPopupSelection) {
|
|
|
|
if (this.openedPopup) {
|
2019-10-09 22:02:57 +03:00
|
|
|
this.sendAsyncMessage("FormAutoComplete:HandleEnter", {
|
Bug 1296638 - AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm need to have popupOpen be in sync. r=MattN
The opening of the AutoCompletePopup will always start from content, but closing the
popup can occur in the parent (for example, if the user switches focus from the browser),
or in content (the user hits Esc, for example, which tells the parent to close the popup).
This relationship between content and the popup has been true for a while, but the
patch in bug 1294502 didn't account for it. In particular, before this patch, it was
possible for AutoCompletePopup in browser-content.js and AutoCompletePopup.jsm to get
out of sync on whether or not the popup is open.
Mainly, this is because the parent wasn't telling the content that the popup had
hidden if the hide was initialized by the parent. The other reason, was because
the popupOpen state in browser-content.js was being set immediately, instead
of waiting for the parent to report that the popup had indeed opened or closed.
MozReview-Commit-ID: CRkg49lP1Hd
--HG--
extra : rebase_source : 0a0383170fc78f8639713a5879ab18468fc0eef7
2016-09-17 19:44:04 +03:00
|
|
|
selectedIndex: this.openedPopup.selectedIndex,
|
|
|
|
isPopupSelection: aIsPopupSelection,
|
|
|
|
});
|
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
}
|
2016-08-18 17:50:58 +03:00
|
|
|
|
2019-10-09 22:02:57 +03:00
|
|
|
stopSearch() {}
|
2016-08-20 00:15:56 +03:00
|
|
|
|
|
|
|
/**
|
2019-10-09 22:02:57 +03:00
|
|
|
* Sends a message to the browser that is requesting the input
|
|
|
|
* that the open popup should be focused.
|
2016-08-20 00:15:56 +03:00
|
|
|
*/
|
2016-12-30 02:34:54 +03:00
|
|
|
requestFocus() {
|
2019-10-09 22:02:57 +03:00
|
|
|
// Bug 1582722 - See the response in AutoCompleteChild.jsm for why this disabled.
|
|
|
|
/*
|
2016-08-20 00:15:56 +03:00
|
|
|
if (this.openedPopup) {
|
2019-10-09 22:02:57 +03:00
|
|
|
this.sendAsyncMessage("FormAutoComplete:Focus");
|
2016-08-20 00:15:56 +03:00
|
|
|
}
|
2019-10-09 22:02:57 +03:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|