Bug 1178722 - Further fix for 1178722 that supports HIDDEN attributes and includes testing and tidying. r=Margaret

--HG--
extra : rebase_source : 076811b9b468f48cee3ffcf7fad45135134435b0
This commit is contained in:
Tristan Martin 2016-04-14 14:57:00 +02:00
Родитель cb755d93ed
Коммит d753291b70
3 изменённых файлов: 198 добавлений и 92 удалений

Просмотреть файл

@ -6,29 +6,60 @@
var SelectHelper = {
_uiBusy: false,
handleEvent: function(aEvent) {
this.handleClick(aEvent.target);
handleEvent: function(event) {
this.handleClick(event.target);
},
handleClick: function(aTarget) {
handleClick: function(target) {
// if we're busy looking at a select we want to eat any clicks that
// come to us, but not to process them
if (this._uiBusy || !this._isMenu(aTarget) || this._isDisabledElement(aTarget))
return;
if (this._uiBusy || !this._isMenu(target) || this._isDisabledElement(target)) {
return;
}
this._uiBusy = true;
this.show(aTarget);
this.show(target);
this._uiBusy = false;
},
show: function(aElement) {
let list = this.getListForElement(aElement);
// This is a callback function to be provided to prompt.show(callBack).
// It will update which Option elements in a Select have been selected
// or unselected and fire the onChange event.
_promptCallBack: function(data, element) {
let selected = data.list;
if (element instanceof Ci.nsIDOMXULMenuListElement) {
if (element.selectedIndex != selected[0]) {
element.selectedIndex = selected[0];
this.fireOnCommand(element);
}
} else if (element instanceof HTMLSelectElement) {
let changed = false;
let i = 0; // The index for the element from `data.list` that we are currently examining.
this.forVisibleOptions(element, function(node) {
if (node.selected && selected.indexOf(i) == -1) {
changed = true;
node.selected = false;
} else if (!node.selected && selected.indexOf(i) != -1) {
changed = true;
node.selected = true;
}
i++;
});
if (changed) {
this.fireOnChange(element);
}
}
},
show: function(element) {
let list = this.getListForElement(element);
let p = new Prompt({
window: aElement.contentDocument
window: element.contentDocument
});
if (aElement.multiple) {
if (element.multiple) {
p.addButton({
label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog")
}).setMultiChoiceItems(list);
@ -36,121 +67,92 @@ var SelectHelper = {
p.setSingleChoiceItems(list);
}
p.show((function(data) {
let selected = data.list;
if (aElement instanceof Ci.nsIDOMXULMenuListElement) {
if (aElement.selectedIndex != selected[0]) {
aElement.selectedIndex = selected[0];
this.fireOnCommand(aElement);
}
} else if (aElement instanceof HTMLSelectElement) {
let changed = false;
let i = 0;
this.forOptions(aElement, function(aNode) {
if (aNode.selected && selected.indexOf(i) == -1) {
changed = true;
aNode.selected = false;
} else if (!aNode.selected && selected.indexOf(i) != -1) {
changed = true;
aNode.selected = true;
}
if (SelectHelper._isListItemVisible(aNode)) {
i++;
}
});
if (changed)
this.fireOnChange(aElement);
}
}).bind(this));
p.show((data) => {
this._promptCallBack(data,element)
});
},
_isListItemVisible: function(aNode){
return (aNode.style.display !== "none");
_isMenu: function(element) {
return (element instanceof HTMLSelectElement || element instanceof Ci.nsIDOMXULMenuListElement);
},
_isMenu: function(aElement) {
return (aElement instanceof HTMLSelectElement ||
aElement instanceof Ci.nsIDOMXULMenuListElement);
},
getListForElement: function(aElement) {
// Return a list of Option elements within a Select excluding
// any that were not visible.
getListForElement: function(element) {
let index = 0;
let items = [];
this.forOptions(aElement, function(aNode, aOptions, aParent) {
if (SelectHelper._isListItemVisible(aNode)) {
let item = {
label: aNode.text || aNode.label,
header: aOptions.isGroup,
disabled: aNode.disabled,
id: index,
selected: aNode.selected,
visible: (aNode.style.display!=="none")
};
this.forVisibleOptions(element, function(node, options,parent) {
let item = {
label: node.text || node.label,
header: options.isGroup,
disabled: node.disabled,
id: index,
selected: node.selected,
};
if (aParent) {
item.child = true;
item.disabled = item.disabled || aParent.disabled;
}
items.push(item);
if (parent) {
item.child = true;
item.disabled = item.disabled || parent.disabled;
}
items.push(item);
index++;
});
return items;
},
forOptions: function(aElement, aFunction, aParent = null) {
let element = aElement;
if (aElement instanceof Ci.nsIDOMXULMenuListElement)
element = aElement.menupopup;
// Apply a function to all visible Option elements in a Select
forVisibleOptions: function(element, aFunction, parent = null) {
if (element instanceof Ci.nsIDOMXULMenuListElement) {
element = element.menupopup;
}
let children = element.children;
let numChildren = children.length;
// if there are no children in this select, we add a dummy row so that at least something appears
if (numChildren == 0)
aFunction.call(this, { label: "" }, { isGroup: false }, aParent);
if (numChildren == 0) {
aFunction.call(this, {label: ""}, {isGroup: false}, parent);
}
for (let i = 0; i < numChildren; i++) {
let child = children[i];
if (child instanceof HTMLOptionElement ||
child instanceof Ci.nsIDOMXULSelectControlItemElement) {
aFunction.call(this, child, { isGroup: false }, aParent);
} else if (child instanceof HTMLOptGroupElement) {
aFunction.call(this, child, { isGroup: true });
this.forOptions(child, aFunction, child);
let style = window.getComputedStyle(child, null);
if (style.display !== "none") {
if (child instanceof HTMLOptionElement ||
child instanceof Ci.nsIDOMXULSelectControlItemElement) {
aFunction.call(this, child, {isGroup: false}, parent);
} else if (child instanceof HTMLOptGroupElement) {
aFunction.call(this, child, {isGroup: true});
this.forVisibleOptions(child, aFunction, child);
}
}
}
},
fireOnChange: function(aElement) {
let evt = aElement.ownerDocument.createEvent("Events");
evt.initEvent("change", true, true, aElement.defaultView, 0,
false, false,
false, false, null);
fireOnChange: function(element) {
let event = element.ownerDocument.createEvent("Events");
event.initEvent("change", true, true, element.defaultView, 0,
false, false, false, false, null);
setTimeout(function() {
aElement.dispatchEvent(evt);
element.dispatchEvent(event);
}, 0);
},
fireOnCommand: function(aElement) {
let evt = aElement.ownerDocument.createEvent("XULCommandEvent");
evt.initCommandEvent("command", true, true, aElement.defaultView, 0,
false, false,
false, false, null);
fireOnCommand: function(element) {
let event = element.ownerDocument.createEvent("XULCommandEvent");
event.initCommandEvent("command", true, true, element.defaultView, 0,
false, false, false, false, null);
setTimeout(function() {
aElement.dispatchEvent(evt);
element.dispatchEvent(event);
}, 0);
},
_isDisabledElement : function(aElement) {
let currentElement = aElement;
_isDisabledElement : function(element) {
let currentElement = element;
while (currentElement) {
if (currentElement.disabled)
return true;
if (currentElement.disabled) {
return true;
}
currentElement = currentElement.parentElement;
}
return false;

Просмотреть файл

@ -26,6 +26,7 @@ skip-if = debug
[test_device_search_engine.html]
[test_get_last_visited.html]
[test_home_provider.html]
[test_hidden_select_option.html]
[test_identity_mode.html]
[test_java_addons.html]
[test_jni.html]

Просмотреть файл

@ -0,0 +1,103 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1178722
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1178722</title>
<style>
.hideme {display:none}
</style>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript;version=1.7">
"use strict";
const VISIBLE_OPTION_COUNT = 5;
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
let win = Services.wm.getMostRecentWindow("navigator:browser");
let SelectHelper = win.SelectHelper;
// Returns whether an element should be visible according to its text content.
function shouldBeVisible(e){
return e.label.indexOf("visible") > 0;
}
// Returns an object for the callback method that would normally be created by Prompt.java's
// addListResult(..) method.
function createCallBackDummyData(select){
var dummyList = new Array();
let listElements = SelectHelper.getListForElement(select);
for (var i = 0; i < listElements.length; i++) {
dummyList.push(i);
}
return {list:dummyList};
}
// Wait until the page has loaded so that we can access the DOM.
SimpleTest.waitForExplicitFinish();
window.onload = function () {
let select = document.getElementById("sample-select");
// ##############################################
// ### Testing SelectHelper.getListForElement ###
// ##############################################
// Check that SelectHelper.getListForElement only includes visible options...
let listElements = SelectHelper.getListForElement(select);
for (var i = 0; i < listElements.length; i++) {
ok(shouldBeVisible(listElements[i]), "Element should be visible: " + listElements[i]);
}
// Check SelectHelper.getListForElement does not include additional options...
is(listElements.length, VISIBLE_OPTION_COUNT, "Correct number of elements were returned.");
// ############################################
// ### Testing SelectHelper._promptCallBack ###
// ############################################
// We will simulate "selecting" (ie choosing via the prompt) all the visible options...
is(select.selectedOptions.length, 0, "No options selected yet.");
let dummyData = createCallBackDummyData(select);
SelectHelper._promptCallBack(dummyData,select);
// Check that only the visible options had the "selected" attribute set...
let selectedOptions = select.selectedOptions;
for (var i = 0; i < selectedOptions.length; i++) {
ok(shouldBeVisible(selectedOptions[i]), "Element should be visible.");
}
// Check that no additional options had the "selected" attribute set...
is(selectedOptions.length, VISIBLE_OPTION_COUNT, "Correct number of options were selected.");
SimpleTest.finish();
}
</script>
</head>
<body>
<p id="display">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1178722">Mozilla Bug 1178722</a>
<select multiple id="sample-select">
<option value="1">1 - visible</option> 0
<option value="2" style="display: none">2 - hidden</option> 1
<option value="3">3 - visible</option> 2
<option value="4" style="display: nOnE">4 - hidden </option> 3
<option value="5">5 - visible</option> 4
<option value="6" class="hideme">6 - hidden</option> 5
<option value="7">7 - visible</option> 6
<option value="8" hiddEn>8 - hidden</option> 7
<option value="9">9 - visible</option> 8
</select>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>