зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
cb755d93ed
Коммит
d753291b70
|
@ -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>
|
Загрузка…
Ссылка в новой задаче