Bug 1252074 - test_form_autocomplete.html and test_form_autocomplete_with_list.html should pass on e10s. r=paolo
MozReview-Commit-ID: CISKztr4p4N
--- a/toolkit/components/satchel/AutoCompleteE10S.jsm
+++ b/toolkit/components/satchel/AutoCompleteE10S.jsm
@@ -81,16 +81,17 @@ var AutoCompleteE10SView = {
this.AutoCompleteE10S = {
init: function() {
let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIMessageListenerManager);
messageManager.addMessageListener("FormAutoComplete:SelectBy", this);
messageManager.addMessageListener("FormAutoComplete:GetSelectedIndex", this);
messageManager.addMessageListener("FormAutoComplete:MaybeOpenPopup", this);
messageManager.addMessageListener("FormAutoComplete:ClosePopup", this);
+ messageManager.addMessageListener("FormAutoComplete:Disconnect", this);
messageManager.addMessageListener("FormAutoComplete:RemoveEntry", this);
},
_initPopup: function(browserWindow, rect, direction) {
this._popupCache = { browserWindow, rect, direction };
this.browser = browserWindow.gBrowser.selectedBrowser;
this.popup = this.browser.autoCompletePopup;
@@ -185,20 +186,27 @@ this.AutoCompleteE10S = {
Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
0, "", message.data.datalistResult.values,
message.data.datalistResult.labels,
[], null);
} else {
message.data.datalistResult = null;
}
+ let previousResult = null;
+ let previousSearchString = message.data.previousSearchString;
+ let searchString = message.data.untrimmedSearchString.toLowerCase();
+ if (previousSearchString && previousSearchString.length > 1 &&
+ searchString.includes(previousSearchString)) {
+ previousResult = this._resultCache;
+ }
formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
message.data.untrimmedSearchString,
message.data.mockField,
- null,
+ previousResult,
message.data.datalistResult,
{ onSearchCompletion:
this.onSearchComplete.bind(this) });
},
// The second half of search, this fills in the popup and returns the
// results to the child.
onSearchComplete: function(results) {
@@ -234,16 +242,24 @@ this.AutoCompleteE10S = {
this._popupCache.rect,
this._resultCache);
}
break;
case "FormAutoComplete:ClosePopup":
this.popup.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.
+ AutoCompleteE10SView.clearResults();
+ break;
}
},
handleEnter: function(aIsPopupSelection) {
this.browser.messageManager.sendAsyncMessage(
"FormAutoComplete:HandleEnter",
{ selectedIndex: this.popup.selectedIndex,
isPopupSelection: aIsPopupSelection }
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -497,16 +497,17 @@ FormAutoCompleteChild.prototype = {
let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
inputName: aInputName,
untrimmedSearchString: aUntrimmedSearchString,
mockField: mockField,
datalistResult: datalistResult,
+ previousSearchString: aPreviousResult && aPreviousResult.searchString.trim().toLowerCase(),
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
direction: direction,
});
let search = this._pendingSearch = {};
@@ -519,32 +520,43 @@ FormAutoCompleteChild.prototype = {
return;
}
this._pendingSearch = null;
let result = new FormAutoCompleteResult(
null,
Array.from(message.data.results, res => ({ text: res })),
null,
- null,
+ aUntrimmedSearchString,
mm
);
if (aListener) {
aListener.onSearchCompletion(result);
}
}
mm.addMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", searchFinished);
this.log("autoCompleteSearchAsync message was sent");
},
stopAutoCompleteSearch : function () {
this.log("stopAutoCompleteSearch");
this._pendingSearch = null;
},
+
+ stopControllingInput(aField) {
+ let window = aField.ownerDocument.defaultView;
+ let topLevelDocshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .sameTypeRootTreeItem
+ .QueryInterface(Ci.nsIDocShell);
+ let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+ mm.sendAsyncMessage("FormAutoComplete:Disconnect");
+ }
}; // end of FormAutoCompleteChild implementation
// nsIAutoCompleteResult implementation
function FormAutoCompleteResult(formHistory,
entries,
fieldName,
searchString,
messageManager) {
@@ -571,17 +583,17 @@ FormAutoCompleteResult.prototype = {
// 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 : null,
+ searchString : "",
errorDescription : "",
get defaultIndex() {
if (this.entries.length == 0)
return -1;
else
return 0;
},
get searchResult() {
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -1186,16 +1186,24 @@ nsFormFillController::StopControllingInp
nsCOMPtr<nsIAutoCompleteInput> input;
mController->GetInput(getter_AddRefs(input));
if (input == this)
mController->SetInput(nullptr);
}
if (mFocusedInputNode) {
MaybeRemoveMutationObserver(mFocusedInputNode);
+
+ nsresult rv;
+ nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
+ do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
+ if (formAutoComplete) {
+ formAutoComplete->StopControllingInput(mFocusedInput);
+ }
+
mFocusedInputNode = nullptr;
mFocusedInput = nullptr;
}
mFocusedPopup = nullptr;
}
nsIDocShell *
nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
--- a/toolkit/components/satchel/nsIFormAutoComplete.idl
+++ b/toolkit/components/satchel/nsIFormAutoComplete.idl
@@ -21,16 +21,21 @@ interface nsIFormAutoComplete: nsISuppor
in nsIAutoCompleteResult aDatalistResult,
in nsIFormAutoCompleteObserver aListener);
/**
* If a search is in progress, stop it. Otherwise, do nothing. This is used
* to cancel an existing search, for example, in preparation for a new search.
*/
void stopAutoCompleteSearch();
+
+ /**
+ * Since the controller is disconnecting, any related data must be cleared.
+ */
+ void stopControllingInput(in nsIDOMHTMLInputElement aField);
};
[scriptable, function, uuid(604419ab-55a0-4831-9eca-1b9e67cc4751)]
interface nsIFormAutoCompleteObserver : nsISupports
{
/*
* Called when a search is complete and the results are ready even if the
* result set is empty. If the search is cancelled or a new search is
--- a/toolkit/components/satchel/test/mochitest.ini
+++ b/toolkit/components/satchel/test/mochitest.ini
@@ -7,15 +7,13 @@ support-files =
parent_utils.js
[test_bug_511615.html]
skip-if = e10s # bug 1162338 (needs refactoring to talk to the autocomplete popup)
[test_bug_787624.html]
skip-if = e10s # bug 1162338 (needs refactoring to talk to the autocomplete popup)
[test_datalist_with_caching.html]
[test_form_autocomplete.html]
-skip-if = e10s # bug 1162329 or bug 1162338
[test_form_autocomplete_with_list.html]
-skip-if = e10s # bug 1162329 (autocomplete impl. in e10s isn't complete)
[test_form_submission.html]
[test_form_submission_cap.html]
[test_form_submission_cap2.html]
[test_popup_enter_event.html]
--- a/toolkit/components/satchel/test/parent_utils.js
+++ b/toolkit/components/satchel/test/parent_utils.js
@@ -67,17 +67,17 @@ var ParentUtils = {
FormHistory.count(obj, listener);
},
checkRowCount(expectedCount, expectedFirstValue = null) {
ContentTaskUtils.waitForCondition(() => {
return gAutocompletePopup.tree.view.rowCount === expectedCount &&
(!expectedFirstValue ||
expectedCount <= 1 ||
- gAutocompletePopup.tree.view.getValueAt(0, gAutocompletePopup.tree.columns[0]) ===
+ gAutocompletePopup.tree.view.getCellText(0, gAutocompletePopup.tree.columns[0]) ===
expectedFirstValue);
}).then(() => {
let results = this.getMenuEntries();
sendAsyncMessage("gotMenuChange", { results });
});
},
observe(subject, topic, data) {
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -646,25 +646,27 @@ function runTest() {
waitForMenuChange(2);
break;
case 205:
checkMenuEntries(["az", "aaz"], testNum);
input.focus();
doKey("left");
expectPopup();
- sendChar("a");
+ // Check case-insensitivity.
+ sendChar("A");
break;
case 206:
checkMenuEntries(["aaz"], testNum);
addEntry("field3", "aazq");
break;
case 207:
+ // check that results were cached
input.focus();
doKey("right");
sendChar("q");
waitForMenuChange(0);
break;
case 208:
// check that results were cached
@@ -966,17 +968,17 @@ function runTest() {
checkForm("");
restoreForm();
doKey("down");
waitForMenuChange(0);
break;
case 601:
checkMenuEntries([], testNum);
- SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
+ input.blur();
SimpleTest.finish();
return;
default:
ok(false, "Unexpected invocation of test #" + testNum);
SimpleTest.finish();
return;
}
--- a/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
@@ -345,78 +345,75 @@ function runTest() {
case 201:
// Adding an attribute after the first one while on the first then going
// down and push enter. Value should be the on from the new suggestion.
doKey("down");
datalist = document.getElementById('suggest');
var added = new Option("Foo");
datalist.insertBefore(added, datalist.children[1]);
-
- SimpleTest.executeSoon(function() {
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("Foo");
-
- // Remove the element.
- datalist.removeChild(added);
- expectPopup();
- restoreForm();
- doKey("down");
- });
+ waitForMenuChange(4);
break;
case 202:
+ doKey("down");
+ doKey("down");
+ doKey("return");
+ checkForm("Foo");
+
+ // Remove the element.
+ datalist = document.getElementById('suggest');
+ datalist.removeChild(datalist.children[1]);
+ waitForMenuChange(0);
+ break;
+
+ case 203:
// Change the first element value attribute.
- doKey("down");
+ restoreForm();
datalist = document.getElementById('suggest');
prevValue = datalist.children[0].value;
datalist.children[0].value = "foo";
expectPopup();
break;
- case 203:
+ case 204:
doKey("down");
doKey("return");
checkForm("foo");
datalist = document.getElementById('suggest');
datalist.children[0].value = prevValue;
- expectPopup();
- restoreForm();
- doKey("down");
+ waitForMenuChange(0);
break;
- case 204:
+ case 205:
// Change the textContent to update the value attribute.
- doKey("down");
+ restoreForm();
datalist = document.getElementById('suggest');
prevValue = datalist.children[0].getAttribute('value');
datalist.children[0].removeAttribute('value');
datalist.children[0].textContent = "foobar";
expectPopup();
break;
- case 205:
+ case 206:
doKey("down");
doKey("return");
checkForm("foobar");
datalist = document.getElementById('suggest');
datalist.children[0].setAttribute('value', prevValue);
testNum = 299;
- expectPopup();
- restoreForm();
- doKey("down");
+ waitForMenuChange(0);
break;
// Tests for filtering (or not).
case 300:
// Filters with first letter of the word.
+ restoreForm();
synthesizeKey("f", {});
expectPopup();
break;
case 301:
doKey("down");
doKey("return");
checkForm("final");
@@ -464,16 +461,17 @@ function runTest() {
case 400:
// Check that the input event is fired.
input.addEventListener("input", function(event) {
input.removeEventListener("input", arguments.callee, false);
ok(true, "oninput should have been received");
ok(event.bubbles, "input event should bubble");
ok(event.cancelable, "input event should be cancelable");
checkForm("Google");
+ input.blur();
SimpleTest.finish();
}, false);
doKey("down");
checkForm("");
doKey("return");
break;