<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[186073] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/186073">186073</a></dd>
<dt>Author</dt> <dd>enrica@apple.com</dd>
<dt>Date</dt> <dd>2015-06-29 10:43:46 -0700 (Mon, 29 Jun 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS] Hardware Keyboard: All combinations of arrow keys and space key do not scroll the view.
https://bugs.webkit.org/show_bug.cgi?id=146290
rdar://problem/18466015

We don't normally get called by the keyboard to handle the event if we are not
interacting with editable content. In order to receive all the hardware keyboard events
we need to implement _handleKeyUIEvent which is called for every key event
when the view is first responder. This gives us the opportunity to send each keystroke
to the WebProcess to let any Javascript handler intercept it and then perform the default
action for the key combination.
Unfortunately this mechanism does not provide key repeat, which is implemented in the
keyboard layer. In order to have it at least for the arrow keys, we use the keyCommands
mechanism only for those.

Reviewed by Darin Adler.

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _scrollByOffset:]):
* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView canPerformAction:withSender:]):
(-[WKContentView keyCommands]):
(-[WKContentView _arrowKey:]):
(-[WKContentView _handleKeyUIEvent:]):
(-[WKContentView handleKeyEvent:]):
(-[WKContentView handleKeyWebEvent:]):
(-[WKContentView _interpretKeyEvent:isCharEvent:]):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2PlatformspiiosUIKitSPIh">trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewInternalh">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm">trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (186072 => 186073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-06-29 17:33:59 UTC (rev 186072)
+++ trunk/Source/WebKit2/ChangeLog        2015-06-29 17:43:46 UTC (rev 186073)
</span><span class="lines">@@ -1,3 +1,34 @@
</span><ins>+2015-06-29  Enrica Casucci  &lt;enrica@apple.com&gt;
+
+        [iOS] Hardware Keyboard: All combinations of arrow keys and space key do not scroll the view.
+        https://bugs.webkit.org/show_bug.cgi?id=146290
+        rdar://problem/18466015
+
+        We don't normally get called by the keyboard to handle the event if we are not
+        interacting with editable content. In order to receive all the hardware keyboard events
+        we need to implement _handleKeyUIEvent which is called for every key event
+        when the view is first responder. This gives us the opportunity to send each keystroke
+        to the WebProcess to let any Javascript handler intercept it and then perform the default
+        action for the key combination.
+        Unfortunately this mechanism does not provide key repeat, which is implemented in the
+        keyboard layer. In order to have it at least for the arrow keys, we use the keyCommands
+        mechanism only for those.
+
+        Reviewed by Darin Adler.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _scrollByOffset:]):
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView canPerformAction:withSender:]):
+        (-[WKContentView keyCommands]):
+        (-[WKContentView _arrowKey:]):
+        (-[WKContentView _handleKeyUIEvent:]):
+        (-[WKContentView handleKeyEvent:]):
+        (-[WKContentView handleKeyWebEvent:]):
+        (-[WKContentView _interpretKeyEvent:isCharEvent:]):
+
</ins><span class="cx"> 2015-06-28  Dan Bernstein  &lt;mitz@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fixed the iOS 8.x build after r186066.
</span></span></pre></div>
<a id="trunkSourceWebKit2PlatformspiiosUIKitSPIh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h (186072 => 186073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h        2015-06-29 17:33:59 UTC (rev 186072)
+++ trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h        2015-06-29 17:43:46 UTC (rev 186073)
</span><span class="lines">@@ -48,6 +48,7 @@
</span><span class="cx"> #import &lt;UIKit/UIPickerContentView_Private.h&gt;
</span><span class="cx"> #import &lt;UIKit/UIPickerView_Private.h&gt;
</span><span class="cx"> #import &lt;UIKit/UIPresentationController_Private.h&gt;
</span><ins>+#import &lt;UIKit/UIResponder_Private.h&gt;
</ins><span class="cx"> #import &lt;UIKit/UIScrollView_Private.h&gt;
</span><span class="cx"> #import &lt;UIKit/UIStringDrawing_Private.h&gt;
</span><span class="cx"> #import &lt;UIKit/UITableViewCell_Private.h&gt;
</span><span class="lines">@@ -110,6 +111,26 @@
</span><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> typedef enum {
</span><ins>+    kUIKeyboardInputRepeat                 = 1 &lt;&lt; 0,
+    kUIKeyboardInputPopupVariant           = 1 &lt;&lt; 1,
+    kUIKeyboardInputMultitap               = 1 &lt;&lt; 2,
+    kUIKeyboardInputSkipCandidateSelection = 1 &lt;&lt; 3,
+    kUIKeyboardInputDeadKey                = 1 &lt;&lt; 4,
+    kUIKeyboardInputModifierFlagsChanged   = 1 &lt;&lt; 5,
+    kUIKeyboardInputFlick                  = 1 &lt;&lt; 6,
+    kUIKeyboardInputPreProcessed           = 1 &lt;&lt; 7,
+} UIKeyboardInputFlags;
+
+@interface UIEvent (Details)
+@property (nonatomic, readonly) UIKeyboardInputFlags _inputFlags;
+- (void *)_hidEvent;
+- (NSString *)_unmodifiedInput;
+- (NSString *)_modifiedInput;
+- (NSInteger)_modifierFlags;
+- (BOOL)_isKeyDown;
+@end
+
+typedef enum {
</ins><span class="cx">     UIFontTraitPlain = 0x00000000,
</span><span class="cx"> } UIFontTrait;
</span><span class="cx"> 
</span><span class="lines">@@ -129,6 +150,10 @@
</span><span class="cx"> - (id)initWithCGImage:(CGImageRef)CGImage imageOrientation:(UIImageOrientation)imageOrientation;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><ins>+@interface UIKeyCommand (Details)
+@property (nonatomic, readonly) UIEvent *_triggeringEvent;
+@end
+
</ins><span class="cx"> @protocol UIKeyboardImplGeometryDelegate
</span><span class="cx"> @property (nonatomic, readwrite, getter=isMinimized) BOOL minimized;
</span><span class="cx"> - (void)prepareForImplBoundsHeightChange:(CGFloat)endDelta suppressNotification:(BOOL)suppressNotification;
</span><span class="lines">@@ -216,12 +241,17 @@
</span><span class="cx"> @property (nonatomic, setter=_setMagnifierEnabled:) BOOL _magnifierEnabled;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><ins>+@interface UIResponder (Details)
+- (void)_handleKeyUIEvent:(UIEvent *)event;
+@end
+
</ins><span class="cx"> @interface UIScrollView (Details)
</span><span class="cx"> - (void)_stopScrollingAndZoomingAnimations;
</span><span class="cx"> - (void)_zoomToCenter:(CGPoint)center scale:(CGFloat)scale duration:(CFTimeInterval)duration force:(BOOL)force;
</span><span class="cx"> - (void)_zoomToCenter:(CGPoint)center scale:(CGFloat)scale duration:(CFTimeInterval)duration;
</span><span class="cx"> @property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
</span><span class="cx"> @property (nonatomic, readonly, getter=_isAnimatingZoom) BOOL isAnimatingZoom;
</span><ins>+@property (nonatomic, readonly, getter=_isAnimatingScroll) BOOL isAnimatingScroll;
</ins><span class="cx"> @property (nonatomic) CGFloat horizontalScrollDecelerationFactor;
</span><span class="cx"> @property (nonatomic) CGFloat verticalScrollDecelerationFactor;
</span><span class="cx"> @end
</span><span class="lines">@@ -743,4 +773,7 @@
</span><span class="cx"> extern const float UIWebViewScalesToFitScale;
</span><span class="cx"> extern const float UIWebViewStandardViewportWidth;
</span><span class="cx"> 
</span><ins>+extern NSString *const UIKeyInputPageUp;
+extern NSString *const UIKeyInputPageDown;
+
</ins><span class="cx"> WTF_EXTERN_C_END
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (186072 => 186073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2015-06-29 17:33:59 UTC (rev 186072)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2015-06-29 17:43:46 UTC (rev 186073)
</span><span class="lines">@@ -110,6 +110,7 @@
</span><span class="cx"> - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
</span><span class="cx"> - (BOOL)_isScrollingToTop;
</span><span class="cx"> - (BOOL)_isInterruptingDeceleration;
</span><ins>+- (CGPoint)_animatedTargetOffset;
</ins><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> @interface UIPeripheralHost(UIKitInternal)
</span><span class="lines">@@ -1197,6 +1198,18 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_scrollByOffset:(WebCore::FloatPoint)offset
+{
+    CGPoint currentOffset = ([_scrollView _isAnimatingScroll]) ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
+
+    CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + offset);
+    
+    if (CGPointEqualToPoint(boundedOffset, currentOffset))
+        return;
+    [_contentView willStartZoomOrScroll];
+    [_scrollView setContentOffset:boundedOffset animated:YES];
+}
+
</ins><span class="cx"> - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
</span><span class="cx"> {
</span><span class="cx">     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewInternalh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h (186072 => 186073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h        2015-06-29 17:33:59 UTC (rev 186072)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h        2015-06-29 17:43:46 UTC (rev 186073)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffset;
</span><span class="cx"> - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance;
</span><ins>+- (void)_scrollByOffset:(WebCore::FloatPoint)offset;
</ins><span class="cx"> - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRect selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll;
</span><span class="cx"> - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance;
</span><span class="cx"> - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm (186072 => 186073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2015-06-29 17:33:59 UTC (rev 186072)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2015-06-29 17:43:46 UTC (rev 186073)
</span><span class="lines">@@ -61,12 +61,17 @@
</span><span class="cx"> #import &lt;WebCore/CoreGraphicsSPI.h&gt;
</span><span class="cx"> #import &lt;WebCore/FloatQuad.h&gt;
</span><span class="cx"> #import &lt;WebCore/Pasteboard.h&gt;
</span><ins>+#import &lt;WebCore/Scrollbar.h&gt;
</ins><span class="cx"> #import &lt;WebCore/SoftLinking.h&gt;
</span><span class="cx"> #import &lt;WebCore/WebEvent.h&gt;
</span><span class="cx"> #import &lt;WebKit/WebSelectionRect.h&gt; // FIXME: WK2 should not include WebKit headers!
</span><span class="cx"> #import &lt;WebKitSystemInterfaceIOS.h&gt;
</span><span class="cx"> #import &lt;wtf/RetainPtr.h&gt;
</span><span class="cx"> 
</span><ins>+@interface UIEvent(UIEventInternal)
+@property (nonatomic, assign) UIKeyboardInputFlags _inputFlags;
+@end
+
</ins><span class="cx"> using namespace WebCore;
</span><span class="cx"> using namespace WebKit;
</span><span class="cx"> 
</span><span class="lines">@@ -1423,6 +1428,9 @@
</span><span class="cx"> {
</span><span class="cx">     BOOL hasWebSelection = _webSelectionAssistant &amp;&amp; !CGRectIsEmpty(_webSelectionAssistant.get().selectionFrame);
</span><span class="cx"> 
</span><ins>+    if (action == @selector(_arrowKey:))
+        return [self isFirstResponder];
+        
</ins><span class="cx">     if (action == @selector(_showTextStyleOptions:))
</span><span class="cx">         return _page-&gt;editorState().isContentRichlyEditable &amp;&amp; _page-&gt;editorState().selectionIsRange &amp;&amp; !_showingTextStyleOptions;
</span><span class="cx">     if (_showingTextStyleOptions)
</span><span class="lines">@@ -2144,10 +2152,46 @@
</span><span class="cx"> 
</span><span class="cx"> - (NSArray *)keyCommands
</span><span class="cx"> {
</span><del>-    return @[[UIKeyCommand keyCommandWithInput:@&quot;\t&quot; modifierFlags:0 action:@selector(_nextAccessoryTab:)],
-             [UIKeyCommand keyCommandWithInput:@&quot;\t&quot; modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]];
</del><ins>+    static NSArray* nonEditableKeyCommands = [@[
+       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
+       
+       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
+       
+       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
+       
+       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
+       
+       [UIKeyCommand keyCommandWithInput:@&quot; &quot; modifierFlags:0 action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:@&quot; &quot; modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
+       
+       [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
+       [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
+    ] retain];
+
+    static NSArray* editableKeyCommands = [@[
+       [UIKeyCommand keyCommandWithInput:@&quot;\t&quot; modifierFlags:0 action:@selector(_nextAccessoryTab:)],
+       [UIKeyCommand keyCommandWithInput:@&quot;\t&quot; modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
+    ] retain];
+    
+    return (_page-&gt;editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_arrowKey:(id)sender
+{
+    UIKeyCommand* command = sender;
+    [self handleKeyEvent:command._triggeringEvent];
+}
+
</ins><span class="cx"> - (void)_nextAccessoryTab:(id)sender
</span><span class="cx"> {
</span><span class="cx">     [self accessoryTab:YES];
</span><span class="lines">@@ -2543,6 +2587,32 @@
</span><span class="cx">     return YES;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_handleKeyUIEvent:(::UIEvent *)event
+{
+    // We only want to handle key event from the hardware keyboard when we are
+    // first responder and we are not interacting with editable content.
+    if ([self isFirstResponder] &amp;&amp; event._hidEvent &amp;&amp; !_page-&gt;editorState().isContentEditable)
+        [self handleKeyEvent:event];
+
+    [super _handleKeyUIEvent:event];
+}
+
+- (void)handleKeyEvent:(::UIEvent *)event
+{
+    ::WebEvent *webEvent = [[[::WebEvent alloc] initWithKeyEventType:(event._isKeyDown) ? WebEventKeyDown : WebEventKeyUp
+                                                           timeStamp:event.timestamp
+                                                          characters:event._modifiedInput
+                                         charactersIgnoringModifiers:event._unmodifiedInput
+                                                           modifiers:event._modifierFlags
+                                                         isRepeating:(event._inputFlags &amp; kUIKeyboardInputRepeat)
+                                                           withFlags:event._inputFlags
+                                                             keyCode:0
+                                                            isTabKey:[event._modifiedInput isEqualToString:@&quot;\t&quot;]
+                                                        characterSet:WebEventCharacterSetUnicode] autorelease];
+    
+    [self handleKeyWebEvent:webEvent];    
+}
+
</ins><span class="cx"> - (void)handleKeyWebEvent:(WebIOSEvent *)theEvent
</span><span class="cx"> {
</span><span class="cx">     _page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent));
</span><span class="lines">@@ -2565,88 +2635,96 @@
</span><span class="cx">     static const unsigned kWebBackspaceKey = 0x0008;
</span><span class="cx">     static const unsigned kWebReturnKey = 0x000D;
</span><span class="cx">     static const unsigned kWebDeleteKey = 0x007F;
</span><del>-    static const unsigned kWebLeftArrowKey = 0x00AC;
-    static const unsigned kWebUpArrowKey = 0x00AD;
-    static const unsigned kWebRightArrowKey = 0x00AE;
-    static const unsigned kWebDownArrowKey = 0x00AF;
</del><span class="cx">     static const unsigned kWebDeleteForwardKey = 0xF728;
</span><ins>+    static const unsigned kWebSpaceKey = 0x20;
</ins><span class="cx"> 
</span><span class="cx">     if (!_page-&gt;editorState().isContentEditable &amp;&amp; event.isTabKey)
</span><span class="cx">         return NO;
</span><span class="cx"> 
</span><span class="cx">     BOOL shift = event.modifierFlags &amp; WebEventFlagMaskShift;
</span><ins>+    BOOL command = event.modifierFlags &amp; WebEventFlagMaskCommand;
+    BOOL option = event.modifierFlags &amp; WebEventFlagMaskAlternate;
+    NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
+    BOOL shouldScroll = YES;
+    FloatPoint scrollOffset;
</ins><span class="cx"> 
</span><del>-    switch (event.characterSet) {
-    case WebEventCharacterSetSymbol: {
-        String command;
-        NSString *characters = [event charactersIgnoringModifiers];
-        if ([characters length] == 0)
-            break;
-        switch ([characters characterAtIndex:0]) {
-        case kWebLeftArrowKey:
-            command = shift ? ASCIILiteral(&quot;moveLeftAndModifySelection&quot;) :  ASCIILiteral(&quot;moveLeft&quot;);
-            break;
</del><ins>+    if ([charactersIgnoringModifiers isEqualToString:UIKeyInputLeftArrow])
+        scrollOffset.setX(-Scrollbar::pixelsPerLineStep());
+    else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputUpArrow]) {
+        if (option)
+            scrollOffset.setY(-_page-&gt;unobscuredContentRect().height());
+        else if (command)
+            scrollOffset.setY(-[self bounds].size.height);
+        else
+            scrollOffset.setY(-Scrollbar::pixelsPerLineStep());
+    } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputRightArrow])
+            scrollOffset.setX(Scrollbar::pixelsPerLineStep());
+    else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputDownArrow]) {
+        if (option)
+            scrollOffset.setY(_page-&gt;unobscuredContentRect().height());
+        else if (command)
+            scrollOffset.setY([self bounds].size.height);
+        else
+            scrollOffset.setY(Scrollbar::pixelsPerLineStep());
+    } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageDown])
+        scrollOffset.setY(_page-&gt;unobscuredContentRect().height());
+    else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageUp])
+        scrollOffset.setY(-_page-&gt;unobscuredContentRect().height());
+    else
+        shouldScroll = NO;
</ins><span class="cx"> 
</span><del>-        case kWebUpArrowKey:
-            command = shift ? ASCIILiteral(&quot;moveUpAndModifySelection&quot;) :  ASCIILiteral(&quot;moveUp&quot;);
-            break;
</del><ins>+    if (shouldScroll) {
+        [_webView _scrollByOffset:scrollOffset];
+        return YES;
+    }
</ins><span class="cx"> 
</span><del>-        case kWebRightArrowKey:
-            command = shift ? ASCIILiteral(&quot;moveRightAndModifySelection&quot;) :  ASCIILiteral(&quot;moveRight&quot;);
-            break;
</del><ins>+    UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance];
+    NSString *characters = [event characters];
+    
+    if (![characters length])
+        return NO;
</ins><span class="cx"> 
</span><del>-        case kWebDownArrowKey:
-            command = shift ? ASCIILiteral(&quot;moveDownAndModifySelection&quot;) :  ASCIILiteral(&quot;moveDown&quot;);
-            break;
</del><ins>+    switch ([characters characterAtIndex:0]) {
+    case kWebBackspaceKey:
+    case kWebDeleteKey:
+        // FIXME: remove deleteFromInput once UIKit adopts deleteFromInputWithFlags
+        if ([keyboard respondsToSelector:@selector(deleteFromInputWithFlags:)])
+            [keyboard deleteFromInputWithFlags:event.keyboardFlags];
+        else
+            [keyboard deleteFromInput];
+        return YES;
+
+    case kWebSpaceKey:
+        if (!_page-&gt;editorState().isContentEditable) {
+            [_webView _scrollByOffset:FloatPoint(0, shift ? -_page-&gt;unobscuredContentRect().height() : _page-&gt;unobscuredContentRect().height())];
+            return YES;
</ins><span class="cx">         }
</span><del>-        if (!command.isEmpty()) {
-            _page-&gt;executeEditCommand(command);
</del><ins>+        if (isCharEvent) {
+            [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
</ins><span class="cx">             return YES;
</span><span class="cx">         }
</span><span class="cx">         break;
</span><del>-    }
-    case WebEventCharacterSetASCII:
-    case WebEventCharacterSetUnicode: {
-        NSString *characters = [event characters];
-        if ([characters length] == 0)
-            break;
-        UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance];
-        switch ([characters characterAtIndex:0]) {
-        case kWebBackspaceKey:
-        case kWebDeleteKey:
-            // FIXME: remove deleteFromInput once UIKit adopts deleteFromInputWithFlags
-            if ([keyboard respondsToSelector:@selector(deleteFromInputWithFlags:)])
-                [keyboard deleteFromInputWithFlags:event.keyboardFlags];
-            else
-                [keyboard deleteFromInput];
</del><ins>+
+    case kWebEnterKey:
+    case kWebReturnKey:
+        if (isCharEvent) {
+            // Map \r from HW keyboard to \n to match the behavior of the soft keyboard.
+            [keyboard addInputString:@&quot;\n&quot; withFlags:0];
</ins><span class="cx">             return YES;
</span><ins>+        }
+        break;
</ins><span class="cx"> 
</span><del>-        case kWebEnterKey:
-        case kWebReturnKey:
-            if (isCharEvent) {
-                // Map \r from HW keyboard to \n to match the behavior of the soft keyboard.
-                [keyboard addInputString:@&quot;\n&quot; withFlags:0];
-                return YES;
-            }
-            return NO;
</del><ins>+    case kWebDeleteForwardKey:
+        _page-&gt;executeEditCommand(ASCIILiteral(&quot;deleteForward&quot;));
+        return YES;
</ins><span class="cx"> 
</span><del>-        case kWebDeleteForwardKey:
-            _page-&gt;executeEditCommand(ASCIILiteral(&quot;deleteForward&quot;));
</del><ins>+    default:
+        if (isCharEvent) {
+            [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
</ins><span class="cx">             return YES;
</span><del>-
-        default: {
-            if (isCharEvent) {
-                [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
-                return YES;
-            }
-            return NO;
</del><span class="cx">         }
</span><del>-    }
</del><span class="cx">         break;
</span><span class="cx">     }
</span><del>-    default:
-        return NO;
-    }
</del><span class="cx"> 
</span><span class="cx">     return NO;
</span><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>