WebCore: Implement IME support for Mac.
authorenrica@apple.com <enrica@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Dec 2010 21:24:09 +0000 (21:24 +0000)
committerenrica@apple.com <enrica@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Dec 2010 21:24:09 +0000 (21:24 +0000)
<rdar://problem/7660589> WebKit2: Implement IME support for Mac.
https://bugs.webkit.org/show_bug.cgi?id=50788

Reviewed by Alexey Proskuryakov.

* dom/KeyboardEvent.h:
(WebCore::KeypressCommand::KeypressCommand): Removed ASSERT in constructor,
since it is now used for more than one command.

WebKit2: Implement IME support for Mac.
<rdar://problem/7660589> WebKit2: Implement IME support for Mac.
https://bugs.webkit.org/show_bug.cgi?id=50788

Reviewed by Alexey Proskuryakov.

This patch addes support for input methods in WebKit2.
In order to support IME, it was necessary to add synchronous calls from the UIProcess
to the WebProcess. These calls all have a timeout of 1 second.
The current implementation still uses the NSTextInput protocol, but the plan is to move
to the NSTextInputClient protocol. This has not been done yet for ease of comparison with
WebKit. attributedSubstringFromRange is the only method that has not been implemented, because
I have not yet decided what is the best way to send an NSAttributedString across the process
boundary.

* Platform/CoreIPC/HandleMessage.h:
(CoreIPC::callMemberFunction): Added template for member function.
with four reply arguments.
* Scripts/webkit2/messages.py: Added CompositionUnderline and relevant header.
* Shared/WebCoreArgumentCoders.h:
(CoreIPC::): Added encoder/decoder for CompositionUnderline.
* UIProcess/API/mac/PageClientImpl.h: Added parameters to interceptKeyEvent and selectionChanged.
* UIProcess/API/mac/PageClientImpl.mm:
(WebKit::PageClientImpl::selectionChanged): Added parameters.
(WebKit::PageClientImpl::interceptKeyEvent): Added parameters.
* UIProcess/API/mac/WKView.mm:
(-[WKView initWithFrame:pageNamespaceRef:pageGroupRef:]): Added initialization of new private members.
(-[WKView insertText:]):
(-[WKView _selectionChanged:isEditable:isPassword:hasMarkedText:range:]): Added parameters.
(-[WKView _interceptKeyEvent:hasComposition:start:end:lines:WebCore::]): Added parameters.
(-[WKView keyDown:]): Modified to reset state on each keyDown.
(-[WKView selectedRange]): Added.
(-[WKView hasMarkedText]): Added.
(-[WKView unmarkText]): Added.
(-[WKView validAttributesForMarkedText]): Added.
(extractUnderlines): Added.
(-[WKView setMarkedText:selectedRange:]): Added.
(-[WKView markedRange]): Added.
(-[WKView attributedSubstringFromRange:]): Added.
(-[WKView characterIndexForPoint:]): Added.
(-[WKView firstRectForCharacterRange:]): Added.
(-[WKView conversationIdentifier]): Added.
* UIProcess/API/mac/WKViewInternal.h: Added parameters to _interceptKeyEvent and _selectionChanged.
* UIProcess/PageClient.h: Added parameters to interpretKeyEvent and selectionChanged.
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::getMarkedRange): Added.
(WebKit::WebPageProxy::characterIndexForPoint): Added.
(WebKit::WebPageProxy::firstRectForCharacterRange): Added.
(WebKit::WebPageProxy::interpretKeyEvent): Additional parameters.
(WebKit::WebPageProxy::didSelectionChange): Additional parameters for Mac platform.
* UIProcess/WebPageProxy.h: Added new methods and parameters to didSelectionChange
and interpretKeyEvent.
* UIProcess/WebPageProxy.messages.in: Added parameters to interpretKeyEvent and selectionChanged messages.
* UIProcess/WebProcessProxy.h:
(WebKit::WebProcessProxy::sendSync): Added support for synchronous messages.
The default timeout is 1 second.
* WebProcess/WebCoreSupport/WebEditorClient.cpp: respondToChangedSelection is now
only for non Mac platform.
* WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm:
(WebKit::WebEditorClient::respondToChangedSelection): Added implementation for Mac platform.
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::didReceiveSyncMessage): Added.
* WebProcess/WebPage/WebPage.h: Added new methods and support for synchronous messages.
* WebProcess/WebPage/WebPage.messages.in: Added new messages.
* WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::interceptEditingKeyboardEvent): Added parameters.
(WebKit::WebPage::convertRangeToPlatformRange): Added.
(WebKit::WebPage::getMarkedRange): Added.
(WebKit::characterRangeAtPoint): Added.
(WebKit::WebPage::characterIndexForPoint): Added.
(WebKit::convertToRange): Added.
(WebKit::WebPage::firstRectForCharacterRange): Added.
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::didReceiveSyncMessage): Added.
* WebProcess/WebProcess.h: Added didReceiveSyncMessage.

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

25 files changed:
WebCore/ChangeLog
WebCore/dom/KeyboardEvent.h
WebKit2/ChangeLog
WebKit2/Platform/CoreIPC/HandleMessage.h
WebKit2/Platform/Logging.cpp
WebKit2/Platform/Logging.h
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/UIProcess/WebProcessProxy.h
WebKit2/WebProcess/WebCoreSupport/WebEditorClient.cpp
WebKit2/WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm
WebKit2/WebProcess/WebPage/WebPage.cpp
WebKit2/WebProcess/WebPage/WebPage.h
WebKit2/WebProcess/WebPage/WebPage.messages.in
WebKit2/WebProcess/WebPage/mac/WebPageMac.mm
WebKit2/WebProcess/WebProcess.cpp
WebKit2/WebProcess/WebProcess.h

index 27f759a..85a60a1 100644 (file)
@@ -1,3 +1,15 @@
+2010-12-09  Enrica Casucci  <enrica@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Implement IME support for Mac.
+        <rdar://problem/7660589> WebKit2: Implement IME support for Mac.
+        https://bugs.webkit.org/show_bug.cgi?id=50788
+
+        * dom/KeyboardEvent.h:
+        (WebCore::KeypressCommand::KeypressCommand): Removed ASSERT in constructor,
+        since it is now used for more than one command.
+
 2010-12-10  Jessie Berlin  <jberlin@apple.com>
 
         Windows build fix. Unreviewed.
index 02f9f63..eeaef80 100644 (file)
@@ -35,7 +35,7 @@ namespace WebCore {
     struct KeypressCommand {
         KeypressCommand() { }
         KeypressCommand(const String& commandName) : commandName(commandName) { }
-        KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { ASSERT(commandName == "insertText:" || commandName == "insertText"); }
+        KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { }
 
         String commandName;
         String text;
index 6c09962..0fc8203 100644 (file)
@@ -1,3 +1,81 @@
+2010-12-09  Enrica Casucci  <enrica@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Implement IME support for Mac.
+        <rdar://problem/7660589> WebKit2: Implement IME support for Mac.
+        https://bugs.webkit.org/show_bug.cgi?id=50788
+            
+        This patch addes support for input methods in WebKit2.
+        In order to support IME, it was necessary to add synchronous calls from the UIProcess
+        to the WebProcess. These calls all have a timeout of 1 second.
+        The current implementation still uses the NSTextInput protocol, but the plan is to move
+        to the NSTextInputClient protocol. This has not been done yet for ease of comparison with
+        WebKit. attributedSubstringFromRange is the only method that has not been implemented, because
+        I have not yet decided what is the best way to send an NSAttributedString across the process
+        boundary.
+
+        * Platform/CoreIPC/HandleMessage.h:
+        (CoreIPC::callMemberFunction): Added template for member function.
+        with four reply arguments.
+        * Scripts/webkit2/messages.py: Added CompositionUnderline and relevant header.
+        * Shared/WebCoreArgumentCoders.h:
+        (CoreIPC::): Added encoder/decoder for CompositionUnderline.
+        * UIProcess/API/mac/PageClientImpl.h: Added parameters to interceptKeyEvent and selectionChanged.
+        * UIProcess/API/mac/PageClientImpl.mm:
+        (WebKit::PageClientImpl::selectionChanged): Added parameters.
+        (WebKit::PageClientImpl::interceptKeyEvent): Added parameters.
+        * UIProcess/API/mac/WKView.mm:
+        (-[WKView initWithFrame:pageNamespaceRef:pageGroupRef:]): Added initialization of new private members.
+        (-[WKView insertText:]):
+        (-[WKView _selectionChanged:isEditable:isPassword:hasMarkedText:range:]): Added parameters.
+        (-[WKView _interceptKeyEvent:hasComposition:start:end:lines:WebCore::]): Added parameters.
+        (-[WKView keyDown:]): Modified to reset state on each keyDown.
+        (-[WKView selectedRange]): Added.
+        (-[WKView hasMarkedText]): Added.
+        (-[WKView unmarkText]): Added.
+        (-[WKView validAttributesForMarkedText]): Added.
+        (extractUnderlines): Added.
+        (-[WKView setMarkedText:selectedRange:]): Added.
+        (-[WKView markedRange]): Added.
+        (-[WKView attributedSubstringFromRange:]): Added.
+        (-[WKView characterIndexForPoint:]): Added.
+        (-[WKView firstRectForCharacterRange:]): Added.
+        (-[WKView conversationIdentifier]): Added.
+        * UIProcess/API/mac/WKViewInternal.h: Added parameters to _interceptKeyEvent and _selectionChanged.
+        * UIProcess/PageClient.h: Added parameters to interpretKeyEvent and selectionChanged.
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::getMarkedRange): Added.
+        (WebKit::WebPageProxy::characterIndexForPoint): Added.
+        (WebKit::WebPageProxy::firstRectForCharacterRange): Added.
+        (WebKit::WebPageProxy::interpretKeyEvent): Additional parameters.
+        (WebKit::WebPageProxy::didSelectionChange): Additional parameters for Mac platform.
+        * UIProcess/WebPageProxy.h: Added new methods and parameters to didSelectionChange
+        and interpretKeyEvent.
+        * UIProcess/WebPageProxy.messages.in: Added parameters to interpretKeyEvent and selectionChanged messages.
+        * UIProcess/WebProcessProxy.h:
+        (WebKit::WebProcessProxy::sendSync): Added support for synchronous messages.
+        The default timeout is 1 second.
+        * WebProcess/WebCoreSupport/WebEditorClient.cpp: respondToChangedSelection is now
+        only for non Mac platform.
+        * WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm:
+        (WebKit::WebEditorClient::respondToChangedSelection): Added implementation for Mac platform.
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::didReceiveSyncMessage): Added.
+        * WebProcess/WebPage/WebPage.h: Added new methods and support for synchronous messages.
+        * WebProcess/WebPage/WebPage.messages.in: Added new messages.
+        * WebProcess/WebPage/mac/WebPageMac.mm:
+        (WebKit::WebPage::interceptEditingKeyboardEvent): Added parameters.
+        (WebKit::WebPage::convertRangeToPlatformRange): Added.
+        (WebKit::WebPage::getMarkedRange): Added.
+        (WebKit::characterRangeAtPoint): Added.
+        (WebKit::WebPage::characterIndexForPoint): Added.
+        (WebKit::convertToRange): Added.
+        (WebKit::WebPage::firstRectForCharacterRange): Added.
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::didReceiveSyncMessage): Added.
+        * WebProcess/WebProcess.h: Added didReceiveSyncMessage.
+
 2010-12-10  Jocelyn Turcotte  <jocelyn.turcotte@nokia.com>
 
         Reviewed by Kenneth Rohde Christiansen.
index 4333cb6..57d30b7 100644 (file)
@@ -103,6 +103,12 @@ void callMemberFunction(const Arguments2<P1, P2>& args, Arguments0&, C* object,
     (object->*function)(args.argument1, args.argument2);
 }
 
+template<typename C, typename MF, typename P1, typename R1, typename R2, typename R3, typename R4>
+void callMemberFunction(const Arguments1<P1>& args, Arguments4<R1, R2, R3, R4>& replyArgs, C* object, MF function)
+{
+    (object->*function)(args.argument1, replyArgs.argument1, replyArgs.argument2, replyArgs.argument3, replyArgs.argument4);
+}
+
 template<typename C, typename MF, typename P1, typename P2, typename R1>
 void callMemberFunction(const Arguments2<P1, P2>& args, Arguments1<R1>& replyArgs, C* object, MF function)
 {
index 381a41d..34cee18 100644 (file)
@@ -29,6 +29,7 @@
 
 WTFLogChannel LogSessionState = { 0x00000001, "WebKit2LogLevel", WTFLogChannelOn };
 WTFLogChannel LogContextMenu  = { 0x00000002, "WebKit2LogLevel", WTFLogChannelOn };
+WTFLogChannel LogTextInput    = { 0x00000003, "WebKit2LogLevel", WTFLogChannelOn };
 
 static inline void initializeLogChannel(WTFLogChannel* channel)
 {
index 59dad73..4e189da 100644 (file)
@@ -38,6 +38,7 @@ EXTERN_C_BEGIN
 
 extern WTFLogChannel LogContextMenu;
 extern WTFLogChannel LogSessionState;
+extern WTFLogChannel LogTextInput;
 
 void initializeLogChannelsIfNecessary(void);
 
index d4c6d00..8fb6f8c 100644 (file)
@@ -250,6 +250,7 @@ def message_to_struct_declaration(message):
 def struct_or_class(namespace, type):
     structs = frozenset([
         'WebCore::KeypressCommand',
+        'WebCore::CompositionUnderline',
         'WebCore::PluginInfo',
         'WebCore::ViewportArguments',
         'WebCore::WindowFeatures',
@@ -412,6 +413,7 @@ def headers_for_type(type):
         'WTF::String': '<wtf/text/WTFString.h>',
         'WebCore::KeypressCommand': '<WebCore/KeyboardEvent.h>',
         'WebCore::PluginInfo': '<WebCore/PluginData.h>',
+        'WebCore::CompositionUnderline': '<WebCore/Editor.h>',
         'WebKit::WebKeyboardEvent': '"WebEvent.h"',
         'WebKit::WebMouseEvent': '"WebEvent.h"',
         'WebKit::WebWheelEvent': '"WebEvent.h"',
index f8ffe00..b46e258 100644 (file)
@@ -33,6 +33,7 @@
 #include <WebCore/AuthenticationChallenge.h>
 #include <WebCore/Credential.h>
 #include <WebCore/Cursor.h>
+#include <WebCore/Editor.h>
 #include <WebCore/FloatRect.h>
 #include <WebCore/IntRect.h>
 #include <WebCore/KeyboardEvent.h>
@@ -327,6 +328,22 @@ template<> struct ArgumentCoder<WebCore::KeypressCommand> {
     }
 };
 #endif
+
+template<> struct ArgumentCoder<WebCore::CompositionUnderline> {
+    static void encode(ArgumentEncoder* encoder, const WebCore::CompositionUnderline& underline)
+    {
+        encoder->encode(CoreIPC::In(underline.startOffset, underline.endOffset, underline.thick, underline.color.rgb()));
+    }
+    
+    static bool decode(ArgumentDecoder* decoder, WebCore::CompositionUnderline& underline)
+    {
+        uint32_t rgb;
+        if (!decoder->decode(CoreIPC::Out(underline.startOffset, underline.endOffset, underline.thick, rgb)))
+            return false;
+        underline.color = rgb;
+        return true;
+    }
+};
     
 } // namespace CoreIPC
 
index 66b6f6f..cddb96a 100644 (file)
@@ -58,13 +58,13 @@ 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 void interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandName, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines);
 
     virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&);
     virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&);
 
     virtual void didNotHandleKeyEvent(const NativeWebKeyboardEvent&);
-    virtual void selectionChanged(bool, bool, bool, bool);
+    virtual void selectionChanged(bool, bool, bool, bool, uint64_t, uint64_t);
 
     virtual PassRefPtr<WebPopupMenuProxy> createPopupMenuProxy();
     virtual PassRefPtr<WebContextMenuProxy> createContextMenuProxy(WebPageProxy*);
index 88da673..68f1838 100644 (file)
@@ -147,9 +147,9 @@ void PageClientImpl::setViewportArguments(const WebCore::ViewportArguments&)
 
 }
 
-void PageClientImpl::selectionChanged(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition)
+void PageClientImpl::selectionChanged(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition, uint64_t location, uint64_t length)
 {
-    [m_wkView _selectionChanged:isNone isEditable:isContentEditable isPassword:isPasswordField hasMarkedText:hasComposition];
+    [m_wkView _selectionChanged:isNone isEditable:isContentEditable isPassword:isPasswordField hasMarkedText:hasComposition range:NSMakeRange(location, length)];
 }
 
 static NSString* nameForEditAction(EditAction editAction)
@@ -222,9 +222,10 @@ 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)
+void PageClientImpl::interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandsList, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines)
 {
     commandsList = [m_wkView _interceptKeyEvent:event.nativeEvent()];
+    [m_wkView _getTextInputState:selectionStart selectionEnd:selectionEnd underlines:underlines];
 }
 
 FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect)
index b8ea533..309466e 100644 (file)
@@ -33,6 +33,7 @@
 #import "FindIndicator.h"
 #import "FindIndicatorWindow.h"
 #import "LayerBackedDrawingAreaProxy.h"
+#import "Logging.h"
 #import "NativeWebKeyboardEvent.h"
 #import "PDFViewController.h"
 #import "PageClientImpl.h"
@@ -46,6 +47,7 @@
 #import "WebProcessProxy.h"
 #import "WebSystemInterface.h"
 #import <QuartzCore/QuartzCore.h>
+#import <WebCore/ColorMac.h>
 #import <WebCore/FloatRect.h>
 #import <WebCore/IntRect.h>
 #import <WebCore/KeyboardEvent.h>
@@ -105,16 +107,26 @@ struct EditCommandState {
     // that has been already sent to WebCore.
     NSEvent *_keyDownEventBeingResent;
     Vector<KeypressCommand> _commandsList;
+
     BOOL _isSelectionNone;
     BOOL _isSelectionEditable;
     BOOL _isSelectionInPasswordField;
     BOOL _hasMarkedText;
+    Vector<CompositionUnderline> _underlines;
+    unsigned _selectionStart;
+    unsigned _selectionEnd;
+    NSRange _selectedRange;
 }
 @end
 
 @implementation WKViewData
 @end
 
+@interface NSObject (NSTextInputContextDetails)
+- (BOOL)wantsToHandleMouseEvents;
+- (BOOL)handleMouseEvent:(NSEvent *)event;
+@end
+
 @implementation WKView
 
 - (id)initWithFrame:(NSRect)frame pageNamespaceRef:(WKPageNamespaceRef)pageNamespaceRef pageGroupRef:(WKPageGroupRef)pageGroupRef
@@ -144,10 +156,12 @@ struct EditCommandState {
 
     _data->_menuEntriesCount = 0;
     _data->_isPerformingUpdate = false;
+
     _data->_isSelectionNone = YES;
     _data->_isSelectionEditable = NO;
     _data->_isSelectionInPasswordField = NO;
     _data->_hasMarkedText = NO;
+    _data->_selectedRange = NSMakeRange(NSNotFound, 0);
 
     return self;
 }
@@ -344,12 +358,9 @@ WEBCORE_COMMAND(takeFindStringFromSelection)
         _data->_page->handle##Type##Event(webEvent); \
     }
 
-EVENT_HANDLER(mouseDown, Mouse)
-EVENT_HANDLER(mouseDragged, Mouse)
 EVENT_HANDLER(mouseEntered, Mouse)
 EVENT_HANDLER(mouseExited, Mouse)
 EVENT_HANDLER(mouseMoved, Mouse)
-EVENT_HANDLER(mouseUp, Mouse)
 EVENT_HANDLER(otherMouseDown, Mouse)
 EVENT_HANDLER(otherMouseDragged, Mouse)
 EVENT_HANDLER(otherMouseMoved, Mouse)
@@ -362,6 +373,22 @@ EVENT_HANDLER(scrollWheel, Wheel)
 
 #undef EVENT_HANDLER
 
+#define MOUSE_EVENT_HANDLER(Selector) \
+    - (void)Selector:(NSEvent *)theEvent \
+    { \
+        NSInputManager *currentInputManager = [NSInputManager currentInputManager]; \
+        if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:theEvent]) \
+            return; \
+        WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(theEvent, self); \
+        _data->_page->handleMouseEvent(webEvent); \
+    }
+
+MOUSE_EVENT_HANDLER(mouseDown)
+MOUSE_EVENT_HANDLER(mouseDragged)
+MOUSE_EVENT_HANDLER(mouseUp)
+
+#undef MOUSE_EVENT_HANDLER
+
 - (void)doCommandBySelector:(SEL)selector
 {
     if (selector != @selector(noop:))
@@ -370,7 +397,33 @@ EVENT_HANDLER(scrollWheel, Wheel)
 
 - (void)insertText:(id)string
 {
-    _data->_commandsList.append(KeypressCommand("insertText", string));
+    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
+    
+    LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
+    NSString *text;
+    bool isFromInputMethod = _data->_hasMarkedText;
+
+    if (isAttributedString) {
+        text = [string string];
+        // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here
+        // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange
+        // event in TSM.  This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an
+        // NSAttributedString
+        NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])];
+        LOG(TextInput, "ReplacementRange: %@", rangeString);
+        if (rangeString)
+            isFromInputMethod = YES;
+    } else
+        text = string;
+    
+    String eventText = text;
+    
+    if (!isFromInputMethod)
+        _data->_commandsList.append(KeypressCommand("insertText", text));
+    else {
+        eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
+        _data->_commandsList.append(KeypressCommand("insertText", eventText));
+    }
 }
 
 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
@@ -432,24 +485,31 @@ EVENT_HANDLER(scrollWheel, Wheel)
     _data->_keyDownEventBeingResent = [event retain];
 }
 
-- (void)_selectionChanged:(BOOL)isNone isEditable:(BOOL)isContentEditable isPassword:(BOOL)isPasswordField hasMarkedText:(BOOL)hasComposition
+- (void)_selectionChanged:(BOOL)isNone isEditable:(BOOL)isContentEditable isPassword:(BOOL)isPasswordField hasMarkedText:(BOOL)hasComposition range:(NSRange)newrange
 {
     _data->_isSelectionNone = isNone;
     _data->_isSelectionEditable = isContentEditable;
     _data->_isSelectionInPasswordField = isPasswordField;
     _data->_hasMarkedText = hasComposition;
+    _data->_selectedRange = newrange;
 }
 
-- (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent
+- (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)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines
+{
+    start = _data->_selectionStart;
+    end = _data->_selectionEnd;
+    lines = _data->_underlines;
+}
+
 - (void)keyUp:(NSEvent *)theEvent
 {
     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
@@ -457,6 +517,9 @@ EVENT_HANDLER(scrollWheel, Wheel)
 
 - (void)keyDown:(NSEvent *)theEvent
 {
+    _data->_underlines.clear();
+    _data->_selectionStart = 0;
+    _data->_selectionEnd = 0;
     // We could be receiving a key down from AppKit if we have re-sent an event
     // that maps to an action that is currently unavailable (for example a copy when
     // there is no range selection).
@@ -472,17 +535,24 @@ EVENT_HANDLER(scrollWheel, Wheel)
 
 - (NSRange)selectedRange
 {
-    return NSMakeRange(NSNotFound, 0);
+    if (_data->_isSelectionNone || !_data->_isSelectionEditable)
+        return NSMakeRange(NSNotFound, 0);
+    
+    LOG(TextInput, "selectedRange -> (%u, %u)", _data->_selectedRange.location, _data->_selectedRange.length);
+    return _data->_selectedRange;
 }
 
 - (BOOL)hasMarkedText
 {
+    LOG(TextInput, "hasMarkedText -> %u", _data-> _hasMarkedText);
     return _data->_hasMarkedText;
 }
 
 - (void)unmarkText
 {
-    // Not implemented
+    LOG(TextInput, "unmarkText");
+    
+    _data->_commandsList.append(KeypressCommand("unmarkText"));
 }
 
 - (NSArray *)validAttributesForMarkedText
@@ -499,41 +569,95 @@ EVENT_HANDLER(scrollWheel, Wheel)
         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
         CFRetain(validAttributes);
     }
+    LOG(TextInput, "validAttributesForMarkedText -> (...)");
     return validAttributes;
 }
 
+static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
+{
+    int length = [[string string] length];
+    
+    int i = 0;
+    while (i < length) {
+        NSRange range;
+        NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
+        
+        if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
+            Color color = Color::black;
+            if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
+                color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
+            result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
+        }
+        
+        i = range.location + range.length;
+    }
+}
+
 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
 {
-    // Not implemented
+    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
+    
+    LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
+    
+    NSString *text = string;
+    
+    if (isAttributedString) {
+        text = [string string];
+        extractUnderlines(string, _data->_underlines);
+    }
+    
+    _data->_commandsList.append(KeypressCommand("setMarkedText", text));
+    _data->_selectionStart = newSelRange.location;
+    _data->_selectionEnd = NSMaxRange(newSelRange);
 }
 
 - (NSRange)markedRange
 {
-    // Not implemented
-    return NSMakeRange(0, 0);
+    uint64_t location;
+    uint64_t length;
+
+    _data->_page->getMarkedRange(location, length);
+    LOG(TextInput, "markedRange -> (%u, %u)", location, length);
+    return NSMakeRange(location, length);
 }
 
 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
 {
-    // Not implemented
+    // This is not implemented for now. Need to figure out how to serialize the attributed string across processes.
+    LOG(TextInput, "attributedSubstringFromRange");
     return nil;
 }
 
-- (NSInteger)conversationIdentifier
-{
-    return (NSInteger)self;
-}
-
 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
 {
-    // Not implemented
-    return NSNotFound;
+    NSWindow *window = [self window];
+    
+    if (window)
+        thePoint = [window convertScreenToBase:thePoint];
+    thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame 
+    
+    uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
+    LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
+    return result;
 }
 
 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
 { 
-    // Not implemented
-    return NSMakeRect(0, 0, 0, 0);
+    // Just to match NSTextView's behavior. Regression tests cannot detect this;
+    // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
+    // (type something; try ranges (1, -1) and (2, -1).
+    if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
+        theRange.length = 0;
+    
+    NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length);
+    resultRect = [self convertRect:resultRect toView:nil];
+    
+    NSWindow *window = [self window];
+    if (window)
+        resultRect.origin = [window convertBaseToScreen:resultRect.origin];
+    
+    LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
+    return resultRect;
 }
 
 - (void)_updateActiveState
@@ -729,6 +853,11 @@ static bool isViewVisible(NSView *view)
     return hitView;
 }
 
+- (NSInteger)conversationIdentifier
+{
+    return (NSInteger)self;
+}
+
 @end
 
 @implementation WKView (Internal)
index 3c13924..8c8b64e 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #import "WKView.h"
+#import <WebCore/Editor.h>
 #import <WebCore/KeyboardEvent.h>
 
 namespace WebKit {
@@ -38,10 +39,11 @@ namespace WebKit {
 - (void)_setCursor:(NSCursor *)cursor;
 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState;
 - (Vector<WebCore::KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent;
+- (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines;
 - (void)_setEventBeingResent:(NSEvent *)event;
 - (NSRect)_convertToDeviceSpace:(NSRect)rect;
 - (NSRect)_convertToUserSpace:(NSRect)rect;
-- (void)_selectionChanged:(BOOL)isNone isEditable:(BOOL)isContentEditable isPassword:(BOOL)isPasswordField hasMarkedText:(BOOL)hasComposition;
+- (void)_selectionChanged:(BOOL)isNone isEditable:(BOOL)isContentEditable isPassword:(BOOL)isPasswordField hasMarkedText:(BOOL)hasComposition range:(NSRange)newrange;
 
 - (void)_setFindIndicator:(PassRefPtr<WebKit::FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut;
 
index bb6cc5f..460d194 100644 (file)
@@ -69,13 +69,15 @@ public:
     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;
+    virtual void interceptKeyEvent(const NativeWebKeyboardEvent&, Vector<WebCore::KeypressCommand>&, uint32_t, uint32_t, Vector<WebCore::CompositionUnderline>&) = 0;
+    virtual void selectionChanged(bool, bool, bool, bool, uint64_t, uint64_t) = 0;
+#else
+    virtual void selectionChanged(bool, bool, bool, bool) = 0;
 #endif
     virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&) = 0;
     virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&) = 0;
 
     virtual void didNotHandleKeyEvent(const NativeWebKeyboardEvent&) = 0;
-    virtual void selectionChanged(bool, bool, bool, bool) = 0;
 
     virtual PassRefPtr<WebPopupMenuProxy> createPopupMenuProxy() = 0;
     virtual PassRefPtr<WebContextMenuProxy> createContextMenuProxy(WebPageProxy*) = 0;
index d1d1d28..8d52cb1 100644 (file)
@@ -486,6 +486,25 @@ void WebPageProxy::windowAndViewFramesChanged(const IntRect& windowFrameInScreen
 
     process()->send(Messages::WebPage::WindowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates), m_pageID);
 }
+
+void WebPageProxy::getMarkedRange(uint64_t& location, uint64_t& length)
+{
+    process()->sendSync(Messages::WebPage::GetMarkedRange(), Messages::WebPage::GetMarkedRange::Reply(location, length), m_pageID);
+}
+    
+uint64_t WebPageProxy::characterIndexForPoint(const IntPoint point)
+{
+    uint64_t result;
+    process()->sendSync(Messages::WebPage::CharacterIndexForPoint(point), Messages::WebPage::CharacterIndexForPoint::Reply(result), m_pageID);
+    return result;
+}
+
+WebCore::IntRect WebPageProxy::firstRectForCharacterRange(uint64_t location, uint64_t length)
+{
+    IntRect resultRect;
+    process()->sendSync(Messages::WebPage::FirstRectForCharacterRange(location, length), Messages::WebPage::FirstRectForCharacterRange::Reply(resultRect), m_pageID);
+    return resultRect;
+}    
 #endif
 
 #if ENABLE(TILED_BACKING_STORE)
@@ -751,9 +770,9 @@ void WebPageProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIP
 }
 
 #if PLATFORM(MAC)
-void WebPageProxy::interpretKeyEvent(uint32_t type, Vector<KeypressCommand>& commandsList)
+void WebPageProxy::interpretKeyEvent(uint32_t type, Vector<KeypressCommand>& commandsList, uint32_t selectionStart, uint32_t selectionEnd, Vector<CompositionUnderline>& underlines)
 {
-    m_pageClient->interceptKeyEvent(m_keyEventQueue.first(), commandsList);
+    m_pageClient->interceptKeyEvent(m_keyEventQueue.first(), commandsList, selectionStart, selectionEnd, underlines);
 }
 #endif
 
@@ -1258,10 +1277,17 @@ void WebPageProxy::backForwardForwardListCount(int32_t& count)
 }
 
 // Selection change support
+#if PLATFORM(MAC)
+void WebPageProxy::didSelectionChange(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition, uint64_t location, uint64_t length)
+{
+    m_pageClient->selectionChanged(isNone, isContentEditable, isPasswordField, hasComposition, location, length);
+}
+#else    
 void WebPageProxy::didSelectionChange(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition)
 {
     m_pageClient->selectionChanged(isNone, isContentEditable, isPasswordField, hasComposition);
 }
+#endif
     
 // Undo management
 
index ebdcaf4..26d5503 100644 (file)
@@ -44,6 +44,7 @@
 #include "WebPolicyClient.h"
 #include "WebUIClient.h"
 #include <WebCore/EditAction.h>
+#include <WebCore/Editor.h>
 #include <WebCore/FrameLoaderTypes.h>
 #include <WebCore/KeyboardEvent.h>
 #include <wtf/HashMap.h>
@@ -176,6 +177,12 @@ public:
 #if PLATFORM(MAC)
     void updateWindowIsVisible(bool windowIsVisible);
     void windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates);
+    void getMarkedRange(uint64_t& location, uint64_t& length);
+    uint64_t characterIndexForPoint(const WebCore::IntPoint);
+    WebCore::IntRect firstRectForCharacterRange(uint64_t, uint64_t);
+    void didSelectionChange(bool, bool, bool, bool, uint64_t, uint64_t);
+#else
+    void didSelectionChange(bool, bool, bool, bool);
 #endif
 
 #if ENABLE(TILED_BACKING_STORE)
@@ -242,7 +249,6 @@ public:
     void addEditCommand(WebEditCommandProxy*);
     void removeEditCommand(WebEditCommandProxy*);
     void registerEditCommand(PassRefPtr<WebEditCommandProxy>, UndoOrRedo);
-    void didSelectionChange(bool, bool, bool, bool);
 
     WebProcessProxy* process() const;
     WebPageNamespace* pageNamespace() const { return m_pageNamespace.get(); }
@@ -353,7 +359,7 @@ private:
 
 #if PLATFORM(MAC)
     // Keyboard handling
-    void interpretKeyEvent(uint32_t eventType, Vector<WebCore::KeypressCommand>&);
+    void interpretKeyEvent(uint32_t eventType, Vector<WebCore::KeypressCommand>&, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines);
 #endif
     
     // Find.
index bab579e..acdcf0d 100644 (file)
@@ -104,7 +104,7 @@ messages -> WebPageProxy {
 
 #if PLATFORM(MAC)
     # Keyboard support
-    InterpretKeyEvent(uint32_t type) -> (Vector<WebCore::KeypressCommand> commandName)
+    InterpretKeyEvent(uint32_t type) -> (Vector<WebCore::KeypressCommand> commandName, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline> underlines)
 #endif
     
     # BackForward messages.
@@ -120,7 +120,12 @@ messages -> WebPageProxy {
     ClearAllEditCommands()
     
     # Selection messages.
+#if PLATFORM(MAC)
+    DidSelectionChange(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition, uint64_t location, uint64_t length)
+#endif
+#if !PLATFORM(MAC)
     DidSelectionChange(bool isNone, bool isContentEditable, bool isPasswordField, bool hasComposition)
+#endif
 
     # Find.
     DidCountStringMatches(WTF::String string, uint32_t matchCount)
index fe24fb5..0d9ec47 100644 (file)
@@ -65,6 +65,7 @@ public:
 
     template<typename E, typename T> bool send(E messageID, uint64_t destinationID, const T& arguments);
     template<typename T> bool send(const T& message, uint64_t destinationID);
+    template<typename U> bool sendSync(const U& message, const typename U::Reply& reply, uint64_t destinationID, double timeout);
     
     CoreIPC::Connection* connection() const
     { 
@@ -170,6 +171,12 @@ bool WebProcessProxy::send(const T& message, uint64_t destinationID)
     return sendMessage(CoreIPC::MessageID(T::messageID), argumentEncoder.release());
 }
 
+template<typename U> 
+bool WebProcessProxy::sendSync(const U& message, const typename U::Reply& reply, uint64_t destinationID, double timeout = 1)
+{
+    return m_connection->sendSync(message, reply, destinationID, timeout);
+}
+    
 } // namespace WebKit
 
 #endif // WebProcessProxy_h
index f83fc90..9fa9b00 100644 (file)
@@ -178,6 +178,7 @@ void WebEditorClient::respondToChangedContents()
     notImplemented();
 }
 
+#if !PLATFORM(MAC)
 void WebEditorClient::respondToChangedSelection()
 {
     static const String WebViewDidChangeSelectionNotification = "WebViewDidChangeSelectionNotification";
@@ -187,7 +188,8 @@ void WebEditorClient::respondToChangedSelection()
         return;
     m_page->send(Messages::WebPageProxy::DidSelectionChange(frame->selection()->isNone(), frame->selection()->isContentEditable(), frame->selection()->isInPasswordField(), frame->editor()->hasComposition()));
 }
-
+#endif
+    
 void WebEditorClient::didEndEditing()
 {
     static const String WebViewDidEndEditingNotification = "WebViewDidEndEditingNotification";
index 63d1617..5f29b0c 100644 (file)
 #include <WebCore/DocumentFragment.h>
 #include <WebCore/DOMDocumentFragmentInternal.h>
 #include <WebCore/DOMDocumentInternal.h>
+#include <WebCore/FocusController.h>
 #include <WebCore/Frame.h>
 #include <WebCore/KeyboardEvent.h>
+#include <WebCore/Page.h>
 #include <WebKit/WebResource.h>
 #include <WebKit/WebNSURLExtras.h>
 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
@@ -61,6 +63,19 @@ using namespace WTF;
 
 namespace WebKit {
 
+void WebEditorClient::respondToChangedSelection()
+{
+    static const String WebViewDidChangeSelectionNotification = "WebViewDidChangeSelectionNotification";
+    m_page->injectedBundleEditorClient().didChangeSelection(m_page, WebViewDidChangeSelectionNotification.impl());
+    WebCore::Frame* frame = m_page->corePage()->focusController()->focusedFrame();
+    if (!frame)
+        return;
+    uint64_t location;
+    uint64_t length;
+    WebPage::convertRangeToPlatformRange(frame, frame->selection()->toNormalizedRange().get(), location, length);
+    m_page->send(Messages::WebPageProxy::DidSelectionChange(frame->selection()->isNone(), frame->selection()->isContentEditable(), frame->selection()->isInPasswordField(), frame->editor()->hasComposition(), location, length));
+}
+    
 void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
 {
     if (m_page->interceptEditingKeyboardEvent(event, false))
index 59fdc63..6518d85 100644 (file)
@@ -1162,6 +1162,11 @@ void WebPage::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::Messag
     didReceiveWebPageMessage(connection, messageID, arguments);
 }
 
+CoreIPC::SyncReplyMode WebPage::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)
+{   
+    return didReceiveSyncWebPageMessage(connection, messageID, arguments, reply);
+}
+    
 InjectedBundleBackForwardList* WebPage::backForwardList()
 {
     if (!m_backForwardList)
index 53ec5f9..5caa340 100644 (file)
@@ -139,6 +139,7 @@ public:
 
     // -- Called from WebProcess.
     void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
+    CoreIPC::SyncReplyMode didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*);
 
     // -- InjectedBundle methods
     void initializeInjectedBundleContextMenuClient(WKBundlePageContextMenuClient*);
@@ -238,7 +239,13 @@ public:
     };
 
     SandboxExtensionTracker& sandboxExtensionTracker() { return m_sandboxExtensionTracker; }
-
+#if PLATFORM(MAC)
+    void getMarkedRange(uint64_t& location, uint64_t& length);
+    void characterIndexForPoint(const WebCore::IntPoint point, uint64_t& result);
+    void firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect);
+    static void convertRangeToPlatformRange(WebCore::Frame* frame, WebCore::Range *range, uint64_t& location, uint64_t& length);
+#endif
+    
 private:
     WebPage(uint64_t pageID, const WebPageCreationParameters&);
 
@@ -247,6 +254,7 @@ private:
     void platformInitialize();
 
     void didReceiveWebPageMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
+    CoreIPC::SyncReplyMode didReceiveSyncWebPageMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*);
 
     static const char* interpretKeyEvent(const WebCore::KeyboardEvent*);
     bool performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&);
index a35c778..8014cea 100644 (file)
@@ -96,6 +96,9 @@ messages -> WebPage {
 #if PLATFORM(MAC)
     SetWindowIsVisible(bool windowIsVisible)
     WindowAndViewFramesChanged(WebCore::IntRect windowFrameInScreenCoordinates, WebCore::IntRect viewFrameInWindowCoordinates)
+    GetMarkedRange() -> (uint64_t location, uint64_t length)
+    CharacterIndexForPoint(WebCore::IntPoint point) -> (uint64_t result)
+    FirstRectForCharacterRange(uint64_t location, uint64_t length) -> (WebCore::IntRect resultRect)
 #endif
 
 #if PLATFORM(QT)
index 2460266..022e2a0 100644 (file)
 #include <WebCore/DocumentLoader.h>
 #include <WebCore/FocusController.h>
 #include <WebCore/Frame.h>
+#include <WebCore/FrameView.h>
+#include <WebCore/HitTestResult.h>
 #include <WebCore/KeyboardEvent.h>
 #include <WebCore/Page.h>
 #include <WebCore/PlatformKeyboardEvent.h>
+#include <WebCore/ScrollView.h>
+#include <WebCore/TextIterator.h>
 #include <WebCore/WindowsKeyboardCodes.h>
 
 using namespace WebCore;
@@ -68,12 +72,31 @@ bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveC
     
     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))
+    if (shouldSaveCommand || !hasKeypressCommand) {
+        Vector<KeypressCommand> commandsList;  
+        Vector<CompositionUnderline> underlines;
+        unsigned start;
+        unsigned end;
+        if (!WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(keyEvent->type()), 
+                                                         Messages::WebPageProxy::InterpretKeyEvent::Reply(commandsList, start, end, underlines),
+                                                         m_pageID, CoreIPC::Connection::NoTimeout))
             return false;
-        for (size_t i = 0; i < commandsList.size(); i++)
-            evt->keypressCommands().append(commandsList[i]);
+        if (commandsList.isEmpty())
+            return eventWasHandled;
+        
+        if (commandsList[0].commandName == "setMarkedText") {
+            frame->editor()->setComposition(commandsList[0].text, underlines, start, end);
+            eventWasHandled = true;
+        } else if (commandsList[0].commandName == "insertText" && frame->editor()->hasComposition()) {
+            frame->editor()->confirmComposition(commandsList[0].text);
+            eventWasHandled = true;
+        } else if (commandsList[0].commandName == "unmarkText") {
+            frame->editor()->confirmComposition();
+            eventWasHandled = true;
+        } else {
+            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?
@@ -101,6 +124,120 @@ bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveC
     return eventWasHandled;
 }
 
+void WebPage::convertRangeToPlatformRange(WebCore::Frame* frame, WebCore::Range *range, uint64_t& location, uint64_t& length)
+{
+    location = NSNotFound;
+    length = 0;
+    if (!range || !range->startContainer())
+        return;
+        
+    Element* selectionRoot = frame->selection()->rootEditableElement();
+    Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
+    
+    // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
+    // that is not inside the current editable region.  These checks ensure we don't produce
+    // potentially invalid data when responding to such requests.
+    if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
+        return;
+    if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
+        return;
+            
+    RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
+    ASSERT(testRange->startContainer() == scope);
+    location = TextIterator::rangeLength(testRange.get());
+            
+    ExceptionCode ec;
+    testRange->setEnd(range->endContainer(), range->endOffset(), ec);
+    ASSERT(testRange->startContainer() == scope);
+    length = TextIterator::rangeLength(testRange.get()) - location;
+}
+    
+void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
+{
+    location = NSNotFound;
+    length = 0;
+    Frame* frame = m_page->focusController()->focusedOrMainFrame();
+    if (!frame)
+        return;
+    
+    convertRangeToPlatformRange(frame, frame->editor()->compositionRange().get(), location, length);
+}
+
+static Range *characterRangeAtPoint(Frame* frame, const IntPoint point)
+{
+    VisiblePosition position = frame->visiblePositionForPoint(point);
+    if (position.isNull())
+        return NULL;
+    
+    VisiblePosition previous = position.previous();
+    if (previous.isNotNull()) {
+        Range *previousCharacterRange = makeRange(previous, position).get();
+        NSRect rect = frame->editor()->firstRectForRange(previousCharacterRange);
+        if (NSPointInRect(point, rect))
+            return previousCharacterRange;
+    }
+    
+    VisiblePosition next = position.next();
+    if (next.isNotNull()) {
+        Range *nextCharacterRange = makeRange(position, next).get();
+        NSRect rect = frame->editor()->firstRectForRange(nextCharacterRange);
+        if (NSPointInRect(point, rect))
+            return nextCharacterRange;
+    }
+    
+    return NULL;
+}
+    
+void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
+{
+    index = NSNotFound;
+    Frame* frame = m_page->mainFrame();
+    if (!frame)
+        return;
+
+    HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
+    frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
+    
+    Range *range = characterRangeAtPoint(frame, result.point());
+    uint64_t length;
+    if (range)
+        convertRangeToPlatformRange(frame, range, index, length);
+}
+
+static PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
+{
+    if (nsrange.location > INT_MAX)
+        return 0;
+    if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
+        nsrange.length = INT_MAX - nsrange.location;
+        
+    // our critical assumption is that we are only called by input methods that
+    // concentrate on a given area containing the selection
+    // We have to do this because of text fields and textareas. The DOM for those is not
+    // directly in the document DOM, so serialization is problematic. Our solution is
+    // to use the root editable element of the selection start as the positional base.
+    // That fits with AppKit's idea of an input context.
+    Element* selectionRoot = frame->selection()->rootEditableElement();
+    Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
+    return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
+}
+    
+void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
+{
+    Frame* frame = m_page->focusController()->focusedOrMainFrame();
+    resultRect.setLocation(IntPoint(0, 0));
+    resultRect.setSize(IntSize(0, 0));
+    
+    RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
+    if (range) {
+        ASSERT(range->startContainer());
+        ASSERT(range->endContainer());
+    }
+     
+    IntRect rect = frame->editor()->firstRectForRange(range.get());
+    resultRect = frame->view()->contentsToWindow(rect);
+}
+
 static inline void scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity)
 {
     page->focusController()->focusedOrMainFrame()->eventHandler()->scrollRecursively(direction, granularity);
index 678119e..3382d83 100644 (file)
@@ -473,6 +473,20 @@ void WebProcess::shutdownIfPossible()
     m_runLoop->stop();
 }
 
+CoreIPC::SyncReplyMode WebProcess::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)
+{   
+    uint64_t pageID = arguments->destinationID();
+    if (!pageID)
+        return CoreIPC::AutomaticReply;
+    
+    WebPage* page = webPage(pageID);
+    if (!page)
+        return CoreIPC::AutomaticReply;
+    
+    page->didReceiveSyncMessage(connection, messageID, arguments, reply);
+    return CoreIPC::AutomaticReply;
+}
+
 void WebProcess::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
 {
     if (messageID.is<CoreIPC::MessageClassWebProcess>()) {
index ecc9da0..9ad4a1f 100644 (file)
@@ -133,6 +133,7 @@ private:
 
     // CoreIPC::Connection::Client
     void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
+    CoreIPC::SyncReplyMode didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*);
     void didClose(CoreIPC::Connection*);
     void didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID);