Bug 317433: Richlistbox attempts to give focus to hidden items. p=Simon Bunzli <zeniko@gmail.com> r=enndeakin

This commit is contained in:
dtownsend@oxymoronical.com 2007-08-23 01:59:59 -07:00
parent a88d6ce071
commit 1b531c7a1b
5 changed files with 293 additions and 25 deletions

View File

@ -68,6 +68,8 @@ _TEST_FILES = test_bug360220.xul \
test_datepicker.xul \
test_timepicker.xul \
xul_selectcontrol.js \
test_hiddenitems.xul \
test_hiddenpaging.xul \
$(NULL)
ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))

View File

@ -0,0 +1,89 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=317422
-->
<window title="Mozilla Bug 317422"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="/MochiKit/packed.js" />
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/>
<!-- test resuls are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=317422"
target="_blank">Mozilla Bug 317422</a>
</body>
<richlistbox id="richlistbox" seltype="multiple">
<richlistitem id="richlistbox_item1"><label value="Item 1"/></richlistitem>
<richlistitem id="richlistbox_item2"><label value="Item 2"/></richlistitem>
<richlistitem id="richlistbox_item3" hidden="true"><label value="Item 3"/></richlistitem>
<richlistitem id="richlistbox_item4"><label value="Item 4"/></richlistitem>
<richlistitem id="richlistbox_item5" collapsed="true"><label value="Item 5"/></richlistitem>
<richlistitem id="richlistbox_item6"><label value="Item 6"/></richlistitem>
<richlistitem id="richlistbox_item7" hidden="true"><label value="Item 7"/></richlistitem>
</richlistbox>
<listbox id="listbox" seltype="multiple">
<listitem id="listbox_item1" label="Item 1"/>
<listitem id="listbox_item2" label="Item 2"/>
<listitem id="listbox_item3" label="Item 3" hidden="true"/>
<listitem id="listbox_item4" label="Item 4"/>
<listitem id="listbox_item5" label="Item 5" collapsed="true"/>
<listitem id="listbox_item6" label="Item 6"/>
<listitem id="listbox_item7" label="Item 7" hidden="true"/>
</listbox>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
/** Test for Bug 317422 **/
SimpleTest.waitForExplicitFinish();
function testListbox(id)
{
var listbox = document.getElementById(id);
is(listbox.getRowCount(), 7, id + ": Returned the wrong number of rows");
is(listbox.getItemAtIndex(2).id, id + "_item3", id + ": Should still return hidden items");
listbox.selectedIndex = 0;
is(listbox.selectedItem.id, id + "_item1", id + ": First item was not selected");
sendKey("DOWN", id);
is(listbox.selectedItem.id, id + "_item2", id + ": Down didn't move to second item");
sendKey("DOWN", id);
is(listbox.selectedItem.id, id + "_item4", id + ": Down didn't skip hidden item");
sendKey("DOWN", id);
is(listbox.selectedItem.id, id + "_item6", id + ": Down didn't skip collapsed item");
sendKey("UP", id);
is(listbox.selectedItem.id, id + "_item4", id + ": Up didn't skip collapsed item");
sendKey("UP", id);
is(listbox.selectedItem.id, id + "_item2", id + ": Up didn't skip hidden item");
listbox.selectAll();
is(listbox.selectedItems.length, 7, id + ": Should have still selected all items");
listbox.invertSelection();
is(listbox.selectedItems.length, 0, id + ": Should have unselected all items");
listbox.selectedIndex = 2;
ok(listbox.selectedItem == listbox.getItemAtIndex(2), id + ": Should have selected the hidden item");
listbox.selectedIndex = 0;
sendKey("END", id);
is(listbox.selectedItem.id, id + "_item6", id + ": Should have moved to the last unhidden item");
sendMouseEvent({type: 'click'}, id + "_item1");
ok(listbox.selectedItem == listbox.getItemAtIndex(0), id + ": Should have selected the first item");
is(listbox.selectedItems.length, 1, id + ": Should only be one selected item");
sendMouseEvent({type: 'click', shiftKey: true}, id + "_item6");
is(listbox.selectedItems.length, 4, id + ": Should have selected all visible items");
listbox.selectedIndex = 0;
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item6", id + ": Page down should go to the last visible item");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item1", id + ": Page up should go to the first visible item");
}
window.onload = function runTests() {
testListbox("richlistbox");
testListbox("listbox");
SimpleTest.finish();
};
]]></script>
</window>

View File

@ -0,0 +1,128 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=317422
-->
<window title="Mozilla Bug 317422"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="/MochiKit/packed.js" />
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/>
<style xmlns="http://www.w3.org/1999/xhtml">
/* This makes the richlistbox about 4.5 rows high */
richlistitem {
height: 30px;
}
richlistbox {
height: 135px;
}
</style>
<!-- test resuls are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=317422"
target="_blank">Mozilla Bug 317422</a>
</body>
<richlistbox id="richlistbox" seltype="multiple">
<richlistitem id="richlistbox_item1"><label value="Item 1"/></richlistitem>
<richlistitem id="richlistbox_item2"><label value="Item 2"/></richlistitem>
<richlistitem id="richlistbox_item3" hidden="true"><label value="Item 3"/></richlistitem>
<richlistitem id="richlistbox_item4"><label value="Item 4"/></richlistitem>
<richlistitem id="richlistbox_item5" collapsed="true"><label value="Item 5"/></richlistitem>
<richlistitem id="richlistbox_item6"><label value="Item 6"/></richlistitem>
<richlistitem id="richlistbox_item7"><label value="Item 7"/></richlistitem>
<richlistitem id="richlistbox_item8"><label value="Item 8"/></richlistitem>
<richlistitem id="richlistbox_item9"><label value="Item 9"/></richlistitem>
<richlistitem id="richlistbox_item10"><label value="Item 10"/></richlistitem>
<richlistitem id="richlistbox_item11"><label value="Item 11"/></richlistitem>
<richlistitem id="richlistbox_item12"><label value="Item 12"/></richlistitem>
<richlistitem id="richlistbox_item13"><label value="Item 13"/></richlistitem>
<richlistitem id="richlistbox_item14"><label value="Item 14"/></richlistitem>
<richlistitem id="richlistbox_item15" hidden="true"><label value="Item 15"/></richlistitem>
</richlistbox>
<listbox id="listbox" seltype="multiple" rows="5">
<listitem id="listbox_item1" label="Item 1"/>
<listitem id="listbox_item2" label="Item 2"/>
<listitem id="listbox_item3" label="Item 3" hidden="true"/>
<listitem id="listbox_item4" label="Item 4"/>
<listitem id="listbox_item5" label="Item 5" hidden="true"/>
<listitem id="listbox_item6" label="Item 6"/>
<listitem id="listbox_item7" label="Item 7"/>
<listitem id="listbox_item8" label="Item 8"/>
<listitem id="listbox_item9" label="Item 9"/>
<listitem id="listbox_item10" label="Item 10"/>
<listitem id="listbox_item11" label="Item 11"/>
<listitem id="listbox_item12" label="Item 12"/>
<listitem id="listbox_item13" label="Item 13"/>
<listitem id="listbox_item14" label="Item 14"/>
<listitem id="listbox_item15" label="Item 15" hidden="true"/>
</listbox>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
/** Test for Bug 317422 **/
SimpleTest.waitForExplicitFinish();
function testRichlistbox()
{
var id = "richlistbox";
var listbox = document.getElementById(id);
listbox.selectedIndex = 0;
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item7", id + ": Page down should go to the item one visible page away");
is(listbox.getIndexOfFirstVisibleRow(), 6, id + ": Page down should have scrolled down a visible page");
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item11", id + ": Second page down should go to the item two visible pages away");
is(listbox.getIndexOfFirstVisibleRow(), 9, id + ": Second page down should not scroll beyond the end");
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item14", id + ": Third page down should go to the last visible item");
is(listbox.getIndexOfFirstVisibleRow(), 9, id + ": Third page down should not have scrolled at all");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item10", id + ": Page up should go to the item one visible page away");
is(listbox.getIndexOfFirstVisibleRow(), 5, id + ": Page up should scroll up a visible page");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item6", id + ": Second page up should go to the item two visible pages away");
is(listbox.getIndexOfFirstVisibleRow(), 0, id + ": Second page up should not scroll beyond the start");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item1", id + ": Third page up should return to the first visible item");
is(listbox.getIndexOfFirstVisibleRow(), 0, id + ": Third page up should not have scrolled at all");
}
function testListbox()
{
var id = "listbox";
var listbox = document.getElementById(id);
listbox.selectedIndex = 0;
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item8", id + ": Page down should go to the item one visible page away");
is(listbox.getIndexOfFirstVisibleRow(), 7, id + ": Page down should have scrolled down a visible page");
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item13", id + ": Second page down should go to the item two visible pages away");
is(listbox.getIndexOfFirstVisibleRow(), 9, id + ": Second page down should not scroll beyond the end");
sendKey("PAGE_DOWN", id);
is(listbox.selectedItem.id, id + "_item14", id + ": Third page down should go to the last visible item");
is(listbox.getIndexOfFirstVisibleRow(), 9, id + ": Third page down should not have scrolled at all");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item9", id + ": Page up should go to the item one visible page away");
// the listScrollbox seems to go haywire when scrolling up with hidden listitems
todo_is(listbox.getIndexOfFirstVisibleRow(), 3, id + ": Page up should scroll up a visible page");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item2", id + ": Second page up should go to the item two visible pages away");
is(listbox.getIndexOfFirstVisibleRow(), 0, id + ": Second page up should not scroll beyond the start");
sendKey("PAGE_UP", id);
is(listbox.selectedItem.id, id + "_item1", id + ": Third page up should return to the first visible item");
is(listbox.getIndexOfFirstVisibleRow(), 0, id + ": Third page up should not have scrolled at all");
}
window.onload = function runTests() {
testRichlistbox();
testListbox();
SimpleTest.finish();
};
]]></script>
</window>

View File

@ -99,8 +99,9 @@
insertItemAt(aIndex, aLabel, aValue)
/** Scroll up/down one page
* @param aDirection - specifies scrolling direction, should be either -1
or 1 */
* @param aDirection - specifies scrolling direction, should be either -1 or 1
* @return the number of elements the selection scrolled
*/
scrollOnePage(aDirection)
/** Fire "select" event */
@ -334,12 +335,15 @@
// Don't use clearSelection() because it causes a lot of noise
// with respect to selection removed notifications used by the
// accessibility API support.
var userSelecting = this._userSelecting;
this._userSelecting = false; // that's US automatically unselecting
for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
this.removeItemFromSelection(currentItem);
for (currentItem = this.getItemAtIndex(0); currentItem != aStartItem;
currentItem = this.getNextItem(currentItem, 1))
this.removeItemFromSelection(currentItem);
this._userSelecting = userSelecting;
this._suppressOnSelect = suppressSelect;
@ -478,8 +482,13 @@
newIndex = numItems - 1;
var newItem = this.getItemAtIndex(newIndex);
// make sure that the item is actually visible/selectable
if (this._userSelecting && newItem && !this._canUserSelect(newItem))
newItem =
aOffset > 0 ? this.getNextItem(newItem, 1) || this.getPreviousItem(newItem, 1) :
this.getPreviousItem(newItem, 1) || this.getNextItem(newItem, 1);
if (newItem) {
this.ensureIndexIsVisible(newIndex);
this.ensureIndexIsVisible(this.getIndexOfItem(newItem));
if (aIsSelectingRange)
this.selectItemRange(null, newItem);
else if (aIsSelecting)
@ -500,7 +509,8 @@
while (aStartItem) {
aStartItem = aStartItem.nextSibling;
if (aStartItem && aStartItem instanceof
Components.interfaces.nsIDOMXULSelectControlItemElement) {
Components.interfaces.nsIDOMXULSelectControlItemElement &&
(!this._userSelecting || this._canUserSelect(aStartItem))) {
--aDelta;
if (aDelta == 0)
return aStartItem;
@ -518,7 +528,8 @@
while (aStartItem) {
aStartItem = aStartItem.previousSibling;
if (aStartItem && aStartItem instanceof
Components.interfaces.nsIDOMXULSelectControlItemElement) {
Components.interfaces.nsIDOMXULSelectControlItemElement &&
(!this._userSelecting || this._canUserSelect(aStartItem))) {
--aDelta;
if (aDelta == 0)
return aStartItem;
@ -529,6 +540,28 @@
</body>
</method>
<method name="_moveByOffsetFromUserEvent">
<parameter name="aOffset"/>
<parameter name="aEvent"/>
<body>
<![CDATA[
this._userSelecting = true;
this.moveByOffset(aOffset, !aEvent.ctrlKey, aEvent.shiftKey);
this._userSelecting = false;
]]>
</body>
</method>
<method name="_canUserSelect">
<parameter name="aItem"/>
<body>
<![CDATA[
var style = document.defaultView.getComputedStyle(aItem, "");
return style.display != "none" && style.visibility == "visible";
]]>
</body>
</method>
<method name="_selectTimeoutHandler">
<parameter name="aMe"/>
<body>
@ -538,6 +571,7 @@
</method>
<field name="_suppressOnSelect">false</field>
<field name="_userSelecting">false</field>
<field name="_selectTimeout">null</field>
<field name="_currentItem">null</field>
<field name="_selectionStart">null</field>
@ -545,22 +579,22 @@
<handlers>
<handler event="keypress" keycode="VK_UP" modifiers="control shift any"
action="moveByOffset(-1, !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(-1, event);"
phase="target" preventdefault="true"/>
<handler event="keypress" keycode="VK_DOWN" modifiers="control shift any"
action="moveByOffset(1, !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(1, event);"
phase="target" preventdefault="true"/>
<handler event="keypress" keycode="VK_HOME" modifiers="control shift any"
action="moveByOffset(-this.currentIndex, !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(-this.currentIndex, event);"
phase="target" preventdefault="true"/>
<handler event="keypress" keycode="VK_END" modifiers="control shift any"
action="moveByOffset(this.getRowCount() - this.currentIndex - 1, !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(this.getRowCount() - this.currentIndex - 1, event);"
phase="target" preventdefault="true"/>
<handler event="keypress" keycode="VK_PAGE_UP" modifiers="control shift any"
action="moveByOffset(this.scrollOnePage(-1), !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(this.scrollOnePage(-1), event);"
phase="target" preventdefault="true"/>
<handler event="keypress" keycode="VK_PAGE_DOWN" modifiers="control shift any"
action="moveByOffset(this.scrollOnePage(1), !event.ctrlKey, event.shiftKey);"
action="this._moveByOffsetFromUserEvent(this.scrollOnePage(1), event);"
phase="target" preventdefault="true"/>
<handler event="keypress" key=" " modifiers="control" phase="target">
<![CDATA[
@ -606,6 +640,8 @@
for (var i = 0; i < rowCount; i++) {
var k = (start + i) % rowCount;
var listitem = this.getItemAtIndex(k);
if (!this._canUserSelect(listitem))
continue;
// allow richlistitems to specify the string being searched for
var searchText = "searchLabel" in listitem ? listitem.searchLabel :
listitem.getAttribute("label"); // (see also bug 250123)
@ -770,14 +806,24 @@
<body>
<![CDATA[
var pageOffset = this.getNumberOfVisibleRows() * direction;
// skip over invisible elements - the user won't care about them
for (var i = 0; i != pageOffset; i += direction) {
var item = this.getItemAtIndex(this.currentIndex + i);
if (item && !this._canUserSelect(item))
pageOffset += direction;
}
var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
if (direction == 1) {
var maxTop = this.getRowCount() - pageOffset;
if (newTop >= maxTop && maxTop > this.currentIndex) {
newTop = maxTop;
var maxTop = this.getRowCount() - this.getNumberOfVisibleRows();
for (i = this.getRowCount(); i >= 0 && i > maxTop; i--) {
item = this.getItemAtIndex(i);
if (item && !this._canUserSelect(item))
maxTop--;
}
if (newTop >= maxTop)
newTop = maxTop;
}
else if (newTop < 0)
if (newTop < 0)
newTop = 0;
this.scrollToIndex(newTop);
return pageOffset;
@ -924,6 +970,7 @@
var control = this.control;
if (!control || control.disabled)
return;
control._userSelecting = true;
if (control.selType != "multiple") {
control.selectItem(this);
}
@ -945,6 +992,7 @@
// doesn't de- and reselect this item if it is selected
control.selectItemRange(this, this);
}
control._userSelecting = false;
]]>
</handler>
</handlers>

View File

@ -265,19 +265,20 @@
// (including the currently selected one), and determine
// the index of the first one lying (partially) outside
var height = this.scrollBoxObject.height;
var border = this.currentItem.boxObject.y;
var startBorder = this.currentItem.boxObject.y;
if (aDirection == -1)
border += this.currentItem.boxObject.height;
startBorder += this.currentItem.boxObject.height;
var index = this.currentIndex;
while (0 <= index && index < children.length) {
var border2 = children[index].boxObject.y;
if (aDirection == -1)
border2 += children[index].boxObject.height;
if ((border2 - border) * aDirection > height)
break;
index += aDirection;
for (var ix = index; 0 <= ix && ix < children.length; ix += aDirection) {
var boxObject = children[ix].boxObject;
if (boxObject.height == 0)
continue; // hidden children have a y of 0
var endBorder = boxObject.y + (aDirection == -1 ? boxObject.height : 0);
if ((endBorder - startBorder) * aDirection > height)
break; // we've reached the desired distance
index = ix;
}
index -= aDirection;
return index != this.currentIndex ? index - this.currentIndex : aDirection;
]]>