Reviewed by Darin.
authorap@webkit.org <ap@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Dec 2007 19:11:46 +0000 (19:11 +0000)
committerap@webkit.org <ap@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Dec 2007 19:11:46 +0000 (19:11 +0000)
        <rdar://problem/5535636>
        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.

        http://bugs.webkit.org/show_bug.cgi?id=13916
        JavaScript detects Tab as a character input on a textfield validation

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

81 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/access-key-self-destruct.html
LayoutTests/fast/events/frame-tab-focus.html
LayoutTests/fast/events/js-keyboard-event-creation-expected.txt
LayoutTests/fast/events/js-keyboard-event-creation.html
LayoutTests/fast/events/key-events-in-input-button-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/key-events-in-input-button.html [new file with mode: 0644]
LayoutTests/fast/events/key-events-in-input-text-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/key-events-in-input-text.html [new file with mode: 0644]
LayoutTests/fast/events/keydown-keypress-preventDefault-expected.txt
LayoutTests/fast/events/keydown-keypress-preventDefault.html
LayoutTests/fast/events/onchange-passwordfield.html
LayoutTests/fast/events/onchange-searchfield.html
LayoutTests/fast/events/onchange-textfield.html
LayoutTests/fast/events/onsearch-enter.html
LayoutTests/fast/events/option-tab.html
LayoutTests/fast/forms/access-key.html
LayoutTests/fast/forms/button-enter-click.html
LayoutTests/fast/forms/check-box-enter-key.html
LayoutTests/fast/forms/enter-clicks-buttons.html
LayoutTests/fast/forms/focus2-expected.txt
LayoutTests/fast/forms/focus2.html
LayoutTests/fast/forms/legend-access-key.html
LayoutTests/fast/forms/listbox-onchange.html
LayoutTests/fast/forms/listbox-selection.html
LayoutTests/fast/forms/onchange-enter-submit.html
LayoutTests/fast/forms/search-event-delay.html
LayoutTests/fast/forms/select-double-onchange.html
LayoutTests/fast/forms/select-enter-key.html
LayoutTests/fast/forms/textfield-onchange-deletion.html
LayoutTests/fast/html/tab-order.html
LayoutTests/platform/mac/fast/events/objc-event-api-expected.txt
LayoutTests/platform/win/fast/events/double-dead-char-expected.txt [new file with mode: 0644]
LayoutTests/platform/win/fast/events/double-dead-char.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/WebCore.base.exp
WebCore/bridge/EditorClient.h
WebCore/dom/Document.cpp
WebCore/dom/Document.h
WebCore/dom/KeyboardEvent.cpp
WebCore/dom/KeyboardEvent.h
WebCore/editing/Editor.cpp
WebCore/editing/Editor.h
WebCore/html/HTMLButtonElement.cpp
WebCore/html/HTMLInputElement.cpp
WebCore/html/HTMLSelectElement.cpp
WebCore/page/EventHandler.cpp
WebCore/page/mac/EventHandlerMac.mm
WebCore/platform/PlatformKeyboardEvent.h
WebCore/platform/gtk/KeyEventGtk.cpp
WebCore/platform/mac/KeyEventMac.mm
WebCore/platform/qt/PlatformKeyboardEventQt.cpp
WebCore/platform/text/PlatformString.h
WebCore/platform/text/String.cpp
WebCore/platform/text/StringImpl.cpp
WebCore/platform/text/StringImpl.h
WebCore/platform/win/KeyEventWin.cpp
WebCore/platform/wx/KeyboardEventWx.cpp
WebCore/svg/graphics/SVGImageEmptyClients.h
WebKit/gtk/ChangeLog
WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp
WebKit/gtk/WebCoreSupport/EditorClientGtk.h
WebKit/mac/ChangeLog
WebKit/mac/WebCoreSupport/WebEditorClient.h
WebKit/mac/WebCoreSupport/WebEditorClient.mm
WebKit/mac/WebView/WebHTMLView.mm
WebKit/mac/WebView/WebPDFView.mm
WebKit/qt/ChangeLog
WebKit/qt/WebCoreSupport/EditorClientQt.cpp
WebKit/qt/WebCoreSupport/EditorClientQt.h
WebKit/win/ChangeLog
WebKit/win/WebEditorClient.cpp
WebKit/win/WebEditorClient.h
WebKit/win/WebView.cpp
WebKit/win/WebView.h
WebKit/wx/ChangeLog
WebKit/wx/WebKitSupport/EditorClientWx.cpp
WebKit/wx/WebKitSupport/EditorClientWx.h
WebKitTools/ChangeLog
WebKitTools/DumpRenderTree/mac/EventSendingController.mm
WebKitTools/DumpRenderTree/win/EventSender.cpp

index 9eb142f..603d80f 100644 (file)
@@ -1,3 +1,73 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * platform/win/fast/events: Added.
+        * platform/win/fast/events/double-dead-char-expected.txt: Added.
+        * platform/win/fast/events/double-dead-char.html: Added.
+        * fast/events/key-events-in-input-button.html: Added
+        * fast/events/key-events-in-input-button-expected.txt: Added
+        * fast/events/key-events-in-input-text.html: Added
+        * fast/events/key-events-in-input-text-expected.txt: Added
+
+        * fast/events/access-key-self-destruct.html:
+        * fast/forms/listbox-onchange.html:
+        * fast/forms/listbox-selection.html:
+        * fast/forms/access-key.html:
+        * fast/forms/legend-access-key.html:
+        * fast/forms/enter-clicks-buttons.html:
+        * fast/forms/check-box-enter-key.html:
+        * fast/forms/button-enter-click.html:
+        * fast/events/onsearch-enter.html:
+        * fast/events/onchange-passwordfield.html:
+        * fast/events/onchange-searchfield.html:
+        * fast/events/onchange-textfield.html:
+        Use eventSender instead of DOM events, because it emulates the real user action much better,
+        and we weren't getting cross-browser compatibility for the tests anyway.
+
+        * fast/events/frame-tab-focus.html:
+        * fast/html/tab-order.html:
+        * fast/forms/select-enter-key.html:
+        * fast/forms/focus2-expected.txt:
+        * fast/forms/focus2.html:
+        * fast/events/option-tab.html:
+        Dispatch a keydown instead of keypress, as this is when default processing is now done.
+        Possibly, it would be better stiull to use eventSender here, as well.
+
+        * fast/forms/onchange-enter-submit.html:
+        * fast/forms/select-double-onchange.html:
+        * fast/forms/textfield-onchange-deletion.html:
+        The correct code is '\r', not '\n' - previously, the difference was lost while converting
+        events back and forth.
+
+        * fast/forms/search-event-delay.html: Use a new named key to dispatch backspace - the
+        character code for it is cross-platform, but key code is not.
+        Also changed the test to call notifyDone() from a timer - otherwise, DRT would hang as 
+        WM_KEYUP was dispatched after WM_QUIT. I tried and couldn't make DRT work, but I'm
+        fairly confident that this issue doesn't affect Safari.
+
+        * fast/events/keydown-keypress-preventDefault-expected.txt:
+        * fast/events/keydown-keypress-preventDefault.html:
+        This test claimed that its expacted behavior matched both IE and Firefox, but it did not.
+        We now match IE.
+
+        * fast/events/js-keyboard-event-creation-expected.txt:
+        * fast/events/js-keyboard-event-creation.html:
+        This test was problematic, because it was tabbing out to chrome, and that doesn't work
+        well in DRT. I have added another input for it to have a nicer target.
+
+        * platform/mac/fast/events/objc-event-api-expected.txt:
+        Updated for new behavior:
+          - eventSender.keyDown() now dispatches a keyUp, too;
+          - raw events do not have charCode;
+          - keypresses do not have keyIdentifiers.
+
 2007-12-10  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by Weinig, Dan and Alexey.
index 00b6010..bce11ff 100644 (file)
@@ -5,15 +5,14 @@ function replace()
 }
 function test()
 {
-    if (window.layoutTestController)
+    if (window.layoutTestController) {
         layoutTestController.dumpAsText();
-    
-    var event = document.createEvent("KeyboardEvent");
-    if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, "a", 0, true, false, false, false, false);
-    else
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, "a", 0, false, true, false, false, false);
-    document.dispatchEvent(event);
+        if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
+            modifier = "ctrlKey";
+        else
+            modifier = "altKey";
+        eventSender.keyDown("a", [modifier]);
+    }
 }
 </script>
 <body onload="test()">
index 8adaaa0..9b7cdfb 100644 (file)
@@ -89,7 +89,7 @@ function dispatchTabPress(element, shiftKey, altKey)
 {
     var event = document.createEvent('KeyboardEvents');
     var tabKeyIdentifier = 'U+0009';
-    event.initKeyboardEvent('keypress', true, true, document.defaultView, tabKeyIdentifier, 0, false, altKey, shiftKey, false, false);
+    event.initKeyboardEvent('keydown', true, true, document.defaultView, tabKeyIdentifier, 0, false, altKey, shiftKey, false, false);
     element.dispatchEvent(event);
 }
 
index 897cfb3..030a98d 100644 (file)
@@ -1,10 +1,6 @@
-
+  
 This tests that DOMKeyboardEvents are created correctly in the JavaScript API.
 
-keydown - key: U+0009@0 (keyCode/charCode: 9/9) modifiers: false,false,false,false
-
-keypress - key: U+0009@0 (keyCode/charCode: 9/9) modifiers: false,false,false,false
-
-keydown - key: U+0009@0 (keyCode/charCode: 9/9) modifiers: false,false,true,false
+keydown - key: U+0009@0 (keyCode/charCode: 9/0) modifiers: false,false,false,false
 
-keypress - key: U+0009@0 (keyCode/charCode: 9/9) modifiers: false,false,true,false
+keyup - key: U+0009@0 (keyCode/charCode: 9/0) modifiers: false,false,true,false
index 92a956e..7a747f9 100644 (file)
@@ -14,7 +14,7 @@ function init() {
     input.addEventListener("keypress", keyevent, true);
     input.addEventListener("keyup", keyevent, true);
    
-    if (layoutTestController)
+    if (window.layoutTestController)
         layoutTestController.dumpAsText();
     
     input.focus();
@@ -28,6 +28,7 @@ function init() {
 <body onload="init()">
     <form>
         <input type="text" size="50" id="testinput" />
+        <input type="text" size="50" />
     </form>
    
     <p>This tests that DOMKeyboardEvents are created correctly in the JavaScript API.</p>
diff --git a/LayoutTests/fast/events/key-events-in-input-button-expected.txt b/LayoutTests/fast/events/key-events-in-input-button-expected.txt
new file mode 100644 (file)
index 0000000..003c63a
--- /dev/null
@@ -0,0 +1,19 @@
+To test manually, press keys and compare results to other browsers.
+
+
+target - type - ctrlKey,altKey,shiftKey,metaKey - keyIdentifier - keyCode - charCode
+Space:
+INPUT - keydown - false,false,false,false - U+0020 - 32 - 0
+INPUT - keypress - false,false,false,false - - 32 - 32
+INPUT - keyup - false,false,false,false - U+0020 - 32 - 0
+INPUT - click
+Enter:
+INPUT - keydown - false,false,false,false - Enter - 13 - 0
+INPUT - keypress - false,false,false,false - - 13 - 13
+INPUT - click
+INPUT - keyup - false,false,false,false - Enter - 13 - 0
+A:
+INPUT - keydown - false,false,true,false - U+0041 - 65 - 0
+INPUT - keypress - false,false,true,false - - 65 - 65
+INPUT - keyup - false,false,true,false - U+0041 - 65 - 0
+
diff --git a/LayoutTests/fast/events/key-events-in-input-button.html b/LayoutTests/fast/events/key-events-in-input-button.html
new file mode 100644 (file)
index 0000000..466b64f
--- /dev/null
@@ -0,0 +1,55 @@
+<p>To test manually, press keys and compare results to other browsers.</p>
+<input type="button" value="Input"
+   onclick="log(eventInfo(event));"
+   onmousedown="log(eventInfo(event));"
+   onmouseup="log(eventInfo(event));"
+   onkeypress="log(eventInfo(event));"
+   onkeydown="log(eventInfo(event));"
+   onkeyup="log(eventInfo(event));">
+</input>
+<div id="log"></div>
+
+<script>
+function log(msg) {
+   document.getElementById("log").innerHTML+= msg + "<br />";
+}
+function eventInfo(event, where) {
+  try {
+  if (!event)
+    event = window.event;
+    target = event.srcElement ? event.srcElement : event.target;
+    if (event.type == "textInput")
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type + " - " + event.data;
+    else if (event.type == "keydown" || event.type == "keypress" || event.type == "keyup") 
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type
+            + ' - ' + [event.ctrlKey, event.altKey, event.shiftKey, event.metaKey]
+            + ' - ' + event.keyIdentifier
+            + ' - ' + event.keyCode
+            + ' - ' + event.charCode;
+    else if (event.type == "mousedown" || event.type == "click" || event.type == "mouseup")
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type;
+
+    } catch (ex) {
+        alert(ex);
+    }
+}
+log("target - type - " + ["ctrlKey", "altKey", "shiftKey", "metaKey"]
+        + ' - ' + "keyIdentifier"
+        + ' - ' + "keyCode"
+        + ' - ' + "charCode");
+
+if (document.getElementsByTagName("input")[0].addEventListener)
+    document.getElementsByTagName("input")[0].addEventListener('textInput', function(e) {log(eventInfo(e));}, false);
+
+document.getElementsByTagName("input")[0].focus();
+
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    log("Space:");
+    eventSender.keyDown(" ", []);
+    log("Enter:");
+    eventSender.keyDown("\r", []);
+    log("A:");
+    eventSender.keyDown("A", []);
+}
+</script>
diff --git a/LayoutTests/fast/events/key-events-in-input-text-expected.txt b/LayoutTests/fast/events/key-events-in-input-text-expected.txt
new file mode 100644 (file)
index 0000000..44faa9b
--- /dev/null
@@ -0,0 +1,18 @@
+To test manually, press keys and compare results to other browsers.
+
+  
+target - type - ctrlKey,altKey,shiftKey,metaKey - keyIdentifier - keyCode - charCode
+Space:
+INPUT - keydown - false,false,false,false - U+0020 - 32 - 0. Value: "".
+INPUT - keypress - false,false,false,false - - 32 - 32. Value: "".
+INPUT - textInput - . Value: "".
+INPUT - keyup - false,false,false,false - U+0020 - 32 - 0. Value: " ".
+Backspace:
+INPUT - keydown - false,false,false,false - U+0008 - 8 - 0. Value: " ".
+INPUT - keyup - false,false,false,false - U+0008 - 8 - 0. Value: "".
+Left Arrow:
+INPUT - keydown - false,false,false,false - Left - 37 - 0. Value: "".
+INPUT - keyup - false,false,false,false - Left - 37 - 0. Value: "".
+Tab:
+INPUT - keydown - false,false,false,false - U+0009 - 9 - 0. Value: "".
+
diff --git a/LayoutTests/fast/events/key-events-in-input-text.html b/LayoutTests/fast/events/key-events-in-input-text.html
new file mode 100644 (file)
index 0000000..bc7f749
--- /dev/null
@@ -0,0 +1,61 @@
+<p>To test manually, press keys and compare results to other browsers.</p>
+<input type="text"
+   onclick="log(eventInfo(event));"
+   onmousedown="log(eventInfo(event));"
+   onmouseup="log(eventInfo(event));"
+   onkeypress="log(eventInfo(event));"
+   onkeydown="log(eventInfo(event));"
+   onkeyup="log(eventInfo(event));">
+</input>
+<input type=text></input>
+<div id="log"></div>
+
+<script>
+function log(msg) {
+   document.getElementById("log").innerHTML+= msg + "<br />";
+}
+function eventInfo(event, where) {
+  try {
+  if (!event)
+    event = window.event;
+    target = event.srcElement ? event.srcElement : event.target;
+    if (event.type == "textInput")
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type + " - " + event.data
+            + '. Value: "' + target.value + '".';
+    else if (event.type == "keydown" || event.type == "keypress" || event.type == "keyup") 
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type
+            + ' - ' + [event.ctrlKey, event.altKey, event.shiftKey, event.metaKey]
+            + ' - ' + event.keyIdentifier
+            + ' - ' + event.keyCode
+            + ' - ' + event.charCode
+            + '. Value: "' + target.value + '".';
+    else if (event.type == "mousedown" || event.type == "click" || event.type == "mouseup")
+        return (where ? "(" + where + ") " : "") + target.tagName + " - " + event.type
+            '. Value: "' + target.value + '".';
+
+    } catch (ex) {
+        alert(ex);
+    }
+}
+log("target - type - " + ["ctrlKey", "altKey", "shiftKey", "metaKey"]
+        + ' - ' + "keyIdentifier"
+        + ' - ' + "keyCode"
+        + ' - ' + "charCode");
+
+if (document.getElementsByTagName("input")[0].addEventListener)
+    document.getElementsByTagName("input")[0].addEventListener('textInput', function(e) {log(eventInfo(e));}, false);
+
+document.getElementsByTagName("input")[0].focus();
+
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    log("Space:");
+    eventSender.keyDown(" ", []);
+    log("Backspace:");
+    eventSender.keyDown("\x08", []);
+    log("Left Arrow:");
+    eventSender.keyDown("leftArrow", []);
+    log("Tab:");
+    eventSender.keyDown("\t", []);
+}
+</script>
index fdfd77a..4358124 100644 (file)
@@ -1,13 +1,9 @@
-This tests that preventing the default behavior for a keydown event will not prevent the keypress event from firing, but will prevent text from being inserted.
-This matches IE7 and Firefox.
+This tests that preventing the default behavior for a keydown event will prevent the keypress event from firing, and will prevent text from being inserted.
+This matches IE7, but not Firefox, which still dispatches a keypress.
  
 key down
-key press
 key down
-key press
 key down
-key press
 key down
-key press
 
 
index d1466c6..314c087 100644 (file)
@@ -1,28 +1,28 @@
-<html>\r
-<script>\r
-    function test() {\r
-        var tf = document.getElementById('tf');\r
-        tf.focus();\r
-        if (window.layoutTestController) {\r
-            layoutTestController.dumpAsText();\r
-            eventSender.keyDown('F');\r
-            eventSender.keyDown('A');\r
-            eventSender.keyDown('I');\r
-            eventSender.keyDown('L');\r
-            log(tf.value);\r
-        }\r
-    }\r
-    \r
-    function log(msg) {\r
-        var res = document.getElementById('res');\r
-        res.innerHTML = res.innerHTML + msg + "<br>";\r
-    }\r
-</script> \r
-<body onload="test()">\r
-    This tests that preventing the default behavior for a keydown event will not prevent the keypress event from firing, but will prevent text from being inserted.<br>\r
-    This matches IE7 and Firefox.<br>\r
-    <input id="tf" onkeydown="log('key down'); event.preventDefault();" onkeypress="log('key press')">\r
-    <br>\r
-    <div id="res"></div>\r
-</body>\r
-</html>\r
+<html>
+<script>
+    function test() {
+        var tf = document.getElementById('tf');
+        tf.focus();
+        if (window.layoutTestController) {
+            layoutTestController.dumpAsText();
+            eventSender.keyDown('F');
+            eventSender.keyDown('A');
+            eventSender.keyDown('I');
+            eventSender.keyDown('L');
+            log(tf.value);
+        }
+    }
+    
+    function log(msg) {
+        var res = document.getElementById('res');
+        res.innerHTML = res.innerHTML + msg + "<br>";
+    }
+</script> 
+<body onload="test()">
+    This tests that preventing the default behavior for a keydown event will prevent the keypress event from firing, and will prevent text from being inserted.<br>
+    This matches IE7, but not Firefox, which still dispatches a keypress.<br>
+    <input id="tf" onkeydown="log('key down'); return false" onkeypress="log('key press')">
+    <br>
+    <div id="res"></div>
+</body>
+</html>
index 852ca65..42bff05 100644 (file)
@@ -27,8 +27,8 @@ document.getElementById('input').focus();
 document.execCommand("InsertText", false, "foo bar baz");
 
 // hit enter
-var enterEvent = document.createEvent("KeyboardEvents");
-enterEvent.initKeyboardEvent("keypress", true, false, window, "Enter", 0, false, false, false, false, false); // This is not at all like pulling teeth
-input.dispatchEvent(enterEvent);
+input.focus();
+if (window.eventSender)
+    eventSender.keyDown("\r", []);
 
 </script>
index 84fb467..2887207 100644 (file)
@@ -27,8 +27,8 @@ document.getElementById('input').focus();
 document.execCommand("InsertText", false, "foo bar baz");
 
 // hit enter
-var enterEvent = document.createEvent("KeyboardEvents");
-enterEvent.initKeyboardEvent("keypress", true, false, window, "Enter", 0, false, false, false, false, false); // This is not at all like pulling teeth
-input.dispatchEvent(enterEvent);
+input.focus();
+if (window.eventSender)
+    eventSender.keyDown("\r", []);
 
 </script>
index 98f9eea..dee0447 100644 (file)
@@ -27,8 +27,8 @@ document.getElementById('input').focus();
 document.execCommand("InsertText", false, "foo bar baz");
 
 // hit enter
-var enterEvent = document.createEvent("KeyboardEvents");
-enterEvent.initKeyboardEvent("keypress", true, false, window, "Enter", 0, false, false, false, false, false); // This is not at all like pulling teeth
-input.dispatchEvent(enterEvent);
+input.focus();
+if (window.eventSender)
+    eventSender.keyDown("\r", []);
 
 </script>
index bc76385..582b31e 100644 (file)
@@ -5,11 +5,10 @@
         {
             var sf = document.getElementById('sf');
             sf.focus();
-            var enterEvent = document.createEvent("KeyboardEvents");
-            enterEvent.initKeyboardEvent("keypress", true, false, window, "Enter", 0, false, false, false, false, false);
-            sf.dispatchEvent(enterEvent);
-            if (window.layoutTestController)
+            if (window.layoutTestController) {
                 layoutTestController.dumpAsText();
+                eventSender.keyDown("\r", []);
+            }
         }
         function log(msg)
         {
index fab86f1..caa8460 100644 (file)
@@ -14,7 +14,7 @@ function test(fieldId)
     window.linkFocused = false;
     document.getElementById(fieldId).focus();
     var event = document.createEvent("KeyboardEvents");
-    event.initKeyboardEvent("keypress", true, true, document.defaultView, "U+0009", 0, false, true, false, false, false);
+    event.initKeyboardEvent("keydown", true, true, document.defaultView, "U+0009", 0, false, true, false, false, false);
     document.getElementById(fieldId).dispatchEvent(event);
     if (window.linkFocused)
         document.getElementById("console").innerHTML += "SUCCESS: Option-tab did tab to the link (" + fieldId + ").\n";
@@ -24,7 +24,7 @@ function test(fieldId)
     window.linkFocused = false;
     document.getElementById(fieldId).focus();
     event = document.createEvent("KeyboardEvents");
-    event.initKeyboardEvent("keypress", true, true, document.defaultView, "U+0009", 0, false, false, false, false, false);
+    event.initKeyboardEvent("keydown", true, true, document.defaultView, "U+0009", 0, false, false, false, false, false);
     document.getElementById(fieldId).dispatchEvent(event);
     if (window.linkFocused)
         document.getElementById("console").innerHTML += "FAIL: Plain old tab did tab to the link (" + fieldId + ").\n";
index d220d12..7e98c6e 100644 (file)
@@ -9,23 +9,23 @@ function log(message)
 }
 function pressKey(key)
 {
-    var event = document.createEvent("KeyboardEvent");
     if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, key, 0, true, false, false, false, false);
+        modifier = "ctrlKey";
     else
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, key, 0, false, true, false, false, false);
-    document.dispatchEvent(event);
+        modifier = "altKey";
+    eventSender.keyDown(key, [modifier]);
 }
 function test()
 {
-    if (window.layoutTestController)
+    if (window.layoutTestController) {
         layoutTestController.dumpAsText();
 
-    for (i = 1; i <= 9; i++)
-        pressKey(i);
-    pressKey("a");
-    pressKey("b");
-    pressKey("c");
+        for (i = 1; i <= 9; i++)
+            pressKey(i.toString());
+        pressKey("a");
+        pressKey("b");
+        pressKey("c");
+    }
 }
 </script>
 </head>
index b04e3fc..115ca63 100644 (file)
@@ -4,11 +4,10 @@
     function test() {
         var bt = document.getElementById('bt');
         bt.focus();
-        if (window.layoutTestController)
+        if (window.layoutTestController) {
             layoutTestController.dumpAsText();
-        var keyEvent = document.createEvent("KeyboardEvents");
-        keyEvent.initKeyboardEvent("keypress", true, true, window, "Enter", 0, false, false, false, false, false);
-        bt.dispatchEvent(keyEvent);
+            eventSender.keyDown("\r", []);
+        }
     }
     function log(msg) {
         var console = document.getElementById('console');
index 724bc1f..f9e7141 100644 (file)
@@ -18,13 +18,12 @@ function submitHandler(e)
 }
 function test()
 {
-    if (window.layoutTestController)
-        layoutTestController.dumpAsText();
-
     document.getElementById("form").onsubmit = submitHandler;
-    var event = document.createEvent("KeyboardEvents");
-    event.initKeyboardEvent("keypress", true, true, document.defaultView, "Enter", 0, false, false, false, false, false);
-    document.getElementById("check").dispatchEvent(event);
+    document.getElementById("check").focus();
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        eventSender.keyDown("\r", []);
+    }
 }
 </script>
 </head>
index a2d1376..d7cc3e7 100644 (file)
@@ -25,18 +25,18 @@ function loaded()
     if (window.layoutTestController)
         layoutTestController.dumpAsText();
 
-    var keys = ['Enter', 'U+0020'];
+    var keys = ['\r', ' '];
+    var keyNames = ['Enter', 'U+0020'];
     var tagNames = ['button', 'input'];
 
     for (var i in keys) {
-        log('\n\nSending ' + keys[i] + ' keypresses...\n');
+        log('\n\nSending ' + keyNames[i] + ' keypresses...\n');
         for (var j in tagNames) {
             var elements = document.getElementsByTagName(tagNames[j]);
             log('\nLooping over ' + elements.length + ' ' + tagNames[j] + ' elements...\n');
             for (var k = 0; k < elements.length; ++k) {
-                var event = elements[k].ownerDocument.createEvent("KeyboardEvent");
-                event.initKeyboardEvent("keypress", true, true, elements[k].ownerDocument.defaultView, keys[i], 0, false, false, false, false, false);
-                elements[k].dispatchEvent(event);
+                elements[k].focus();
+                eventSender.keyDown(keys[i], []);
             }
         }
     }
index f1ec0e2..f8e5edb 100644 (file)
@@ -5,86 +5,86 @@ anchor
 
 PARENT DOCUMENT:
 focus event: [to] BUTTON
-keypress event: [to] BUTTON
+keydown event: [to] BUTTON
 blur event: [to] BUTTON
 focus event: [to] CHECKBOX
-keypress event: [to] CHECKBOX
+keydown event: [to] CHECKBOX
 blur event: [to] CHECKBOX
 focus event: [to] FILE
-keypress event: [to] FILE
+keydown event: [to] FILE
 blur event: [to] FILE
 focus event: [to] IMAGE
-keypress event: [to] IMAGE
+keydown event: [to] IMAGE
 blur event: [to] IMAGE
 focus event: [to] ISINDEX
-keypress event: [to] ISINDEX
+keydown event: [to] ISINDEX
 blur event: [to] ISINDEX
 focus event: [to] PASSWORD
-keypress event: [to] PASSWORD
+keydown event: [to] PASSWORD
 blur event: [to] PASSWORD
 focus event: [to] RANGE
-keypress event: [to] RANGE
+keydown event: [to] RANGE
 blur event: [to] RANGE
 focus event: [to] RESET
-keypress event: [to] RESET
+keydown event: [to] RESET
 blur event: [to] RESET
 focus event: [to] SEARCH
-keypress event: [to] SEARCH
+keydown event: [to] SEARCH
 blur event: [to] SEARCH
 focus event: [to] SUBMIT
-keypress event: [to] SUBMIT
+keydown event: [to] SUBMIT
 blur event: [to] SUBMIT
 focus event: [to] TEXT
-keypress event: [to] TEXT
+keydown event: [to] TEXT
 blur event: [to] TEXT
 focus event: [to] TEXTAREA
-keypress event: [to] TEXTAREA
+keydown event: [to] TEXTAREA
 blur event: [to] TEXTAREA
 focus event: [to] DIV
-keypress event: [to] DIV
+keydown event: [to] DIV
 blur event: [to] DIV
 focus event: [to] ANCHOR
 blur event: [to] ANCHOR
 
 IFRAME DOCUMENT:
 focus event: [to] BUTTON
-keypress event: [to] BUTTON
+keydown event: [to] BUTTON
 blur event: [to] BUTTON
 focus event: [to] CHECKBOX
-keypress event: [to] CHECKBOX
+keydown event: [to] CHECKBOX
 blur event: [to] CHECKBOX
 focus event: [to] FILE
-keypress event: [to] FILE
+keydown event: [to] FILE
 blur event: [to] FILE
 focus event: [to] IMAGE
-keypress event: [to] IMAGE
+keydown event: [to] IMAGE
 blur event: [to] IMAGE
 focus event: [to] ISINDEX
-keypress event: [to] ISINDEX
+keydown event: [to] ISINDEX
 blur event: [to] ISINDEX
 focus event: [to] PASSWORD
-keypress event: [to] PASSWORD
+keydown event: [to] PASSWORD
 blur event: [to] PASSWORD
 focus event: [to] RANGE
-keypress event: [to] RANGE
+keydown event: [to] RANGE
 blur event: [to] RANGE
 focus event: [to] RESET
-keypress event: [to] RESET
+keydown event: [to] RESET
 blur event: [to] RESET
 focus event: [to] SEARCH
-keypress event: [to] SEARCH
+keydown event: [to] SEARCH
 blur event: [to] SEARCH
 focus event: [to] SUBMIT
-keypress event: [to] SUBMIT
+keydown event: [to] SUBMIT
 blur event: [to] SUBMIT
 focus event: [to] TEXT
-keypress event: [to] TEXT
+keydown event: [to] TEXT
 blur event: [to] TEXT
 focus event: [to] TEXTAREA
-keypress event: [to] TEXTAREA
+keydown event: [to] TEXTAREA
 blur event: [to] TEXTAREA
 focus event: [to] DIV
-keypress event: [to] DIV
+keydown event: [to] DIV
 blur event: [to] DIV
 focus event: [to] ANCHOR
 
index 6ee1e39..69ac70b 100644 (file)
@@ -35,9 +35,9 @@ function description(element)
     return element.toString();
 }
 
-function keypressListener(event)
+function keydownListener(event)
 {
-    log("keypress event: [to] " + description(event.target)  + "\n");
+    log("keydown event: [to] " + description(event.target)  + "\n");
 }
 
 function blurListener(event)
@@ -54,7 +54,7 @@ function focusListener(event)
 
 function addEventListeners(element)
 {
-    element.addEventListener('keypress', keypressListener, false);
+    element.addEventListener('keydown', keydownListener, false);
     element.addEventListener('focus', focusListener, false);
     element.addEventListener('blur', blurListener, false);
 }
@@ -109,7 +109,7 @@ function dispatchOptionTab(element, shiftKey)
 {
     var event = document.createEvent("KeyboardEvents");
     var tabKeyIdentifier = "U+0009";
-    event.initKeyboardEvent("keypress", true, true, document.defaultView, tabKeyIdentifier, 0, false, true, shiftKey, false, false);
+    event.initKeyboardEvent("keydown", true, true, document.defaultView, tabKeyIdentifier, 0, false, true, shiftKey, false, false);
     element.dispatchEvent(event);
 }
 
index 6462c62..34a36b5 100644 (file)
@@ -9,15 +9,15 @@ function log(message)
 }
 function test()
 {
-    if (window.layoutTestController)
+    if (window.layoutTestController) {
         layoutTestController.dumpAsText();
 
-    var event = document.createEvent("KeyboardEvent");
-    if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, "f", 0, true, false, false, false, false);
-    else
-        event.initKeyboardEvent("keydown", true, true, document.defaultView, "f", 0, false, true, false, false, false);
-    document.dispatchEvent(event);
+        if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
+            modifier = "ctrlKey";
+        else
+            modifier = "altKey";
+        eventSender.keyDown("f", [modifier]);
+    }
 }
 </script>
 </head>
index a2b786c..675206a 100644 (file)
@@ -51,7 +51,7 @@
                 checkSelection("0");
 
                 log("6) Make sure onChange fires when changing the selection with the keyboard");
-                keyPressOnSelect("sl1", "Down", true, false);
+                keyDownOnSelect("sl1", "downArrow", true, false);
                 checkSelection("0,1");
 
                 log("7) Make sure onChange doesn't fire when setting the select element's value from JS");
                 sl.dispatchEvent(event);
             }
 
-            function keyPressOnSelect(selId, identifier, shift, metaOrCtrl)
+            function keyDownOnSelect(selId, identifier, shift, metaOrCtrl)
             {
-                var meta = false;
-                var ctrl = false;
+                modifiers = [];
+                if (shift)
+                    modifiers[0] = "shiftKey";
                 if (metaOrCtrl) {
                     if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
-                        meta = true;
+                        modifiers[modifiers.length] = "metaKey";
                     else
-                        ctrl = true;
+                        modifiers[modifiers.length] = "controlKey";
                 }
-                var sl = document.getElementById(selId);
-                var event = document.createEvent("KeyboardEvents");
-                event.initKeyboardEvent("keypress", true, true, document.defaultView, identifier, 0, ctrl, false, shift, meta, false);
-                sl.dispatchEvent(event);
+
+                document.getElementById(selId).focus();
+                eventSender.keyDown(identifier, modifiers);
             }
             
             function getSelectedOptions(selId)
index b6b108d..200ef83 100644 (file)
                 testResults(expectedSelectionResults, 2);
                                 
                 // 3) Select one item with the keyboard (no previous selection)
-                keyPressOnSelect("sl3", "Up", false, false);               
+                keyDownOnSelect("sl3", "upArrow", false, false);               
                 expectedSelectionResults = new Array(false, false, false, false, true);
                 testResults(expectedSelectionResults, 3);
 
                 // 4) Select one item with the keyboard (with previous selection)
-                keyPressOnSelect("sl4", "Down", false, false);
+                keyDownOnSelect("sl4", "downArrow", false, false);
                 expectedSelectionResults = new Array(false, false, true, false, false);
                 testResults(expectedSelectionResults, 4);
 
@@ -64,7 +64,7 @@
                 testResults(expectedSelectionResults, 6);
                 
                 // 7) Attempt to select a range with the keyboard
-                keyPressOnSelect("sl7", "Down", true, false);
+                keyDownOnSelect("sl7", "downArrow", true, false);
                 expectedSelectionResults = new Array(false, false, true, false, false);
                 testResults(expectedSelectionResults, 7);
                 
                 testResults(expectedSelectionResults, 9);
                 
                 // 10) Select one item with the keyboard (no previous selection)
-                keyPressOnSelect("sl10", "Up", false, false);
+                keyDownOnSelect("sl10", "upArrow", false, false);
                 expectedSelectionResults = new Array(false, false, false, false, true);
                 testResults(expectedSelectionResults, 10);
                                 
                 // 11) Select one item with the keyboard (with previous selection)
-                keyPressOnSelect("sl11", "Down", false, false);
+                keyDownOnSelect("sl11", "downArrow", false, false);
                 expectedSelectionResults = new Array(false, false, true, false, false);
                 testResults(expectedSelectionResults, 11);
                 
                 testResults(expectedSelectionResults, 13);
                                 
                 // 14) Select a range with the keyboard
-                keyPressOnSelect("sl14", "Down", true, false);
+                keyDownOnSelect("sl14", "downArrow", true, false);
                 expectedSelectionResults = new Array(false, true, true, false, false);
                 testResults(expectedSelectionResults, 14);
                 
                 sl.dispatchEvent(event);
             }
             
-            function keyPressOnSelect(selId, identifier, shift, metaOrCtrl)
+            function keyDownOnSelect(selId, identifier, shift, metaOrCtrl)
             {
-                var meta = false;
-                var ctrl = false;
+                modifiers = [];
+                if (shift)
+                    modifiers[0] = "shiftKey";
                 if (metaOrCtrl) {
                     if (navigator.userAgent.search(/\bMac OS X\b/) != -1)
-                        meta = true;
+                        modifiers[modifiers.length] = "metaKey";
                     else
-                        ctrl = true;
+                        modifiers[modifiers.length] = "controlKey";
                 }
-                var sl = document.getElementById(selId);
-                sl.focus();
-                var event = document.createEvent("KeyboardEvents");
-                event.initKeyboardEvent("keypress", true, true, document.defaultView, identifier, 0, ctrl, false, shift, meta, false);
-                sl.dispatchEvent(event);
+
+                document.getElementById(selId).focus();
+                eventSender.keyDown(identifier, modifiers);
             }
             
             function createSelect(idName, sz, mlt, selIndex, testMsg)
index cc526fe..024af18 100644 (file)
@@ -16,7 +16,7 @@
         tf.focus();
         if (window.layoutTestController) {
             eventSender.keyDown('a');
-            eventSender.keyDown('\n');
+            eventSender.keyDown('\r');
         }
 
     } 
index ca9f710..6139e7e 100644 (file)
@@ -12,7 +12,7 @@ function sendKeyEvent()
 function sendDeleteKeyEvent()
 {
     if (window.eventSender)
-        eventSender.keyDown("\x7F");
+        eventSender.keyDown("delete");
 }
 
 function keyEvent(event)
@@ -33,7 +33,7 @@ function searchEvent(event)
         }
     } else {
         if (window.layoutTestController)
-            layoutTestController.notifyDone();
+            setTimeout("layoutTestController.notifyDone()", 0); // Do it on a timer to avoid Windows DRT hanging.
     }
 }
 
@@ -55,7 +55,7 @@ function startTest()
 
 <p>As of this writing we can't use DOM events to type into a search field, so the test uses the event sender and only runs under DumpRenderTree.</p>
 
-<p><input id="search" type="search" incremental onkeypress="keyEvent(event)" onsearch="searchEvent(event)"></p>
+<p><input id="search" type="search" incremental onkeydown="keyEvent(event)" onsearch="searchEvent(event)"></p>
 
 <div>The two rows below should match.</div>
 <div>0.5 0.4 0.3 0.2 0.2 0</p>
index 1280005..8fad87e 100644 (file)
@@ -15,7 +15,7 @@
                 popup.focus();
 
                 eventSender.keyDown("t", null);
-                eventSender.keyDown("\n", null);
+                eventSender.keyDown("\r", null);
             }
 
             function changed(select)
index 860b684..d6b73e5 100644 (file)
@@ -13,13 +13,12 @@ function submitHandler(e)
 }
 function test()
 {
-    if (window.layoutTestController)
-        layoutTestController.dumpAsText();
-
     document.getElementById("form").onsubmit = submitHandler;
-    var event = document.createEvent("KeyboardEvents");
-    event.initKeyboardEvent("keypress", true, true, document.defaultView, "Enter", 0, false, false, false, false, false);
-    document.getElementById("select").dispatchEvent(event);
+    document.getElementById("select").focus();
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        eventSender.keyDown("\r", []);
+    }
 }
 </script>
 </head>
index 5affe33..8aba54f 100644 (file)
@@ -10,7 +10,7 @@
     function test2() {\r
         document.getElementById('tf').focus();\r
         eventSender.keyDown('a');\r
-        eventSender.keyDown("\n", null);\r
+        eventSender.keyDown("\r", null);\r
         if (window.layoutTestController)\r
             layoutTestController.notifyDone();\r
     }\r
index 8c3056e..123cd1e 100644 (file)
@@ -19,7 +19,7 @@
         {
             var event = document.createEvent('KeyboardEvents');
             var tabKeyIdentifier = 'U+0009';
-            event.initKeyboardEvent('keypress', true, true, document.defaultView, tabKeyIdentifier, 0, false, false, shiftKey, false, false);
+            event.initKeyboardEvent('keydown', true, true, document.defaultView, tabKeyIdentifier, 0, false, false, shiftKey, false, false);
             element.dispatchEvent(event);
         }
 
index bf19cb9..9000c65 100644 (file)
@@ -79,7 +79,7 @@ event type:      keydown
   keyLocation:   0
   modifier keys: c:0 s:0 a:0 m:0
   keyCode:       65
-  charCode:      97
+  charCode:      0
 event type:      keypress
   target:        <body>
   eventPhase:    3
@@ -87,11 +87,23 @@ event type:      keypress
   cancelable:    1
   detail:        0
   view:          OK (document: OK)
-  keyIdentifier: U+0041
+  keyIdentifier: 
   keyLocation:   0
   modifier keys: c:0 s:0 a:0 m:0
   keyCode:       97
   charCode:      97
+event type:      keyup
+  target:        <body>
+  eventPhase:    3
+  bubbles:       1
+  cancelable:    1
+  detail:        0
+  view:          OK (document: OK)
+  keyIdentifier: U+0041
+  keyLocation:   0
+  modifier keys: c:0 s:0 a:0 m:0
+  keyCode:       65
+  charCode:      0
 event type:      keydown
   target:        <body>
   eventPhase:    3
@@ -103,7 +115,7 @@ event type:      keydown
   keyLocation:   0
   modifier keys: c:1 s:0 a:0 m:0
   keyCode:       66
-  charCode:      98
+  charCode:      0
 event type:      keypress
   target:        <body>
   eventPhase:    3
@@ -111,11 +123,23 @@ event type:      keypress
   cancelable:    1
   detail:        0
   view:          OK (document: OK)
-  keyIdentifier: U+0042
+  keyIdentifier: 
   keyLocation:   0
   modifier keys: c:1 s:0 a:0 m:0
   keyCode:       98
   charCode:      98
+event type:      keyup
+  target:        <body>
+  eventPhase:    3
+  bubbles:       1
+  cancelable:    1
+  detail:        0
+  view:          OK (document: OK)
+  keyIdentifier: U+0042
+  keyLocation:   0
+  modifier keys: c:1 s:0 a:0 m:0
+  keyCode:       66
+  charCode:      0
 event type:      keydown
   target:        <body>
   eventPhase:    3
@@ -127,7 +151,7 @@ event type:      keydown
   keyLocation:   0
   modifier keys: c:0 s:1 a:0 m:0
   keyCode:       68
-  charCode:      100
+  charCode:      0
 event type:      keypress
   target:        <body>
   eventPhase:    3
@@ -135,11 +159,23 @@ event type:      keypress
   cancelable:    1
   detail:        0
   view:          OK (document: OK)
-  keyIdentifier: U+0044
+  keyIdentifier: 
   keyLocation:   0
   modifier keys: c:0 s:1 a:0 m:0
   keyCode:       100
   charCode:      100
+event type:      keyup
+  target:        <body>
+  eventPhase:    3
+  bubbles:       1
+  cancelable:    1
+  detail:        0
+  view:          OK (document: OK)
+  keyIdentifier: U+0044
+  keyLocation:   0
+  modifier keys: c:0 s:1 a:0 m:0
+  keyCode:       68
+  charCode:      0
 event type:      keydown
   target:        <body>
   eventPhase:    3
@@ -151,7 +187,7 @@ event type:      keydown
   keyLocation:   0
   modifier keys: c:0 s:0 a:1 m:1
   keyCode:       69
-  charCode:      101
+  charCode:      0
 event type:      keypress
   target:        <body>
   eventPhase:    3
@@ -159,11 +195,23 @@ event type:      keypress
   cancelable:    1
   detail:        0
   view:          OK (document: OK)
-  keyIdentifier: U+0045
+  keyIdentifier: 
   keyLocation:   0
   modifier keys: c:0 s:0 a:1 m:1
   keyCode:       101
   charCode:      101
+event type:      keyup
+  target:        <body>
+  eventPhase:    3
+  bubbles:       1
+  cancelable:    1
+  detail:        0
+  view:          OK (document: OK)
+  keyIdentifier: U+0045
+  keyLocation:   0
+  modifier keys: c:0 s:0 a:1 m:1
+  keyCode:       69
+  charCode:      0
 event type:      mousemove
   target:        <div>
   eventPhase:    3
diff --git a/LayoutTests/platform/win/fast/events/double-dead-char-expected.txt b/LayoutTests/platform/win/fast/events/double-dead-char-expected.txt
new file mode 100644 (file)
index 0000000..7666599
--- /dev/null
@@ -0,0 +1,3 @@
+Test for rdar://problem/5535636: Have to press 4 times instead of 2 times to get the expected result of ^^ with German keyboard.
+
+^^
diff --git a/LayoutTests/platform/win/fast/events/double-dead-char.html b/LayoutTests/platform/win/fast/events/double-dead-char.html
new file mode 100644 (file)
index 0000000..f52d563
--- /dev/null
@@ -0,0 +1,23 @@
+<body>
+    <p>Test for <a href="rdar://problem/5535636">rdar://problem/5535636</a>:
+    Have to press 4 times instead of 2 times to get the expected result of ^^
+    with German keyboard.
+</p>
+<div contenteditable id=ce></div>
+<script>
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        document.getElementById("ce").focus();
+
+        eventSender.dispatchMessage(eventSender.WM_KEYDOWN, 220 /* VK_OEM_5 */, 0x00290001);
+        eventSender.dispatchMessage(eventSender.WM_DEADCHAR, 94 /* '^' */, 0x00290001);
+        eventSender.dispatchMessage(eventSender.WM_KEYUP, 220, 0xc0290001);
+        eventSender.dispatchMessage(eventSender.WM_KEYDOWN, 220, 0x00290001);
+        eventSender.dispatchMessage(eventSender.WM_CHAR, 94, 0x00290001);
+        eventSender.dispatchMessage(eventSender.WM_CHAR, 94, 0x00290001);
+        eventSender.dispatchMessage(eventSender.WM_KEYUP, 220, 0xc0290001);
+    } else
+        document.write("To test manually, switch to German keyboard layout, and press circumflex key two times " +
+            "(on my MBP with Russian physical keyboard, it is located to the left of 1).");
+</script>
+</body>
index ebd5539..a116cc7 100644 (file)
@@ -1,3 +1,130 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        Test: platform/win/fast/events/double-dead-char.html
+
+        * platform/PlatformKeyboardEvent.h:
+        (WebCore::PlatformKeyboardEvent::):
+        (WebCore::PlatformKeyboardEvent::type):
+        (WebCore::PlatformKeyboardEvent::windowsVirtualKeyCode):
+        (WebCore::PlatformKeyboardEvent::setWindowsVirtualKeyCode):
+        (WebCore::PlatformKeyboardEvent::keyIdentifier):
+        (WebCore::PlatformKeyboardEvent::setIsAutoRepeat):
+        Added an explicit type member to differentiate different kinds of events:
+          RawKeyDown == keydown == WM_KEYDOWN
+          KeyUp == keyup == WM_KEYUP
+          Char == keypress == WM_CHAR
+          KeyDown == e.g. NSKeyDown or NSFlagsChanged, used on platforms that have a different model for
+          event processing, and needs to be converted to RawKeyDown (+ Char) for processing in DOM.
+
+        * platform/mac/KeyEventMac.mm:
+        (WebCore::PlatformKeyboardEvent::PlatformKeyboardEvent): Updated for changed data members.
+        Fix Enter (numeric keypad) charCode to match Return, as we check for it from keypress default handlers.
+        (WebCore::windowsKeyCodeForKeyEvent): 
+        (WebCore::isKeyUpEvent): Made it do something closer to what it claims; added a FIXME explaining
+        that it still fails.
+        (WebCore::disambiguateKeyDownEvent): Downgrade from KeyDown to RawKeyDown or Char, removing information that
+        should not be available in those (because it cannot be provided on Windows).
+
+        * platform/win/KeyEventWin.cpp:
+        (WebCore::PlatformKeyboardEvent::PlatformKeyboardEvent): Updated for changed data members.
+        Used standard Windows constants for bit masks instead of our own ones.
+        (WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent): Should never be called on Windows.
+
+        * platform/gtk/KeyEventGtk.cpp:
+        (WebCore::PlatformKeyboardEvent::PlatformKeyboardEvent):
+        (WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent):
+        * platform/qt/PlatformKeyboardEventQt.cpp:
+        (WebCore::PlatformKeyboardEvent::PlatformKeyboardEvent):
+        (WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent):
+        * platform/wx/KeyboardEventWx.cpp:
+        (WebCore::PlatformKeyboardEvent::PlatformKeyboardEvent):
+        (WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent):
+        Updated for cross-platform changes as much as it was possible without appropriate build
+        environments.
+
+        * WebCore.base.exp: Export PlatformKeyboardEvent::disambiguateKeyDownEvent(), used by platforms that need to
+        convert their fancy key events to RawKeyDown/Char pairs. Export Editor::isTextInsertionCommand().
+
+        * bridge/EditorClient.h:
+        Renamed handleKeypress() to handleKeyboardEvent(), as it gets both keydowns and keypresses.
+        Renamed handleInputMethodKeypress() to handleInputMethodKeydown(), as IMs work with raw keydowns.
+
+        * dom/Document.h:
+        * dom/Document.cpp:
+        (WebCore::Document::defaultEventHandler): Moved accesskey processing to EventHandler.
+
+        * dom/KeyboardEvent.h: Added comments describing keyCode/charCode behavior.
+
+        * dom/KeyboardEvent.cpp:
+        (WebCore::eventTypeForKeyboardEventType):
+        (WebCore::KeyboardEvent::KeyboardEvent): Conversion between platform and DOM event types is
+        now straightforward, so scary hacks such as using autorepeat to distinguish types are
+        not needed.
+        (WebCore::KeyboardEvent::keyCode): Added a comment describing other browsers' behavior.
+        (WebCore::KeyboardEvent::charCode): Added a comment describing other browsers' behavior.
+        Changed to a more compatible behavior: raw keydown/keyup events do not and can not have
+        character codes.
+
+        * editing/Editor.h:
+        * editing/Editor.cpp:
+        (WebCore::Editor::isTextInsertionCommand): Is this command actually text input in disguise?
+        (WebCore::Editor::handleKeyboardEvent): Updated for new function names.
+        (WebCore::Editor::handleInputMethodKeydown): Ditto.
+
+        * html/HTMLButtonElement.cpp:
+        (WebCore::HTMLButtonElement::defaultEventHandler): Perform the default action when handling an
+        appropriate event. Enter is processed on keypress (and thus should be checked for via charCode,
+        not keyIdentifier), Space is processed on keydown+keyup! We now match IE in that a button is
+        highlighted when Space is pressed.
+
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::defaultEventHandler):
+        * html/HTMLSelectElement.cpp:
+        (WebCore::HTMLSelectElement::menuListDefaultEventHandler):
+        (WebCore::HTMLSelectElement::listBoxDefaultEventHandler):
+        Made a number of fixes to when default actions take place, similar to HTMLButtonElement ones
+        described above.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::keyEvent): Unless we have a combined KeyDown, just forward the event
+        to the target. Call accesskey handling directly, as it doesn't seem to be part of normal event
+        handling in IE. Also streamlined the code in KeyDown case, thanks to handleInputMethodKeypress()
+        now being handleInputMethodKeydown().
+        (WebCore::EventHandler::handleTextInputEvent): Check that we were not called from keydown.
+        (WebCore::EventHandler::defaultTextInputEventHandler): Removed a call to defaultTabEventHandler,
+        as default tab handling happens when processing keydown.
+        (WebCore::handleAccessKey): Moved from Document, as access keys are processed outside normal
+        event handling. Fixed accesskey processing to use information that's available in a raw keydown
+        event.
+
+        (WebCore::EventHandler::defaultKeyboardEventHandler): Do not ignore keydown; in particular,
+        handle tabs during keydown processing.
+
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::EventHandler::currentKeyboardEvent): Disambiguate KeyDown as RawKeyDown, as this is
+        what callers want.
+
+        * platform/text/PlatformString.h:
+        * platform/text/String.cpp:
+        (WebCore::String::characterStartingAt):
+        * platform/text/StringImpl.cpp:
+        (WebCore::StringImpl::characterStartingAt):
+        * platform/text/StringImpl.h:
+        Added a UChar32 accessor.
+
+        * svg/graphics/SVGImageEmptyClients.h:
+        (WebCore::SVGEmptyEditorClient::handleKeyboardEvent):
+        (WebCore::SVGEmptyEditorClient::handleInputMethodKeydown):
+        Updated for new function names.
+
 2007-12-11  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Adele
index bf87475..0c0d6b9 100644 (file)
@@ -354,7 +354,8 @@ __ZN7WebCore20JavaScriptStatistics24setShouldPrintExceptionsEb
 __ZN7WebCore20JavaScriptStatistics31garbageCollectOnAlternateThreadEb
 __ZN7WebCore20ResourceResponseBase24setExpectedContentLengthEx
 __ZN7WebCore21ContextMenuController16clearContextMenuEv
-__ZN7WebCore21PlatformKeyboardEventC1EP7NSEventb
+__ZN7WebCore21PlatformKeyboardEventC1EP7NSEvent
+__ZN7WebCore21PlatformKeyboardEvent24disambiguateKeyDownEventENS0_4TypeEb
 __ZN7WebCore21WindowsLatin1EncodingEv
 __ZN7WebCore21findEventWithKeyStateEPNS_5EventE
 __ZN7WebCore21isBackForwardLoadTypeENS_13FrameLoadTypeE
@@ -414,6 +415,7 @@ __ZN7WebCore5equalEPKNS_10StringImplES2_
 __ZN7WebCore6Editor10applyStyleEPNS_19CSSStyleDeclarationENS_10EditActionE
 __ZN7WebCore6Editor10insertTextERKNS_6StringEPNS_5EventE
 __ZN7WebCore6Editor11canDHTMLCutEv
+__ZN7WebCore6Editor22isTextInsertionCommandERKNS_12AtomicStringE
 __ZN7WebCore6Editor11execCommandERKNS_12AtomicStringEPNS_5EventE
 __ZN7WebCore6Editor11tryDHTMLCutEv
 __ZN7WebCore6Editor12canDHTMLCopyEv
index fd5a327..802aa4f 100644 (file)
@@ -108,8 +108,8 @@ public:
     virtual void undo() = 0;
     virtual void redo() = 0;
 
-    virtual void handleKeypress(KeyboardEvent*) = 0;
-    virtual void handleInputMethodKeypress(KeyboardEvent*) = 0;
+    virtual void handleKeyboardEvent(KeyboardEvent*) = 0;
+    virtual void handleInputMethodKeydown(KeyboardEvent*) = 0;
     
     virtual void textFieldDidBeginEditing(Element*) = 0;
     virtual void textFieldDidEndEditing(Element*) = 0;
index 1cc5e60..ad917cb 100644 (file)
@@ -2442,32 +2442,6 @@ void Document::handleWindowEvent(Event *evt, bool useCapture)
             (*it)->listener()->handleEvent(evt, true);
 }
 
-
-void Document::defaultEventHandler(Event *evt)
-{
-    // handle accesskey
-    if (evt->type() == keydownEvent) {
-        KeyboardEvent* kevt = static_cast<KeyboardEvent *>(evt);
-#if PLATFORM(MAC)
-        if (kevt->ctrlKey())
-#else
-        if (kevt->altKey())
-#endif
-        {
-            const PlatformKeyboardEvent* ev = kevt->keyEvent();
-            String key = (ev ? ev->unmodifiedText() : kevt->keyIdentifier()).lower();
-            Element* elem = getElementByAccessKey(key);
-            if (elem) {
-                elem->accessKeyAction(false);
-                evt->setDefaultHandled();
-                return;
-            }
-        }
-    }
-
-    ContainerNode::defaultEventHandler(evt);
-}
-
 void Document::setHTMLWindowEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener)
 {
     // If we already have it we don't want removeWindowEventListener to delete it
index dd25e9a..afa690d 100644 (file)
@@ -483,7 +483,6 @@ public:
 
     CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt);
 
-    virtual void defaultEventHandler(Event*);
     void handleWindowEvent(Event*, bool useCapture);
     void setHTMLWindowEventListener(const AtomicString &eventType, PassRefPtr<EventListener>);
     EventListener* getHTMLWindowEventListener(const AtomicString &eventType);
index a9b91c0..8a90ae4 100644 (file)
 #include "config.h"
 #include "KeyboardEvent.h"
 
+#include "Document.h"
+#include "DOMWindow.h"
 #include "EventNames.h"
 #include "PlatformKeyboardEvent.h"
+#include "Settings.h"
 
 namespace WebCore {
 
 using namespace EventNames;
 
+static inline const AtomicString& eventTypeForKeyboardEventType(PlatformKeyboardEvent::Type type)
+{
+    switch (type) {
+        case PlatformKeyboardEvent::KeyUp:
+            return keyupEvent;
+        case PlatformKeyboardEvent::RawKeyDown:
+            return keydownEvent;
+        case PlatformKeyboardEvent::Char:
+            return keypressEvent;
+        case PlatformKeyboardEvent::KeyDown:
+            // The caller should disambiguate the combined event into RawKeyDown or Char events.
+            break;
+    }
+    ASSERT_NOT_REACHED();
+    return keydownEvent;
+}
+
 KeyboardEvent::KeyboardEvent()
     : m_keyEvent(0)
     , m_keyLocation(DOM_KEY_LOCATION_STANDARD)
@@ -38,11 +58,11 @@ KeyboardEvent::KeyboardEvent()
 }
 
 KeyboardEvent::KeyboardEvent(const PlatformKeyboardEvent& key, AbstractView* view)
-    : UIEventWithKeyState(key.isKeyUp() ? keyupEvent : key.isAutoRepeat() ? keypressEvent : keydownEvent,
+    : UIEventWithKeyState(eventTypeForKeyboardEventType(key.type()),
                           true, true, view, 0, key.ctrlKey(), key.altKey(), key.shiftKey(), key.metaKey())
     , m_keyEvent(new PlatformKeyboardEvent(key))
     , m_keyIdentifier(key.keyIdentifier())
-    , m_keyLocation(key.isKeypad() ? DOM_KEY_LOCATION_NUMPAD : DOM_KEY_LOCATION_STANDARD)
+    , m_keyLocation(key.isKeypad() ? DOM_KEY_LOCATION_NUMPAD : DOM_KEY_LOCATION_STANDARD) // FIXME: differentiate right/left, too
     , m_altGraphKey(false)
 {
 }
@@ -96,21 +116,30 @@ bool KeyboardEvent::getModifierState(const String& keyIdentifier) const
 
 int KeyboardEvent::keyCode() const
 {
+    // IE: virtual key code for keyup/keydown, character code for keypress
+    // Firefox: virtual key code for keyup/keydown, zero for keypress
+    // We match IE.
     if (!m_keyEvent)
         return 0;
     if (type() == keydownEvent || type() == keyupEvent)
-        return m_keyEvent->WindowsKeyCode();
+        return m_keyEvent->windowsVirtualKeyCode();
     return charCode();
 }
 
 int KeyboardEvent::charCode() const
 {
-    if (!m_keyEvent)
+    // IE: not supported
+    // Firefox: 0 for keydown/keyup events, character code for keypress
+    // We match Firefox, unless in Dashboard compatibility mode, where we always return the character code.
+    bool dashboardCompatibilityMode = false;
+    if (view())
+        if (Settings* settings = view()->document()->settings())
+            dashboardCompatibilityMode = settings->usesDashboardBackwardCompatibilityMode();
+
+    if (!m_keyEvent || (type() != keypressEvent && !dashboardCompatibilityMode))
         return 0;
     String text = m_keyEvent->text();
-    if (text.length() != 1)
-        return 0;
-    return text[0];
+    return static_cast<int>(text.characterStartingAt(0));
 }
 
 bool KeyboardEvent::isKeyboardEvent() const
index bf1141a..0867397 100644 (file)
@@ -70,8 +70,8 @@ namespace WebCore {
     
         const PlatformKeyboardEvent* keyEvent() const { return m_keyEvent; }
 
-        int keyCode() const; // key code for keydown and keyup, character for other events
-        int charCode() const;
+        int keyCode() const; // key code for keydown and keyup, character for keypress
+        int charCode() const; // character code for keypress, 0 for keydown and keyup
     
         virtual bool isKeyboardEvent() const;
         virtual int which() const;
index de27ff5..5e7db13 100644 (file)
@@ -111,18 +111,18 @@ EditorClient* Editor::client() const
     return 0;
 }
 
-void Editor::handleKeypress(KeyboardEvent* event)
+void Editor::handleKeyboardEvent(KeyboardEvent* event)
 {
     if (EditorClient* c = client())
         if (selectionForEvent(m_frame, event).isContentEditable())
-            c->handleKeypress(event);
+            c->handleKeyboardEvent(event);
 }
 
-void Editor::handleInputMethodKeypress(KeyboardEvent* event)
+void Editor::handleInputMethodKeydown(KeyboardEvent* event)
 {
     if (EditorClient* c = client())
         if (selectionForEvent(m_frame, event).isContentEditable())
-            c->handleInputMethodKeypress(event);
+            c->handleInputMethodKeydown(event);
 }
 
 bool Editor::canEdit() const
@@ -1326,6 +1326,14 @@ void Editor::clear()
     m_customCompositionUnderlines.clear();
 }
 
+bool Editor::isTextInsertionCommand(const AtomicString& command)
+{
+    return command == "InsertBacktab"
+        || command == "InsertTab"
+        || command == "InsertLineBreak"
+        || command == "InsertNewline";
+}
+
 bool Editor::execCommand(const AtomicString& command, Event* triggeringEvent)
 {
     if (!m_frame->document())
index 59527d2..a8262b2 100644 (file)
@@ -77,8 +77,8 @@ public:
     DeleteButtonController* deleteButtonController() const { return m_deleteButtonController.get(); }
     EditCommand* lastEditCommand() { return m_lastEditCommand.get(); }
 
-    void handleKeypress(KeyboardEvent*);
-    void handleInputMethodKeypress(KeyboardEvent*);
+    void handleKeyboardEvent(KeyboardEvent*);
+    void handleInputMethodKeydown(KeyboardEvent*);
 
     bool canEdit() const;
     bool canEditRichly() const;
@@ -156,7 +156,9 @@ public:
     bool selectionStartHasStyle(CSSStyleDeclaration*) const;
 
     bool clientIsEditable() const;
-    
+
+    static bool isTextInsertionCommand(const AtomicString&);
+
     bool execCommand(const AtomicString&, Event* triggeringEvent = 0);
     
     bool insertText(const String&, Event* triggeringEvent);
index 3dd3439..6e57540 100644 (file)
@@ -91,11 +91,29 @@ void HTMLButtonElement::defaultEventHandler(Event* evt)
             form()->reset();
     }
 
-    if (evt->type() == keypressEvent && evt->isKeyboardEvent()) {
-        String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
-
-        if (key == "Enter" || key == "U+0020") {
-            dispatchSimulatedClick(evt);
+    if (evt->isKeyboardEvent()) {
+        if (evt->type() == keydownEvent && static_cast<KeyboardEvent*>(evt)->keyIdentifier() == "U+0020") {
+            setActive(true, true);
+            // No setDefaultHandled() - IE dispatches a keypress in this case.
+            return;
+        }
+        if (evt->type() == keypressEvent) {
+            switch (static_cast<KeyboardEvent*>(evt)->charCode()) {
+                case '\r':
+                    dispatchSimulatedClick(evt);
+                    evt->setDefaultHandled();
+                    return;
+                case ' ':
+                    // Prevent scrolling down the page.
+                    evt->setDefaultHandled();
+                    return;
+                default:
+                    break;
+            }
+        }
+        if (evt->type() == keyupEvent && static_cast<KeyboardEvent*>(evt)->keyIdentifier() == "U+0020") {
+            if (active())
+                dispatchSimulatedClick(evt);
             evt->setDefaultHandled();
             return;
         }
index 9b367bb..ba9f860 100644 (file)
@@ -1122,8 +1122,7 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
         }
     }
 
-    // Before calling the base class defaultEventHandler, which will call handleKeypress, call doTextFieldCommandFromEvent.
-    if (isTextField() && evt->type() == keypressEvent && evt->isKeyboardEvent() && focused() && document()->frame()
+    if (isTextField() && evt->type() == keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame()
                 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
         evt->setDefaultHandled();
         return;
@@ -1173,37 +1172,9 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
     if (evt->type() == keypressEvent && evt->isKeyboardEvent()) {
         bool clickElement = false;
 
-        String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
-
-        if (key == "U+0020") {
-            switch (inputType()) {
-                case BUTTON:
-                case CHECKBOX:
-                case FILE:
-                case IMAGE:
-                case RESET:
-                case SUBMIT:
-                    // Simulate mouse click for spacebar for these types of elements.
-                    // The AppKit already does this for some, but not all, of them.
-                    clickElement = true;
-                    break;
-                case RADIO:
-                    // If an unselected radio is tabbed into (because the entire group has nothing
-                    // checked, or because of some explicit .focus() call), then allow space to check it.
-                    if (!checked())
-                        clickElement = true;
-                    break;
-                case HIDDEN:
-                case ISINDEX:
-                case PASSWORD:
-                case RANGE:
-                case SEARCH:
-                case TEXT:
-                    break;
-            }
-        }
+        int charCode = static_cast<KeyboardEvent*>(evt)->charCode();
 
-        if (key == "Enter") {
+        if (charCode == '\r') {
             switch (inputType()) {
                 case CHECKBOX:
                 case HIDDEN:
@@ -1226,6 +1197,48 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
                 case RADIO:
                     break; // Don't do anything for enter on a radio button.
             }
+        } else if (charCode == ' ') {
+            switch (inputType()) {
+                case BUTTON:
+                case CHECKBOX:
+                case FILE:
+                case IMAGE:
+                case RESET:
+                case SUBMIT:
+                case RADIO:
+                    // Prevent scrolling down the page.
+                    evt->setDefaultHandled();
+                    return;
+                default:
+                    break;
+            }
+        }
+
+        if (clickElement) {
+            dispatchSimulatedClick(evt);
+            evt->setDefaultHandled();
+            return;
+        }
+    }
+
+    if (evt->type() == keydownEvent && evt->isKeyboardEvent()) {
+        String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
+
+        if (key == "U+0020") {
+            switch (inputType()) {
+                case BUTTON:
+                case CHECKBOX:
+                case FILE:
+                case IMAGE:
+                case RESET:
+                case SUBMIT:
+                case RADIO:
+                    setActive(true, true);
+                    // No setDefaultHandled() - IE dispatches a keypress in this case.
+                    return;
+                default:
+                    break;
+            }
         }
 
         if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
@@ -1262,9 +1275,44 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
                 }
             }
         }
+    }
+
+    if (evt->type() == keyupEvent && evt->isKeyboardEvent()) {
+        bool clickElement = false;
+
+        String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
+
+        if (key == "U+0020") {
+            switch (inputType()) {
+                case BUTTON:
+                case CHECKBOX:
+                case FILE:
+                case IMAGE:
+                case RESET:
+                case SUBMIT:
+                    // Simulate mouse click for spacebar for these types of elements.
+                    // The AppKit already does this for some, but not all, of them.
+                    clickElement = true;
+                    break;
+                case RADIO:
+                    // If an unselected radio is tabbed into (because the entire group has nothing
+                    // checked, or because of some explicit .focus() call), then allow space to check it.
+                    if (!checked())
+                        clickElement = true;
+                    break;
+                case HIDDEN:
+                case ISINDEX:
+                case PASSWORD:
+                case RANGE:
+                case SEARCH:
+                case TEXT:
+                    break;
+            }
+        }
 
         if (clickElement) {
-            dispatchSimulatedClick(evt);
+            if (active())
+                dispatchSimulatedClick(evt);
             evt->setDefaultHandled();
             return;
         }        
index e01c81e..50c4e74 100644 (file)
@@ -619,21 +619,13 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
 {
     RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer());
 
-    // Use key press event here since sending simulated mouse events
-    // on key down blocks the proper sending of the key press event.
-    if (evt->type() == keypressEvent) {
+    if (evt->type() == keydownEvent) {
         if (!renderer() || !evt->isKeyboardEvent())
             return;
         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
         bool handled = false;
 #if ARROW_KEYS_POP_MENU
-        if (keyIdentifier == "Enter") {
-            menuListOnChange();
-            if (form())
-                form()->submitClick(evt);
-            handled = true;
-        }
-        if (keyIdentifier == "Down" || keyIdentifier == "Up" || keyIdentifier == "U+0020") {
+        if (keyIdentifier == "Down" || keyIdentifier == "Up") {
             focus();
             // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex,
             // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
@@ -661,7 +653,37 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
             if (listIndex >= 0 && listIndex < size)
                 setSelectedIndex(listToOptionIndex(listIndex));
             handled = true;
-        } else if (keyIdentifier == "Enter") {
+        }
+#endif
+        if (handled)
+            evt->setDefaultHandled();
+    }
+
+    // Use key press event here since sending simulated mouse events
+    // on key down blocks the proper sending of the key press event.
+    if (evt->type() == keypressEvent) {
+        if (!renderer() || !evt->isKeyboardEvent())
+            return;
+        int keyCode = static_cast<KeyboardEvent*>(evt)->keyCode();
+        bool handled = false;
+#if ARROW_KEYS_POP_MENU
+        if (keyCode == ' ') {
+            focus();
+            // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex,
+            // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
+            saveLastSelection();
+            menuList->showPopup();
+            handled = true;
+        }
+        if (keyCode == '\r') {
+            menuListOnChange();
+            if (form())
+                form()->submitClick(evt);
+            handled = true;
+        }
+#else
+        int listIndex = optionToListIndex(selectedIndex());
+        if (keyCode == '\r') {
             // listIndex should already be selected, but this will fire the onchange handler.
             setSelectedIndex(listToOptionIndex(listIndex), true, true);
             handled = true;
@@ -669,8 +691,8 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
 #endif
         if (handled)
             evt->setDefaultHandled();
-
     }
+
     if (evt->type() == mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
         focus();
         if (menuList->popupIsVisible())
@@ -750,18 +772,11 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
     } else if (evt->type() == mouseupEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton && document()->frame()->eventHandler()->autoscrollRenderer() != renderer())
         // This makes sure we fire onChange for a single click.  For drag selection, onChange will fire when the autoscroll timer stops.
         listBoxOnChange();
-    else if (evt->type() == keypressEvent) {
+    else if (evt->type() == keydownEvent) {
         if (!evt->isKeyboardEvent())
             return;
         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
 
-        if (keyIdentifier == "Enter") {
-            if (form())
-                form()->submitClick(evt);
-            evt->setDefaultHandled();
-            return;
-        }
-
         int endIndex = 0;        
         if (m_activeSelectionEndIndex < 0) {
             // Initialize the end index
@@ -798,6 +813,17 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
             listBoxOnChange();
             evt->setDefaultHandled();
         }
+    } else if (evt->type() == keypressEvent) {
+        if (!evt->isKeyboardEvent())
+            return;
+        int keyCode = static_cast<KeyboardEvent*>(evt)->keyCode();
+
+        if (keyCode == '\r') {
+            if (form())
+                form()->submitClick(evt);
+            evt->setDefaultHandled();
+            return;
+        }
     }
 }
 
index 50ba0f1..98613eb 100644 (file)
@@ -1464,75 +1464,123 @@ static EventTargetNode* eventTargetNodeForDocument(Document* doc)
     return EventTargetNodeCast(node);
 }
 
+static bool handleAccessKey(Document* document, const PlatformKeyboardEvent& evt)
+{
+#if PLATFORM(MAC)
+    if (evt.ctrlKey())
+#else
+    if (evt.altKey())
+#endif
+    {
+        String key = evt.unmodifiedText();
+        Element* elem = document->getElementByAccessKey(key.lower());
+        if (elem) {
+            elem->accessKeyAction(false);
+            return true;
+        }
+    }
+
+    return false;
+}
 
 bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
 {
     // Check for cases where we are too early for events -- possible unmatched key up
     // from pressing return in the location bar.
-    EventTargetNode* node = eventTargetNodeForDocument(m_frame->document());
+    RefPtr<EventTargetNode> node = eventTargetNodeForDocument(m_frame->document());
     if (!node)
         return false;
-        
-    if (initialKeyEvent.isKeyUp())
-        return !node->dispatchKeyEvent(initialKeyEvent);
-        
+
+    // FIXME: what is this doing here, in keyboard event handler?
     m_frame->loader()->resetMultipleFormSubmissionProtection();
+
+    // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
+    // On Windows, we process them before dispatching keypress event (and rely on WebKit to not drop WM_SYSCHAR events when a keydown representing WM_SYSKEYDOWN is canceled).
+    // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict with access keys.
+    // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
+    bool matchedAnAccessKey = false;
+    if (initialKeyEvent.type() == PlatformKeyboardEvent::Char) {
+        if (handleAccessKey(m_frame->document(), initialKeyEvent))
+            return true;
+    } else if (initialKeyEvent.type() == PlatformKeyboardEvent::KeyDown)
+        matchedAnAccessKey = handleAccessKey(m_frame->document(), initialKeyEvent);
+
+    // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
+    if (initialKeyEvent.type() == PlatformKeyboardEvent::KeyUp || initialKeyEvent.type() == PlatformKeyboardEvent::Char)
+        return !node->dispatchKeyEvent(initialKeyEvent);
+
+    bool dashboardCompatibilityMode = false;
+    if (Settings* settings = node->document()->settings())
+        dashboardCompatibilityMode = settings->usesDashboardBackwardCompatibilityMode();
+
+    ExceptionCode ec;
+    PlatformKeyboardEvent keyDownEvent = initialKeyEvent;    
+    if (keyDownEvent.type() != PlatformKeyboardEvent::RawKeyDown)
+        keyDownEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown, dashboardCompatibilityMode);
+    RefPtr<KeyboardEvent> keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView());
+    if (matchedAnAccessKey)
+        keydown->setDefaultPrevented(true);
+    keydown->setTarget(node);
+
+    if (initialKeyEvent.type() == PlatformKeyboardEvent::RawKeyDown) {
+        node->dispatchEvent(keydown, ec, true);
+        return keydown->defaultHandled() || keydown->defaultPrevented();
+    }
+
+    // Run input method in advance of DOM event handling.  This may result in the IM
+    // modifying the page prior the keydown event, but this behaviour is necessary
+    // in order to match IE:
+    // 1. preventing default handling of keydown and keypress events has no effect on IM input;
+    // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
+    m_frame->editor()->handleInputMethodKeydown(keydown.get());
+    
+    bool handledByInputMethod = keydown->defaultHandled();
+    
+    if (handledByInputMethod) {
+        keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
+        keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView());
+        keydown->setTarget(node);
+        keydown->setDefaultHandled();
+    }
+
+    node->dispatchEvent(keydown, ec, true);
+    bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented();
+    if (handledByInputMethod || (keydownResult && !dashboardCompatibilityMode) || initialKeyEvent.text().isEmpty())
+        return keydownResult;
     
-    // Prepare the keyPress in advance of the keyDown so we can fire the input method
-    // in advance of keyDown
-    PlatformKeyboardEvent keyPressEvent = initialKeyEvent;    
-    keyPressEvent.setIsAutoRepeat(true);
+    // Focus may have changed during keydown handling, so refetch node.
+    // But if we are dispatching a fake Dashboard compatibility keypress, then we pretend that the keypress happened on the original node.
+    if (!keydownResult) {
+        node = eventTargetNodeForDocument(m_frame->document());
+        if (!node)
+            return false;
+    }
+
+    PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
+    keyPressEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::Char, dashboardCompatibilityMode);
     RefPtr<KeyboardEvent> keypress = new KeyboardEvent(keyPressEvent, m_frame->document()->defaultView());
     keypress->setTarget(node);
-    
-    // Run input method in advance of DOM event handling.  This may result in the IM
-    // modifying the page prior the keydown event, however this behaviour is necessary
-    // in order to match IE
-    m_frame->editor()->handleInputMethodKeypress(keypress.get());
-    
-    bool handledByInputMethod = keypress->defaultHandled();
-    
-    PlatformKeyboardEvent keyDownEvent = initialKeyEvent; 
-    
-    if (handledByInputMethod) 
-        keyDownEvent.setWindowsKeyCode(CompositionEventKeyCode);
-        
-    // We always send keyDown and keyPress for all events, including autorepeat keys
-    keyDownEvent.setIsAutoRepeat(false);
-    
-    bool result = !node->dispatchKeyEvent(keyDownEvent);
-    
-    // Focus may have change during the keyDown handling, so refetch node
-    node = eventTargetNodeForDocument(m_frame->document());
-    if (!node)
-        return result;
-    
-    if (keypress->defaultHandled())
-        return true;
-    
-    if (handledByInputMethod || initialKeyEvent.isModifierKeyPress())
-        return result;
-    
-    // If the default handling has been prevented on the keydown, we prevent it on
-    // the keypress as well
-    if (result)
-        keypress->setDefaultHandled();
-    
-    ExceptionCode ec;
+    if (keydownResult)
+        keypress->setDefaultPrevented(true);
     node->dispatchEvent(keypress, ec, true);
-    
-    return result || keypress->defaultHandled() || keypress->defaultPrevented();
+
+    return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
 }
 
 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
 {
-   if (event->type() == keypressEvent) {
-        m_frame->editor()->handleKeypress(event);
+   if (event->type() == keydownEvent) {
+        m_frame->editor()->handleKeyboardEvent(event);
         if (event->defaultHandled())
             return;
         if (event->keyIdentifier() == "U+0009")
             defaultTabEventHandler(event, false);
-    }
+   }
+   if (event->type() == keypressEvent) {
+        m_frame->editor()->handleKeyboardEvent(event);
+        if (event->defaultHandled())
+            return;
+   }
 }
 
 bool EventHandler::dragHysteresisExceeded(const FloatPoint& floatDragViewportLocation) const
@@ -1724,6 +1772,12 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve
 {
     if (!m_frame)
         return false;
+#ifndef NDEBUG
+    // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline),
+    // and avoid dispatching text input events from keydown default handlers.
+    if (underlyingEvent && underlyingEvent->isKeyboardEvent())
+        ASSERT(static_cast<KeyboardEvent*>(underlyingEvent)->type() == keypressEvent);
+#endif
     EventTarget* target;
     if (underlyingEvent)
         target = underlyingEvent->target();
@@ -1762,11 +1816,6 @@ bool EventHandler::tabsToLinks(KeyboardEvent* event) const
 void EventHandler::defaultTextInputEventHandler(TextEvent* event)
 {
     String data = event->data();
-    if (data == "\t") {
-        defaultTabEventHandler(event, event->isBackTab());
-        if (event->defaultHandled())
-            return;
-    }
     if (data == "\n") {
         if (event->isLineBreak()) {
             if (m_frame->editor()->insertLineBreak())
index ac91b2c..28eec96 100644 (file)
@@ -87,7 +87,11 @@ PassRefPtr<KeyboardEvent> EventHandler::currentKeyboardEvent() const
     if (!event)
         return 0;
     switch ([event type]) {
-        case NSKeyDown:
+        case NSKeyDown: {
+            PlatformKeyboardEvent platformEvent(event);
+            platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
+            return new KeyboardEvent(platformEvent, m_frame->document() ? m_frame->document()->defaultView() : 0);
+        }
         case NSKeyUp:
             return new KeyboardEvent(event, m_frame->document() ? m_frame->document()->defaultView() : 0);
         default:
index 2ae7c8e..368bfe9 100644 (file)
@@ -60,30 +60,59 @@ namespace WebCore {
 
     class PlatformKeyboardEvent {
     public:
+        enum Type {
+            // KeyDown is sent by platforms such as Mac OS X, gtk and Qt, and has information about both physical pressed key, and its translation.
+            // For DOM processing, it needs to be disambiguated as RawKeyDown or Char event.
+            KeyDown,
+
+            // KeyUp is sent by all platforms.
+            KeyUp,
+
+            // These events are sent by platforms such as Windows and wxWidgets. RawKeyDown only has information about a physical key, and Char
+            // only has information about a character it was translated into.
+            RawKeyDown,
+            Char
+        };
+
+        Type type() const { return m_type; }
+        void disambiguateKeyDownEvent(Type, bool dashboardCompatibilityMode = false); // Only used on platforms that need it, i.e. those that generate KeyDown events.
+
+        // Text as as generated by processing a virtual key code with a keyboard layout
+        // (in most cases, just a character code, but the layout can emit several
+        // characters in a single keypress event on some platforms).
+        // This may bear no resemblance to the ultimately inserted text if an input method
+        // processes the input.
+        // Will be null for KeyUp and RawKeyDown events.
         String text() const { return m_text; }
+
+        // Text that would have been generated by the keyboard if no modifiers were pressed
+        // (except for Shift); useful for shortcut (accelerator) key handling.
+        // Otherwise, same as text().
         String unmodifiedText() const { return m_unmodifiedText; }
+
+        // Most compatible Windows virtual key code associated with the event.
+        // Zero for Char events.
+        int windowsVirtualKeyCode() const { return m_windowsVirtualKeyCode; }
+        void setWindowsVirtualKeyCode(int code) { m_windowsVirtualKeyCode = code; }
+
         String keyIdentifier() const { return m_keyIdentifier; }
-        bool isKeyUp() const { return m_isKeyUp; }
         bool isAutoRepeat() const { return m_autoRepeat; }
         void setIsAutoRepeat(bool in) { m_autoRepeat = in; }
-        int WindowsKeyCode() const { return m_WindowsKeyCode; }
-        void setWindowsKeyCode(int code) { m_WindowsKeyCode = code; }
         bool isKeypad() const { return m_isKeypad; }
         bool shiftKey() const { return m_shiftKey; }
         bool ctrlKey() const { return m_ctrlKey; }
         bool altKey() const { return m_altKey; }
         bool metaKey() const { return m_metaKey; }
-        bool isModifierKeyPress() const { return m_isModifierKeyPress; }
 
         static bool currentCapsLockState();
 
 #if PLATFORM(MAC)
-        PlatformKeyboardEvent(NSEvent*, bool forceAutoRepeat = false);
+        PlatformKeyboardEvent(NSEvent*);
         NSEvent* macEvent() const { return m_macEvent.get(); }
 #endif
 
 #if PLATFORM(WIN)
-        PlatformKeyboardEvent(HWND, WPARAM, LPARAM, UChar, bool);
+        PlatformKeyboardEvent(HWND, WPARAM, LPARAM, Type, bool);
         bool isSystemKey() const { return m_isSystemKey; }
 #endif
 
@@ -97,28 +126,21 @@ namespace WebCore {
 
 #if PLATFORM(WX)
         PlatformKeyboardEvent(wxKeyEvent&);
-        bool isWxCharEvent() const { return m_isWxCharEvent; }
 #endif
 
     private:
+        Type m_type;
         String m_text;
         String m_unmodifiedText;
         String m_keyIdentifier;
-        bool m_isKeyUp;
         bool m_autoRepeat;
-        int m_WindowsKeyCode;
+        int m_windowsVirtualKeyCode;
         bool m_isKeypad;
         bool m_shiftKey;
         bool m_ctrlKey;
         bool m_altKey;
         bool m_metaKey;
-        
-        // A control key event -- eg. keydown/up for shift, ctrl, alt, and meta -- needs
-        // a flag to indicate that we should not generate a keyPress event.
-        bool m_isModifierKeyPress;
-#if PLATFORM(WX)
-        bool m_isWxCharEvent;
-#endif
+
 #if PLATFORM(MAC)
         RetainPtr<NSEvent> m_macEvent;
 #endif
index 1b77ed6..001da55 100644 (file)
@@ -474,21 +474,35 @@ static inline String singleCharacterString(guint val)
 }
 
 PlatformKeyboardEvent::PlatformKeyboardEvent(GdkEventKey* event)
-    : m_text(singleCharacterString(event->keyval))
+    : m_type((event->type == GDK_KEY_RELEASE) ? KeyUp : KeyDown)
+    , m_text(singleCharacterString(event->keyval))
     , m_unmodifiedText(singleCharacterString(event->keyval))
     , m_keyIdentifier(keyIdentifierForGdkKeyCode(event->keyval))
-    , m_isKeyUp(event->type == GDK_KEY_RELEASE)
     , m_autoRepeat(false)
-    , m_WindowsKeyCode(windowsKeyCodeForKeyEvent(event->keyval))
+    , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event->keyval))
     , m_isKeypad(false)
     , m_shiftKey((event->state & GDK_SHIFT_MASK) || (event->keyval == GDK_3270_BackTab))
     , m_ctrlKey(event->state & GDK_CONTROL_MASK)
     , m_altKey(event->state & GDK_MOD1_MASK)
     , m_metaKey(event->state & GDK_MOD2_MASK)
-    , m_isModifierKeyPress(false)
 {
 }
 
+void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool)
+{
+    // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
+    ASSERT(m_type == KeyDown);
+    m_type = type;
+
+    if (type == RawKeyDown) {
+        m_text = String();
+        m_unmodifiedText = String();
+    } else {
+        m_keyIdentifier = String();
+        m_windowsVirtualKeyCode = 0;
+    }
+}
+
 bool PlatformKeyboardEvent::currentCapsLockState()
 {
     notImplemented();
index 3e17335..c44c4d7 100644 (file)
@@ -393,7 +393,7 @@ static bool isKeypadEvent(NSEvent* event)
      return false;
 }
 
-static int WindowsKeyCodeForKeyEvent(NSEvent* event)
+static int windowsKeyCodeForKeyEvent(NSEvent* event)
 {
     switch ([event keyCode]) {
         // VK_TAB (09) TAB key
@@ -764,7 +764,9 @@ static int WindowsKeyCodeForKeyEvent(NSEvent* event)
 static inline bool isKeyUpEvent(NSEvent *event)
 {
     if ([event type] != NSFlagsChanged)
-        return false;
+        return [event type] == NSKeyUp;
+    // FIXME: This logic fails if the user presses both Shift keys at once, for example:
+    // we treat releasing one of them as keyDown.
     switch ([event keyCode]) {
         case 54: // Right Command
         case 55: // Left Command
@@ -805,34 +807,59 @@ static inline String unmodifiedTextFromEvent(NSEvent* event)
         return "";
     return [event charactersIgnoringModifiers];
 }
-    
-PlatformKeyboardEvent::PlatformKeyboardEvent(NSEvent *event, bool forceAutoRepeat)
-    : m_text(textFromEvent(event))
+
+PlatformKeyboardEvent::PlatformKeyboardEvent(NSEvent *event)
+    : m_type(isKeyUpEvent(event) ? PlatformKeyboardEvent::KeyUp : PlatformKeyboardEvent::KeyDown)
+    , m_text(textFromEvent(event))
     , m_unmodifiedText(unmodifiedTextFromEvent(event))
     , m_keyIdentifier(keyIdentifierForKeyEvent(event))
-    , m_isKeyUp([event type] == NSKeyUp || isKeyUpEvent(event))
-    , m_autoRepeat(([event type] != NSFlagsChanged) && (forceAutoRepeat || [event isARepeat]))
-    , m_WindowsKeyCode(WindowsKeyCodeForKeyEvent(event))
+    , m_autoRepeat(([event type] != NSFlagsChanged) && [event isARepeat])
+    , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event))
     , m_isKeypad(isKeypadEvent(event))
     , m_shiftKey([event modifierFlags] & NSShiftKeyMask)
     , m_ctrlKey([event modifierFlags] & NSControlKeyMask)
     , m_altKey([event modifierFlags] & NSAlternateKeyMask)
     , m_metaKey([event modifierFlags] & NSCommandKeyMask)
-    , m_isModifierKeyPress([event type] == NSFlagsChanged)
     , m_macEvent(event)
 {
+    // Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter.
+    if (m_windowsVirtualKeyCode == '\r') {
+        m_text = "\r";
+        m_unmodifiedText = "\r";
+    }
+
+    // The adjustments below are only needed in Dashboard compatibility mode, but we cannot tell what mode we are in from here.
+
     // Turn 0x7F into 8, because backspace needs to always be 8.
     if (m_text == "\x7F")
         m_text = "\x8";
     if (m_unmodifiedText == "\x7F")
         m_unmodifiedText = "\x8";
     // Always use 9 for tab -- we don't want to use AppKit's different character for shift-tab.
-    if (m_WindowsKeyCode == 9) {
+    if (m_windowsVirtualKeyCode == 9) {
         m_text = "\x9";
         m_unmodifiedText = "\x9";
     }
 }
 
+void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool dashboardCompatibilityMode)
+{
+    // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
+    ASSERT(m_type == KeyDown);
+    ASSERT(type == RawKeyDown || type == Char);
+    m_type = type;
+    if (dashboardCompatibilityMode)
+        return;
+
+    if (type == RawKeyDown) {
+        m_text = String();
+        m_unmodifiedText = String();
+    } else {
+        m_keyIdentifier = String();
+        m_windowsVirtualKeyCode = 0;
+    }
+}
+
 bool PlatformKeyboardEvent::currentCapsLockState()
 {
     return GetCurrentKeyModifiers() & alphaLock;
index 9170edf..5a54d8a 100644 (file)
@@ -435,19 +435,34 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode)
 PlatformKeyboardEvent::PlatformKeyboardEvent(QKeyEvent* event)
 {
     const int state = event->modifiers();
+    m_type = (event->type() == QEvent::KeyRelease) ? KeyUp : KeyDown;
     m_text = event->text();
     m_unmodifiedText = event->text(); // FIXME: not correct
     m_keyIdentifier = keyIdentifierForQtKeyCode(event->key());
-    m_isKeyUp = (event->type() == QEvent::KeyRelease);
     m_autoRepeat = event->isAutoRepeat();
     m_ctrlKey = (state & Qt::ControlModifier) != 0;
     m_altKey = (state & Qt::AltModifier) != 0;
     m_metaKey = (state & Qt::MetaModifier) != 0;    
-    m_WindowsKeyCode = windowsKeyCodeForKeyEvent(event->key());
+    m_windowsVirtualKeyCode = windowsKeyCodeForKeyEvent(event->key());
     m_isKeypad = (state & Qt::KeypadModifier) != 0;
     m_shiftKey = (state & Qt::ShiftModifier) != 0 || event->key() == Qt::Key_Backtab; // Simulate Shift+Tab with Key_Backtab
 }
 
+void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool)
+{
+    // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
+    ASSERT(m_type == KeyDown);
+    m_type = type;
+
+    if (type == RawKeyDown) {
+        m_text = String();
+        m_unmodifiedText = String();
+    } else {
+        m_keyIdentifier = String();
+        m_windowsVirtualKeyCode = 0;
+    }
+}
+
 bool PlatformKeyboardEvent::currentCapsLockState()
 {
     notImplemented();
index 48a8ade..4a9ac0d 100644 (file)
@@ -74,7 +74,8 @@ public:
     const UChar* characters() const;
     const UChar* charactersWithNullTermination();
     
-    UChar operator[](unsigned i) const; // if i >= length(), returns 0
+    UChar operator[](unsigned i) const; // if i >= length(), returns 0    
+    UChar32 characterStartingAt(unsigned) const; // Ditto.
     
     bool contains(UChar c) const { return find(c) != -1; }
     bool contains(const char* str, bool caseSensitive = true) const { return find(str, 0, caseSensitive) != -1; }
index a4a33f8..bf99453 100644 (file)
@@ -175,6 +175,12 @@ UChar String::operator[](unsigned i) const
     return m_impl->characters()[i];
 }
 
+UChar32 String::characterStartingAt(unsigned i) const
+{
+    if (!m_impl || i >= m_impl->length())
+        return 0;
+    return m_impl->characterStartingAt(i);
+}
 unsigned String::length() const
 {
     if (!m_impl)
index 6f4fa64..4c9133e 100644 (file)
@@ -271,6 +271,15 @@ StringImpl* StringImpl::substring(unsigned pos, unsigned len)
     return new StringImpl(m_data + pos, len);
 }
 
+UChar32 StringImpl::characterStartingAt(unsigned i) const
+{
+    if (U16_IS_SINGLE(m_data[i]))
+        return m_data[i];
+    if (i + 1 < m_length && U16_IS_LEAD(m_data[i]) && U16_IS_TRAIL(m_data[i + 1]))
+        return U16_GET_SUPPLEMENTARY(m_data[i], m_data[i + 1]);
+    return 0;
+}
+
 static Length parseLength(const UChar* m_data, unsigned int m_length)
 {
     if (m_length == 0)
index 41d3315..d770012 100644 (file)
@@ -93,6 +93,7 @@ public:
     StringImpl* substring(unsigned pos, unsigned len = UINT_MAX);
 
     UChar operator[](int pos) const { return m_data[pos]; }
+    UChar32 characterStartingAt(unsigned) const;
 
     Length toLength() const;
 
index 5ef6bc0..2434cd1 100644 (file)
@@ -33,10 +33,6 @@ using namespace WTF;
 
 namespace WebCore {
 
-static const unsigned REPEAT_COUNT_MASK = 0x0000FFFF;
-static const unsigned NEW_RELEASE_STATE_MASK = 0x80000000;
-static const unsigned PREVIOUS_DOWN_STATE_MASK = 0x40000000;
-
 static const unsigned short HIGH_BIT_MASK_SHORT = 0x8000;
 
 // FIXME: This is incomplete. We could change this to mirror
@@ -148,23 +144,28 @@ static String keyIdentifierForWindowsKeyCode(unsigned short keyCode)
 
 static inline String singleCharacterString(UChar c) { return String(&c, 1); }
 
-PlatformKeyboardEvent::PlatformKeyboardEvent(HWND, WPARAM virtualKeyCode, LPARAM keyData, UChar characterCode, bool systemKey)
-    : m_text(singleCharacterString(characterCode))
-    , m_unmodifiedText(singleCharacterString(characterCode))
-    , m_keyIdentifier(keyIdentifierForWindowsKeyCode(virtualKeyCode))
-    , m_isKeyUp((keyData & NEW_RELEASE_STATE_MASK))
-    , m_autoRepeat((keyData & REPEAT_COUNT_MASK) > 1)
-    , m_WindowsKeyCode(virtualKeyCode)
+PlatformKeyboardEvent::PlatformKeyboardEvent(HWND, WPARAM code, LPARAM keyData, Type type, bool systemKey)
+    : m_type(type)
+    , m_text((type == Char) ? singleCharacterString(code) : String())
+    , m_unmodifiedText((type == Char) ? singleCharacterString(code) : String())
+    , m_keyIdentifier((type == Char) ? String() : keyIdentifierForWindowsKeyCode(code))
+    , m_autoRepeat(keyData & KF_REPEAT)
+    , m_windowsVirtualKeyCode((type == RawKeyDown || type == KeyUp) ? code : 0)
     , m_isKeypad(false) // FIXME: Need to implement this.
     , m_shiftKey(GetKeyState(VK_SHIFT) & HIGH_BIT_MASK_SHORT)
     , m_ctrlKey(GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT)
     , m_altKey(GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT)
     , m_metaKey(m_altKey)
-    , m_isModifierKeyPress(virtualKeyCode == VK_SHIFT || virtualKeyCode == VK_CONTROL || virtualKeyCode == VK_MENU || virtualKeyCode == VK_CAPITAL)
     , m_isSystemKey(systemKey)
 {
 }
 
+void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type, bool)
+{
+    // No KeyDown events here to change.
+    ASSERT_NOT_REACHED();
+}
+
 bool PlatformKeyboardEvent::currentCapsLockState()
 {
      return GetKeyState(VK_CAPITAL) & 1;
index 04da204..f2b9292 100644 (file)
@@ -325,19 +325,43 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode)
 
 PlatformKeyboardEvent::PlatformKeyboardEvent(wxKeyEvent& event)
 {
-    m_text = wxString(event.GetUnicodeKey());
-    m_unmodifiedText = m_text;
-    m_keyIdentifier = keyIdentifierForWxKeyCode(event.GetKeyCode());
-    m_isKeyUp = event.GetEventType() == wxEVT_KEY_UP;
+    switch (event.GetEventType()) {
+        case wxEVT_KEY_UP:
+            m_type = KeyUp;
+            break;
+        case wxEVT_KEY_DOWN:
+            m_type = KeyDown;
+            break;
+        case wxEVT_CHAR:
+            m_type = Char;
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+    }
+    m_text = (type == Char) ? wxString(event.GetUnicodeKey()) : String();
+    m_unmodifiedText = (type == Char) ? m_text : String();
+    m_keyIdentifier = (type == Char) ? String() : keyIdentifierForWxKeyCode(event.GetKeyCode());
     m_autoRepeat = false; // FIXME: not correct.
-    m_WindowsKeyCode = windowsKeyCodeForKeyEvent(event.GetKeyCode());
+    m_windowsVirtualKeyCode = windowsKeyCodeForKeyEvent(event.GetKeyCode());
     m_isKeypad = (event.GetKeyCode() >= WXK_NUMPAD_SPACE) && (event.GetKeyCode() <= WXK_NUMPAD_DIVIDE);
     m_shiftKey = event.ShiftDown();
     m_ctrlKey = event.CmdDown();
     m_altKey = event.AltDown();
     m_metaKey = event.MetaDown();
-    m_isModifierKeyPress = false;
-    m_isWxCharEvent = event.GetEventType() == wxEVT_CHAR;
+}
+
+void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool)
+{
+    // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
+    ASSERT(m_type == KeyDown);
+    m_type = type;
+    if (type == RawKeyDown) {
+        m_text = String();
+        m_unmodifiedText = String();
+    } else {
+        m_keyIdentifier = String();
+        m_windowsVirtualKeyCode = 0;
+    }
 }
 
 bool PlatformKeyboardEvent::currentCapsLockState()
index a38712e..bb2f25c 100644 (file)
@@ -322,8 +322,8 @@ public:
     virtual void undo() { }
     virtual void redo() { }
 
-    virtual void handleKeypress(KeyboardEvent*) { }
-    virtual void handleInputMethodKeypress(KeyboardEvent*) { }
+    virtual void handleKeyboardEvent(KeyboardEvent*) { }
+    virtual void handleInputMethodKeydown(KeyboardEvent*) { }
 
     virtual void textFieldDidBeginEditing(Element*) { }
     virtual void textFieldDidEndEditing(Element*) { }
index 081e99d..d73117e 100644 (file)
@@ -1,3 +1,19 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * WebCoreSupport/EditorClientGtk.cpp:
+        (WebKit::EditorClient::handleKeyboardEvent):
+        (WebKit::EditorClient::handleInputMethodKeydown):
+        * WebCoreSupport/EditorClientGtk.h:
+        Updated for cross-platform changes as much as it was possible without a gtk build environment.
+
 2007-12-08  Luca Bruno  <lethalman88@gmail.com>
 
         Reviewed by Alp Toker.
index 0634e29..fb43879 100644 (file)
@@ -212,14 +212,14 @@ void EditorClient::toggleGrammarChecking()
 {
 }
 
-void EditorClient::handleKeypress(KeyboardEvent* event)
+void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
 {
     Frame* frame = core(m_page)->focusController()->focusedOrMainFrame();
     if (!frame || !frame->document()->focusedNode())
         return;
 
     const PlatformKeyboardEvent* kevent = event->keyEvent();
-    if (!kevent || kevent->isKeyUp())
+    if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
         return;
 
     Node* start = frame->selectionController()->start().node();
@@ -230,7 +230,7 @@ void EditorClient::handleKeypress(KeyboardEvent* event)
     // http://bugs.webkit.org/show_bug.cgi?id=15911
 
     if (start->isContentEditable()) {
-        switch(kevent->WindowsKeyCode()) {
+        switch (kevent->windowsVirtualKeyCode()) {
             case VK_BACK:
                 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
                         kevent->ctrlKey() ? WordGranularity : CharacterGranularity, false, true);
@@ -304,7 +304,7 @@ void EditorClient::handleKeypress(KeyboardEvent* event)
                     }
                     frame->editor()->insertText(kevent->text(), event);
                 } else if (kevent->ctrlKey()) {
-                    switch (kevent->WindowsKeyCode()) {
+                    switch (kevent->windowsVirtualKeyCode()) {
                         case VK_B:
                             frame->editor()->execCommand("ToggleBold");
                             break;
@@ -323,7 +323,7 @@ void EditorClient::handleKeypress(KeyboardEvent* event)
                 } else return;
         }
     } else {
-        switch (kevent->WindowsKeyCode()) {
+        switch (kevent->windowsVirtualKeyCode()) {
             case VK_UP:
                 frame->editor()->execCommand("MoveUp");
                 break;
@@ -352,7 +352,7 @@ void EditorClient::handleKeypress(KeyboardEvent* event)
 }
 
 
-void EditorClient::handleInputMethodKeypress(KeyboardEvent*)
+void EditorClient::handleInputMethodKeydown(KeyboardEvent*)
 {
     notImplemented();
 }
index d13d33b..1a0f27d 100644 (file)
@@ -87,8 +87,8 @@ namespace WebKit {
         virtual void undo();
         virtual void redo();
 
-        virtual void handleKeypress(WebCore::KeyboardEvent*);
-        virtual void handleInputMethodKeypress(WebCore::KeyboardEvent*);
+        virtual void handleKeyboardEvent(WebCore::KeyboardEvent*);
+        virtual void handleInputMethodKeydown(WebCore::KeyboardEvent*);
 
         virtual void textFieldDidBeginEditing(WebCore::Element*);
         virtual void textFieldDidEndEditing(WebCore::Element*);
index bb0384c..a678a11 100644 (file)
@@ -1,3 +1,29 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * WebCoreSupport/WebEditorClient.h:
+        Renamed handleKeypress() to handleKeyboardEvent(), as it gets both keydowns and keypresses.
+        Renamed handleInputMethodKeypress() to handleInputMethodKeydown().
+        * WebCoreSupport/WebEditorClient.mm:
+        (WebEditorClient::handleKeyboardEvent): This change makes sense only remotely, but it helped
+        to get tests working. I guess Mac keyboard event handling needs further refactoring.
+
+        * WebView/WebHTMLView.mm:
+        (selectorToCommandName): Convert AppKit editing selector name to Editor command name - extracted
+        from callWebCoreCommand:.
+        (_interceptEditingKeyEvent:shouldSaveCommand:): Insert text from keypress.
+
+        * WebView/WebPDFView.mm:
+        (-[WebPDFView PDFViewWillClickOnLink:withURL:]):
+        Convert incoming platform KeyDown into RawKeyDown, as this is what the view is interested in.
+
 2007-12-10  Brady Eidson  <beidson@apple.com>
 
         Reviewed by John Sullivan
index d5bac29..ef54d2a 100644 (file)
@@ -87,8 +87,8 @@ public:
     virtual void undo();
     virtual void redo();
     
-    virtual void handleKeypress(WebCore::KeyboardEvent*);
-    virtual void handleInputMethodKeypress(WebCore::KeyboardEvent*);
+    virtual void handleKeyboardEvent(WebCore::KeyboardEvent*);
+    virtual void handleInputMethodKeydown(WebCore::KeyboardEvent*);
 
     virtual void textFieldDidBeginEditing(WebCore::Element*);
     virtual void textFieldDidEndEditing(WebCore::Element*);
index 7d27e91..62a6675 100644 (file)
@@ -430,7 +430,7 @@ void WebEditorClient::redo()
         [[m_webView undoManager] redo];    
 }
 
-void WebEditorClient::handleKeypress(KeyboardEvent* event)
+void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
 {
     Frame* frame = event->target()->toNode()->document()->frame();
     WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
@@ -438,7 +438,7 @@ void WebEditorClient::handleKeypress(KeyboardEvent* event)
         event->setDefaultHandled();
 }
 
-void WebEditorClient::handleInputMethodKeypress(KeyboardEvent* event)
+void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* event)
 {
     Frame* frame = event->target()->toNode()->document()->frame();
     WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
index 7bc7784..f173a21 100644 (file)
@@ -2047,17 +2047,17 @@ static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
 }
 
-- (void)callWebCoreCommand:(SEL)selector
+static AtomicString selectorToCommandName(SEL selector)
 {
-    if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
-        return;
-
-    Frame* coreFrame = core([self _frame]);
-    if (!coreFrame)
-        return;
-
     // Capitalize the first letter of the selector, since we use capitalized command
     // names in the Editor object (why?). And remove the trailing colon.
+    // And change a few command names into ones supported by WebCore::Editor.
+
+    if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
+        return "InsertNewline";
+    if (selector == @selector(insertTabIgnoringFieldEditor:))
+        return "InsertTab";
+
     const char* selectorName = sel_getName(selector);
     size_t selectorNameLength = strlen(selectorName);
     ASSERT(selectorNameLength >= 2);
@@ -2067,7 +2067,19 @@ static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
     memcpy(&commandName[1], &selectorName[1], selectorNameLength - 2);
     commandName[selectorNameLength - 1] = 0;
 
-    coreFrame->editor()->execCommand(commandName.data());
+    return AtomicString(commandName.data());
+}
+
+- (void)callWebCoreCommand:(SEL)selector
+{
+    if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
+        return;
+
+    Frame* coreFrame = core([self _frame]);
+    if (!coreFrame)
+        return;
+
+    coreFrame->editor()->execCommand(selectorToCommandName(selector));
 }
 
 // These commands are forwarded to the Editor object in WebCore.
@@ -5185,7 +5197,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
     // and only change this assumption if one of the NSTextInput/Responder callbacks is used.
     // We assume the IM will *not* consume hotkey sequences
     parameters.consumedByIM = !event->metaKey() && shouldSave;
-        
+
     if (const PlatformKeyboardEvent* platformEvent = event->keyEvent()) {
         NSEvent *macEvent = platformEvent->macEvent();
         if ([macEvent type] == NSKeyDown && [_private->compController filterKeyDown:macEvent])
@@ -5200,16 +5212,26 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
         KeypressCommand command = event->keypressCommand();
         bool hasKeypressCommand = !command.commandNames.isEmpty() || !command.text.isEmpty();
 
+        // FIXME: interpretKeyEvents doesn't match application key equivalents (such as Cmd+A),
+        // and sends noop: for those. As a result, we don't handle those from within WebCore,
+        // but send a full sequence of DOM events, including an unneeded keypress.
         if (parameters.shouldSaveCommand || !hasKeypressCommand)
             [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
         else {
-            if (!command.text.isEmpty())
-                [self insertText:command.text];
-            else {
-                size_t size = command.commandNames.size();
+            ASSERT(platformEvent->type() == PlatformKeyboardEvent::RawKeyDown);
+            size_t size = command.commandNames.size();
+            // Are there commands that would just cause text insertion if executed via Editor?
+            // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
+            // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
+            // (e.g. Tab that inserts a Tab character, or Enter).
+            bool haveTextInsertionCommands = false;
+            for (size_t i = 0; i < size; ++i) {
+                if (Editor::isTextInsertionCommand(selectorToCommandName(NSSelectorFromString(command.commandNames[i]))))
+                    haveTextInsertionCommands = true;
+            }
+            if (!haveTextInsertionCommands)
                 for (size_t i = 0; i < size; ++i)
                     [self doCommandBySelector:NSSelectorFromString(command.commandNames[i])];
-            }
         }
         _private->interpretKeyEventsParameters = 0;
     }
@@ -5547,14 +5569,9 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
         WebView *webView = [self _webView];
         Frame* coreFrame = core([self _frame]);
         if (![[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector] && coreFrame) {
-            if (selector == @selector(insertNewline:) || selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
-                eventWasHandled = coreFrame->editor()->execCommand("InsertNewline", event);
-            else if (selector == @selector(insertLineBreak:))
-                eventWasHandled = coreFrame->editor()->execCommand("InsertLineBreak", event);
-            else if (selector == @selector(insertTab:) || selector == @selector(insertTabIgnoringFieldEditor:))
-                eventWasHandled = coreFrame->editor()->execCommand("InsertTab", event);
-            else if (selector == @selector(insertBacktab:))
-                eventWasHandled = coreFrame->editor()->execCommand("InsertBacktab", event);
+            AtomicString commandName = selectorToCommandName(selector);
+            if (Editor::isTextInsertionCommand(commandName))
+                eventWasHandled = coreFrame->editor()->execCommand(commandName, event);
             else {
                 _private->selectorForDoCommandBySelector = selector;
                 [super doCommandBySelector:selector];
index bdf2697..630b6de 100644 (file)
@@ -947,8 +947,9 @@ static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selec
             break;
         case NSKeyDown: {
             PlatformKeyboardEvent pe(nsEvent);
+            pe.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
             event = new KeyboardEvent(keydownEvent, true, true, 0,
-                pe.keyIdentifier(), pe.WindowsKeyCode(),
+                pe.keyIdentifier(), pe.windowsVirtualKeyCode(),
                 pe.ctrlKey(), pe.altKey(), pe.shiftKey(), pe.metaKey(), false);
         }
         default:
index e0c50e1..9a4bdf8 100644 (file)
@@ -1,3 +1,19 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * WebCoreSupport/EditorClientQt.cpp:
+        (WebCore::EditorClientQt::handleKeyboardEvent):
+        (WebCore::EditorClientQt::handleInputMethodKeydown):
+        * WebCoreSupport/EditorClientQt.h:
+        Updated for cross-platform changes as much as it was possible without a Qt build environment.
+
 2007-12-07  Darin Adler  <darin@apple.com>
 
         - try to fix build
index 1625063..eeed5eb 100644 (file)
@@ -325,14 +325,14 @@ void EditorClientQt::toggleGrammarChecking()
     notImplemented();
 }
 
-void EditorClientQt::handleKeypress(KeyboardEvent* event)
+void EditorClientQt::handleKeyboardEvent(KeyboardEvent* event)
 {
     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
     if (!frame || !frame->document()->focusedNode())
         return;
 
     const PlatformKeyboardEvent* kevent = event->keyEvent();
-    if (!kevent || kevent->isKeyUp())
+    if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
         return;
 
     Node* start = frame->selectionController()->start().node();
@@ -341,7 +341,7 @@ void EditorClientQt::handleKeypress(KeyboardEvent* event)
 
     // FIXME: refactor all of this to use Actions or something like them
     if (start->isContentEditable()) {
-        switch(kevent->WindowsKeyCode()) {
+        switch (kevent->windowsVirtualKeyCode()) {
             case VK_RETURN:
                 frame->editor()->execCommand("InsertLineBreak");
                 break;
@@ -385,7 +385,7 @@ void EditorClientQt::handleKeypress(KeyboardEvent* event)
                 if (!kevent->ctrlKey() && !kevent->altKey() && !kevent->text().isEmpty()) {
                     frame->editor()->insertText(kevent->text(), event);
                 } else if (kevent->ctrlKey()) {
-                    switch (kevent->WindowsKeyCode()) {
+                    switch (kevent->windowsVirtualKeyCode()) {
                         case VK_A:
                             frame->editor()->execCommand("SelectAll");
                             break;
@@ -416,7 +416,7 @@ void EditorClientQt::handleKeypress(KeyboardEvent* event)
                 } else return;
         }
     } else {
-        switch (kevent->WindowsKeyCode()) {
+        switch (kevent->windowsVirtualKeyCode()) {
             case VK_UP:
                 frame->editor()->execCommand("MoveUp");
                 break;
@@ -439,7 +439,7 @@ void EditorClientQt::handleKeypress(KeyboardEvent* event)
                 break;
             default:
                 if (kevent->ctrlKey()) {
-                    switch(kevent->WindowsKeyCode()) {
+                    switch(kevent->windowsVirtualKeyCode()) {
                         case VK_A:
                             frame->editor()->execCommand("SelectAll");
                             break;
@@ -455,7 +455,7 @@ void EditorClientQt::handleKeypress(KeyboardEvent* event)
     event->setDefaultHandled();
 }
 
-void EditorClientQt::handleInputMethodKeypress(KeyboardEvent*)
+void EditorClientQt::handleInputMethodKeydown(KeyboardEvent*)
 {
 }
 
index d06918c..41147b6 100644 (file)
@@ -84,8 +84,8 @@ public:
     virtual void undo();
     virtual void redo();
 
-    virtual void handleKeypress(KeyboardEvent*);
-    virtual void handleInputMethodKeypress(KeyboardEvent*);
+    virtual void handleKeyboardEvent(KeyboardEvent*);
+    virtual void handleInputMethodKeydown(KeyboardEvent*);
 
     virtual void textFieldDidBeginEditing(Element*);
     virtual void textFieldDidEndEditing(Element*);
index af07d2c..377d1fb 100644 (file)
@@ -1,3 +1,39 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        Listen to WM_CHAR messages, and actually pass the type of message received down to WebCore.
+        Since WM_KEYDOWN == keydown and WM_CHAR == keypress, this allows for much better IE compatibility
+        than layering Windows keyboard event handling on top of Mac one.
+
+        * WebView.cpp:
+        (WebView::keyUp): Do not special case Alt+F4 and Alt+Space - we don't get keyups for those anyway!
+        (WebView::interpretKeyEvent): Renamed WindowsKeyCode() to windowsVirtualKeyCode().
+        (WebView::handleEditingKeyboardEvent): Use the additional information about event type that
+        we now pass with PlatformKeyboardEvent.
+        (WebView::keyDown): (WebView::keyPress): Split WM_KEYDOWN and WM_CHAR handling in separate
+        functions, pass them down as is, without trying to guess what WM_CHAR Windows would have sent
+        for a given WM_KEYDOWN.
+
+        (WebViewWndProc): Handle WM_CHAR and WM_SYSCHAR.
+
+        * WebView.h: Removed inIMEKeyDown() - it doesn't look like we need it at all. At least, I didn't
+        notice any regressions after removing the only call to it in WebEditorClient.
+
+        * WebEditorClient.cpp:
+        (WebEditorClient::handleKeyboardEvent):
+        (WebEditorClient::handleInputMethodKeydown):
+        * WebEditorClient.h:
+        Renamed handleKeypress() to handleKeyboardEvent(), as it gets both keydowns and keypresses.
+        Renamed handleInputMethodKeypress() to handleInputMethodKeydown() and removed
+        inIMEKeyDown()-related code.
+
 2007-12-10  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Sam Weinig.
index 430c37d..cdfce34 100644 (file)
@@ -594,16 +594,14 @@ void WebEditorClient::redo()
     }
 }
 
-void WebEditorClient::handleKeypress(KeyboardEvent* evt)
+void WebEditorClient::handleKeyboardEvent(KeyboardEvent* evt)
 {
     if (m_webView->handleEditingKeyboardEvent(evt))
         evt->setDefaultHandled();
 }
 
-void WebEditorClient::handleInputMethodKeypress(KeyboardEvent* evt)
+void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* )
 {
-    if (m_webView->inIMEKeyDown())
-        evt->setDefaultHandled();
 }
 
 bool WebEditorClient::isEditable()
index 4a7b796..e01707e 100644 (file)
@@ -94,8 +94,8 @@ public:
     virtual void textWillBeDeletedInTextField(WebCore::Element* input);
     virtual void textDidChangeInTextArea(WebCore::Element*);
 
-    void handleKeypress(WebCore::KeyboardEvent*);
-    void handleInputMethodKeypress(WebCore::KeyboardEvent*);
+    void handleKeyboardEvent(WebCore::KeyboardEvent*);
+    void handleInputMethodKeydown(WebCore::KeyboardEvent*);
 
     virtual void ignoreWordInSpellDocument(const WebCore::String&);
     virtual void learnWord(const WebCore::String&);
index e01b1fa..51d728c 100644 (file)
@@ -1239,11 +1239,7 @@ bool WebView::execCommand(WPARAM wParam, LPARAM /*lParam*/)
 
 bool WebView::keyUp(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
 {
-    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, m_currentCharacterCode, systemKeyDown);
-
-    // Don't send key events for alt+space and alt+f4.
-    if (keyEvent.altKey() && (virtualKeyCode == VK_SPACE || virtualKeyCode == VK_F4))
-        return false;
+    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, PlatformKeyboardEvent::KeyUp, systemKeyDown);
 
     Frame* frame = m_page->focusController()->focusedOrMainFrame();
     m_currentCharacterCode = 0;
@@ -1256,13 +1252,19 @@ static const unsigned AltKey = 1 << 1;
 static const unsigned ShiftKey = 1 << 2;
 
 
-struct KeyEntry {
+struct KeyDownEntry {
     unsigned virtualKey;
     unsigned modifiers;
     const char* name;
 };
 
-static const KeyEntry keyEntries[] = {
+struct KeyPressEntry {
+    unsigned charCode;
+    unsigned modifiers;
+    const char* name;
+};
+
+static const KeyDownEntry keyDownEntries[] = {
     { VK_LEFT,   0,                  "MoveLeft"                                    },
     { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"                  },
     { VK_LEFT,   CtrlKey,            "MoveWordLeft"                                },
@@ -1316,55 +1318,78 @@ static const KeyEntry keyEntries[] = {
     { 'Z',       CtrlKey | ShiftKey, "Redo"                                        },
 };
 
+static const KeyPressEntry keyPressEntries[] = {
+    { '\t',   0,                  "InsertTab"                                   },
+    { '\t',   ShiftKey,           "InsertBacktab"                               },
+    { '\r',   0,                  "InsertNewline"                               },
+    { '\r',   CtrlKey,            "InsertNewline"                               },
+    { '\r',   AltKey,             "InsertNewline"                               },
+    { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
+};
+
 const char* WebView::interpretKeyEvent(const KeyboardEvent* evt)
 {
-    const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
-    if (!keyEvent)
-        return "";
+    ASSERT(evt->type() == keydownEvent || evt->type() == keypressEvent);
+
+    static HashMap<int, const char*>* keyDownCommandsMap = 0;
+    static HashMap<int, const char*>* keyPressCommandsMap = 0;
 
-    static HashMap<int, const char*>* commandsMap = 0;
+    if (!keyDownCommandsMap) {
+        keyDownCommandsMap = new HashMap<int, const char*>;
+        keyPressCommandsMap = new HashMap<int, const char*>;
 
-    if (!commandsMap) {
-        commandsMap = new HashMap<int, const char*>;
+        for (unsigned i = 0; i < _countof(keyDownEntries); i++)
+            keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
 
-        for (unsigned i = 0; i < _countof(keyEntries); i++)
-            commandsMap->set(keyEntries[i].modifiers << 16 | keyEntries[i].virtualKey, keyEntries[i].name);
+        for (unsigned i = 0; i < _countof(keyPressEntries); i++)
+            keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].virtualKey, keyPressEntries[i].name);
     }
 
     unsigned modifiers = 0;
-    if (keyEvent->shiftKey())
+    if (evt->shiftKey())
         modifiers |= ShiftKey;
-    if (keyEvent->altKey())
+    if (evt->altKey())
         modifiers |= AltKey;
-    if (keyEvent->ctrlKey())
+    if (evt->ctrlKey())
         modifiers |= CtrlKey;
 
-    return commandsMap->get(modifiers << 16 | keyEvent->WindowsKeyCode());
+    return evt->type() == keydownEvent ? 
+        keyDownCommandsMap->get(modifiers << 16 | evt->keyCode()) :
+        keyPressCommandsMap->get(modifiers << 16 | evt->charCode());
 }
 
 bool WebView::handleEditingKeyboardEvent(KeyboardEvent* evt)
 {
-    String command(interpretKeyEvent(evt));
-
     Node* node = evt->target()->toNode();
     ASSERT(node);
     Frame* frame = node->document()->frame();
     ASSERT(frame);
 
-    if (!command.isEmpty())
+    const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
+    if (!keyEvent || keyEvent->isSystemKey())  // do not treat this as text input if it's a system key event
+        return false;
+
+    if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) {
+        String command = interpretKeyEvent(evt);
+
+        // WebKit doesn't have enough information about mode to decide how commands that just insert text if executed via Editor should be treated,
+        // so we leave it upon WebCore to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
+        // (e.g. Tab that inserts a Tab character, or Enter).
+        if (!command.isEmpty() && !Editor::isTextInsertionCommand(command))
+            if (frame->editor()->execCommand(command, evt))
+                return true;
+        return false;
+    }
+
+    String command = interpretKeyEvent(evt);
+     if (!command.isEmpty())
         if (frame->editor()->execCommand(command, evt))
             return true;
 
-    if (!evt->keyEvent() || evt->keyEvent()->isSystemKey())  // do not treat this as text input if it's a system key event
+    // Don't insert null or control characters as they can result in unexpected behaviour
+    if (evt->charCode() < ' ')
         return false;
 
-    if (evt->keyEvent()->text().length() == 1) {
-        UChar ch = evt->keyEvent()->text()[0];
-        // Don't insert null or control characters as they can reslt in unexpected behaviour
-        if (ch < ' ')
-            return false;
-    }
-
     return frame->editor()->insertText(evt->keyEvent()->text(), evt);
 }
 
@@ -1374,43 +1399,33 @@ bool WebView::keyDown(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
     if (virtualKeyCode == VK_CAPITAL)
         frame->eventHandler()->capsLockStateMayHaveChanged();
 
-    // Don't send key events for alt+space and alt+f4, since the OS needs to handle that.
-    if (systemKeyDown && (virtualKeyCode == VK_SPACE || virtualKeyCode == VK_F4))
-        return false;
-
-    MSG msg;
-    // If the next message is a WM_CHAR message, then take it out of the queue, and use
-    // the message parameters to get the character code to construct the PlatformKeyboardEvent.
-    if (systemKeyDown) {
-        if (::PeekMessage(&msg, m_viewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_NOREMOVE))     // don't remove sys key message from the windows message queue until we know we can handle it
-            m_currentCharacterCode = (UChar)msg.wParam;
-    } else if (::PeekMessage(&msg, m_viewWindow, WM_CHAR, WM_CHAR, PM_REMOVE)) 
-        m_currentCharacterCode = (UChar)msg.wParam;
-
-    // FIXME: We need to check WM_UNICHAR to support supplementary characters.
-    // FIXME: We may need to handle other messages for international text.
-
-    m_inIMEKeyDown = virtualKeyCode == VK_PROCESSKEY;
-    if (virtualKeyCode == VK_PROCESSKEY && !m_inIMEComposition)
-        virtualKeyCode = MapVirtualKey(LOBYTE(HIWORD(keyData)), 1);
-
-    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, m_currentCharacterCode, systemKeyDown);
+    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, PlatformKeyboardEvent::RawKeyDown, systemKeyDown);
     bool handled = frame->eventHandler()->keyEvent(keyEvent);
-    m_inIMEKeyDown = false;
-    if (handled)
-        goto exit;
+
+    // These events cannot be canceled.
+    // FIXME: match IE list more closely, see <http://msdn2.microsoft.com/en-us/library/ms536938.aspx>.
+    if (systemKeyDown)
+        handled = false;
+
+    if (handled) {
+        // FIXME: remove WM_UNICHAR, too
+        MSG msg;
+        // WM_SYSCHAR events should not be removed, because access keys are implemented in WebCore in WM_SYSCHAR handler.
+        if (!systemKeyDown)
+            ::PeekMessage(&msg, m_viewWindow, WM_CHAR, WM_CHAR, PM_REMOVE);
+        return true;
+    }
 
     // We need to handle back/forward using either Backspace(+Shift) or Ctrl+Left/Right Arrow keys.
-    int windowsKeyCode = keyEvent.WindowsKeyCode();
-    if ((windowsKeyCode == VK_BACK && keyEvent.shiftKey()) || (windowsKeyCode == VK_RIGHT && keyEvent.ctrlKey()))
+    if ((virtualKeyCode == VK_BACK && keyEvent.shiftKey()) || (virtualKeyCode == VK_RIGHT && keyEvent.ctrlKey()))
         m_page->goForward();
-    else if (windowsKeyCode == VK_BACK || (windowsKeyCode == VK_LEFT && keyEvent.ctrlKey()))
+    else if (virtualKeyCode == VK_BACK || (virtualKeyCode == VK_LEFT && keyEvent.ctrlKey()))
         m_page->goBack();
     
     // Need to scroll the page if the arrow keys, space(shift), pgup/dn, or home/end are hit.
     ScrollDirection direction;
     ScrollGranularity granularity;
-    switch (windowsKeyCode) {
+    switch (virtualKeyCode) {
         case VK_LEFT:
             granularity = ScrollByLine;
             direction = ScrollLeft;
@@ -1450,17 +1465,20 @@ bool WebView::keyDown(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
         default:
             // We want to let Windows handle the WM_SYSCHAR event if we can't handle it
             // We do want to return true for regular key down case so the WM_CHAR handler won't pick up unhandled messages
-            return !systemKeyDown;
+            return false;
     }
 
     if (!frame->eventHandler()->scrollOverflow(direction, granularity))
         frame->view()->scroll(direction, granularity);
+    return true;
+}
 
-exit:
-    if (systemKeyDown)  // remove sys key message if we have handled it
-        ::PeekMessage(&msg, m_viewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE);
+bool WebView::keyPress(WPARAM charCode, LPARAM keyData, bool systemKeyDown)
+{
+    Frame* frame = m_page->focusController()->focusedOrMainFrame();
 
-    return true;
+    PlatformKeyboardEvent keyEvent(m_viewWindow, charCode, keyData, PlatformKeyboardEvent::Char, systemKeyDown);
+    return frame->eventHandler()->keyEvent(keyEvent);
 }
 
 bool WebView::inResizer(LPARAM lParam)
@@ -1576,6 +1594,13 @@ static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, L
         case WM_KEYUP:
             handled = webView->keyUp(wParam, lParam);
             break;
+        case WM_SYSCHAR:
+            handled = webView->keyPress(wParam, lParam, true);
+            break;
+        case WM_CHAR:
+            handled = webView->keyPress(wParam, lParam);
+            break;
+        // FIXME: We need to check WM_UNICHAR to support supplementary characters (that don't fit in 16 bits).
         case WM_SIZE:
             if (webView->isBeingDestroyed())
                 // If someone has sent us this message while we're being destroyed, we should bail out so we don't crash.
index 9444458..fbe7d22 100644 (file)
@@ -639,6 +639,7 @@ public:
     bool execCommand(WPARAM wParam, LPARAM lParam);
     bool keyDown(WPARAM, LPARAM, bool systemKeyDown = false);
     bool keyUp(WPARAM, LPARAM, bool systemKeyDown = false);
+    bool keyPress(WPARAM, LPARAM, bool systemKeyDown = false);
     bool inResizer(LPARAM lParam);
     void paint(HDC, LPARAM);
     void paintIntoBackingStore(WebCore::FrameView*, HDC bitmapDC, const WebCore::IntRect& dirtyRect);
@@ -665,7 +666,6 @@ public:
     bool onIMESetContext(WPARAM, LPARAM);
     void selectionChanged();
     void resetIME(WebCore::Frame*);
-    bool inIMEKeyDown() const { return m_inIMEKeyDown; }
     void setInputMethodState(bool);
 
     HRESULT registerDragDrop();
@@ -759,7 +759,6 @@ protected:
     bool m_didClose;
     bool m_hasCustomDropTarget;
     unsigned m_inIMEComposition;
-    bool m_inIMEKeyDown;
     HWND m_toolTipHwnd;
     WebCore::String m_toolTip;
 
index 3ee52bd..b83e3f4 100644 (file)
@@ -1,3 +1,21 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * WebKitSupport/EditorClientWx.cpp:
+        (WebCore::EditorClientWx::handleInputMethodKeydown):
+        (WebCore::EditorClientWx::handleKeyboardEvent):
+        * WebKitSupport/EditorClientWx.h:
+        Updated for cross-platform changes as much as it was possible without a wx build environment.
+        The keyboard event model of wx is similar to Windows one, so further fixes can be modeled
+        after the Windows port.
+
 2007-12-06  Kevin Ollivier  <kevino@theolliviers.com>
 
         Fix page leak caused because the Frame's page pointer is 0 by the 
index 1f1a5df..76b2a28 100644 (file)
@@ -232,27 +232,27 @@ void EditorClientWx::redo()
     notImplemented();
 }
 
-void EditorClientWx::handleInputMethodKeypress(KeyboardEvent* event)
+void EditorClientWx::handleInputMethodKeydown(KeyboardEvent* event)
 {
 // NOTE: we don't currently need to handle this. When key events occur,
-// both this method and handleKeypress get a chance at handling them.
+// both this method and handleKeyboardEvent get a chance at handling them.
 // We might use this method later on for IME-specific handling.
 }
 
-void EditorClientWx::handleKeypress(KeyboardEvent* event)
+void EditorClientWx::handleKeyboardEvent(KeyboardEvent* event)
 {
     Frame* frame = m_page->focusController()->focusedOrMainFrame();
     if (!frame)
         return;
 
     const PlatformKeyboardEvent* kevent = event->keyEvent();
-    if (!kevent->isKeyUp()) {
+    if (!kevent->type() == PlatformKeyboardEvent::KeyUp) {
         Node* start = frame->selectionController()->start().node();
         if (!start || !start->isContentEditable())
             return; 
         
-        if (kevent->isWxCharEvent() && !kevent->ctrlKey() && !kevent->altKey()) {
-            switch(kevent->WindowsKeyCode()) {
+        if (kevent->type() == PlatformKeyboardEvent::Char && !kevent->ctrlKey() && !kevent->altKey()) {
+            switch (kevent->windowsVirtualKeyCode()) {
                 // we handled these on key down, ignore them for char events
                 case VK_BACK:
                 case VK_DELETE:
@@ -269,7 +269,7 @@ void EditorClientWx::handleKeypress(KeyboardEvent* event)
             return; 
         }
         
-        switch(kevent->WindowsKeyCode()) {
+        switch (kevent->windowsVirtualKeyCode()) {
             case VK_BACK:
                 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
                                                      CharacterGranularity, false, true);
index 81f4fa1..c2234d0 100644 (file)
@@ -80,8 +80,8 @@ public:
     virtual void undo();
     virtual void redo();
     
-    virtual void handleKeypress(KeyboardEvent*);
-    virtual void handleInputMethodKeypress(KeyboardEvent*);
+    virtual void handleKeyboardEvent(KeyboardEvent*);
+    virtual void handleInputMethodKeydown(KeyboardEvent*);
     
     virtual void textFieldDidBeginEditing(Element*);
     virtual void textFieldDidEndEditing(Element*);
index 201460a..37ba205 100644 (file)
@@ -1,3 +1,22 @@
+2007-12-07  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        <rdar://problem/5535636>
+        Have to press 4 times instead of 2 times to get the expected result of ^^ with german keyboard.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13916
+        JavaScript detects Tab as a character input on a textfield validation
+
+        * DumpRenderTree/mac/EventSendingController.mm:
+        (-[EventSendingController keyDown:withModifiers:]): Added a few more named keys.
+        Dispatch a keyup to better match what happens when a key is physically pressed.
+
+        * DumpRenderTree/win/EventSender.cpp:
+        (keyDownCallback): Ditto. Also make sure that WM_CHAR is consistently dispatched before
+        returning from keyDown().
+        (getConstantCallback): Fixed a couple copy/paste mistakes.
+
 2007-12-07  Kevin McCullough  <kmccullough@apple.com>
 
         Reviewed by Oliver.
index 5493938..42f9790 100644 (file)
@@ -350,9 +350,21 @@ BOOL replayingSavedEvents;
 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers
 {
     NSString *eventCharacter = character;
-    if ([character isEqualToString:@"rightArrow"]) {
-        const unichar rightArrowCharacter = NSRightArrowFunctionKey;
-        eventCharacter = [NSString stringWithCharacters:&rightArrowCharacter length:1];
+    if ([character isEqualToString:@"leftArrow"]) {
+        const unichar ch = NSLeftArrowFunctionKey;
+        eventCharacter = [NSString stringWithCharacters:&ch length:1];
+    } else if ([character isEqualToString:@"rightArrow"]) {
+        const unichar ch = NSRightArrowFunctionKey;
+        eventCharacter = [NSString stringWithCharacters:&ch length:1];
+    } else if ([character isEqualToString:@"upArrow"]) {
+        const unichar ch = NSUpArrowFunctionKey;
+        eventCharacter = [NSString stringWithCharacters:&ch length:1];
+    } else if ([character isEqualToString:@"downArrow"]) {
+        const unichar ch = NSDownArrowFunctionKey;
+        eventCharacter = [NSString stringWithCharacters:&ch length:1];
+    } else if ([character isEqualToString:@"delete"]) {
+        const unichar ch = 0x7f;
+        eventCharacter = [NSString stringWithCharacters:&ch length:1];
     }
 
     NSString *charactersIgnoringModifiers = eventCharacter;
@@ -391,6 +403,19 @@ BOOL replayingSavedEvents;
                         keyCode:0];
 
     [[[[mainFrame webView] window] firstResponder] keyDown:event];
+
+    event = [NSEvent keyEventWithType:NSKeyUp
+                        location:NSMakePoint(5, 5)
+                        modifierFlags:modifierFlags
+                        timestamp:[self currentEventTime]
+                        windowNumber:[[[mainFrame webView] window] windowNumber]
+                        context:[NSGraphicsContext currentContext]
+                        characters:eventCharacter
+                        charactersIgnoringModifiers:charactersIgnoringModifiers
+                        isARepeat:NO
+                        keyCode:0];
+
+    [[[[mainFrame webView] window] firstResponder] keyUp:event];
 }
 
 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
index caf489f..cfea959 100644 (file)
@@ -70,9 +70,9 @@ static JSValueRef getConstantCallback(JSContextRef context, JSObjectRef object,
     if (JSStringIsEqualToUTF8CString(propertyName, "WM_KEYUP"))
         return JSValueMakeNumber(context, WM_KEYUP);
     if (JSStringIsEqualToUTF8CString(propertyName, "WM_CHAR"))
-        return JSValueMakeNumber(context, WM_KEYDOWN);
-    if (JSStringIsEqualToUTF8CString(propertyName, "WM_DEADCHAR"))
         return JSValueMakeNumber(context, WM_CHAR);
+    if (JSStringIsEqualToUTF8CString(propertyName, "WM_DEADCHAR"))
+        return JSValueMakeNumber(context, WM_DEADCHAR);
     if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYDOWN"))
         return JSValueMakeNumber(context, WM_SYSKEYDOWN);
     if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYUP"))
@@ -292,9 +292,17 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS
     int virtualKeyCode;
     int charCode = 0;
     bool needsShiftKeyModifier = false;
-    if (JSStringIsEqualToUTF8CString(character, "rightArrow")) {
+    if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
+        virtualKeyCode = VK_LEFT;
+    else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
         virtualKeyCode = VK_RIGHT;
-    } else {
+    else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
+        virtualKeyCode = VK_UP;
+    else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
+        virtualKeyCode = VK_DOWN;
+    else if (JSStringIsEqualToUTF8CString(character, "delete"))
+        virtualKeyCode = VK_BACK;
+    else {
         charCode = JSStringGetCharactersPtr(character)[0];
         virtualKeyCode = LOBYTE(VkKeyScan(charCode));
         if (isupper(charCode))
@@ -343,9 +351,15 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS
         // For characters that do not exist in the active keyboard layout,
         // ::Translate will not work, so we post an WM_CHAR event ourselves.
         ::PostMessage(webViewWindow, WM_CHAR, charCode, 0);
-        ::DispatchMessage(&msg);
     }
 
+    // Tests expect that all messages are processed by the time keyDown() returns.
+    if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE))
+        ::DispatchMessage(&msg);
+
+    MSG msgUp = makeMsg(webViewWindow, WM_KEYUP, virtualKeyCode, 0);
+    ::DispatchMessage(&msgUp);
+
     if (argumentCount > 1 || needsShiftKeyModifier)
         ::SetKeyboardState(keyState);
 
@@ -384,7 +398,7 @@ static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef func
     } else
         msg.pt = lastMousePosition;
 
-    dispatchMessage(&msg);
+    ::DispatchMessage(&msg);
 
     return JSValueMakeUndefined(context);
 }