mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 06:05:44 +00:00
Bug 628616 - Make sure suggestions from <datalist> are shown in Firefox Mobile UI [r=mfinkle]
This commit is contained in:
parent
b7b02ca66a
commit
c49a92cb4b
@ -661,8 +661,9 @@ var FormHelperUI = {
|
||||
value: aElement.value,
|
||||
maxLength: aElement.maxLength,
|
||||
type: aElement.type,
|
||||
choices: aElement.choices,
|
||||
isAutocomplete: aElement.isAutocomplete,
|
||||
list: aElement.choices
|
||||
list: aElement.list,
|
||||
}
|
||||
|
||||
this._updateContainerForSelect(lastElement, this._currentElement);
|
||||
@ -835,7 +836,7 @@ var FormHelperUI = {
|
||||
doAutoComplete: function formHelperDoAutoComplete(aElement) {
|
||||
// Suggestions are only in <label>s. Ignore the rest.
|
||||
if (aElement instanceof Ci.nsIDOMXULLabelElement)
|
||||
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.value });
|
||||
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.getAttribute("data") });
|
||||
},
|
||||
|
||||
get _open() {
|
||||
@ -902,9 +903,10 @@ var FormHelperUI = {
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
let value = suggestions[i];
|
||||
let suggestion = suggestions[i];
|
||||
let button = document.createElement("label");
|
||||
button.setAttribute("value", value);
|
||||
button.setAttribute("value", suggestion.label);
|
||||
button.setAttribute("data", suggestion.value);
|
||||
button.className = "form-helper-suggestions-label";
|
||||
fragment.appendChild(button);
|
||||
}
|
||||
@ -931,10 +933,16 @@ var FormHelperUI = {
|
||||
if (value == aElement.value)
|
||||
continue;
|
||||
|
||||
suggestions.push(value);
|
||||
suggestions.push({ "label": value, "value": value});
|
||||
}
|
||||
}
|
||||
|
||||
// Add the datalist elements provided by the website, note that the
|
||||
// displayed value can differ from the real value of the element.
|
||||
let options = aElement.list;
|
||||
for (let i = 0; i < options.length; i++)
|
||||
suggestions.push(options[i]);
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
@ -1042,11 +1050,11 @@ var FormHelperUI = {
|
||||
|
||||
/** Helper for _updateContainer that handles the case where the new element is a select. */
|
||||
_updateContainerForSelect: function _formHelperUpdateContainerForSelect(aLastElement, aCurrentElement) {
|
||||
let lastHasChoices = aLastElement && (aLastElement.list != null);
|
||||
let currentHasChoices = aCurrentElement && (aCurrentElement.list != null);
|
||||
let lastHasChoices = aLastElement && (aLastElement.choices != null);
|
||||
let currentHasChoices = aCurrentElement && (aCurrentElement.choices != null);
|
||||
|
||||
if (currentHasChoices)
|
||||
SelectHelperUI.show(aCurrentElement.list, aCurrentElement.title);
|
||||
SelectHelperUI.show(aCurrentElement.choices, aCurrentElement.title);
|
||||
else if (lastHasChoices)
|
||||
SelectHelperUI.hide();
|
||||
},
|
||||
|
@ -339,14 +339,6 @@ textarea:active {
|
||||
background-color: rgba(141, 184, 216, 0.5) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Until datalist are fully supported on mobile (bug 628616) set the display
|
||||
* to inline in order to allow fallback to works
|
||||
*/
|
||||
datalist {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an additional space after the anonymous div to make it easier to
|
||||
* to position the caret at the end of the text
|
||||
|
@ -508,6 +508,38 @@ FormAssistant.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
/*
|
||||
* This function is similar to getListSuggestions from
|
||||
* components/satchel/src/nsInputListAutoComplete.js but sadly this one is
|
||||
* used by the autocomplete.xml binding which is not in used in fennec
|
||||
*/
|
||||
_getListSuggestions: function formHelperGetListSuggestions(aElement) {
|
||||
if (!(aElement instanceof HTMLInputElement) || !aElement.list)
|
||||
return [];
|
||||
|
||||
let suggestions = [];
|
||||
let filter = !aElement.hasAttribute("mozNoFilter");
|
||||
let lowerFieldValue = aElement.value.toLowerCase();
|
||||
|
||||
let options = aElement.list.options;
|
||||
let length = options.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let item = options.item(i);
|
||||
|
||||
let label = item.value;
|
||||
if (item.label)
|
||||
label = item.label;
|
||||
else if (item.text)
|
||||
label = item.text;
|
||||
|
||||
if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1)
|
||||
continue;
|
||||
suggestions.push({ label: label, value: item.value });
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
_isValidElement: function formHelperIsValidElement(aElement) {
|
||||
if (!aElement.getAttribute)
|
||||
return false;
|
||||
@ -664,7 +696,8 @@ FormAssistant.prototype = {
|
||||
|
||||
_getJSON: function() {
|
||||
let element = this.currentElement;
|
||||
let list = getListForElement(element);
|
||||
let choices = getListForElement(element);
|
||||
|
||||
let labels = this._getLabels();
|
||||
return {
|
||||
current: {
|
||||
@ -674,8 +707,9 @@ FormAssistant.prototype = {
|
||||
value: element.value,
|
||||
maxLength: element.maxLength,
|
||||
type: (element.getAttribute("type") || "").toLowerCase(),
|
||||
choices: list,
|
||||
choices: choices,
|
||||
isAutocomplete: this._isAutocomplete(this.currentElement),
|
||||
list: this._getListSuggestions(this.currentElement),
|
||||
rect: this._getRect(),
|
||||
caretRect: this._getCaretRect()
|
||||
},
|
||||
|
@ -48,11 +48,15 @@ include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
head.js \
|
||||
remote_autocomplete.js \
|
||||
remote_head.js \
|
||||
remote_forms.js \
|
||||
remote_formsZoom.js \
|
||||
remote_vkb.js \
|
||||
browser_addons.js \
|
||||
browser_autocomplete.html \
|
||||
browser_autocomplete.js \
|
||||
browser_autocompletesearch.js\
|
||||
browser_awesomescreen.js \
|
||||
browser_blank_01.html \
|
||||
browser_blank_02.html \
|
||||
@ -89,7 +93,6 @@ _BROWSER_FILES = \
|
||||
browser_thumbnails.js \
|
||||
browser_install.xml \
|
||||
browser_upgrade.rdf\
|
||||
browser_autocompletesearch.js\
|
||||
mock_autocomplete.json\
|
||||
$(NULL)
|
||||
|
||||
|
26
mobile/chrome/tests/browser_autocomplete.html
Normal file
26
mobile/chrome/tests/browser_autocomplete.html
Normal file
@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<body>
|
||||
<datalist id="datalist-1">
|
||||
<option>foo</option>
|
||||
<option disabled="true">bar</option>
|
||||
<option value="bar">Somewhat bar</option>
|
||||
<option label="foobar" value="_"></option>
|
||||
</datalist>
|
||||
<input id="input-datalist-1" list="datalist-1"></input>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<datalist id="datalist-2">
|
||||
<option>foo</option>
|
||||
<option>bar</option>
|
||||
<option>foobar</option>
|
||||
<option>foobar foo</option>
|
||||
<option>foobar foo titi</option>
|
||||
<option>foobar foo titi toto</option>
|
||||
<option>foobar foo titi toto tutu</option>
|
||||
</datalist>
|
||||
<input id="input-datalist-2" list="datalist-2"></input>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
140
mobile/chrome/tests/browser_autocomplete.js
Normal file
140
mobile/chrome/tests/browser_autocomplete.js
Normal file
@ -0,0 +1,140 @@
|
||||
let testURL = chromeRoot + "browser_autocomplete.html";
|
||||
messageManager.loadFrameScript(chromeRoot + "remote_autocomplete.js", true);
|
||||
|
||||
let newTab = null;
|
||||
|
||||
// A queue to order the tests and a handle for each test
|
||||
var gTests = [];
|
||||
var gCurrentTest = null;
|
||||
|
||||
function test() {
|
||||
// This test is async
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Need to wait until the page is loaded
|
||||
messageManager.addMessageListener("pageshow", function(aMessage) {
|
||||
if (newTab && newTab.browser.currentURI.spec != "about:blank") {
|
||||
messageManager.removeMessageListener(aMessage.name, arguments.callee);
|
||||
BrowserUI.closeAutoComplete(true);
|
||||
setTimeout(runNextTest, 0);
|
||||
}
|
||||
});
|
||||
|
||||
newTab = Browser.addTab(testURL, true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Iterating tests by shifting test out one by one as runNextTest is called.
|
||||
function runNextTest() {
|
||||
// Run the next test until all tests completed
|
||||
if (gTests.length > 0) {
|
||||
gCurrentTest = gTests.shift();
|
||||
info(gCurrentTest.desc);
|
||||
gCurrentTest.run();
|
||||
}
|
||||
else {
|
||||
// Cleanup. All tests are completed at this point
|
||||
try {
|
||||
// Add any cleanup code here
|
||||
}
|
||||
finally {
|
||||
// We must finialize the tests
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function waitForAutocomplete(aCallback) {
|
||||
messageManager.addMessageListener("FormAssist:AutoComplete", function(aMessage) {
|
||||
messageManager.removeMessageListener(aMessage.name, arguments.callee);
|
||||
setTimeout(function() {
|
||||
aCallback(aMessage.json.current.list);
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
|
||||
let data = [
|
||||
{ label: "foo", value: "foo" },
|
||||
{ label: "Somewhat bar", value: "bar" },
|
||||
{ label: "foobar", value: "_" }
|
||||
];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Case: Click on a datalist element and show suggestions
|
||||
gTests.push({
|
||||
desc: "Click on a datalist element and show suggestions",
|
||||
|
||||
run: function() {
|
||||
waitForAutocomplete(gCurrentTest.checkData);
|
||||
AsyncTests.waitFor("TestRemoteAutocomplete:Click",
|
||||
{ id: "input-datalist-1" }, function(json) {});
|
||||
},
|
||||
|
||||
// Check that the data returned by the autocomplete handler on the content
|
||||
// side is correct
|
||||
checkData: function(aOptions) {
|
||||
for (let i = 0; i < aOptions.length; i++) {
|
||||
let option = aOptions[i];
|
||||
let valid = data[i];
|
||||
|
||||
is(option.label, valid.label, "Label should be equal (" + option.label + ", " + valid.label +")");
|
||||
is(option.value, valid.value, "Value should be equal (" + option.value + ", " + valid.value +")");
|
||||
}
|
||||
|
||||
// Wait until suggestions box has been popupated
|
||||
waitFor(gCurrentTest.checkUI, function() {
|
||||
let suggestionsBox = document.getElementById("form-helper-suggestions");
|
||||
return suggestionsBox.childNodes.length;
|
||||
});
|
||||
},
|
||||
|
||||
// Check that the UI reflect the specificity of the data
|
||||
checkUI: function() {
|
||||
let suggestionsBox = document.getElementById("form-helper-suggestions");
|
||||
let suggestions = suggestionsBox.childNodes;
|
||||
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
let suggestion = suggestions[i];
|
||||
let valid = data[i];
|
||||
let label = suggestion.getAttribute("value");
|
||||
let value = suggestion.getAttribute("data");
|
||||
|
||||
is(label, valid.label, "Label should be equal (" + label + ", " + valid.label +")");
|
||||
is(value, valid.value, "Value should be equal (" + value + ", " + valid.value +")");
|
||||
}
|
||||
|
||||
gCurrentTest.checkUIClick(0);
|
||||
},
|
||||
|
||||
// Ensure that clicking on a given datalist element set the right value in
|
||||
// the input box
|
||||
checkUIClick: function(aIndex) {
|
||||
let suggestionsBox = document.getElementById("form-helper-suggestions");
|
||||
|
||||
let suggestion = suggestionsBox.childNodes[aIndex];
|
||||
if (!suggestion) {
|
||||
gCurrentTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the form helper autocompletion helper
|
||||
FormHelperUI.doAutoComplete(suggestion);
|
||||
|
||||
AsyncTests.waitFor("TestRemoteAutocomplete:Check", { id: "input-datalist-1" }, function(json) {
|
||||
is(json.result, suggestion.getAttribute("data"), "The target input value should be set to " + data);
|
||||
gCurrentTest.checkUIClick(aIndex + 1);
|
||||
});
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
// Close the form assistant
|
||||
FormHelperUI.hide();
|
||||
|
||||
// Close our tab when finished
|
||||
Browser.closeTab(newTab);
|
||||
|
||||
// We must finalize the tests
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
18
mobile/chrome/tests/remote_autocomplete.js
Normal file
18
mobile/chrome/tests/remote_autocomplete.js
Normal file
@ -0,0 +1,18 @@
|
||||
dump("====================== Content Script Loaded =======================\n");
|
||||
|
||||
let assistant = Content._formAssistant;
|
||||
|
||||
AsyncTests.add("TestRemoteAutocomplete:Click", function(aMessage, aJson) {
|
||||
let element = content.document.getElementById(aJson.id);
|
||||
assistant.open(element);
|
||||
assistant._executeDelayed(function(assistant) {
|
||||
sendAsyncMessage("FormAssist:AutoComplete", assistant._getJSON());
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
AsyncTests.add("TestRemoteAutocomplete:Check", function(aMessage, aJson) {
|
||||
let element = content.document.getElementById(aJson.id);
|
||||
return element.value;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user