2011-04-06 Naoki Takano <takano.naoki@gmail.com>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Apr 2011 20:52:17 +0000 (20:52 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Apr 2011 20:52:17 +0000 (20:52 +0000)
        Reviewed by David Levin.

        Webkit ignores PgUp/PgDown/Home/End in SELECT tag objects
        https://bugs.webkit.org/show_bug.cgi?id=27658

        * fast/events/select-element-expected.txt: Added to check PgUp/PgDown/Home/End keys are working correctly in SELECT tag.
        * fast/events/select-element.html: Added for expectation.
2011-04-06  Naoki Takano  <takano.naoki@gmail.com>

        Reviewed by David Levin.

        Webkit ignores PgUp/PgDown/Home/End in SELECT tag objects
        https://bugs.webkit.org/show_bug.cgi?id=27658

        Test: fast/events/select-element.html

        * dom/SelectElement.cpp:
        (WebCore::nextValidIndex): Moved from elsewhere in the file to be used by other routines.
        (WebCore::nextSelectableListIndexPageAway): Returns a selectable index one page away from the given index.
        (WebCore::nextSelectableListIndex): Implemented with nextValidIndex.
        And converted to a normal static function from a private function of SelectElement.
        (WebCore::previousSelectableListIndex): Implemented with nextValidIndex.
        And converted to a normal static function from a private function of SelectElement.
        (WebCore::firstSelectableListIndex): Returns the first selectable index.
        (WebCore::lastSelectableListIndex): Returns the last selectable index.
        (WebCore::SelectElement::menuListDefaultEventHandler): Converted from C cast to C++ cast.
        (WebCore::SelectElement::listBoxDefaultEventHandler): Adds support for PageUp/PageDown/Home/End with both single and multiple selection.

        * dom/SelectElement.h:
        (WebCore::SelectElement::): Remove nextSelectableListIndex() and previousSelectableListIndex().

        * rendering/RenderListBox.h: Makes RenderListBox::size public so that PageUp/PageDown behavior can use the actual list size rather than that specified in HTML.
        They can differ due to the minimum size imposed by RenderListBox.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@83096 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/events/select-element-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/select-element.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/SelectElement.cpp
Source/WebCore/dom/SelectElement.h
Source/WebCore/rendering/RenderListBox.h

index d1a9819..b67f59f 100644 (file)
@@ -1,3 +1,13 @@
+2011-04-06  Naoki Takano  <takano.naoki@gmail.com>
+
+        Reviewed by David Levin.
+
+        Webkit ignores PgUp/PgDown/Home/End in SELECT tag objects
+        https://bugs.webkit.org/show_bug.cgi?id=27658
+
+        * fast/events/select-element-expected.txt: Added to check PgUp/PgDown/Home/End keys are working correctly in SELECT tag.
+        * fast/events/select-element.html: Added for expectation.
+
 2011-04-06  Adrienne Walker  <enne@google.com>
 
         Unreviewed, rebaseline Chromium Win tests after r83075.
diff --git a/LayoutTests/fast/events/select-element-expected.txt b/LayoutTests/fast/events/select-element-expected.txt
new file mode 100644 (file)
index 0000000..9c31635
--- /dev/null
@@ -0,0 +1,118 @@
+This test verifies that the Home/End/PageUp/PageDown keys work correctly for <select> elements. Since it requires eventSender.keyDown, it will not run solo in the web browser; it must be run with run-webkit-tests.
+
+    
+PASS sendKeyAndExpectIndex("ss", "pageDown", 0, 3) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 1, 4) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 2, 5) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 3, 6) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 4, 6) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 5, 6) is true
+PASS sendKeyAndExpectIndex("ss", "pageDown", 6, 6) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 6, 3) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 5, 2) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 4, 1) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 3, 0) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 2, 0) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 1, 0) is true
+PASS sendKeyAndExpectIndex("ss", "pageUp", 0, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 6, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 5, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 4, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 3, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 2, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 1, 0) is true
+PASS sendKeyAndExpectIndex("ss", "home", 0, 0) is true
+PASS sendKeyAndExpectIndex("ss", "end", 6, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 5, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 4, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 3, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 2, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 1, 6) is true
+PASS sendKeyAndExpectIndex("ss", "end", 0, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 0, 4) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 1, 4) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 2, 5) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 4, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 5, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 6, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "pageDown", 7, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 7, 4) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 6, 2) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 5, 2) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 4, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 2, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 1, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "pageUp", 0, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 7, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 6, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 5, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 4, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 3, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 2, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 1, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "home", 0, 1) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 7, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 6, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 5, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 4, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 3, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 2, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 1, 6) is true
+PASS sendKeyAndExpectIndex("ssd", "end", 0, 6) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "end", [0, 1, 2, 3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "end", [0, 1, 2, 3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "home", [0]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "home", [0]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageDown", [3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageDown", [3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "end", [3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "end", [3, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "home", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ss", "home", [0, 1, 2, 3]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "end", [1, 2, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "end", [1, 2, 4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "home", [1]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "home", [1]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [2, 4]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2, 4]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2, 4]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "end", [4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "end", [4, 5, 6]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "home", [1, 2, 4]) is true
+PASS sendWithShiftKeyAndExpectIndices("ssd", "home", [1, 2, 4]) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 0, 3) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 1, 3) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 2, 4) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 3, 5) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 4, 6) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 5, 6) is true
+PASS sendKeyAndExpectIndex("ssg", "pageDown", 6, 6) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 6, 4) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 5, 3) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 4, 2) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 3, 1) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 2, 0) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 1, 0) is true
+PASS sendKeyAndExpectIndex("ssg", "pageUp", 0, 0) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/select-element.html b/LayoutTests/fast/events/select-element.html
new file mode 100644 (file)
index 0000000..804fa14
--- /dev/null
@@ -0,0 +1,306 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p>This test verifies that the Home/End/PageUp/PageDown keys work correctly for &lt;select&gt; elements.
+Since it requires <CODE>eventSender.keyDown</CODE>, it will not run solo in the web browser; it must be run with <KBD>run-webkit-tests</KBD>.</p>
+<hr>
+<form>
+     <!-- We specify a size of 3 but will end up rendering with 4 because webkit imposes a minimum size of 4.
+     It is important to make sure the code handles this case  -->
+    <select name="singleselect" id="ss" size="3" multiple="true">
+        <option value="0">0 </option>
+        <option value="1">1 </option>
+        <option value="2">2 </option>
+        <option value="3">3 </option>
+        <option value="4">4 </option>
+        <option value="5">5 </option>
+        <option value="6">6 </option>
+    </select>
+    <select name="singleselectwithdisabled" id="ssd" size="4" multiple="false">
+        <option value="0" disabled="true">0 </option>
+        <option value="1">1 </option>
+        <option value="2">2 </option>
+        <option value="3" disabled="true">3 </option>
+        <option value="4">4 </option>
+        <option value="5">5 </option>
+        <option value="6">6 </option>
+        <option value="7" disabled = "true">7 </option>
+    </select>
+    <select name="singleselectwithgroup" id="ssg" size="4" multiple="false">
+        <optgroup label="gp0">
+            <option value="0">0 </option>
+            <option value="1">1 </option>
+            <option value="2">2 </option>
+        </optgroup>
+        <optgroup label="gp1">
+            <option value="3">3 </option>
+        </optgroup>
+        <option value="4">4 </option>
+        <optgroup label="gp2">
+            <option value="5">5 </option>
+        </optgroup>
+        <option value="6">6 </option>
+    </select>
+</form>
+
+<p id="description"></p>
+<div id="console"></div>
+<div id="log"></div>
+
+<script>
+
+function log(message) {
+    document.getElementById('log').appendChild(document.createTextNode(message + "\n"));
+}
+
+function sendKeyAndExpectIndex(selectId, key, initialIndex, expectedIndex) {
+    var select = document.getElementById(selectId);
+    clearSelection(select);
+    select.focus();
+    select.selectedIndex = initialIndex;
+    if (select.selectedIndex != initialIndex) {
+        log("can't set selectedIndex to " + initialIndex + ' (is ' + select.selectedIndex + ')');
+        return false;
+    }
+    if (window.layoutTestController)
+        eventSender.keyDown(key);
+    if (select.selectedIndex != expectedIndex) {
+        log('selectedIndex should be ' + expectedIndex + ' (is ' + select.selectedIndex + ') after a ' + key + ' from index ' + initialIndex);
+        return false;
+    }
+    return true;
+}
+
+function equalArrays(a1, a2) {
+    if (a1.length != a2.length)
+        return false;
+    for (i = 0; i < a1.length; i++) {
+        if (a1[i] != a2[i])
+            return false;
+    }
+    return true;
+}
+
+function dumpArray(a) {
+    s = "[";
+    for (i = 0; i < a.length; i++) {
+        s = s + a[i];
+        if (i < a.length - 1)
+             s = s + ", ";
+    }
+    return s + "]";
+}
+
+function getSelectedIndices(select) {
+    nowSelected = [];
+    for (i = 0; i < select.options.length; i++)
+        if (select.options[i].selected)
+              nowSelected.push(i);
+    return nowSelected;
+}
+
+function clearSelection(select) {
+    for (i = 0; i < select.options.length; i++)
+        select.options[i].selected = false;
+}
+
+// expectedIndices should be in sorted order
+function sendWithShiftKeyAndExpectIndices(selectId, key, expectedIndices) {
+    var select = document.getElementById(selectId);
+    select.focus();
+    if (window.layoutTestController)
+        eventSender.keyDown(key, ["shiftKey"]);
+    nowSelected = getSelectedIndices(select);
+    if (!equalArrays(nowSelected, expectedIndices)) {
+        log('selected indices should be ' + dumpArray(expectedIndices) + ' (is ' +
+            dumpArray(nowSelected) + ') after a ' + key);
+        return false;
+    }
+    return true;
+}
+
+function testPageDownNoDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 0, 3)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 1, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 2, 5)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 3, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 4, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 5, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageDown", 6, 6)', 'true');
+}
+
+function testPageUpNoDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 6, 3)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 5, 2)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 4, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 3, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 2, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 1, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "pageUp", 0, 0)', 'true');
+}
+
+function testHomeNoDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 6, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 5, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 4, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 3, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 2, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 1, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "home", 0, 0)', 'true');
+}
+
+function testEndNoDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 6, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 5, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 4, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 3, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 2, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 1, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ss", "end", 0, 6)', 'true');
+}
+
+function testPageDownWithDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 0, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 1, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 2, 5)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 4, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 5, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 6, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageDown", 7, 6)', 'true');
+}
+
+function testPageUpWithDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 7, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 6, 2)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 5, 2)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 4, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 2, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 1, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "pageUp", 0, 1)', 'true');
+}
+
+function testHomeWithDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 7, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 6, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 5, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 4, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 3, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 2, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 1, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "home", 0, 1)', 'true');
+}
+
+function testEndWithDisabledElements() {
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 7, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 6, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 5, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 4, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 3, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 2, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 1, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssd", "end", 0, 6)', 'true');
+}
+
+function testVariousShiftKeysNoDisabledElements() {
+    var select = document.getElementById("ss");
+    select.focus();
+    clearSelection(select);
+    select.selectedIndex = 0;
+
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageDown", [0, 1, 2, 3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "end", [0, 1, 2, 3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "end", [0, 1, 2, 3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "home", [0])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "home", [0])', 'true');
+
+    clearSelection(select);
+    select.selectedIndex = 3;
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageDown", [3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageDown", [3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "pageUp", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "end", [3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "end", [3, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "home", [0, 1, 2, 3])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ss", "home", [0, 1, 2, 3])', 'true');
+}
+
+function testVariousShiftKeysWithDisabledElements() {
+    var select = document.getElementById('ssd');
+    select.focus();
+    clearSelection(select);
+    select.selectedIndex = 1;
+
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [1, 2, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "end", [1, 2, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "end", [1, 2, 4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "home", [1])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "home", [1])', 'true');
+
+    clearSelection(select);
+    select.selectedIndex = 4;
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageDown", [4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [2, 4])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2, 4])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "pageUp", [1, 2, 4])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "end", [4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "end", [4, 5, 6])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "home", [1, 2, 4])', 'true');
+    shouldBe('sendWithShiftKeyAndExpectIndices("ssd", "home", [1, 2, 4])', 'true');
+}
+
+function testPageDownWithGroup() {
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 0, 3)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 1, 3)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 2, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 3, 5)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 4, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 5, 6)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageDown", 6, 6)', 'true');
+}
+
+function testPageUpWithGroup() {
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 6, 4)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 5, 3)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 4, 2)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 3, 1)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 2, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 1, 0)', 'true');
+    shouldBe('sendKeyAndExpectIndex("ssg", "pageUp", 0, 0)', 'true');
+}
+
+testPageDownNoDisabledElements();
+testPageUpNoDisabledElements();
+testHomeNoDisabledElements();
+testEndNoDisabledElements();
+testPageDownWithDisabledElements();
+testPageUpWithDisabledElements();
+testHomeWithDisabledElements();
+testEndWithDisabledElements();
+testVariousShiftKeysNoDisabledElements();
+testVariousShiftKeysWithDisabledElements();
+testPageDownWithGroup();
+testPageUpWithGroup();
+
+var successfullyParsed = true;
+</script>
+
+<script src="../../fast/js/resources/js-test-post.js"></script> 
+</body>
+</html>
index 6896099..a8083a1 100644 (file)
@@ -1,3 +1,30 @@
+2011-04-06  Naoki Takano  <takano.naoki@gmail.com>
+
+        Reviewed by David Levin.
+
+        Webkit ignores PgUp/PgDown/Home/End in SELECT tag objects
+        https://bugs.webkit.org/show_bug.cgi?id=27658
+
+        Test: fast/events/select-element.html
+
+        * dom/SelectElement.cpp:
+        (WebCore::nextValidIndex): Moved from elsewhere in the file to be used by other routines.
+        (WebCore::nextSelectableListIndexPageAway): Returns a selectable index one page away from the given index.
+        (WebCore::nextSelectableListIndex): Implemented with nextValidIndex.
+        And converted to a normal static function from a private function of SelectElement.
+        (WebCore::previousSelectableListIndex): Implemented with nextValidIndex.
+        And converted to a normal static function from a private function of SelectElement.
+        (WebCore::firstSelectableListIndex): Returns the first selectable index.
+        (WebCore::lastSelectableListIndex): Returns the last selectable index.
+        (WebCore::SelectElement::menuListDefaultEventHandler): Converted from C cast to C++ cast.
+        (WebCore::SelectElement::listBoxDefaultEventHandler): Adds support for PageUp/PageDown/Home/End with both single and multiple selection.
+
+        * dom/SelectElement.h:
+        (WebCore::SelectElement::): Remove nextSelectableListIndex() and previousSelectableListIndex().
+
+        * rendering/RenderListBox.h: Makes RenderListBox::size public so that PageUp/PageDown behavior can use the actual list size rather than that specified in HTML.
+        They can differ due to the minimum size imposed by RenderListBox.
+
 2011-04-06  David Hyatt  <hyatt@apple.com>
 
         Reviewed by Dan Bernstein.
index 15c69ad..545f271 100644 (file)
@@ -70,6 +70,74 @@ namespace WebCore {
 
 static const DOMTimeStamp typeAheadTimeout = 1000;
 
+enum SkipDirection {
+    SkipBackwards = -1,
+    SkipForwards = 1
+};
+
+// Returns the 1st valid item |skip| items from |listIndex| in direction |direction| if there is one.
+// Otherwise, it returns the valid item closest to that boundary which is past |listIndex| if there is one.
+// Otherwise, it returns |listIndex|.
+// Valid means that it is enabled and an option element.
+static int nextValidIndex(const Vector<Element*>& listItems, int listIndex, SkipDirection direction, int skip)
+{
+    ASSERT(direction == -1 || direction == 1);
+    int lastGoodIndex = listIndex;
+    int size = listItems.size();
+    for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
+        --skip;
+        if (!listItems[listIndex]->disabled() && isOptionElement(listItems[listIndex])) {
+            lastGoodIndex = listIndex;
+            if (skip <= 0)
+                break;
+        }
+    }
+    return lastGoodIndex;
+}
+
+static int nextSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
+{
+    return nextValidIndex(data.listItems(element), startIndex, SkipForwards, 1);
+}
+
+static int previousSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
+{
+    if (startIndex == -1)
+        startIndex = data.listItems(element).size();
+    return nextValidIndex(data.listItems(element), startIndex, SkipBackwards, 1);
+}
+
+static int firstSelectableListIndex(SelectElementData& data, Element* element)
+{
+    const Vector<Element*>& items = data.listItems(element);
+    int index = nextValidIndex(items, items.size(), SkipBackwards, INT_MAX);
+    if (static_cast<unsigned>(index) == items.size())
+        return -1;
+    return index;
+}
+
+static int lastSelectableListIndex(SelectElementData& data, Element* element)
+{
+    return nextValidIndex(data.listItems(element), -1, SkipForwards, INT_MAX);
+}
+
+// Returns the index of the next valid item one page away from |startIndex| in direction |direction|.
+static int nextSelectableListIndexPageAway(SelectElementData& data, Element* element, int startIndex, SkipDirection direction)
+{
+    const Vector<Element*>& items = data.listItems(element);
+    // Can't use data->size() because renderer forces a minimum size.
+    int pageSize = 0;
+    if (element->renderer()->isListBox())
+        pageSize = toRenderListBox(element->renderer())->size() - 1; // -1 so we still show context
+
+    // One page away, but not outside valid bounds.
+    // If there is a valid option item one page away, the index is chosen.
+    // If there is no exact one page away valid option, returns startIndex or the most far index.
+    int edgeIndex = (direction == SkipForwards) ? 0 : (items.size() - 1);
+    int skipAmount = pageSize + ((direction == SkipForwards) ? startIndex : (edgeIndex - startIndex));
+    return nextValidIndex(items, edgeIndex, direction, skipAmount);
+}
+
 void SelectElement::selectAll(SelectElementData& data, Element* element)
 {
     ASSERT(!data.usesMenuList());
@@ -104,30 +172,6 @@ void SelectElement::saveLastSelection(SelectElementData& data, Element* element)
     }
 }
 
-int SelectElement::nextSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
-{
-    const Vector<Element*>& items = data.listItems(element);
-    int index = startIndex + 1;
-    while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled()))
-        ++index;
-    if ((unsigned) index == items.size())
-        return startIndex;
-    return index;
-}
-
-int SelectElement::previousSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
-{
-    const Vector<Element*>& items = data.listItems(element);
-    if (startIndex == -1)
-        startIndex = items.size();
-    int index = startIndex - 1;
-    while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled()))
-        --index;
-    if (index == -1)
-        return startIndex;
-    return index;
-}
-
 void SelectElement::setActiveSelectionAnchorIndex(SelectElementData& data, Element* element, int index)
 {
     data.setActiveSelectionAnchorIndex(index);
@@ -515,27 +559,6 @@ void SelectElement::reset(SelectElementData& data, Element* element)
     setOptionsChangedOnRenderer(data, element);
     element->setNeedsStyleRecalc();
 }
-    
-enum SkipDirection {
-    SkipBackwards = -1,
-    SkipForwards = 1
-};
-
-// Returns the index of the next valid list item |skip| items past |listIndex| in direction |direction|.
-static int nextValidIndex(const Vector<Element*>& listItems, int listIndex, SkipDirection direction, int skip)
-{
-    int lastGoodIndex = listIndex;
-    int size = listItems.size();
-    for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
-        --skip;
-        if (!listItems[listIndex]->disabled() && isOptionElement(listItems[listIndex])) {
-            lastGoodIndex = listIndex;
-            if (skip <= 0)
-                break;
-        }
-    }
-    return lastGoodIndex;
-}
 
 void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm)
 {
@@ -594,8 +617,8 @@ void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element
             listIndex = nextValidIndex(listItems, listItems.size(), SkipBackwards, 1);
             handled = true;
         }
-        
-        if (handled && listIndex >= 0 && (unsigned)listIndex < listItems.size())
+
+        if (handled && listIndex >= 0 && static_cast<unsigned>(listIndex) < listItems.size())
             setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex));
 
         if (handled)
@@ -760,19 +783,47 @@ void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element*
             return;
         const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
 
-        int endIndex = 0;        
+        bool handled = false;
+        int endIndex = 0;
         if (data.activeSelectionEndIndex() < 0) {
             // Initialize the end index
-            if (keyIdentifier == "Down")
-                endIndex = nextSelectableListIndex(data, element, lastSelectedListIndex(data, element));
-            else if (keyIdentifier == "Up")
-                endIndex = previousSelectableListIndex(data, element, optionToListIndex(data, element, selectedIndex(data, element)));
+            if (keyIdentifier == "Down" || keyIdentifier == "PageDown") {
+                int startIndex = lastSelectedListIndex(data, element);
+                handled = true;
+                if (keyIdentifier == "Down")
+                    endIndex = nextSelectableListIndex(data, element, startIndex);
+                else
+                    endIndex = nextSelectableListIndexPageAway(data, element, startIndex, SkipForwards);
+            } else if (keyIdentifier == "Up" || keyIdentifier == "PageUp") {
+                int startIndex = optionToListIndex(data, element, selectedIndex(data, element));
+                handled = true;
+                if (keyIdentifier == "Up")
+                    endIndex = previousSelectableListIndex(data, element, startIndex);
+                else
+                    endIndex = nextSelectableListIndexPageAway(data, element, startIndex, SkipBackwards);
+            }
         } else {
             // Set the end index based on the current end index
-            if (keyIdentifier == "Down")
+            if (keyIdentifier == "Down") {
                 endIndex = nextSelectableListIndex(data, element, data.activeSelectionEndIndex());
-            else if (keyIdentifier == "Up")
-                endIndex = previousSelectableListIndex(data, element, data.activeSelectionEndIndex());    
+                handled = true;
+            } else if (keyIdentifier == "Up") {
+                endIndex = previousSelectableListIndex(data, element, data.activeSelectionEndIndex());
+                handled = true;
+            } else if (keyIdentifier == "PageDown") {
+                endIndex = nextSelectableListIndexPageAway(data, element, data.activeSelectionEndIndex(), SkipForwards);
+                handled = true;
+            } else if (keyIdentifier == "PageUp") {
+                endIndex = nextSelectableListIndexPageAway(data, element, data.activeSelectionEndIndex(), SkipBackwards);
+                handled = true;
+            }
+        }
+        if (keyIdentifier == "Home") {
+            endIndex = firstSelectableListIndex(data, element);
+            handled = true;
+        } else if (keyIdentifier == "End") {
+            endIndex = lastSelectableListIndex(data, element);
+            handled = true;
         }
 
         if (isSpatialNavigationEnabled(element->document()->frame()))
@@ -780,13 +831,13 @@ void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element*
             if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == data.activeSelectionEndIndex()))
                 return;
 
-        if (keyIdentifier == "Down" || keyIdentifier == "Up") {
+        if (endIndex >= 0 && handled) {
             // Save the selection so it can be compared to the new selection when dispatching change events immediately after making the new selection.
             saveLastSelection(data, element);
 
-            ASSERT_UNUSED(listItems, !listItems.size() || (endIndex >= 0 && (unsigned) endIndex < listItems.size()));
+            ASSERT_UNUSED(listItems, !listItems.size() || (endIndex >= 0 && static_cast<unsigned>(endIndex) < listItems.size()));
             setActiveSelectionEndIndex(data, endIndex);
-            
+
             bool selectNewItem = !data.multiple() || static_cast<KeyboardEvent*>(event)->shiftKey() || !isSpatialNavigationEnabled(element->document()->frame());
             if (selectNewItem)
                 data.setActiveSelectionState(true);
index 222a1bb..dd073a2 100644 (file)
@@ -72,8 +72,6 @@ protected:
 
     static void selectAll(SelectElementData&, Element*);
     static void saveLastSelection(SelectElementData&, Element*);
-    static int nextSelectableListIndex(SelectElementData&, Element*, int startIndex);
-    static int previousSelectableListIndex(SelectElementData&, Element*, int startIndex);
     static void setActiveSelectionAnchorIndex(SelectElementData&, Element*, int index);
     static void setActiveSelectionEndIndex(SelectElementData&, int index);
     static void updateListBoxSelection(SelectElementData&, Element*, bool deselectOtherOptions);
index 0e2cfd6..610f55e 100644 (file)
@@ -53,6 +53,8 @@ public:
 
     int scrollToward(const IntPoint&); // Returns the new index or -1 if no scroll occurred
 
+    int size() const;
+
 private:
     virtual const char* renderName() const { return "RenderListBox"; }
 
@@ -125,7 +127,6 @@ private:
     
     int itemHeight() const;
     void valueChanged(unsigned listIndex);
-    int size() const;
     int numVisibleItems() const;
     int numItems() const;
     int listHeight() const;