Bug 1498769 - Also require a frame for an <option> to be interactively selectable. r=enndeakin

This commit is contained in:
Mats Palmgren 2018-10-17 00:13:06 +02:00
parent e17d968203
commit 4ff1bdbeb2
4 changed files with 156 additions and 17 deletions

View File

@ -36,6 +36,7 @@
#include <algorithm>
using namespace mozilla;
using namespace mozilla::dom;
// Constants
const uint32_t kMaxDropDownRows = 20; // This matches the setting for 4.x browsers
@ -1147,7 +1148,7 @@ nsListControlFrame::GetNonDisabledOptionFrom(int32_t aFromIndex,
if (!node) {
break;
}
if (!selectElement->IsOptionDisabled(node)) {
if (IsOptionInteractivelySelectable(selectElement, node)) {
if (aFoundIndex) {
*aFoundIndex = i;
}
@ -1549,16 +1550,23 @@ nsListControlFrame::GetBSizeOfARow()
return BSizeOfARow();
}
nsresult
nsListControlFrame::IsOptionDisabled(int32_t anIndex, bool &aIsDisabled)
bool
nsListControlFrame::IsOptionInteractivelySelectable(int32_t aIndex) const
{
RefPtr<dom::HTMLSelectElement> sel =
dom::HTMLSelectElement::FromNode(mContent);
if (sel) {
sel->IsOptionDisabled(anIndex, &aIsDisabled);
return NS_OK;
if (HTMLSelectElement* sel = HTMLSelectElement::FromNode(mContent)) {
if (HTMLOptionElement* item = sel->Item(aIndex)) {
return IsOptionInteractivelySelectable(sel, item);
}
}
return NS_ERROR_FAILURE;
return false;
}
bool
nsListControlFrame::IsOptionInteractivelySelectable(HTMLSelectElement* aSelect,
HTMLOptionElement* aOption)
{
return !aSelect->IsOptionDisabled(aOption) &&
aOption->GetPrimaryFrame();
}
//----------------------------------------------------------------------
@ -1664,10 +1672,8 @@ nsListControlFrame::MouseUp(dom::Event* aMouseEvent)
int32_t selectedIndex;
if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
// If it's disabled, disallow the click and leave.
bool isDisabled = false;
IsOptionDisabled(selectedIndex, isDisabled);
if (isDisabled) {
// If it's not selectable, disallow the click and leave.
if (!IsOptionInteractivelySelectable(selectedIndex)) {
aMouseEvent->PreventDefault();
aMouseEvent->StopPropagation();
CaptureMouseEvents(false);
@ -2036,9 +2042,8 @@ nsListControlFrame::AdjustIndexForDisabledOpt(int32_t aStartIndex,
}
while (1) {
// if the newIndex isn't disabled, we are golden, bail out
bool isDisabled = true;
if (NS_SUCCEEDED(IsOptionDisabled(newIndex, isDisabled)) && !isDisabled) {
// if the newIndex is selectable, we are golden, bail out
if (IsOptionInteractivelySelectable(newIndex)) {
break;
}

View File

@ -36,6 +36,7 @@ namespace mozilla {
namespace dom {
class Event;
class HTMLOptionElement;
class HTMLSelectElement;
class HTMLOptionsCollection;
} // namespace dom
} // namespace mozilla
@ -272,7 +273,17 @@ protected:
*/
void DropDownToggleKey(mozilla::dom::Event* aKeyEvent);
nsresult IsOptionDisabled(int32_t anIndex, bool &aIsDisabled);
/**
* @return true if the <option> at aIndex is selectable by the user.
*/
bool IsOptionInteractivelySelectable(int32_t aIndex) const;
/**
* @return true if aOption in aSelect is selectable by the user.
*/
static bool
IsOptionInteractivelySelectable(mozilla::dom::HTMLSelectElement* aSelect,
mozilla::dom::HTMLOptionElement* aOption);
/**
* @note This method might destroy the frame, pres shell and other objects.
*/

View File

@ -66,3 +66,4 @@ skip-if = e10s || toolkit == 'android' # Bug 1170129 - vertical <select> popup n
skip-if = toolkit == 'android'
[test_bug1327129.html]
[test_readonly.html]
[test_select_key_navigation_bug1498769.html]

View File

@ -0,0 +1,122 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1498769
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1498769</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1498769 **/
SimpleTest.waitForExplicitFinish();
function test() {
const kIsMac = navigator.platform.indexOf("Mac") == 0;
SimpleTest.waitForFocus(function() {
[...document.querySelectorAll('select')].forEach(function(e) {
e.focus();
is(e.selectedIndex, 1, "the 'selected' attribute is respected");
if (kIsMac && e.size == "1") {
// On OSX, UP/DOWN opens the dropdown menu rather than changing
// the value so we skip the rest of this test there in this case.
return;
}
synthesizeKey("VK_DOWN", {});
is(e.selectedIndex, 2, "VK_DOWN selected the first option below");
synthesizeKey("VK_UP", {});
is(e.selectedIndex, 0, "VK_UP skips the display:none/contents option");
synthesizeKey("VK_DOWN", {});
is(e.selectedIndex, 2, "VK_DOWN skips the display:none/contents option");
});
SimpleTest.finish();
});
}
</script>
</head>
<body onload="test()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1498769">Mozilla Bug 1498769</a>
<div>
<select size="4">
<option>0</option>
<option selected style="display:none">1</option>
<option>2</option>
<option>3</option>
</select>
<select size="4">
<option>0</option>
<option selected style="display:contents">1</option>
<option>2</option>
<option>3</option>
</select>
<select size="4">
<option>0</option>
<optgroup label="group" style="display:none">
<option selected>1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
<select size="4">
<option>0</option>
<optgroup label="group" style="display:contents">
<option selected>1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
<select size="4">
<option>0</option>
<optgroup label="group" style="display:contents">
<option selected style="display:none">1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
<!-- Same as above but with size="1" -->
<select size="1">
<option>0</option>
<option selected style="display:none">1</option>
<option>2</option>
<option>3</option>
</select>
<select size="1">
<option>0</option>
<option selected style="display:contents">1</option>
<option>2</option>
<option>3</option>
</select>
<select size="1">
<option>0</option>
<optgroup label="group" style="display:none">
<option selected>1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
<select size="1">
<option>0</option>
<optgroup label="group" style="display:contents">
<option selected>1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
<select size="1">
<option>0</option>
<optgroup label="group" style="display:contents">
<option selected style="display:none">1</option>
</optgroup>
<option>2</option>
<option>3</option>
</select>
</div>
<pre id="test">
</pre>
</body>
</html>