WebCore: Support Appkit key bindings and custom key bindings in WebKit2
authorenrica@apple.com <enrica@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Oct 2010 00:04:50 +0000 (00:04 +0000)
committerenrica@apple.com <enrica@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Oct 2010 00:04:50 +0000 (00:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=48271
<rdar://problem/7660723>

Reviewed by Alexey Proskuryakov.

* dom/KeyboardEvent.h:
(WebCore::KeypressCommand::KeypressCommand): Added default constructor and modified assert for text
insertion command constructor to support WebKit2 scenarios.
The default constructor is needed to support the generated WebKit2 message processing code.

WebKit2: Support Appkit key bindings and custom key bindings in WebKit2
https://bugs.webkit.org/show_bug.cgi?id=48271
<rdar://problem/7660723>

Reviewed by Alexey Proskuryakov.

We need to support AppKit key bindings and custom key bindings in WebKit2.
Every keyboard event is first sent to the WebProcess and we inform AppKit that
we don't need further processing. When the event is processed by the WebProcess and no handler consumes it,
we send a synchronous message back to the UI process to let AppKit perform the key bindings.
This operation can result in one or more editing commands to execute or a noop.
The WebProcess then replies back to the UI process to inform whether further processing is required, in
which case the event is sent back to the application to be mapped.
There is a potential for a race condition: in case the WebProcess is very slow to process the key events, the UI
process could be in a completely different state when the event is being resent (for example it might not have the
keyboard focus anymore) and the command could be lost. We should look out for user experience impact.

* Scripts/webkit2/messages.py: Added header file in the generated file.
* Shared/WebCoreArgumentCoders.h: Added encoder/decoder for KeypressCommand.
* UIProcess/API/mac/PageClientImpl.h:
* UIProcess/API/mac/PageClientImpl.mm:
(WebKit::PageClientImpl::interceptKeyEvent): Pass-through call to WKView.
(WebKit::PageClientImpl::didNotHandleKeyEvent): Added logic to resend the event to the application.
* UIProcess/API/mac/WKView.mm:
(-[WKView validateUserInterfaceItem:]): Changed the default return value to YES, otherwise no menu shortcut is performed.
(-[WKView doCommandBySelector:]): Added.
(-[WKView insertText:]): Added.
(-[WKView _handleStyleKeyEquivalent:]): Added to handle command-B and command-I.
(-[WKView performKeyEquivalent:]): Added to intercept key binding sequences.
(-[WKView _setEventBeingResent:]):
(-[WKView _interceptKeyEvent:]):
* UIProcess/API/mac/WKViewInternal.h:
* UIProcess/PageClient.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::interpretKeyEvent):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* WebProcess/WebCoreSupport/WebEditorClient.cpp:
* WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm:
(WebKit::WebEditorClient::handleKeyboardEvent):
(WebKit::WebEditorClient::handleInputMethodKeydown):
* WebProcess/WebPage/WebPage.cpp:
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/mac/WebPageMac.mm: Moved handleKeyboardEvent and handleInputMethodKeydown
to the Mac specific implementation.
(WebKit::WebPage::interceptEditingKeyboardEvent): Added.

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

18 files changed:
WebCore/ChangeLog
WebCore/dom/KeyboardEvent.h
WebKit2/ChangeLog
WebKit2/Scripts/webkit2/messages.py
WebKit2/Shared/WebCoreArgumentCoders.h
WebKit2/UIProcess/API/mac/PageClientImpl.h
WebKit2/UIProcess/API/mac/PageClientImpl.mm
WebKit2/UIProcess/API/mac/WKView.mm
WebKit2/UIProcess/API/mac/WKViewInternal.h
WebKit2/UIProcess/PageClient.h
WebKit2/UIProcess/WebPageProxy.cpp
WebKit2/UIProcess/WebPageProxy.h
WebKit2/UIProcess/WebPageProxy.messages.in
WebKit2/WebProcess/WebCoreSupport/WebEditorClient.cpp
WebKit2/WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm
WebKit2/WebProcess/WebPage/WebPage.cpp
WebKit2/WebProcess/WebPage/WebPage.h
WebKit2/WebProcess/WebPage/mac/WebPageMac.mm

index 3acb7ab..9cb43d9 100644 (file)
@@ -1,3 +1,16 @@
+2010-10-27  Enrica Casucci  <enrica@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Support Appkit key bindings and custom key bindings in WebKit2
+        https://bugs.webkit.org/show_bug.cgi?id=48271
+        <rdar://problem/7660723>
+
+        * dom/KeyboardEvent.h:
+        (WebCore::KeypressCommand::KeypressCommand): Added default constructor and modified assert for text
+        insertion command constructor to support WebKit2 scenarios.
+        The default constructor is needed to support the generated WebKit2 message processing code.
+
 2010-10-26  Darin Adler  <darin@apple.com>
 
         Reviewed by Sam Weinig.
index 793ac41..30a2ef0 100644 (file)
@@ -33,8 +33,9 @@ namespace WebCore {
 
 #if PLATFORM(MAC)
     struct KeypressCommand {
-        KeypressCommand(const String& commandName) : commandName(commandName) {}
-        KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { ASSERT(commandName == "insertText:"); }
+        KeypressCommand() { }
+        KeypressCommand(const String& commandName) : commandName(commandName) { }
+        KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { ASSERT(commandName == "insertText:" || commandName == "insertText"); }
 
         String commandName;
         String text;
index 67ba6f1..3da8e35 100644 (file)
@@ -1,3 +1,52 @@
+2010-10-27  Enrica Casucci  <enrica@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Support Appkit key bindings and custom key bindings in WebKit2
+        https://bugs.webkit.org/show_bug.cgi?id=48271
+        <rdar://problem/7660723>
+        
+        We need to support AppKit key bindings and custom key bindings in WebKit2.
+        Every keyboard event is first sent to the WebProcess and we inform AppKit that
+        we don't need further processing. When the event is processed by the WebProcess and no handler consumes it,
+        we send a synchronous message back to the UI process to let AppKit perform the key bindings.
+        This operation can result in one or more editing commands to execute or a noop.
+        The WebProcess then replies back to the UI process to inform whether further processing is required, in
+        which case the event is sent back to the application to be mapped.
+        There is a potential for a race condition: in case the WebProcess is very slow to process the key events, the UI
+        process could be in a completely different state when the event is being resent (for example it might not have the
+        keyboard focus anymore) and the command could be lost. We should look out for user experience impact.
+
+        * Scripts/webkit2/messages.py: Added header file in the generated file.
+        * Shared/WebCoreArgumentCoders.h: Added encoder/decoder for KeypressCommand.
+        * UIProcess/API/mac/PageClientImpl.h:
+        * UIProcess/API/mac/PageClientImpl.mm:
+        (WebKit::PageClientImpl::interceptKeyEvent): Pass-through call to WKView.
+        (WebKit::PageClientImpl::didNotHandleKeyEvent): Added logic to resend the event to the application.
+        * UIProcess/API/mac/WKView.mm:
+        (-[WKView validateUserInterfaceItem:]): Changed the default return value to YES, otherwise no menu shortcut is performed.
+        (-[WKView doCommandBySelector:]): Added.
+        (-[WKView insertText:]): Added.
+        (-[WKView _handleStyleKeyEquivalent:]): Added to handle command-B and command-I.
+        (-[WKView performKeyEquivalent:]): Added to intercept key binding sequences.
+        (-[WKView _setEventBeingResent:]):
+        (-[WKView _interceptKeyEvent:]):
+        * UIProcess/API/mac/WKViewInternal.h:
+        * UIProcess/PageClient.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::interpretKeyEvent):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+        * WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm:
+        (WebKit::WebEditorClient::handleKeyboardEvent):
+        (WebKit::WebEditorClient::handleInputMethodKeydown):
+        * WebProcess/WebPage/WebPage.cpp:
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/mac/WebPageMac.mm: Moved handleKeyboardEvent and handleInputMethodKeydown
+        to the Mac specific implementation.
+        (WebKit::WebPage::interceptEditingKeyboardEvent): Added.
+
 2010-10-27  Brian Weinstein  <bweinstein@apple.com>
 
         More Windows build fixage. Copy another new header into WebKitOutputDir.
index bba89cf..b36ef72 100644 (file)
@@ -226,6 +226,7 @@ def message_to_struct_declaration(message):
 
 def struct_or_class(namespace, type):
     structs = frozenset([
+        'WebCore::KeypressCommand',
         'WebCore::ViewportArguments',
         'WebCore::WindowFeatures',
         'WebKit::WebPageCreationParameters',
index 2055991..8c5aa20 100644 (file)
@@ -33,6 +33,7 @@
 #include <WebCore/Cursor.h>
 #include <WebCore/FloatRect.h>
 #include <WebCore/IntRect.h>
+#include <WebCore/KeyboardEvent.h>
 #include <WebCore/PluginData.h>
 #include <WebCore/ResourceError.h>
 #include <WebCore/ResourceRequest.h>
@@ -229,6 +230,20 @@ template<> struct ArgumentCoder<WebCore::WindowFeatures> {
     }
 };
 
+#if PLATFORM(MAC)
+template<> struct ArgumentCoder<WebCore::KeypressCommand> {
+    static void encode(ArgumentEncoder* encoder, const WebCore::KeypressCommand& keypressCommand)
+    {
+        encoder->encode(CoreIPC::In(keypressCommand.commandName, keypressCommand.text));
+    }
+    
+    static bool decode(ArgumentDecoder* decoder, WebCore::KeypressCommand& keypressCommand)
+    {
+        return decoder->decode(CoreIPC::Out(keypressCommand.commandName, keypressCommand.text));
+    }
+};
+#endif
+    
 } // namespace CoreIPC
 
 #endif // WebCoreArgumentCoders_h
index dd62b6a..8fc7376 100644 (file)
@@ -58,6 +58,7 @@ private:
     virtual void registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo);
     virtual void clearAllEditCommands();
     virtual void setEditCommandState(const String& commandName, bool isEnabled, int state);
+    virtual void interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandName);
 
     virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&);
     virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&);
index 6cc3f61..d623ade 100644 (file)
@@ -23,6 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#import "NativeWebKeyboardEvent.h"
 #import "PageClientImpl.h"
 
 #import "FindIndicator.h"
@@ -34,6 +35,7 @@
 #import <WebCore/Cursor.h>
 #import <WebCore/FloatRect.h>
 #import <WebCore/FoundationExtras.h>
+#import <webCore/KeyboardEvent.h>
 #import <wtf/PassOwnPtr.h>
 #import <wtf/text/CString.h>
 #import <wtf/text/WTFString.h>
@@ -214,6 +216,11 @@ void PageClientImpl::setEditCommandState(const String& commandName, bool isEnabl
     [m_wkView _setUserInterfaceItemState:nsStringFromWebCoreString(commandName) enabled:isEnabled state:newState];
 }
 
+void PageClientImpl::interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandsList)
+{
+    commandsList = [m_wkView _interceptKeyEvent:event.nativeEvent()];
+}
+
 FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect)
 {
     return [m_wkView _convertToDeviceSpace:rect];
@@ -224,8 +231,13 @@ FloatRect PageClientImpl::convertToUserSpace(const FloatRect& rect)
     return [m_wkView _convertToUserSpace:rect];
 }
 
-void PageClientImpl::didNotHandleKeyEvent(const NativeWebKeyboardEvent&)
+void PageClientImpl::didNotHandleKeyEvent(const NativeWebKeyboardEvent& event)
 {
+    NSEvent* nativeEvent = event.nativeEvent();
+    if ([nativeEvent type] == NSKeyDown) {
+        [m_wkView _setEventBeingResent:nativeEvent];
+        [[NSApplication sharedApplication] sendEvent:nativeEvent];
+    }
 }
 
 PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy()
index e34fa88..3d0a4bf 100644 (file)
@@ -47,6 +47,7 @@
 #import <QuartzCore/QuartzCore.h>
 #import <WebCore/FloatRect.h>
 #import <WebCore/IntRect.h>
+#import <WebCore/KeyboardEvent.h>
 #import <WebCore/PlatformScreen.h>
 #import <wtf/RefPtr.h>
 #import <wtf/RetainPtr.h>
@@ -87,6 +88,11 @@ struct EditCommandState {
     HashMap<String, EditCommandState> _menuMap;
 
     OwnPtr<FindIndicatorWindow> _findIndicatorWindow;
+    // We keep here the event when resending it to
+    // the application to distinguish the case of a new event from one 
+    // that has been already sent to WebCore.
+    NSEvent *_keyDownEventBeingResent;
+    Vector<KeypressCommand> _commandsList;
 }
 @end
 
@@ -260,7 +266,7 @@ WEBCORE_COMMAND(selectAll)
         return info.m_isEnabled;
     }
 
-    return NO;
+    return YES;
 }
 
 // Events
@@ -296,6 +302,86 @@ EVENT_HANDLER(scrollWheel, Wheel)
 
 #undef EVENT_HANDLER
 
+- (void)doCommandBySelector:(SEL)selector
+{
+    if (selector != @selector(noop:))
+        _data->_commandsList.append(KeypressCommand(commandNameForSelector(selector)));
+}
+
+- (void)insertText:(id)string
+{
+    _data->_commandsList.append(KeypressCommand("insertText", string));
+}
+
+- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
+{
+    if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
+        return NO;
+    
+    // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason.
+    // This should not be changed, since it could break some Mac applications that
+    // rely on this inherent behavior.
+    // See https://bugs.webkit.org/show_bug.cgi?id=24943
+    
+    NSString *string = [event characters];
+    if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
+        _data->_page->executeEditCommand("ToggleBold");
+        return YES;
+    }
+    if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
+        _data->_page->executeEditCommand("ToggleItalic");
+        return YES;
+    }
+    
+    return NO;
+}
+
+- (BOOL)performKeyEquivalent:(NSEvent *)event
+{
+    // There's a chance that responding to this event will run a nested event loop, and
+    // fetching a new event might release the old one. Retaining and then autoreleasing
+    // the current event prevents that from causing a problem inside WebKit or AppKit code.
+    [[event retain] autorelease];
+    
+    BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
+    BOOL ret = NO;
+    
+    [_data->_keyDownEventBeingResent release];
+    _data->_keyDownEventBeingResent = nil;
+    
+    [self retain];
+    
+    // Pass key combos through WebCore if there is a key binding available for
+    // this event. This lets web pages have a crack at intercepting key-modified keypresses.
+    // But don't do it if we have already handled the event.
+    // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
+    if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) {
+        _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self));
+        return YES;
+    }
+    
+    ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
+    
+    [self release];
+    
+    return ret;
+}
+
+- (void)_setEventBeingResent:(NSEvent *)event
+{
+    _data->_keyDownEventBeingResent = [event retain];
+}
+
+- (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent
+{
+    _data->_commandsList.clear();
+    // interpretKeyEvents will trigger one or more calls to doCommandBySelector or setText
+    // that will populate the commandsList vector.
+    [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+    
+    return _data->_commandsList;
+}
+
 - (void)keyUp:(NSEvent *)theEvent
 {
     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
index b6a8804..e13b069 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #import "WKView.h"
+#import <WebCore/KeyboardEvent.h>
 
 namespace WebKit {
     class FindIndicator;
@@ -36,7 +37,8 @@ namespace WebKit {
 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip;
 - (void)_setCursor:(NSCursor *)cursor;
 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState;
-
+- (Vector<WebCore::KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent;
+- (void)_setEventBeingResent:(NSEvent *)event;
 - (NSRect)_convertToDeviceSpace:(NSRect)rect;
 - (NSRect)_convertToUserSpace:(NSRect)rect;
 
index 5c5c726..b94f0ad 100644 (file)
@@ -40,6 +40,7 @@ namespace WebKit {
 class FindIndicator;
 class NativeWebKeyboardEvent;
 class WebEditCommandProxy;
+class NativeWebKeyboardEvent;
 class WebPopupMenuProxy;
 
 class PageClient {
@@ -62,7 +63,9 @@ public:
     virtual void registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo) = 0;
     virtual void clearAllEditCommands() = 0;
     virtual void setEditCommandState(const String& commandName, bool isEnabled, int state) = 0;
-
+#if PLATFORM(MAC)
+    virtual void interceptKeyEvent(const NativeWebKeyboardEvent&, Vector<WebCore::KeypressCommand>&) = 0;
+#endif
     virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&) = 0;
     virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&) = 0;
 
index ea20d82..2de6dcd 100644 (file)
@@ -619,6 +619,13 @@ void WebPageProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIP
     didReceiveSyncWebPageProxyMessage(connection, messageID, arguments, reply);
 }
 
+#if PLATFORM(MAC)
+void WebPageProxy::interpretKeyEvent(uint32_t type, Vector<KeypressCommand>& commandsList)
+{
+    m_pageClient->interceptKeyEvent(m_keyEventQueue.first(), commandsList);
+}
+#endif
+
 void WebPageProxy::didCreateMainFrame(uint64_t frameID)
 {
     ASSERT(!m_mainFrame);
index 742f23d..88a43aa 100644 (file)
@@ -43,6 +43,7 @@
 #include "WebUIClient.h"
 #include <WebCore/EditAction.h>
 #include <WebCore/FrameLoaderTypes.h>
+#include <WebCore/KeyboardEvent.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/OwnPtr.h>
@@ -296,6 +297,11 @@ private:
     void registerEditCommandForUndo(uint64_t commandID, uint32_t editAction);
     void clearAllEditCommands();
 
+#if PLATFORM(MAC)
+    // Keyboard handling
+    void interpretKeyEvent(uint32_t eventType, Vector<WebCore::KeypressCommand>&);
+#endif
+    
     // Find.
     void didCountStringMatches(const String&, uint32_t matchCount);
     void setFindIndicator(const WebCore::FloatRect& selectionRect, const Vector<WebCore::FloatRect>& textRects, const SharedMemory::Handle& contentImageHandle, bool fadeOut);
index fe88cfc..69d4a48 100644 (file)
@@ -82,6 +82,11 @@ messages -> WebPageProxy {
     DidGetSourceForFrame(WTF::String resultString, uint64_t callbackID)
     DidRunJavaScriptInMainFrame(WTF::String resultString, uint64_t callbackID)
 
+#if PLATFORM(MAC)
+    # Keyboard support
+    InterpretKeyEvent(uint32_t type) -> (Vector<WebCore::KeypressCommand> commandName)
+#endif
+    
     # BackForward messages.
     BackForwardAddItem(uint64_t itemID)
     BackForwardGoToItem(uint64_t itemID)
index 5852df1..cde9be8 100644 (file)
@@ -245,6 +245,7 @@ void WebEditorClient::redo()
     notImplemented();
 }
 
+#if !PLATFORM(MAC)
 void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
 {
     if (m_page->handleEditingKeyboardEvent(event))
@@ -255,6 +256,7 @@ void WebEditorClient::handleInputMethodKeydown(KeyboardEvent*)
 {
     notImplemented();
 }
+#endif
 
 void WebEditorClient::textFieldDidBeginEditing(Element* element)
 {
index fb37409..fba0194 100644 (file)
 
 #include "WebPage.h"
 #include "WebFrame.h"
+#include "WebPageProxyMessages.h"
+#include "WebProcess.h"
 #include <WebCore/ArchiveResource.h>
 #include <WebCore/DocumentFragment.h>
 #include <WebCore/DOMDocumentFragmentInternal.h>
 #include <WebCore/DOMDocumentInternal.h>
 #include <WebCore/Frame.h>
+#include <WebCore/KeyboardEvent.h>
 #include <WebKit/WebResource.h>
 #include <WebKit/WebNSURLExtras.h>
 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
@@ -58,6 +61,18 @@ using namespace WTF;
 
 namespace WebKit {
 
+void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
+{
+    if (m_page->interceptEditingKeyboardEvent(event, false))
+        event->setDefaultHandled();
+}
+
+void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* event)
+{
+    if (m_page->interceptEditingKeyboardEvent(event, true))
+        event->setDefaultHandled();
+}
+    
 NSString *WebEditorClient::userVisibleString(NSURL *url)
 {
     return [url _web_userVisibleString];
index 48b30a8..2a231b7 100644 (file)
@@ -782,6 +782,7 @@ WebInspector* WebPage::inspector()
     return m_inspector.get();
 }
 
+#if !PLATFORM(MAC)
 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* evt)
 {
     Node* node = evt->target()->toNode();
@@ -811,7 +812,8 @@ bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* evt)
 
     return frame->editor()->insertText(evt->keyEvent()->text(), evt);
 }
-
+#endif
+    
 WebEditCommand* WebPage::webEditCommand(uint64_t commandID)
 {
     return m_editCommandMap.get(commandID).get();
index 9728e54..0b8cb88 100644 (file)
@@ -108,7 +108,9 @@ public:
     void layoutIfNeeded();
 
     // -- Called from WebCore clients.
+#if !PLATFORM(MAC)
     bool handleEditingKeyboardEvent(WebCore::KeyboardEvent*);
+#endif
     void show();
     String userAgent() const;
     WebCore::IntRect windowResizerRect() const;
@@ -167,6 +169,7 @@ public:
     bool windowIsVisible() const { return m_windowIsVisible; }
     const WebCore::IntRect& windowFrame() const { return m_windowFrame; }
     bool windowIsFocused() const;
+    bool interceptEditingKeyboardEvent(WebCore::KeyboardEvent*, bool);
 #elif PLATFORM(WIN)
     HWND nativeWindow() const { return m_nativeWindow; }
 #endif
index 6150d8d..fb07f26 100644 (file)
@@ -26,6 +26,8 @@
 #include "WebPage.h"
 
 #include "WebEvent.h"
+#include "WebPageProxyMessages.h"
+#include "WebProcess.h"
 #include <WebCore/FocusController.h>
 #include <WebCore/Frame.h>
 #include <WebCore/KeyboardEvent.h>
@@ -46,139 +48,56 @@ void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
 {
 }
 
-// FIXME: Editor commands should not be hard coded and instead should come from AppKit.  
-
-static const unsigned CtrlKey   = 1 << 0;
-static const unsigned AltKey    = 1 << 1;
-static const unsigned ShiftKey  = 1 << 2;
-static const unsigned MetaKey   = 1 << 3;
-
-// Keys with special meaning. These will be delegated to the editor using
-// the execCommand() method
-struct KeyDownEntry {
-    unsigned virtualKey;
-    unsigned modifiers;
-    const char* name;
-};
-
-struct KeyPressEntry {
-    unsigned charCode;
-    unsigned modifiers;
-    const char* name;
-};
-
-static const KeyDownEntry keyDownEntries[] = {
-    { VK_LEFT,     0,                   "MoveLeft"                                      },
-    { VK_LEFT,     ShiftKey,            "MoveLeftAndModifySelection"                    },
-    { VK_LEFT,     AltKey,              "MoveWordLeft"                                  },
-    { VK_LEFT,     AltKey | ShiftKey,   "MoveWordLeftAndModifySelection"                },
-    { VK_RIGHT,    0,                   "MoveRight"                                     },
-    { VK_RIGHT,    ShiftKey,            "MoveRightAndModifySelection"                   },
-    { VK_RIGHT,    AltKey,              "MoveWordRight"                                 },
-    { VK_RIGHT,    AltKey | ShiftKey,   "MoveWordRightAndModifySelection"               },
-    { VK_UP,       0,                   "MoveUp"                                        },
-    { VK_UP,       ShiftKey,            "MoveUpAndModifySelection"                      },
-    { VK_PRIOR,    ShiftKey,            "MovePageUpAndModifySelection"                  },
-    { VK_DOWN,     0,                   "MoveDown"                                      },
-    { VK_DOWN,     ShiftKey,            "MoveDownAndModifySelection"                    },
-    { VK_NEXT,     ShiftKey,            "MovePageDownAndModifySelection"                },
-    { VK_PRIOR,    0,                   "MovePageUp"                                    },
-    { VK_NEXT,     0,                   "MovePageDown"                                  },
-
-    { VK_HOME,     0,                   "MoveToBeginningOfLine"                         },
-    { VK_HOME,     ShiftKey,            "MoveToBeginningOfLineAndModifySelection"       },
-    { VK_LEFT,     MetaKey,             "MoveToBeginningOfLine"                         },
-    { VK_LEFT,     MetaKey | ShiftKey,  "MoveToBeginningOfLineAndModifySelection"       },
-    { VK_UP,       MetaKey,             "MoveToBeginningOfDocument"                     },
-    { VK_UP,       MetaKey | ShiftKey,  "MoveToBeginningOfDocumentAndModifySelection"   },
-
-    { VK_END,      0,                   "MoveToEndOfLine"                               },
-    { VK_END,      ShiftKey,            "MoveToEndOfLineAndModifySelection"             },
-    { VK_DOWN,     MetaKey,             "MoveToEndOfDocument"                           },
-    { VK_DOWN,     MetaKey | ShiftKey,  "MoveToEndOfDocumentAndModifySelection"         },
-    { VK_RIGHT,    MetaKey,             "MoveToEndOfLine"                               },
-    { VK_RIGHT,    MetaKey | ShiftKey,  "MoveToEndOfLineAndModifySelection"             },
-
-    { VK_BACK,     0,                   "DeleteBackward"                                },
-    { VK_BACK,     ShiftKey,            "DeleteBackward"                                },
-    { VK_DELETE,   0,                   "DeleteForward"                                 },
-    { VK_BACK,     AltKey,              "DeleteWordBackward"                            },
-    { VK_DELETE,   AltKey,              "DeleteWordForward"                             },
-
-    { 'B',         CtrlKey,             "ToggleBold"                                    },
-    { 'I',         CtrlKey,             "ToggleItalic"                                  },
-
-    { VK_ESCAPE,   0,                   "Cancel"                                        },
-    { VK_OEM_PERIOD, CtrlKey,           "Cancel"                                        },
-    { VK_TAB,      0,                   "InsertTab"                                     },
-    { VK_TAB,      ShiftKey,            "InsertBacktab"                                 },
-    { VK_RETURN,   0,                   "InsertNewline"                                 },
-    { VK_RETURN,   CtrlKey,             "InsertNewline"                                 },
-    { VK_RETURN,   AltKey,              "InsertNewline"                                 },
-    { VK_RETURN,   AltKey | ShiftKey,   "InsertNewline"                                 },
-    { VK_RETURN,   ShiftKey,            "InsertLineBreak"                               },
-
-    { 'C',         MetaKey,             "Copy"                                          },
-    { 'V',         MetaKey,             "Paste"                                         },
-    { 'X',         MetaKey,             "Cut"                                           },
-    { 'A',         MetaKey,             "SelectAll"                                     },
-    { VK_INSERT,   CtrlKey,             "Copy"                                          },
-    { VK_INSERT,   ShiftKey,            "Paste"                                         },
-    { VK_DELETE,   ShiftKey,            "Cut"                                           },
-    { 'Z',         MetaKey,             "Undo"                                          },
-    { 'Z',         MetaKey | ShiftKey,  "Redo"                                          },
-};
-
-static const KeyPressEntry keyPressEntries[] = {
-    { '\t',        0,                   "InsertTab"                                     },
-    { '\t',        ShiftKey,            "InsertBacktab"                                 },
-    { '\r',        0,                   "InsertNewline"                                 },
-    { '\r',        CtrlKey,             "InsertNewline"                                 },
-    { '\r',        ShiftKey,            "InsertLineBreak"                               },
-    { '\r',        AltKey,              "InsertNewline"                                 },
-    { '\r',        AltKey | ShiftKey,   "InsertNewline"                                 },
-};
-
-const char* WebPage::interpretKeyEvent(const KeyboardEvent* evt)
+// FIXME: need to add support for input methods
+    
+bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveCommand)
 {
+    Node* node = evt->target()->toNode();
+    ASSERT(node);
+    Frame* frame = node->document()->frame();
+    ASSERT(frame);
+    
     const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
-    ASSERT(keyEvent);
-
-    static HashMap<int, const char*>* keyDownCommandsMap = 0;
-    static HashMap<int, const char*>* keyPressCommandsMap = 0;
-
-    if (!keyDownCommandsMap) {
-        keyDownCommandsMap = new HashMap<int, const char*>;
-        keyPressCommandsMap = new HashMap<int, const char*>;
-
-        for (unsigned i = 0; i < (sizeof(keyDownEntries) / sizeof(keyDownEntries[0])); i++) {
-            keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey,
-                                    keyDownEntries[i].name);
+    if (!keyEvent)
+        return false;
+    const Vector<KeypressCommand>& commands = evt->keypressCommands();
+    bool hasKeypressCommand = !commands.isEmpty();
+    
+    bool eventWasHandled = false;
+    
+    if (shouldSaveCommand && !hasKeypressCommand) {
+        Vector<KeypressCommand> commandsList;        
+        if (!WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(keyEvent->type()), 
+                                                         Messages::WebPageProxy::InterpretKeyEvent::Reply(commandsList),
+                                                         m_pageID, CoreIPC::Connection::NoTimeout))
+            return false;
+        for (size_t i = 0; i < commandsList.size(); i++)
+            evt->keypressCommands().append(commandsList[i]);
+    } else {
+        size_t size = commands.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 (frame->editor()->command(commands[i].commandName).isTextInsertion())
+                haveTextInsertionCommands = true;
         }
-
-        for (unsigned i = 0; i < (sizeof(keyPressEntries) / sizeof(keyPressEntries[0])); i++) {
-            keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode,
-                                     keyPressEntries[i].name);
+        if (!haveTextInsertionCommands || keyEvent->type() == PlatformKeyboardEvent::Char) {
+            for (size_t i = 0; i < size; ++i) {
+                if (commands[i].commandName == "insertText") {
+                    // Don't insert null or control characters as they can result in unexpected behaviour
+                    if (evt->charCode() < ' ')
+                        return false;
+                    eventWasHandled = frame->editor()->insertText(commands[i].text, evt);
+                } else
+                    if (frame->editor()->command(commands[i].commandName).isSupported())
+                        eventWasHandled = frame->editor()->command(commands[i].commandName).execute(evt);
+            }
         }
     }
-
-    unsigned modifiers = 0;
-    if (keyEvent->shiftKey())
-        modifiers |= ShiftKey;
-    if (keyEvent->altKey())
-        modifiers |= AltKey;
-    if (keyEvent->ctrlKey())
-        modifiers |= CtrlKey;
-    if (keyEvent->metaKey())
-        modifiers |= MetaKey;
-
-    if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) {
-        int mapKey = modifiers << 16 | evt->keyCode();
-        return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
-    }
-
-    int mapKey = modifiers << 16 | evt->charCode();
-    return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
+    return eventWasHandled;
 }
 
 static inline void scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity)