<!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>[165972] 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/165972">165972</a></dd>
<dt>Author</dt> <dd>ap@apple.com</dd>
<dt>Date</dt> <dd>2014-03-20 10:45:04 -0700 (Thu, 20 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Mac] Support asynchronous NSTextInputClient
https://bugs.webkit.org/show_bug.cgi?id=130479

Reviewed by Anders Carlsson.

The implementation is currently disabled, pending lower level support.
Most of the code is not under compile time guard however, to facilitate cross-platform
reuse, or at least under a PLATFORM(COCOA) guard to share the code with iOS.

* UIProcess/API/mac/WKView.mm: Added a compile time branch for USE(ASYNC_NSTEXTINPUTCLIENT).
We still implement sync NSTextInputClient here, in order to get assertions when
its methods are unexpectedly called.
The new code first sends an event to input method asynchronously, handling any callbacks
that may arrive. During this time, we no longer care about WKViewInterpretKeyEventsParameters
at all. Once done, we interpret key bindings synchronously, collecting them into
a vector.

* UIProcess/API/mac/WKViewInternal.h: We no longer expose _interpretKeyEvent outside
WKView.

* UIProcess/WebPageProxy.cpp:
* UIProcess/WebPageProxy.h:
Added async calls and callbacks. Removed unnecessary and slightly harmful .get() when moving
a callback pointer into map. Moved insertDictatedText() and getAttributedSubstringFromRange()
from PLATFORM(COCOA) to PLATFORM(MAC), because they are unused and unimplemented on
iOS, and unlikely to be needed any time soon. Changed USE(APPKIT) to PLATFORM(MAC),
because that's more accurate in this case (nothing depends on AppKit, it's just code
that we only need on Mac).

* UIProcess/WebPageProxy.messages.in: Added messages for new async IM responses.

* UIProcess/ios/WebPageProxyIOS.mm: Removed insertDictatedText() and getAttributedSubstringFromRange().

* UIProcess/mac/WebPageProxyMac.mm:
(WebKit::WebPageProxy::insertDictatedTextAsync):
(WebKit::WebPageProxy::attributedSubstringForCharacterRangeAsync):
(WebKit::WebPageProxy::attributedStringForCharacterRangeCallback):
Added async calls and callbacks that are Mac only.

* WebProcess/WebPage/WebPage.cpp:
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/mac/WebPageMac.mm:
Added async implementations (which are essentially the same as sync ones, sadly).

* WebProcess/WebPage/WebPage.messages.in: Added async messages, moved some messages
under PLATFORM(MAC).

* WebProcess/WebPage/ios/WebPageIOS.mm: More of deleting functions that are Mac only,
and cannot be easily implemented in WebPage.cpp with shared code.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPImacWKViewmm">trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPImacWKViewInternalh">trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxycpp">trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxyh">trunk/Source/WebKit2/UIProcess/WebPageProxy.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxymessagesin">trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWebPageProxyIOSmm">trunk/Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacWebPageProxyMacmm">trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPagecpp">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPageh">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPagemessagesin">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageiosWebPageIOSmm">trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagemacWebPageMacmm">trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/ChangeLog        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -1,3 +1,55 @@
</span><ins>+2014-03-19  Alexey Proskuryakov  &lt;ap@apple.com&gt;
+
+        [Mac] Support asynchronous NSTextInputClient
+        https://bugs.webkit.org/show_bug.cgi?id=130479
+
+        Reviewed by Anders Carlsson.
+
+        The implementation is currently disabled, pending lower level support.
+        Most of the code is not under compile time guard however, to facilitate cross-platform
+        reuse, or at least under a PLATFORM(COCOA) guard to share the code with iOS.
+
+        * UIProcess/API/mac/WKView.mm: Added a compile time branch for USE(ASYNC_NSTEXTINPUTCLIENT).
+        We still implement sync NSTextInputClient here, in order to get assertions when
+        its methods are unexpectedly called.
+        The new code first sends an event to input method asynchronously, handling any callbacks
+        that may arrive. During this time, we no longer care about WKViewInterpretKeyEventsParameters
+        at all. Once done, we interpret key bindings synchronously, collecting them into
+        a vector.
+
+        * UIProcess/API/mac/WKViewInternal.h: We no longer expose _interpretKeyEvent outside
+        WKView.
+
+        * UIProcess/WebPageProxy.cpp:
+        * UIProcess/WebPageProxy.h:
+        Added async calls and callbacks. Removed unnecessary and slightly harmful .get() when moving
+        a callback pointer into map. Moved insertDictatedText() and getAttributedSubstringFromRange()
+        from PLATFORM(COCOA) to PLATFORM(MAC), because they are unused and unimplemented on
+        iOS, and unlikely to be needed any time soon. Changed USE(APPKIT) to PLATFORM(MAC),
+        because that's more accurate in this case (nothing depends on AppKit, it's just code
+        that we only need on Mac).
+
+        * UIProcess/WebPageProxy.messages.in: Added messages for new async IM responses.
+
+        * UIProcess/ios/WebPageProxyIOS.mm: Removed insertDictatedText() and getAttributedSubstringFromRange().
+
+        * UIProcess/mac/WebPageProxyMac.mm:
+        (WebKit::WebPageProxy::insertDictatedTextAsync):
+        (WebKit::WebPageProxy::attributedSubstringForCharacterRangeAsync):
+        (WebKit::WebPageProxy::attributedStringForCharacterRangeCallback):
+        Added async calls and callbacks that are Mac only.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/mac/WebPageMac.mm:
+        Added async implementations (which are essentially the same as sync ones, sadly).
+
+        * WebProcess/WebPage/WebPage.messages.in: Added async messages, moved some messages
+        under PLATFORM(MAC).
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm: More of deleting functions that are Mac only,
+        and cannot be easily implemented in WebPage.cpp with shared code.
+
</ins><span class="cx"> 2014-03-20  Martin Robinson  &lt;mrobinson@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove remaining GTK+ unused parameter warnings from WebKit2
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPImacWKViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -115,6 +115,14 @@
</span><span class="cx"> - (void)_maskRoundedBottomCorners:(NSRect)clipRect;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><ins>+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+@interface NSTextInputContext (WKNSTextInputContextDetails)
+- (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
+- (void)handleEventByInputMethod:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
+- (BOOL)handleEventByKeyboardLayout:(NSEvent *)theEvent;
+@end
+#endif
+
</ins><span class="cx"> #if defined(__has_include) &amp;&amp; __has_include(&lt;CoreGraphics/CoreGraphicsPrivate.h&gt;)
</span><span class="cx"> #import &lt;CoreGraphics/CoreGraphicsPrivate.h&gt;
</span><span class="cx"> #import &lt;CoreGraphics/CGSCapture.h&gt;
</span><span class="lines">@@ -139,12 +147,14 @@
</span><span class="cx"> 
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if !USE(ASYNC_NSTEXTINPUTCLIENT)
</ins><span class="cx"> struct WKViewInterpretKeyEventsParameters {
</span><span class="cx">     bool eventInterpretationHadSideEffects;
</span><span class="cx">     bool consumedByIM;
</span><span class="cx">     bool executingSavedKeypressCommands;
</span><span class="cx">     Vector&lt;KeypressCommand&gt;* commands;
</span><span class="cx"> };
</span><ins>+#endif
</ins><span class="cx"> 
</span><span class="cx"> @interface WKViewData : NSObject {
</span><span class="cx"> @public
</span><span class="lines">@@ -173,7 +183,11 @@
</span><span class="cx">     // the application to distinguish the case of a new event from one 
</span><span class="cx">     // that has been already sent to WebCore.
</span><span class="cx">     RetainPtr&lt;NSEvent&gt; _keyDownEventBeingResent;
</span><ins>+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+    Vector&lt;KeypressCommand&gt;* _collectedKeypressCommands;
+#else
</ins><span class="cx">     WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters;
</span><ins>+#endif
</ins><span class="cx"> 
</span><span class="cx">     NSSize _resizeScrollOffset;
</span><span class="cx"> 
</span><span class="lines">@@ -1083,11 +1097,31 @@
</span><span class="cx">     _data-&gt;_mouseDownEvent = [event retain];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if USE(ASYNC_NSTEXTINPUTCLIENT)
</ins><span class="cx"> #define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
</span><span class="cx">     - (void)Selector:(NSEvent *)theEvent \
</span><span class="cx">     { \
</span><span class="cx">         if ([self shouldIgnoreMouseEvents]) \
</span><span class="cx">             return; \
</span><ins>+        if (NSTextInputContext *context = [self inputContext]) { \
+            [context handleEvent:theEvent completionHandler:^(BOOL handled) { \
+                if (handled) \
+                    LOG(TextInput, &quot;%s was handled by text input context&quot;, String(#Selector).substring(0, String(#Selector).find(&quot;Internal&quot;)).ascii().data()); \
+                else { \
+                    NativeWebMouseEvent webEvent(theEvent, self); \
+                    _data-&gt;_page-&gt;handleMouseEvent(webEvent); \
+                } \
+            }]; \
+        } \
+        NativeWebMouseEvent webEvent(theEvent, self); \
+        _data-&gt;_page-&gt;handleMouseEvent(webEvent); \
+    }
+#else
+#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
+    - (void)Selector:(NSEvent *)theEvent \
+    { \
+        if ([self shouldIgnoreMouseEvents]) \
+            return; \
</ins><span class="cx">         if ([[self inputContext] handleEvent:theEvent]) { \
</span><span class="cx">             LOG(TextInput, &quot;%s was handled by text input context&quot;, String(#Selector).substring(0, String(#Selector).find(&quot;Internal&quot;)).ascii().data()); \
</span><span class="cx">             return; \
</span><span class="lines">@@ -1095,6 +1129,7 @@
</span><span class="cx">         NativeWebMouseEvent webEvent(theEvent, self); \
</span><span class="cx">         _data-&gt;_page-&gt;handleMouseEvent(webEvent); \
</span><span class="cx">     }
</span><ins>+#endif
</ins><span class="cx"> 
</span><span class="cx"> NATIVE_MOUSE_EVENT_HANDLER(mouseEntered)
</span><span class="cx"> NATIVE_MOUSE_EVENT_HANDLER(mouseExited)
</span><span class="lines">@@ -1213,20 +1248,126 @@
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_disableComplexTextInputIfNecessary
+{
+    if (!_data-&gt;_pluginComplexTextInputIdentifier)
+        return;
+
+    if (_data-&gt;_pluginComplexTextInputState != PluginComplexTextInputEnabled)
+        return;
+
+    // Check if the text input window has been dismissed.
+    if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
+        [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled];
+}
+
+- (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event
+{
+    ASSERT(_data-&gt;_pluginComplexTextInputIdentifier);
+    ASSERT(_data-&gt;_pluginComplexTextInputState != PluginComplexTextInputDisabled);
+
+    BOOL usingLegacyCocoaTextInput = _data-&gt;_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
+
+    NSString *string = nil;
+    BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&amp;string];
+
+    if (string) {
+        _data-&gt;_page-&gt;sendComplexTextInputToPlugin(_data-&gt;_pluginComplexTextInputIdentifier, string);
+
+        if (!usingLegacyCocoaTextInput)
+            _data-&gt;_pluginComplexTextInputState = PluginComplexTextInputDisabled;
+    }
+
+    return didHandleEvent;
+}
+
+- (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event
+{
+    if (!_data-&gt;_pluginComplexTextInputIdentifier || _data-&gt;_pluginComplexTextInputState == PluginComplexTextInputDisabled)
+        return NO;
+
+    // Check if the text input window has been dismissed and let the plug-in process know.
+    // This is only valid with the updated Cocoa text input spec.
+    [self _disableComplexTextInputIfNecessary];
+
+    // Try feeding the keyboard event directly to the plug-in.
+    if (_data-&gt;_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
+        return [self _handlePluginComplexTextInputKeyDown:event];
+
+    return NO;
+}
+
+static void extractUnderlines(NSAttributedString *string, Vector&lt;CompositionUnderline&gt;&amp; result)
+{
+    int length = [[string string] length];
+    
+    int i = 0;
+    while (i &lt; length) {
+        NSRange range;
+        NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&amp;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] &gt; 1));
+        }
+        
+        i = range.location + range.length;
+    }
+}
+
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+
+- (void)_collectKeyboardLayoutCommandsForEvent:(NSEvent *)event to:(Vector&lt;KeypressCommand&gt;&amp;)commands
+{
+    if ([event type] != NSKeyDown)
+        return;
+
+    ASSERT(!_data-&gt;_collectedKeypressCommands);
+    _data-&gt;_collectedKeypressCommands = &amp;commands;
+
+    if (NSTextInputContext *context = [self inputContext])
+        [context handleEventByKeyboardLayout:event];
+    else
+        [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+
+    _data-&gt;_collectedKeypressCommands = nullptr;
+}
+
+- (void)_interpretKeyEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled, const Vector&lt;KeypressCommand&gt;&amp; commands))completionHandler
+{
+    if (![self inputContext]) {
+        Vector&lt;KeypressCommand&gt; commands;
+        [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
+        completionHandler(NO, commands);
+        return;
+    }
+
+    LOG(TextInput, &quot;-&gt; handleEventByInputMethod:%p %@&quot;, event, event);
+    [[self inputContext] handleEventByInputMethod:event completionHandler:^(BOOL handled) {
+        
+        LOG(TextInput, &quot;... handleEventByInputMethod%s handled&quot;, handled ? &quot;&quot; : &quot; not&quot;);
+        if (handled) {
+            completionHandler(YES, Vector&lt;KeypressCommand&gt;());
+            return;
+        }
+
+        Vector&lt;KeypressCommand&gt; commands;
+        [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
+        completionHandler(NO, commands);
+    }];
+}
+
</ins><span class="cx"> - (void)doCommandBySelector:(SEL)selector
</span><span class="cx"> {
</span><span class="cx">     LOG(TextInput, &quot;doCommandBySelector:\&quot;%s\&quot;&quot;, sel_getName(selector));
</span><span class="cx"> 
</span><del>-    WKViewInterpretKeyEventsParameters* parameters = _data-&gt;_interpretKeyEventsParameters;
-    if (parameters)
-        parameters-&gt;consumedByIM = false;
</del><ins>+    Vector&lt;KeypressCommand&gt;* keypressCommands = _data-&gt;_collectedKeypressCommands;
</ins><span class="cx"> 
</span><del>-    // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text.
-    bool isFromInputMethod = _data-&gt;_page-&gt;editorState().hasComposition;
-
-    if (parameters &amp;&amp; !isFromInputMethod) {
</del><ins>+    if (keypressCommands) {
</ins><span class="cx">         KeypressCommand command(NSStringFromSelector(selector));
</span><del>-        parameters-&gt;commands-&gt;append(command);
</del><ins>+        keypressCommands-&gt;append(command);
</ins><span class="cx">         LOG(TextInput, &quot;...stored&quot;);
</span><span class="cx">         _data-&gt;_page-&gt;registerKeypressCommandName(command.commandName);
</span><span class="cx">     } else {
</span><span class="lines">@@ -1238,7 +1379,7 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)insertText:(id)string
</span><span class="cx"> {
</span><del>-    // Unlike and NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
</del><ins>+    // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
</ins><span class="cx">     // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
</span><span class="cx">     // command ensures that a keypress event is dispatched as appropriate.
</span><span class="cx">     [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
</span><span class="lines">@@ -1253,13 +1394,8 @@
</span><span class="cx">         LOG(TextInput, &quot;insertText:\&quot;%@\&quot; replacementRange:(%u, %u)&quot;, isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
</span><span class="cx">     else
</span><span class="cx">         LOG(TextInput, &quot;insertText:\&quot;%@\&quot;&quot;, isAttributedString ? [string string] : string);
</span><del>-    WKViewInterpretKeyEventsParameters* parameters = _data-&gt;_interpretKeyEventsParameters;
-    if (parameters)
-        parameters-&gt;consumedByIM = false;
</del><span class="cx"> 
</span><span class="cx">     NSString *text;
</span><del>-    bool isFromInputMethod = _data-&gt;_page-&gt;editorState().hasComposition;
-
</del><span class="cx">     Vector&lt;TextAlternativeWithRange&gt; dictationAlternatives;
</span><span class="cx"> 
</span><span class="cx">     if (isAttributedString) {
</span><span class="lines">@@ -1272,32 +1408,254 @@
</span><span class="cx">         text = string;
</span><span class="cx"> 
</span><span class="cx">     // insertText can be called for several reasons:
</span><del>-    // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
-    // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
-    // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
</del><ins>+    // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
+    // - If it's from an input method, then we should go ahead and insert the text now.
</ins><span class="cx">     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
</span><span class="cx">     // then we also execute it immediately, as there will be no other chance.
</span><del>-    if (parameters &amp;&amp; !isFromInputMethod) {
-        // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see &lt;rdar://11940670&gt;).
</del><ins>+    Vector&lt;KeypressCommand&gt;* keypressCommands = _data-&gt;_collectedKeypressCommands;
+    if (keypressCommands) {
</ins><span class="cx">         ASSERT(replacementRange.location == NSNotFound);
</span><span class="cx">         KeypressCommand command(&quot;insertText:&quot;, text);
</span><del>-        parameters-&gt;commands-&gt;append(command);
</del><ins>+        keypressCommands-&gt;append(command);
+        LOG(TextInput, &quot;...stored&quot;);
</ins><span class="cx">         _data-&gt;_page-&gt;registerKeypressCommandName(command.commandName);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     String eventText = text;
</span><span class="cx">     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
</span><del>-    bool eventHandled;
</del><span class="cx">     if (!dictationAlternatives.isEmpty())
</span><del>-        eventHandled = _data-&gt;_page-&gt;insertDictatedText(eventText, replacementRange, dictationAlternatives);
</del><ins>+        _data-&gt;_page-&gt;insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives);
</ins><span class="cx">     else
</span><del>-        eventHandled = _data-&gt;_page-&gt;insertText(eventText, replacementRange);
</del><ins>+        _data-&gt;_page-&gt;insertTextAsync(eventText, replacementRange);
+}
</ins><span class="cx"> 
</span><del>-    if (parameters)
-        parameters-&gt;eventInterpretationHadSideEffects |= eventHandled;
</del><ins>+- (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;selectedRange&quot;);
+    _data-&gt;_page-&gt;getSelectedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange&amp; editingRangeResult) {
+        void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...selectedRange failed.&quot;);
+            completionHandlerBlock(NSMakeRange(NSNotFound, 0));
+            return;
+        }
+        NSRange result = editingRangeResult;
+        if (result.location == NSNotFound)
+            LOG(TextInput, &quot;    -&gt; selectedRange returned (NSNotFound, %llu)&quot;, result.length);
+        else
+            LOG(TextInput, &quot;    -&gt; selectedRange returned (%llu, %llu)&quot;, result.location, result.length);
+        completionHandlerBlock(result);
+    }));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;markedRange&quot;);
+    _data-&gt;_page-&gt;getMarkedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange&amp; editingRangeResult) {
+        void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...markedRange failed.&quot;);
+            completionHandlerBlock(NSMakeRange(NSNotFound, 0));
+            return;
+        }
+        NSRange result = editingRangeResult;
+        if (result.location == NSNotFound)
+            LOG(TextInput, &quot;    -&gt; markedRange returned (NSNotFound, %llu)&quot;, result.length);
+        else
+            LOG(TextInput, &quot;    -&gt; markedRange returned (%llu, %llu)&quot;, result.location, result.length);
+        completionHandlerBlock(result);
+    }));
+}
+
+- (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;hasMarkedText&quot;);
+    _data-&gt;_page-&gt;getMarkedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange&amp; editingRangeResult) {
+        void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...hasMarkedText failed.&quot;);
+            completionHandlerBlock(NO);
+            return;
+        }
+        BOOL hasMarkedText = editingRangeResult.location != notFound;
+        LOG(TextInput, &quot;    -&gt; hasMarkedText returned %u&quot;, hasMarkedText);
+        completionHandlerBlock(hasMarkedText);
+    }));
+}
+
+- (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;attributedSubstringFromRange:(%u, %u)&quot;, nsRange.location, nsRange.length);
+    _data-&gt;_page-&gt;attributedSubstringForCharacterRangeAsync(nsRange, AttributedStringForCharacterRangeCallback::create([completionHandler](bool error, const AttributedString&amp; string, const EditingRange&amp; actualRange) {
+        void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...attributedSubstringFromRange failed.&quot;);
+            completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
+            return;
+        }
+        LOG(TextInput, &quot;    -&gt; attributedSubstringFromRange returned %@&quot;, [string.string.get() string]);
+        completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
+    }));
+}
+
+- (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;firstRectForCharacterRange:(%u, %u)&quot;, theRange.location, theRange.length);
+
+    // 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 &lt; theRange.location) &amp;&amp; (theRange.location + theRange.length != 0))
+        theRange.length = 0;
+
+    _data-&gt;_page-&gt;firstRectForCharacterRangeAsync(theRange, RectForCharacterRangeCallback::create([self, completionHandler](bool error, const IntRect&amp; rect, const EditingRange&amp; actualRange) {
+        void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...firstRectForCharacterRange failed.&quot;);
+            completionHandlerBlock(NSMakeRect(0, 0, 0, 0), NSMakeRange(NSNotFound, 0));
+            return;
+        }
+
+        NSRect resultRect = [self convertRect:rect toView:nil];
+        resultRect = [self.window convertRectToScreen:resultRect];
+
+        LOG(TextInput, &quot;    -&gt; firstRectForCharacterRange returned (%f, %f, %f, %f)&quot;, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
+        completionHandlerBlock(resultRect, actualRange);
+    }));
+}
+
+- (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
+{
+    RetainPtr&lt;id&gt; completionHandler = adoptNS([completionHandlerPtr copy]);
+
+    LOG(TextInput, &quot;characterIndexForPoint:(%f, %f)&quot;, thePoint.x, thePoint.y);
+
+    NSWindow *window = [self window];
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored &quot;-Wdeprecated-declarations&quot;
+    if (window)
+        thePoint = [window convertScreenToBase:thePoint];
+#pragma clang diagnostic pop
+    thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
+
+    _data-&gt;_page-&gt;characterIndexForPointAsync(IntPoint(thePoint), UnsignedCallback::create([completionHandler](bool error, uint64_t result) {
+        void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get();
+        if (error) {
+            LOG(TextInput, &quot;    ...characterIndexForPoint failed.&quot;);
+            completionHandlerBlock(0);
+            return;
+        }
+        if (result == notFound)
+            result = NSNotFound;
+        LOG(TextInput, &quot;    -&gt; characterIndexForPoint returned %lu&quot;, result);
+        completionHandlerBlock(result);
+    }));
+}
+
+- (NSTextInputContext *)inputContext
+{
+    bool collectingKeypressCommands = _data-&gt;_collectedKeypressCommands;
+
+    if (_data-&gt;_pluginComplexTextInputIdentifier &amp;&amp; !collectingKeypressCommands)
+        return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
+
+    // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
+    if (!_data-&gt;_page-&gt;editorState().isContentEditable)
+        return nil;
+
+    return [super inputContext];
+}
+
+- (void)unmarkText
+{
+    LOG(TextInput, &quot;unmarkText&quot;);
+
+    _data-&gt;_page-&gt;confirmCompositionAsync();
+}
+
+- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+{
+    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+    ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+    LOG(TextInput, &quot;setMarkedText:\&quot;%@\&quot; selectedRange:(%u, %u) replacementRange:(%u, %u)&quot;, isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);
+
+    Vector&lt;CompositionUnderline&gt; underlines;
+    NSString *text;
+
+    if (isAttributedString) {
+        // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
+        text = [string string];
+        extractUnderlines(string, underlines);
+    } else
+        text = string;
+
+    if (_data-&gt;_inSecureInputState) {
+        // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
+        // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
+        ASSERT(!_data-&gt;_page-&gt;editorState().hasComposition);
+        [self _notifyInputContextAboutDiscardedComposition];
+        // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition.
+        if ([text length] == 1 &amp;&amp; isASCII([text characterAtIndex:0]))
+            _data-&gt;_page-&gt;insertTextAsync(text, replacementRange);
+        else
+            NSBeep();
+        return;
+    }
+
+    _data-&gt;_page-&gt;setCompositionAsync(text, underlines, selectedRange, replacementRange);
+}
+
+// Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed.
+
+- (NSRange)selectedRange NO_RETURN_DUE_TO_ASSERT
+{
+    ASSERT_NOT_REACHED();
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (BOOL)hasMarkedText NO_RETURN_DUE_TO_ASSERT
+{
+    ASSERT_NOT_REACHED();
+    return NO;
+}
+
+- (NSRange)markedRange NO_RETURN_DUE_TO_ASSERT
+{
+    ASSERT_NOT_REACHED();
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
+{
+    ASSERT_NOT_REACHED();
+    return nil;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint NO_RETURN_DUE_TO_ASSERT
+{
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
+{ 
+    ASSERT_NOT_REACHED();
+    return NSMakeRect(0, 0, 0, 0);
+}
+
</ins><span class="cx"> - (BOOL)performKeyEquivalent:(NSEvent *)event
</span><span class="cx"> {
</span><span class="cx">     // There's a chance that responding to this event will run a nested event loop, and
</span><span class="lines">@@ -1324,9 +1682,9 @@
</span><span class="cx">     // this event. This lets web pages have a crack at intercepting key-modified keypresses.
</span><span class="cx">     // FIXME: Why is the firstResponder check needed?
</span><span class="cx">     if (self == [[self window] firstResponder]) {
</span><del>-        Vector&lt;KeypressCommand&gt; commands;
-        BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
-        _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
</del><ins>+        [self _interpretKeyEvent:event completionHandler:^(BOOL handledByInputMethod, const Vector&lt;KeypressCommand&gt;&amp; commands) {
+            _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+        }];
</ins><span class="cx">         return YES;
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -1336,68 +1694,17 @@
</span><span class="cx"> - (void)keyUp:(NSEvent *)theEvent
</span><span class="cx"> {
</span><span class="cx">     LOG(TextInput, &quot;keyUp:%p %@&quot;, theEvent, theEvent);
</span><del>-    // We don't interpret the keyUp event, as this breaks key bindings (see &lt;https://bugs.webkit.org/show_bug.cgi?id=130100&gt;).
-    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector&lt;KeypressCommand&gt;()));
-}
</del><span class="cx"> 
</span><del>-- (void)_disableComplexTextInputIfNecessary
-{
-    if (!_data-&gt;_pluginComplexTextInputIdentifier)
-        return;
-
-    if (_data-&gt;_pluginComplexTextInputState != PluginComplexTextInputEnabled)
-        return;
-
-    // Check if the text input window has been dismissed.
-    if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
-        [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled];
</del><ins>+    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector&lt;KeypressCommand&gt;&amp; commands) {
+        ASSERT(!handledByInputMethod || commands.isEmpty());
+        _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
+    }];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event
-{
-    ASSERT(_data-&gt;_pluginComplexTextInputIdentifier);
-    ASSERT(_data-&gt;_pluginComplexTextInputState != PluginComplexTextInputDisabled);
-
-    BOOL usingLegacyCocoaTextInput = _data-&gt;_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
-
-    NSString *string = nil;
-    BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&amp;string];
-
-    if (string) {
-        _data-&gt;_page-&gt;sendComplexTextInputToPlugin(_data-&gt;_pluginComplexTextInputIdentifier, string);
-
-        if (!usingLegacyCocoaTextInput)
-            _data-&gt;_pluginComplexTextInputState = PluginComplexTextInputDisabled;
-    }
-
-    return didHandleEvent;
-}
-
-- (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event
-{
-    if (!_data-&gt;_pluginComplexTextInputIdentifier || _data-&gt;_pluginComplexTextInputState == PluginComplexTextInputDisabled)
-        return NO;
-
-    // Check if the text input window has been dismissed and let the plug-in process know.
-    // This is only valid with the updated Cocoa text input spec.
-    [self _disableComplexTextInputIfNecessary];
-
-    // Try feeding the keyboard event directly to the plug-in.
-    if (_data-&gt;_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
-        return [self _handlePluginComplexTextInputKeyDown:event];
-
-    return NO;
-}
-
</del><span class="cx"> - (void)keyDown:(NSEvent *)theEvent
</span><span class="cx"> {
</span><span class="cx">     LOG(TextInput, &quot;keyDown:%p %@%s&quot;, theEvent, theEvent, (theEvent == _data-&gt;_keyDownEventBeingResent) ? &quot; (re-sent)&quot; : &quot;&quot;);
</span><span class="cx"> 
</span><del>-    // 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.
-    [[theEvent retain] autorelease];
-
</del><span class="cx">     if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
</span><span class="cx">         LOG(TextInput, &quot;...handled by plug-in&quot;);
</span><span class="cx">         return;
</span><span class="lines">@@ -1412,36 +1719,65 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    Vector&lt;KeypressCommand&gt; commands;
-    BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
-    if (!commands.isEmpty()) {
-        // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
-        // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
-        // should be handled like normal text input after DOM event dispatch.
-        handledByInputMethod = NO;
-    }
-
-    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
</del><ins>+    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector&lt;KeypressCommand&gt;&amp; commands) {
+        ASSERT(!handledByInputMethod || commands.isEmpty());
+        _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
+    }];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)flagsChanged:(NSEvent *)theEvent
</span><span class="cx"> {
</span><span class="cx">     LOG(TextInput, &quot;flagsChanged:%p %@&quot;, theEvent, theEvent);
</span><span class="cx"> 
</span><del>-    // 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.
-    [[theEvent retain] autorelease];
-
</del><span class="cx">     unsigned short keyCode = [theEvent keyCode];
</span><span class="cx"> 
</span><span class="cx">     // Don't make an event from the num lock and function keys
</span><span class="cx">     if (!keyCode || keyCode == 10 || keyCode == 63)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector&lt;KeypressCommand&gt;()));
</del><ins>+    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector&lt;KeypressCommand&gt;&amp; commands) {
+        _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
+    }];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#else // USE(ASYNC_NSTEXTINPUTCLIENT)
+
+- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector&lt;WebCore::KeypressCommand&gt;&amp;)commands
+{
+    ASSERT(!_data-&gt;_interpretKeyEventsParameters);
+    ASSERT(commands.isEmpty());
+
+    if ([event type] == NSFlagsChanged)
+        return NO;
+
+    WKViewInterpretKeyEventsParameters parameters;
+    parameters.eventInterpretationHadSideEffects = false;
+    parameters.executingSavedKeypressCommands = false;
+    // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
+    // We assume the IM will *not* consume hotkey sequences.
+    parameters.consumedByIM = !([event modifierFlags] &amp; NSCommandKeyMask);
+    parameters.commands = &amp;commands;
+    _data-&gt;_interpretKeyEventsParameters = &amp;parameters;
+
+    LOG(TextInput, &quot;-&gt; interpretKeyEvents:%p %@&quot;, event, event);
+    [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+
+    _data-&gt;_interpretKeyEventsParameters = nullptr;
+
+    // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
+    // in which case we should not bubble the event up the DOM.
+    if (parameters.consumedByIM) {
+        ASSERT(commands.isEmpty());
+        LOG(TextInput, &quot;...event %p was consumed by an input method&quot;, event);
+        return YES;
+    }
+
+    LOG(TextInput, &quot;...interpretKeyEvents for event %p done, returns %d&quot;, event, parameters.eventInterpretationHadSideEffects);
+
+    // If we have already executed all or some of the commands, the event is &quot;handled&quot;. Note that there are additional checks on web process side.
+    return parameters.eventInterpretationHadSideEffects;
+}
+
</ins><span class="cx"> - (void)_executeSavedKeypressCommands
</span><span class="cx"> {
</span><span class="cx">     WKViewInterpretKeyEventsParameters* parameters = _data-&gt;_interpretKeyEventsParameters;
</span><span class="lines">@@ -1463,20 +1799,91 @@
</span><span class="cx">     LOG(TextInput, &quot;...done executing saved keypress commands.&quot;);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (NSTextInputContext *)inputContext
</del><ins>+- (void)doCommandBySelector:(SEL)selector
</ins><span class="cx"> {
</span><ins>+    LOG(TextInput, &quot;doCommandBySelector:\&quot;%s\&quot;&quot;, sel_getName(selector));
+
</ins><span class="cx">     WKViewInterpretKeyEventsParameters* parameters = _data-&gt;_interpretKeyEventsParameters;
</span><ins>+    if (parameters)
+        parameters-&gt;consumedByIM = false;
</ins><span class="cx"> 
</span><del>-    if (_data-&gt;_pluginComplexTextInputIdentifier &amp;&amp; !parameters)
-        return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
</del><ins>+    // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text.
+    bool isFromInputMethod = _data-&gt;_page-&gt;editorState().hasComposition;
</ins><span class="cx"> 
</span><del>-    // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
-    if (!_data-&gt;_page-&gt;editorState().isContentEditable)
-        return nil;
</del><ins>+    if (parameters &amp;&amp; !isFromInputMethod) {
+        KeypressCommand command(NSStringFromSelector(selector));
+        parameters-&gt;commands-&gt;append(command);
+        LOG(TextInput, &quot;...stored&quot;);
+        _data-&gt;_page-&gt;registerKeypressCommandName(command.commandName);
+    } else {
+        // FIXME: Send the command to Editor synchronously and only send it along the
+        // responder chain if it's a selector that does not correspond to an editing command.
+        [super doCommandBySelector:selector];
+    }
+}
</ins><span class="cx"> 
</span><del>-    return [super inputContext];
</del><ins>+- (void)insertText:(id)string
+{
+    // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
+    // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
+    // command ensures that a keypress event is dispatched as appropriate.
+    [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
+{
+    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+    ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+    if (replacementRange.location != NSNotFound)
+        LOG(TextInput, &quot;insertText:\&quot;%@\&quot; replacementRange:(%u, %u)&quot;, isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
+    else
+        LOG(TextInput, &quot;insertText:\&quot;%@\&quot;&quot;, isAttributedString ? [string string] : string);
+    WKViewInterpretKeyEventsParameters* parameters = _data-&gt;_interpretKeyEventsParameters;
+    if (parameters)
+        parameters-&gt;consumedByIM = false;
+
+    NSString *text;
+    bool isFromInputMethod = _data-&gt;_page-&gt;editorState().hasComposition;
+
+    Vector&lt;TextAlternativeWithRange&gt; dictationAlternatives;
+
+    if (isAttributedString) {
+#if USE(DICTATION_ALTERNATIVES)
+        collectDictationTextAlternatives(string, dictationAlternatives);
+#endif
+        // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
+        text = [string string];
+    } else
+        text = string;
+
+    // insertText can be called for several reasons:
+    // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
+    // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
+    // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
+    // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
+    // then we also execute it immediately, as there will be no other chance.
+    if (parameters &amp;&amp; !isFromInputMethod) {
+        // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see &lt;rdar://11940670&gt;).
+        ASSERT(replacementRange.location == NSNotFound);
+        KeypressCommand command(&quot;insertText:&quot;, text);
+        parameters-&gt;commands-&gt;append(command);
+        _data-&gt;_page-&gt;registerKeypressCommandName(command.commandName);
+        return;
+    }
+
+    String eventText = text;
+    eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
+    bool eventHandled;
+    if (!dictationAlternatives.isEmpty())
+        eventHandled = _data-&gt;_page-&gt;insertDictatedText(eventText, replacementRange, dictationAlternatives);
+    else
+        eventHandled = _data-&gt;_page-&gt;insertText(eventText, replacementRange);
+
+    if (parameters)
+        parameters-&gt;eventInterpretationHadSideEffects |= eventHandled;
+}
+
</ins><span class="cx"> - (NSRange)selectedRange
</span><span class="cx"> {
</span><span class="cx">     [self _executeSavedKeypressCommands];
</span><span class="lines">@@ -1532,48 +1939,6 @@
</span><span class="cx">     _data-&gt;_page-&gt;confirmComposition();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (NSArray *)validAttributesForMarkedText
-{
-    static NSArray *validAttributes;
-    if (!validAttributes) {
-        validAttributes = [[NSArray alloc] initWithObjects:
-                           NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
-                           NSMarkedClauseSegmentAttributeName,
-#if USE(DICTATION_ALTERNATIVES)
-                           NSTextAlternativesAttributeName,
-#endif
-                           nil];
-        // NSText also supports the following attributes, but it's
-        // hard to tell which are really required for text input to
-        // work well; I have not seen any input method make use of them yet.
-        //     NSFontAttributeName, NSForegroundColorAttributeName,
-        //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
-        CFRetain(validAttributes);
-    }
-    LOG(TextInput, &quot;validAttributesForMarkedText -&gt; (...)&quot;);
-    return validAttributes;
-}
-
-static void extractUnderlines(NSAttributedString *string, Vector&lt;CompositionUnderline&gt;&amp; result)
-{
-    int length = [[string string] length];
-    
-    int i = 0;
-    while (i &lt; length) {
-        NSRange range;
-        NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&amp;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] &gt; 1));
-        }
-        
-        i = range.location + range.length;
-    }
-}
-
</del><span class="cx"> - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
</span><span class="cx"> {
</span><span class="cx">     [self _executeSavedKeypressCommands];
</span><span class="lines">@@ -1699,6 +2064,125 @@
</span><span class="cx">     return resultRect;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (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];
+
+    // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
+    // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
+    // Don't interpret this event again, avoiding re-entrancy and infinite loops.
+    if ([[event charactersIgnoringModifiers] isEqualToString:@&quot;\e&quot;] &amp;&amp; !([event modifierFlags] &amp; NSDeviceIndependentModifierFlagsMask))
+        return [super performKeyEquivalent:event];
+
+    // If we are already re-sending the event, then WebCore has already seen it, no need for custom processing.
+    BOOL eventWasSentToWebCore = (_data-&gt;_keyDownEventBeingResent == event);
+    if (eventWasSentToWebCore)
+        return [super performKeyEquivalent:event];
+
+    ASSERT(event == [NSApp currentEvent]);
+
+    [self _disableComplexTextInputIfNecessary];
+
+    // 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.
+    // FIXME: Why is the firstResponder check needed?
+    if (self == [[self window] firstResponder]) {
+        Vector&lt;KeypressCommand&gt; commands;
+        BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
+        _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+        return YES;
+    }
+    
+    return [super performKeyEquivalent:event];
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+    LOG(TextInput, &quot;keyUp:%p %@&quot;, theEvent, theEvent);
+    // We don't interpret the keyUp event, as this breaks key bindings (see &lt;https://bugs.webkit.org/show_bug.cgi?id=130100&gt;).
+    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector&lt;KeypressCommand&gt;()));
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+    LOG(TextInput, &quot;keyDown:%p %@%s&quot;, theEvent, theEvent, (theEvent == _data-&gt;_keyDownEventBeingResent) ? &quot; (re-sent)&quot; : &quot;&quot;);
+
+    // 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.
+    [[theEvent retain] autorelease];
+
+    if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
+        LOG(TextInput, &quot;...handled by plug-in&quot;);
+        return;
+    }
+
+    // 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).
+    // If this is the case we should ignore the key down.
+    if (_data-&gt;_keyDownEventBeingResent == theEvent) {
+        [super keyDown:theEvent];
+        return;
+    }
+
+    Vector&lt;KeypressCommand&gt; commands;
+    BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
+    if (!commands.isEmpty()) {
+        // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
+        // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
+        // should be handled like normal text input after DOM event dispatch.
+        handledByInputMethod = NO;
+    }
+
+    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+    LOG(TextInput, &quot;flagsChanged:%p %@&quot;, theEvent, theEvent);
+
+    // 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.
+    [[theEvent retain] autorelease];
+
+    unsigned short keyCode = [theEvent keyCode];
+
+    // Don't make an event from the num lock and function keys
+    if (!keyCode || keyCode == 10 || keyCode == 63)
+        return;
+
+    _data-&gt;_page-&gt;handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector&lt;KeypressCommand&gt;()));
+}
+
+#endif // USE(ASYNC_NSTEXTINPUTCLIENT)
+
+- (NSArray *)validAttributesForMarkedText
+{
+    static NSArray *validAttributes;
+    if (!validAttributes) {
+        validAttributes = [[NSArray alloc] initWithObjects:
+                           NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
+                           NSMarkedClauseSegmentAttributeName,
+#if USE(DICTATION_ALTERNATIVES)
+                           NSTextAlternativesAttributeName,
+#endif
+                           nil];
+        // NSText also supports the following attributes, but it's
+        // hard to tell which are really required for text input to
+        // work well; I have not seen any input method make use of them yet.
+        //     NSFontAttributeName, NSForegroundColorAttributeName,
+        //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
+        CFRetain(validAttributes);
+    }
+    LOG(TextInput, &quot;validAttributesForMarkedText -&gt; (...)&quot;);
+    return validAttributes;
+}
+
</ins><span class="cx"> #if ENABLE(DRAG_SUPPORT)
</span><span class="cx"> - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
</span><span class="cx"> {
</span><span class="lines">@@ -2319,42 +2803,6 @@
</span><span class="cx">     _data-&gt;_keyDownEventBeingResent = nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector&lt;WebCore::KeypressCommand&gt;&amp;)commands
-{
-    ASSERT(!_data-&gt;_interpretKeyEventsParameters);
-    ASSERT(commands.isEmpty());
-
-    if ([event type] == NSFlagsChanged)
-        return NO;
-
-    WKViewInterpretKeyEventsParameters parameters;
-    parameters.eventInterpretationHadSideEffects = false;
-    parameters.executingSavedKeypressCommands = false;
-    // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
-    // We assume the IM will *not* consume hotkey sequences.
-    parameters.consumedByIM = !([event modifierFlags] &amp; NSCommandKeyMask);
-    parameters.commands = &amp;commands;
-    _data-&gt;_interpretKeyEventsParameters = &amp;parameters;
-
-    LOG(TextInput, &quot;-&gt; interpretKeyEvents:%p %@&quot;, event, event);
-    [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-
-    _data-&gt;_interpretKeyEventsParameters = nullptr;
-
-    // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
-    // in which case we should not bubble the event up the DOM.
-    if (parameters.consumedByIM) {
-        ASSERT(commands.isEmpty());
-        LOG(TextInput, &quot;...event %p was consumed by an input method&quot;, event);
-        return YES;
-    }
-
-    LOG(TextInput, &quot;...interpretKeyEvents for event %p done, returns %d&quot;, event, parameters.eventInterpretationHadSideEffects);
-
-    // If we have already executed all or some of the commands, the event is &quot;handled&quot;. Note that there are additional checks on web process side.
-    return parameters.eventInterpretationHadSideEffects;
-}
-
</del><span class="cx"> - (NSRect)_convertToDeviceSpace:(NSRect)rect
</span><span class="cx"> {
</span><span class="cx">     return toDeviceSpace(rect, [self window]);
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPImacWKViewInternalh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -72,7 +72,6 @@
</span><span class="cx"> - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip;
</span><span class="cx"> - (void)_setCursor:(NSCursor *)cursor;
</span><span class="cx"> - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState;
</span><del>-- (BOOL)_interpretKeyEvent:(NSEvent *)theEvent savingCommandsTo:(Vector&lt;WebCore::KeypressCommand&gt;&amp;)commands;
</del><span class="cx"> - (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled;
</span><span class="cx"> - (bool)_executeSavedCommandBySelector:(SEL)selector;
</span><span class="cx"> - (void)_setIntrinsicContentSize:(NSSize)intrinsicContentSize;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -1091,7 +1091,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_validateCommandCallbacks.set(callbackID, callback.get());
</del><ins>+    m_validateCommandCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::ValidateCommand(commandName, callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1946,7 +1946,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_scriptValueCallbacks.set(callbackID, callback.get());
</del><ins>+    m_scriptValueCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::RunJavaScriptInMainFrame(script, callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1959,7 +1959,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_stringCallbacks.set(callbackID, callback.get());
</del><ins>+    m_stringCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetRenderTreeExternalRepresentation(callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1973,7 +1973,7 @@
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><span class="cx">     m_loadDependentStringCallbackIDs.add(callbackID);
</span><del>-    m_stringCallbacks.set(callbackID, callback.get());
</del><ins>+    m_stringCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetSourceForFrame(frame-&gt;frameID(), callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1987,7 +1987,7 @@
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><span class="cx">     m_loadDependentStringCallbackIDs.add(callbackID);
</span><del>-    m_stringCallbacks.set(callbackID, callback.get());
</del><ins>+    m_stringCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetContentsAsString(callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2001,7 +2001,7 @@
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><span class="cx">     m_loadDependentStringCallbackIDs.add(callbackID);
</span><del>-    m_stringCallbacks.set(callbackID, callback.get());
</del><ins>+    m_stringCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetBytecodeProfile(callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx">     
</span><span class="lines">@@ -2015,7 +2015,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetContentsAsMHTMLData(callbackID, useBinaryEncoding), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="lines">@@ -2029,7 +2029,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_stringCallbacks.set(callbackID, callback.get());
</del><ins>+    m_stringCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetSelectionOrContentsAsString(callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2042,7 +2042,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetSelectionAsWebArchiveData(callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2055,7 +2055,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetMainResourceDataOfFrame(frame-&gt;frameID(), callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2068,7 +2068,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetResourceDataFromFrame(frame-&gt;frameID(), resourceURL-&gt;string(), callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2081,7 +2081,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::GetWebArchiveOfFrame(frame-&gt;frameID(), callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2094,7 +2094,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_voidCallbacks.set(callbackID, callback.get());
</del><ins>+    m_voidCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_drawingArea-&gt;waitForBackingStoreUpdateOnNextPaint();
</span><span class="cx">     m_process-&gt;send(Messages::WebPage::ForceRepaint(callbackID), m_pageID); 
</span><span class="cx"> }
</span><span class="lines">@@ -3791,6 +3791,46 @@
</span><span class="cx">     callback-&gt;performCallbackWithReturnValue(commandName.impl(), isEnabled, state);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPageProxy::unsignedCallback(uint64_t result, uint64_t callbackID)
+{
+    RefPtr&lt;UnsignedCallback&gt; callback = m_unsignedCallbacks.take(callbackID);
+    if (!callback) {
+        // FIXME: Log error or assert.
+        // this can validly happen if a load invalidated the callback, though
+        return;
+    }
+
+    callback-&gt;performCallbackWithReturnValue(result);
+}
+
+void WebPageProxy::editingRangeCallback(const EditingRange&amp; range, uint64_t callbackID)
+{
+    MESSAGE_CHECK(range.isValid());
+
+    RefPtr&lt;EditingRangeCallback&gt; callback = m_editingRangeCallbacks.take(callbackID);
+    if (!callback) {
+        // FIXME: Log error or assert.
+        // this can validly happen if a load invalidated the callback, though
+        return;
+    }
+
+    callback-&gt;performCallbackWithReturnValue(range);
+}
+
+void WebPageProxy::rectForCharacterRangeCallback(const IntRect&amp; rect, const EditingRange&amp; actualRange, uint64_t callbackID)
+{
+    MESSAGE_CHECK(actualRange.isValid());
+
+    RefPtr&lt;RectForCharacterRangeCallback&gt; callback = m_rectForCharacterRangeCallbacks.take(callbackID);
+    if (!callback) {
+        // FIXME: Log error or assert.
+        // this can validly happen if a load invalidated the callback, though
+        return;
+    }
+
+    callback-&gt;performCallbackWithReturnValue(rect, actualRange);
+}
+
</ins><span class="cx"> #if PLATFORM(GTK)
</span><span class="cx"> void WebPageProxy::printFinishedCallback(const ResourceError&amp; printError, uint64_t callbackID)
</span><span class="cx"> {
</span><span class="lines">@@ -3933,6 +3973,12 @@
</span><span class="cx">     invalidateCallbackMap(m_scriptValueCallbacks);
</span><span class="cx">     invalidateCallbackMap(m_computedPagesCallbacks);
</span><span class="cx">     invalidateCallbackMap(m_validateCommandCallbacks);
</span><ins>+    invalidateCallbackMap(m_unsignedCallbacks);
+    invalidateCallbackMap(m_editingRangeCallbacks);
+    invalidateCallbackMap(m_rectForCharacterRangeCallbacks);
+#if PLATFORM(MAC)
+    invalidateCallbackMap(m_attributedStringForCharacterRangeCallbacks);
+#endif
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     invalidateCallbackMap(m_gestureCallbacks);
</span><span class="cx">     invalidateCallbackMap(m_touchesCallbacks);
</span><span class="lines">@@ -4340,7 +4386,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_computedPagesCallbacks.set(callbackID, callback.get());
</del><ins>+    m_computedPagesCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_isInPrintingMode = true;
</span><span class="cx">     m_process-&gt;send(Messages::WebPage::ComputePagesForPrinting(frame-&gt;frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
</span><span class="cx"> }
</span><span class="lines">@@ -4355,7 +4401,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_imageCallbacks.set(callbackID, callback.get());
</del><ins>+    m_imageCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::DrawRectToImage(frame-&gt;frameID(), printInfo, rect, imageSize, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -4368,7 +4414,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_dataCallbacks.set(callbackID, callback.get());
</del><ins>+    m_dataCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_process-&gt;send(Messages::WebPage::DrawPagesToPDF(frame-&gt;frameID(), printInfo, first, count, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
</span><span class="cx"> }
</span><span class="cx"> #elif PLATFORM(GTK)
</span><span class="lines">@@ -4381,7 +4427,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = callback-&gt;callbackID();
</span><del>-    m_printFinishedCallbacks.set(callbackID, callback.get());
</del><ins>+    m_printFinishedCallbacks.set(callbackID, callback);
</ins><span class="cx">     m_isInPrintingMode = true;
</span><span class="cx">     m_process-&gt;send(Messages::WebPage::DrawPagesForPrinting(frame-&gt;frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
</span><span class="cx"> }
</span><span class="lines">@@ -4600,6 +4646,89 @@
</span><span class="cx">     m_process-&gt;send(Messages::WebPage::AddMIMETypeWithCustomContentProvider(mimeType), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if PLATFORM(COCOA)
+
+void WebPageProxy::insertTextAsync(const String&amp; text, const EditingRange&amp; replacementRange)
+{
+    if (!isValid())
+        return;
+
+    process().send(Messages::WebPage::InsertTextAsync(text, replacementRange), m_pageID);
+}
+
+void WebPageProxy::getMarkedRangeAsync(PassRefPtr&lt;EditingRangeCallback&gt; callback)
+{
+    if (!isValid()) {
+        callback-&gt;invalidate();
+        return;
+    }
+
+    uint64_t callbackID = callback-&gt;callbackID();
+    m_editingRangeCallbacks.set(callbackID, callback);
+
+    process().send(Messages::WebPage::GetMarkedRangeAsync(callbackID), m_pageID);
+}
+
+void WebPageProxy::getSelectedRangeAsync(PassRefPtr&lt;EditingRangeCallback&gt; callback)
+{
+    if (!isValid()) {
+        callback-&gt;invalidate();
+        return;
+    }
+
+    uint64_t callbackID = callback-&gt;callbackID();
+    m_editingRangeCallbacks.set(callbackID, callback);
+
+    process().send(Messages::WebPage::GetSelectedRangeAsync(callbackID), m_pageID);
+}
+
+void WebPageProxy::characterIndexForPointAsync(const WebCore::IntPoint&amp; point, PassRefPtr&lt;UnsignedCallback&gt; callback)
+{
+    if (!isValid()) {
+        callback-&gt;invalidate();
+        return;
+    }
+
+    uint64_t callbackID = callback-&gt;callbackID();
+    m_unsignedCallbacks.set(callbackID, callback);
+
+    process().send(Messages::WebPage::CharacterIndexForPointAsync(point, callbackID), m_pageID);
+}
+
+void WebPageProxy::firstRectForCharacterRangeAsync(const EditingRange&amp; range, PassRefPtr&lt;RectForCharacterRangeCallback&gt; callback)
+{
+    if (!isValid()) {
+        callback-&gt;invalidate();
+        return;
+    }
+
+    uint64_t callbackID = callback-&gt;callbackID();
+    m_rectForCharacterRangeCallbacks.set(callbackID, callback);
+
+    process().send(Messages::WebPage::FirstRectForCharacterRangeAsync(range, callbackID), m_pageID);
+}
+
+void WebPageProxy::setCompositionAsync(const String&amp; text, Vector&lt;CompositionUnderline&gt; underlines, const EditingRange&amp; selectionRange, const EditingRange&amp; replacementRange)
+{
+    if (!isValid()) {
+        // If this fails, we should call -discardMarkedText on input context to notify the input method.
+        // This will happen naturally later, as part of reloading the page.
+        return;
+    }
+
+    process().send(Messages::WebPage::SetCompositionAsync(text, underlines, selectionRange, replacementRange), m_pageID);
+}
+
+void WebPageProxy::confirmCompositionAsync()
+{
+    if (!isValid())
+        return;
+
+    process().send(Messages::WebPage::ConfirmCompositionAsync(), m_pageID);
+}
+
+#endif
+
</ins><span class="cx"> void WebPageProxy::takeSnapshot(IntRect rect, IntSize bitmapSize, SnapshotOptions options, ImageCallback::CallbackFunction callbackFunction)
</span><span class="cx"> {
</span><span class="cx">     RefPtr&lt;ImageCallback&gt; callback = ImageCallback::create(callbackFunction);
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.h (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebPageProxy.h        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.h        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> #include &quot;ContextMenuContextData.h&quot;
</span><span class="cx"> #include &quot;DragControllerAction.h&quot;
</span><span class="cx"> #include &quot;DrawingAreaProxy.h&quot;
</span><ins>+#include &quot;EditingRange.h&quot;
</ins><span class="cx"> #include &quot;EditorState.h&quot;
</span><span class="cx"> #include &quot;GeolocationPermissionRequestManagerProxy.h&quot;
</span><span class="cx"> #include &quot;InteractionInformationAtPosition.h&quot;
</span><span class="lines">@@ -98,6 +99,10 @@
</span><span class="cx"> #include &quot;LayerRepresentation.h&quot;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if PLATFORM(MAC)
+#include &quot;AttributedString.h&quot;
+#endif
+
</ins><span class="cx"> namespace API {
</span><span class="cx"> class LoaderClient;
</span><span class="cx"> class PolicyClient;
</span><span class="lines">@@ -170,6 +175,8 @@
</span><span class="cx"> class WebVibrationProxy;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+typedef GenericCallback&lt;uint64_t&gt; UnsignedCallback;
+typedef GenericCallback&lt;EditingRange&gt; EditingRangeCallback;
</ins><span class="cx"> typedef GenericCallback&lt;StringImpl*&gt; StringCallback;
</span><span class="cx"> typedef GenericCallback&lt;WebSerializedScriptValue*&gt; ScriptValueCallback;
</span><span class="cx"> 
</span><span class="lines">@@ -231,6 +238,96 @@
</span><span class="cx">     CallbackFunction m_callback;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+// FIXME: Make a version of CallbackBase with two arguments, and define RectForCharacterRangeCallback as a specialization.
+class RectForCharacterRangeCallback : public CallbackBase {
+public:
+    typedef std::function&lt;void (bool, const WebCore::IntRect&amp;, const EditingRange&amp;)&gt; CallbackFunction;
+
+    static PassRefPtr&lt;RectForCharacterRangeCallback&gt; create(CallbackFunction callback)
+    {
+        return adoptRef(new RectForCharacterRangeCallback(callback));
+    }
+
+    virtual ~RectForCharacterRangeCallback()
+    {
+        ASSERT(!m_callback);
+    }
+
+    void performCallbackWithReturnValue(const WebCore::IntRect&amp; rect, const EditingRange&amp; range)
+    {
+        ASSERT(m_callback);
+
+        m_callback(false, rect, range);
+
+        m_callback = 0;
+    }
+    
+    void invalidate()
+    {
+        ASSERT(m_callback);
+
+        m_callback(true, WebCore::IntRect(), EditingRange());
+
+        m_callback = 0;
+    }
+
+private:
+
+    RectForCharacterRangeCallback(CallbackFunction callback)
+        : m_callback(callback)
+    {
+    }
+
+    CallbackFunction m_callback;
+};
+
+#if PLATFORM(MAC)
+
+// FIXME: Make a version of CallbackBase with two arguments, and define AttributedStringForCharacterRangeCallback as a specialization.
+class AttributedStringForCharacterRangeCallback : public CallbackBase {
+public:
+    typedef std::function&lt;void (bool, const AttributedString&amp;, const EditingRange&amp;)&gt; CallbackFunction;
+
+    static PassRefPtr&lt;AttributedStringForCharacterRangeCallback&gt; create(CallbackFunction callback)
+    {
+        return adoptRef(new AttributedStringForCharacterRangeCallback(callback));
+    }
+
+    virtual ~AttributedStringForCharacterRangeCallback()
+    {
+        ASSERT(!m_callback);
+    }
+
+    void performCallbackWithReturnValue(const AttributedString&amp; string, const EditingRange&amp; range)
+    {
+        ASSERT(m_callback);
+
+        m_callback(false, string, range);
+
+        m_callback = 0;
+    }
+    
+    void invalidate()
+    {
+        ASSERT(m_callback);
+
+        m_callback(true, AttributedString(), EditingRange());
+
+        m_callback = 0;
+    }
+
+private:
+
+    AttributedStringForCharacterRangeCallback(CallbackFunction callback)
+        : m_callback(callback)
+    {
+    }
+
+    CallbackFunction m_callback;
+};
+
+#endif
+
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx"> class GestureCallback : public CallbackBase {
</span><span class="cx"> public:
</span><span class="lines">@@ -535,10 +632,8 @@
</span><span class="cx">     void confirmComposition();
</span><span class="cx">     void cancelComposition();
</span><span class="cx">     bool insertText(const String&amp; text, const EditingRange&amp; replacementRange);
</span><del>-    bool insertDictatedText(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::TextAlternativeWithRange&gt;&amp; dictationAlternatives);
</del><span class="cx">     void getMarkedRange(EditingRange&amp;);
</span><span class="cx">     void getSelectedRange(EditingRange&amp;);
</span><del>-    void getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;);
</del><span class="cx">     uint64_t characterIndexForPoint(const WebCore::IntPoint);
</span><span class="cx">     WebCore::IntRect firstRectForCharacterRange(const EditingRange&amp;);
</span><span class="cx">     bool executeKeypressCommands(const Vector&lt;WebCore::KeypressCommand&gt;&amp;);
</span><span class="lines">@@ -550,7 +645,20 @@
</span><span class="cx">     void setAcceleratedCompositingRootLayer(LayerOrView*);
</span><span class="cx">     LayerOrView* acceleratedCompositingRootLayer() const;
</span><span class="cx"> 
</span><del>-#if USE(APPKIT)
</del><ins>+    void insertTextAsync(const String&amp; text, const EditingRange&amp; replacementRange);
+    void getMarkedRangeAsync(PassRefPtr&lt;EditingRangeCallback&gt;);
+    void getSelectedRangeAsync(PassRefPtr&lt;EditingRangeCallback&gt;);
+    void characterIndexForPointAsync(const WebCore::IntPoint&amp;, PassRefPtr&lt;UnsignedCallback&gt;);
+    void firstRectForCharacterRangeAsync(const EditingRange&amp;, PassRefPtr&lt;RectForCharacterRangeCallback&gt;);
+    void setCompositionAsync(const String&amp; text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, const EditingRange&amp; selectionRange, const EditingRange&amp; replacementRange);
+    void confirmCompositionAsync();
+
+#if PLATFORM(MAC)
+    bool insertDictatedText(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::TextAlternativeWithRange&gt;&amp; dictationAlternatives);
+    void insertDictatedTextAsync(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::TextAlternativeWithRange&gt;&amp; dictationAlternatives);
+    void getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;);
+    void attributedSubstringForCharacterRangeAsync(const EditingRange&amp;, PassRefPtr&lt;AttributedStringForCharacterRangeCallback&gt;);
+
</ins><span class="cx">     WKView* wkView() const;
</span><span class="cx">     void intrinsicContentSizeDidChange(const WebCore::IntSize&amp; intrinsicContentSize);
</span><span class="cx"> #endif
</span><span class="lines">@@ -1155,6 +1263,12 @@
</span><span class="cx">     void scriptValueCallback(const IPC::DataReference&amp;, uint64_t);
</span><span class="cx">     void computedPagesCallback(const Vector&lt;WebCore::IntRect&gt;&amp;, double totalScaleFactorForPrinting, uint64_t);
</span><span class="cx">     void validateCommandCallback(const String&amp;, bool, int, uint64_t);
</span><ins>+    void unsignedCallback(uint64_t, uint64_t);
+    void editingRangeCallback(const EditingRange&amp;, uint64_t);
+    void rectForCharacterRangeCallback(const WebCore::IntRect&amp;, const EditingRange&amp;, uint64_t);
+#if PLATFORM(MAC)
+    void attributedStringForCharacterRangeCallback(const AttributedString&amp;, const EditingRange&amp;, uint64_t);
+#endif
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     void gestureCallback(const WebCore::IntPoint&amp;, uint32_t, uint32_t, uint32_t, uint64_t);
</span><span class="cx">     void touchesCallback(const WebCore::IntPoint&amp;, uint32_t, uint64_t);
</span><span class="lines">@@ -1292,6 +1406,12 @@
</span><span class="cx">     HashMap&lt;uint64_t, RefPtr&lt;ScriptValueCallback&gt;&gt; m_scriptValueCallbacks;
</span><span class="cx">     HashMap&lt;uint64_t, RefPtr&lt;ComputedPagesCallback&gt;&gt; m_computedPagesCallbacks;
</span><span class="cx">     HashMap&lt;uint64_t, RefPtr&lt;ValidateCommandCallback&gt;&gt; m_validateCommandCallbacks;
</span><ins>+    HashMap&lt;uint64_t, RefPtr&lt;UnsignedCallback&gt;&gt; m_unsignedCallbacks;
+    HashMap&lt;uint64_t, RefPtr&lt;EditingRangeCallback&gt;&gt; m_editingRangeCallbacks;
+    HashMap&lt;uint64_t, RefPtr&lt;RectForCharacterRangeCallback&gt;&gt; m_rectForCharacterRangeCallbacks;
+#if PLATFORM(MAC)
+    HashMap&lt;uint64_t, RefPtr&lt;AttributedStringForCharacterRangeCallback&gt;&gt; m_attributedStringForCharacterRangeCallbacks;
+#endif
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     HashMap&lt;uint64_t, RefPtr&lt;GestureCallback&gt;&gt; m_gestureCallbacks;
</span><span class="cx">     HashMap&lt;uint64_t, RefPtr&lt;TouchesCallback&gt;&gt; m_touchesCallbacks;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxymessagesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -151,6 +151,12 @@
</span><span class="cx">     ScriptValueCallback(IPC::DataReference resultData, uint64_t callbackID)
</span><span class="cx">     ComputedPagesCallback(Vector&lt;WebCore::IntRect&gt; pageRects, double totalScaleFactorForPrinting, uint64_t callbackID)
</span><span class="cx">     ValidateCommandCallback(String command, bool isEnabled, int32_t state, uint64_t callbackID)
</span><ins>+    EditingRangeCallback(WebKit::EditingRange range, uint64_t callbackID)
+    UnsignedCallback(uint64_t result, uint64_t callbackID)
+    RectForCharacterRangeCallback(WebCore::IntRect rect, WebKit::EditingRange actualRange, uint64_t callbackID)
+#if PLATFORM(MAC)
+    AttributedStringForCharacterRangeCallback(WebKit::AttributedString string, WebKit::EditingRange actualRange, uint64_t callbackID)
+#endif
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     GestureCallback(WebCore::IntPoint point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, uint64_t callbackID)
</span><span class="cx">     TouchesCallback(WebCore::IntPoint point, uint32_t touches, uint64_t callbackID)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWebPageProxyIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -130,12 +130,6 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool WebPageProxy::insertDictatedText(const String&amp;, const EditingRange&amp;, const Vector&lt;WebCore::TextAlternativeWithRange&gt;&amp;)
-{
-    notImplemented();
-    return false;
-}
-
</del><span class="cx"> void WebPageProxy::getMarkedRange(EditingRange&amp;)
</span><span class="cx"> {
</span><span class="cx">     notImplemented();
</span><span class="lines">@@ -146,11 +140,6 @@
</span><span class="cx">     notImplemented();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebPageProxy::getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;)
-{
-    notImplemented();
-}
-
</del><span class="cx"> uint64_t WebPageProxy::characterIndexForPoint(const IntPoint)
</span><span class="cx"> {
</span><span class="cx">     notImplemented();
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacWebPageProxyMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -220,6 +220,31 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPageProxy::insertDictatedTextAsync(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;TextAlternativeWithRange&gt;&amp; dictationAlternativesWithRange)
+{
+#if USE(DICTATION_ALTERNATIVES)
+    if (!isValid())
+        return;
+
+    Vector&lt;DictationAlternative&gt; dictationAlternatives;
+
+    for (const TextAlternativeWithRange&amp; alternativeWithRange : dictationAlternativesWithRange) {
+        uint64_t dictationContext = m_pageClient.addDictationAlternatives(alternativeWithRange.alternatives);
+        if (dictationContext)
+            dictationAlternatives.append(DictationAlternative(alternativeWithRange.range.location, alternativeWithRange.range.length, dictationContext));
+    }
+
+    if (dictationAlternatives.isEmpty()) {
+        insertTextAsync(text, replacementRange);
+        return;
+    }
+
+    process().send(Messages::WebPage::InsertDictatedTextAsync(text, replacementRange, dictationAlternatives), m_pageID);
+#else
+    insertTextAsync(text, replacementRange);
+#endif
+}
+
</ins><span class="cx"> void WebPageProxy::getMarkedRange(EditingRange&amp; result)
</span><span class="cx"> {
</span><span class="cx">     result = EditingRange();
</span><span class="lines">@@ -249,6 +274,33 @@
</span><span class="cx">     process().sendSync(Messages::WebPage::GetAttributedSubstringFromRange(range), Messages::WebPage::GetAttributedSubstringFromRange::Reply(result), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPageProxy::attributedSubstringForCharacterRangeAsync(const EditingRange&amp; range, PassRefPtr&lt;AttributedStringForCharacterRangeCallback&gt; callback)
+{
+    if (!isValid()) {
+        callback-&gt;invalidate();
+        return;
+    }
+
+    uint64_t callbackID = callback-&gt;callbackID();
+    m_attributedStringForCharacterRangeCallbacks.set(callbackID, callback);
+
+    process().send(Messages::WebPage::AttributedSubstringForCharacterRangeAsync(range, callbackID), m_pageID);
+}
+
+void WebPageProxy::attributedStringForCharacterRangeCallback(const AttributedString&amp; string, const EditingRange&amp; actualRange, uint64_t callbackID)
+{
+    MESSAGE_CHECK(actualRange.isValid());
+
+    RefPtr&lt;AttributedStringForCharacterRangeCallback&gt; callback = m_attributedStringForCharacterRangeCallbacks.take(callbackID);
+    if (!callback) {
+        // FIXME: Log error or assert.
+        // this can validly happen if a load invalidated the callback, though
+        return;
+    }
+
+    callback-&gt;performCallbackWithReturnValue(string, actualRange);
+}
+
</ins><span class="cx"> uint64_t WebPageProxy::characterIndexForPoint(const IntPoint point)
</span><span class="cx"> {
</span><span class="cx">     if (!isValid())
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -3896,6 +3896,117 @@
</span><span class="cx">     return m_mimeTypesWithCustomContentProviders.contains(response.mimeType()) &amp;&amp; !canPluginHandleResponse(response);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if PLATFORM(COCOA)
+
+void WebPage::insertTextAsync(const String&amp; text, const EditingRange&amp; replacementEditingRange)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    if (replacementEditingRange.location != notFound) {
+        RefPtr&lt;Range&gt; replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
+        if (replacementRange)
+            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
+    }
+
+    if (!frame.editor().hasComposition()) {
+        // An insertText: might be handled by other responders in the chain if we don't handle it.
+        // One example is space bar that results in scrolling down the page.
+        frame.editor().insertText(text, nullptr);
+    } else
+        frame.editor().confirmComposition(text);
+}
+
+void WebPage::getMarkedRangeAsync(uint64_t callbackID)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    RefPtr&lt;Range&gt; range = frame.editor().compositionRange();
+    size_t location;
+    size_t length;
+    if (!range || !TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), location, length)) {
+        location = notFound;
+        length = 0;
+    }
+
+    send(Messages::WebPageProxy::EditingRangeCallback(EditingRange(location, length), callbackID));
+}
+
+void WebPage::getSelectedRangeAsync(uint64_t callbackID)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    size_t location;
+    size_t length;
+    RefPtr&lt;Range&gt; range = frame.selection().toNormalizedRange();
+    if (!range || !TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), location, length)) {
+        location = notFound;
+        length = 0;
+    }
+
+    send(Messages::WebPageProxy::EditingRangeCallback(EditingRange(location, length), callbackID));
+}
+
+void WebPage::characterIndexForPointAsync(const WebCore::IntPoint&amp; point, uint64_t callbackID)
+{
+    uint64_t index = notFound;
+
+    HitTestResult result = m_page-&gt;mainFrame().eventHandler().hitTestResultAtPoint(point);
+    Frame* frame = result.innerNonSharedNode() ? result.innerNodeFrame() : &amp;m_page-&gt;focusController().focusedOrMainFrame();
+    
+    RefPtr&lt;Range&gt; range = frame-&gt;rangeForPoint(result.roundedPointInInnerNodeFrame());
+    if (range) {
+        size_t location;
+        size_t length;
+        if (TextIterator::getLocationAndLengthFromRange(frame-&gt;selection().rootEditableElementOrDocumentElement(), range.get(), location, length))
+            index = static_cast&lt;uint64_t&gt;(location);
+    }
+
+    send(Messages::WebPageProxy::UnsignedCallback(index, callbackID));
+}
+
+void WebPage::firstRectForCharacterRangeAsync(const EditingRange&amp; editingRange, uint64_t callbackID)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+    IntRect result(IntPoint(0, 0), IntSize(0, 0));
+    
+    RefPtr&lt;Range&gt; range = rangeFromEditingRange(frame, editingRange);
+    if (!range) {
+        send(Messages::WebPageProxy::RectForCharacterRangeCallback(result, EditingRange(notFound, 0), callbackID));
+        return;
+    }
+
+    ASSERT(range-&gt;startContainer());
+    ASSERT(range-&gt;endContainer());
+
+    result = frame.view()-&gt;contentsToWindow(frame.editor().firstRectForRange(range.get()));
+
+    // FIXME: Update actualRange to match the range of first rect.
+    send(Messages::WebPageProxy::RectForCharacterRangeCallback(result, editingRange, callbackID));
+}
+
+void WebPage::setCompositionAsync(const String&amp; text, Vector&lt;CompositionUnderline&gt; underlines, const EditingRange&amp; selection, const EditingRange&amp; replacementEditingRange)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    if (frame.selection().selection().isContentEditable()) {
+        RefPtr&lt;Range&gt; replacementRange;
+        if (replacementEditingRange.location != notFound) {
+            replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
+            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
+        }
+
+        frame.editor().setComposition(text, underlines, selection.location, selection.location + selection.length);
+    }
+}
+
+void WebPage::confirmCompositionAsync()
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+    frame.editor().confirmComposition();
+}
+
+#endif // PLATFORM(COCOA)
+
</ins><span class="cx"> #if PLATFORM(GTK)
</span><span class="cx"> static Frame* targetFrameForEditing(WebPage* page)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -538,15 +538,27 @@
</span><span class="cx">     
</span><span class="cx">     void sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String&amp; textInput);
</span><span class="cx"> 
</span><del>-    void cancelComposition(EditorState&amp; newState);
-#if !PLATFORM(IOS)
</del><ins>+#if PLATFORM(MAC)
</ins><span class="cx">     void insertText(const String&amp; text, const EditingRange&amp; replacementRange, bool&amp; handled, EditorState&amp; newState);
</span><span class="cx">     void setComposition(const String&amp; text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, const EditingRange&amp; selectionRange, const EditingRange&amp; replacementRange, EditorState&amp; newState);
</span><span class="cx">     void confirmComposition(EditorState&amp; newState);
</span><ins>+    void insertDictatedText(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::DictationAlternative&gt;&amp; dictationAlternativeLocations, bool&amp; handled, EditorState&amp; newState);
+    void insertDictatedTextAsync(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::DictationAlternative&gt;&amp; dictationAlternativeLocations);
+    void getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;);
+    void attributedSubstringForCharacterRangeAsync(const EditingRange&amp;, uint64_t callbackID);
</ins><span class="cx"> #endif
</span><ins>+
+    void insertTextAsync(const String&amp; text, const EditingRange&amp; replacementRange);
+    void getMarkedRangeAsync(uint64_t callbackID);
+    void getSelectedRangeAsync(uint64_t callbackID);
+    void characterIndexForPointAsync(const WebCore::IntPoint&amp;, uint64_t callbackID);
+    void firstRectForCharacterRangeAsync(const EditingRange&amp;, uint64_t callbackID);
+    void setCompositionAsync(const String&amp; text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, const EditingRange&amp; selectionRange, const EditingRange&amp; replacementRange);
+    void confirmCompositionAsync();
+
+    void cancelComposition(EditorState&amp; newState);
</ins><span class="cx">     void getMarkedRange(EditingRange&amp;);
</span><span class="cx">     void getSelectedRange(EditingRange&amp;);
</span><del>-    void getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;);
</del><span class="cx">     void characterIndexForPoint(const WebCore::IntPoint point, uint64_t&amp; result);
</span><span class="cx">     void firstRectForCharacterRange(const EditingRange&amp;, WebCore::IntRect&amp; resultRect);
</span><span class="cx">     void executeKeypressCommands(const Vector&lt;WebCore::KeypressCommand&gt;&amp;, bool&amp; handled, EditorState&amp; newState);
</span><span class="lines">@@ -556,7 +568,6 @@
</span><span class="cx">     void shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent&amp;, bool&amp; result);
</span><span class="cx">     void acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent&amp;, bool&amp; result);
</span><span class="cx">     bool performNonEditingBehaviorForSelector(const String&amp;, WebCore::KeyboardEvent*);
</span><del>-    void insertDictatedText(const String&amp; text, const EditingRange&amp; replacementRange, const Vector&lt;WebCore::DictationAlternative&gt;&amp; dictationAlternativeLocations, bool&amp; handled, EditorState&amp; newState);
</del><span class="cx"> #elif PLATFORM(EFL)
</span><span class="cx">     void confirmComposition(const String&amp; compositionString);
</span><span class="cx">     void setComposition(const WTF::String&amp; compositionString, const WTF::Vector&lt;WebCore::CompositionUnderline&gt;&amp; underlines, uint64_t cursorPosition);
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPagemessagesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -56,6 +56,7 @@
</span><span class="cx">     SyncApplyAutocorrection(String correction, String originalText) -&gt; (bool autocorrectionApplied)
</span><span class="cx">     RequestAutocorrectionContext(uint64_t callbackID)
</span><span class="cx">     GetAutocorrectionContext() -&gt; (String beforeContext, String markedText, String selectedText, String afterContext, uint64_t location, uint64_t length) 
</span><ins>+    # FIXME: Use shared COCOA text input methods like insertTextAsync.
</ins><span class="cx">     InsertText(String text, WebKit::EditingRange replacementRange)
</span><span class="cx">     SetComposition(String text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, WebKit::EditingRange selectionRange)
</span><span class="cx">     ConfirmComposition()
</span><span class="lines">@@ -298,18 +299,28 @@
</span><span class="cx">     CancelComposition() -&gt; (WebKit::EditorState newState)
</span><span class="cx">     GetMarkedRange() -&gt; (WebKit::EditingRange range)
</span><span class="cx">     GetSelectedRange() -&gt; (WebKit::EditingRange range)
</span><del>-    GetAttributedSubstringFromRange(WebKit::EditingRange range) -&gt; (WebKit::AttributedString result)
</del><span class="cx">     CharacterIndexForPoint(WebCore::IntPoint point) -&gt; (uint64_t result)
</span><span class="cx">     FirstRectForCharacterRange(WebKit::EditingRange range) -&gt; (WebCore::IntRect resultRect)
</span><span class="cx">     ExecuteKeypressCommands(Vector&lt;WebCore::KeypressCommand&gt; savedCommands) -&gt; (bool handled, WebKit::EditorState newState)
</span><span class="cx">     ShouldDelayWindowOrderingEvent(WebKit::WebMouseEvent event) -&gt; (bool result)
</span><span class="cx">     AcceptsFirstMouse(int eventNumber, WebKit::WebMouseEvent event) -&gt; (bool result)
</span><del>-    InsertDictatedText(String text, WebKit::EditingRange replacementRange, Vector&lt;WebCore::DictationAlternative&gt; dictationAlternatives) -&gt; (bool handled, WebKit::EditorState newState)
</del><ins>+
+    InsertTextAsync(String text, WebKit::EditingRange replacementRange)
+    GetMarkedRangeAsync(uint64_t callbackID)
+    GetSelectedRangeAsync(uint64_t callbackID)
+    CharacterIndexForPointAsync(WebCore::IntPoint point, uint64_t callbackID);
+    FirstRectForCharacterRangeAsync(WebKit::EditingRange range, uint64_t callbackID);
+    SetCompositionAsync(String text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, WebKit::EditingRange selectionRange, WebKit::EditingRange replacementRange)
+    ConfirmCompositionAsync()
</ins><span class="cx"> #endif
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     InsertText(String text, WebKit::EditingRange replacementRange) -&gt; (bool handled, WebKit::EditorState newState)
</span><span class="cx">     SetComposition(String text, Vector&lt;WebCore::CompositionUnderline&gt; underlines, WebKit::EditingRange selectionRange, WebKit::EditingRange replacementRange) -&gt; (WebKit::EditorState newState)
</span><span class="cx">     ConfirmComposition() -&gt; (WebKit::EditorState newState)
</span><ins>+    InsertDictatedText(String text, WebKit::EditingRange replacementRange, Vector&lt;WebCore::DictationAlternative&gt; dictationAlternatives) -&gt; (bool handled, WebKit::EditorState newState)
+    InsertDictatedTextAsync(String text, WebKit::EditingRange replacementRange, Vector&lt;WebCore::DictationAlternative&gt; dictationAlternatives)
+    GetAttributedSubstringFromRange(WebKit::EditingRange range) -&gt; (WebKit::AttributedString result)
+    AttributedSubstringForCharacterRangeAsync(WebKit::EditingRange range, uint64_t callbackID);
</ins><span class="cx"> #endif
</span><span class="cx">     SetMinimumLayoutSize(WebCore::IntSize minimumLayoutSize)
</span><span class="cx">     SetAutoSizingShouldExpandToViewHeight(bool shouldExpand)
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageiosWebPageIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -204,11 +204,6 @@
</span><span class="cx">         frame.editor().confirmComposition(text);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebPage::insertDictatedText(const String&amp;, const EditingRange&amp;, const Vector&lt;WebCore::DictationAlternative&gt;&amp;, bool&amp;, EditorState&amp;)
-{
-    notImplemented();
-}
-
</del><span class="cx"> void WebPage::getMarkedRange(EditingRange&amp;)
</span><span class="cx"> {
</span><span class="cx">     notImplemented();
</span><span class="lines">@@ -219,11 +214,6 @@
</span><span class="cx">     notImplemented();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebPage::getAttributedSubstringFromRange(const EditingRange&amp;, AttributedString&amp;)
-{
-    notImplemented();
-}
-
</del><span class="cx"> void WebPage::characterIndexForPoint(IntPoint, uint64_t&amp;)
</span><span class="cx"> {
</span><span class="cx">     notImplemented();
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagemacWebPageMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm (165971 => 165972)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm        2014-03-20 17:37:57 UTC (rev 165971)
+++ trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm        2014-03-20 17:45:04 UTC (rev 165972)
</span><span class="lines">@@ -314,6 +314,20 @@
</span><span class="cx">     newState = editorState();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPage::insertDictatedTextAsync(const String&amp; text, const EditingRange&amp; replacementEditingRange, const Vector&lt;WebCore::DictationAlternative&gt;&amp; dictationAlternativeLocations)
+{
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    if (replacementEditingRange.location != notFound) {
+        RefPtr&lt;Range&gt; replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
+        if (replacementRange)
+            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
+    }
+
+    ASSERT(!frame.editor().hasComposition());
+    frame.editor().insertDictatedText(text, dictationAlternativeLocations, nullptr);
+}
+
</ins><span class="cx"> void WebPage::getMarkedRange(EditingRange&amp; result)
</span><span class="cx"> {
</span><span class="cx">     Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
</span><span class="lines">@@ -365,6 +379,39 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPage::attributedSubstringForCharacterRangeAsync(const EditingRange&amp; editingRange, uint64_t callbackID)
+{
+    AttributedString result;
+
+    Frame&amp; frame = m_page-&gt;focusController().focusedOrMainFrame();
+
+    const VisibleSelection&amp; selection = frame.selection().selection();
+    if (selection.isNone() || !selection.isContentEditable() || selection.isInPasswordField()) {
+        send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
+        return;
+    }
+
+    RefPtr&lt;Range&gt; range = rangeFromEditingRange(frame, editingRange);
+    if (!range) {
+        send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
+        return;
+    }
+
+    result.string = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
+    NSAttributedString* attributedString = result.string.get();
+    
+    // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing 
+    // whitespace at the end of the string which breaks the ATOK input method.  &lt;rdar://problem/5400551&gt;
+    // To work around this we truncate the resultant string to the correct length.
+    if ([attributedString length] &gt; editingRange.length) {
+        ASSERT([attributedString length] == editingRange.length + 1);
+        ASSERT([[attributedString string] characterAtIndex:editingRange.length] == '\n' || [[attributedString string] characterAtIndex:editingRange.length] == ' ');
+        result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
+    }
+
+    send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(editingRange.location, [result.string length]), callbackID));
+}
+
</ins><span class="cx"> void WebPage::characterIndexForPoint(IntPoint point, uint64_t&amp; index)
</span><span class="cx"> {
</span><span class="cx">     index = notFound;
</span></span></pre>
</div>
</div>

</body>
</html>