Changeset 165972 in webkit


Ignore:
Timestamp:
Mar 20, 2014 10:45:04 AM (10 years ago)
Author:
ap@apple.com
Message:

[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.

Location:
trunk/Source/WebKit2
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit2/ChangeLog

    r165968 r165972  
     12014-03-19  Alexey Proskuryakov  <ap@apple.com>
     2
     3        [Mac] Support asynchronous NSTextInputClient
     4        https://bugs.webkit.org/show_bug.cgi?id=130479
     5
     6        Reviewed by Anders Carlsson.
     7
     8        The implementation is currently disabled, pending lower level support.
     9        Most of the code is not under compile time guard however, to facilitate cross-platform
     10        reuse, or at least under a PLATFORM(COCOA) guard to share the code with iOS.
     11
     12        * UIProcess/API/mac/WKView.mm: Added a compile time branch for USE(ASYNC_NSTEXTINPUTCLIENT).
     13        We still implement sync NSTextInputClient here, in order to get assertions when
     14        its methods are unexpectedly called.
     15        The new code first sends an event to input method asynchronously, handling any callbacks
     16        that may arrive. During this time, we no longer care about WKViewInterpretKeyEventsParameters
     17        at all. Once done, we interpret key bindings synchronously, collecting them into
     18        a vector.
     19
     20        * UIProcess/API/mac/WKViewInternal.h: We no longer expose _interpretKeyEvent outside
     21        WKView.
     22
     23        * UIProcess/WebPageProxy.cpp:
     24        * UIProcess/WebPageProxy.h:
     25        Added async calls and callbacks. Removed unnecessary and slightly harmful .get() when moving
     26        a callback pointer into map. Moved insertDictatedText() and getAttributedSubstringFromRange()
     27        from PLATFORM(COCOA) to PLATFORM(MAC), because they are unused and unimplemented on
     28        iOS, and unlikely to be needed any time soon. Changed USE(APPKIT) to PLATFORM(MAC),
     29        because that's more accurate in this case (nothing depends on AppKit, it's just code
     30        that we only need on Mac).
     31
     32        * UIProcess/WebPageProxy.messages.in: Added messages for new async IM responses.
     33
     34        * UIProcess/ios/WebPageProxyIOS.mm: Removed insertDictatedText() and getAttributedSubstringFromRange().
     35
     36        * UIProcess/mac/WebPageProxyMac.mm:
     37        (WebKit::WebPageProxy::insertDictatedTextAsync):
     38        (WebKit::WebPageProxy::attributedSubstringForCharacterRangeAsync):
     39        (WebKit::WebPageProxy::attributedStringForCharacterRangeCallback):
     40        Added async calls and callbacks that are Mac only.
     41
     42        * WebProcess/WebPage/WebPage.cpp:
     43        * WebProcess/WebPage/WebPage.h:
     44        * WebProcess/WebPage/mac/WebPageMac.mm:
     45        Added async implementations (which are essentially the same as sync ones, sadly).
     46
     47        * WebProcess/WebPage/WebPage.messages.in: Added async messages, moved some messages
     48        under PLATFORM(MAC).
     49
     50        * WebProcess/WebPage/ios/WebPageIOS.mm: More of deleting functions that are Mac only,
     51        and cannot be easily implemented in WebPage.cpp with shared code.
     52
    1532014-03-20  Martin Robinson  <mrobinson@igalia.com>
    254
  • trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm

    r165823 r165972  
    116116@end
    117117
     118#if USE(ASYNC_NSTEXTINPUTCLIENT)
     119@interface NSTextInputContext (WKNSTextInputContextDetails)
     120- (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
     121- (void)handleEventByInputMethod:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
     122- (BOOL)handleEventByKeyboardLayout:(NSEvent *)theEvent;
     123@end
     124#endif
     125
    118126#if defined(__has_include) && __has_include(<CoreGraphics/CoreGraphicsPrivate.h>)
    119127#import <CoreGraphics/CoreGraphicsPrivate.h>
     
    140148}
    141149
     150#if !USE(ASYNC_NSTEXTINPUTCLIENT)
    142151struct WKViewInterpretKeyEventsParameters {
    143152    bool eventInterpretationHadSideEffects;
     
    146155    Vector<KeypressCommand>* commands;
    147156};
     157#endif
    148158
    149159@interface WKViewData : NSObject {
     
    174184    // that has been already sent to WebCore.
    175185    RetainPtr<NSEvent> _keyDownEventBeingResent;
     186#if USE(ASYNC_NSTEXTINPUTCLIENT)
     187    Vector<KeypressCommand>* _collectedKeypressCommands;
     188#else
    176189    WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters;
     190#endif
    177191
    178192    NSSize _resizeScrollOffset;
     
    10841098}
    10851099
     1100#if USE(ASYNC_NSTEXTINPUTCLIENT)
     1101#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
     1102    - (void)Selector:(NSEvent *)theEvent \
     1103    { \
     1104        if ([self shouldIgnoreMouseEvents]) \
     1105            return; \
     1106        if (NSTextInputContext *context = [self inputContext]) { \
     1107            [context handleEvent:theEvent completionHandler:^(BOOL handled) { \
     1108                if (handled) \
     1109                    LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \
     1110                else { \
     1111                    NativeWebMouseEvent webEvent(theEvent, self); \
     1112                    _data->_page->handleMouseEvent(webEvent); \
     1113                } \
     1114            }]; \
     1115        } \
     1116        NativeWebMouseEvent webEvent(theEvent, self); \
     1117        _data->_page->handleMouseEvent(webEvent); \
     1118    }
     1119#else
    10861120#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
    10871121    - (void)Selector:(NSEvent *)theEvent \
     
    10961130        _data->_page->handleMouseEvent(webEvent); \
    10971131    }
     1132#endif
    10981133
    10991134NATIVE_MOUSE_EVENT_HANDLER(mouseEntered)
     
    12141249}
    12151250
     1251- (void)_disableComplexTextInputIfNecessary
     1252{
     1253    if (!_data->_pluginComplexTextInputIdentifier)
     1254        return;
     1255
     1256    if (_data->_pluginComplexTextInputState != PluginComplexTextInputEnabled)
     1257        return;
     1258
     1259    // Check if the text input window has been dismissed.
     1260    if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
     1261        [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled];
     1262}
     1263
     1264- (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event
     1265{
     1266    ASSERT(_data->_pluginComplexTextInputIdentifier);
     1267    ASSERT(_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled);
     1268
     1269    BOOL usingLegacyCocoaTextInput = _data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
     1270
     1271    NSString *string = nil;
     1272    BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
     1273
     1274    if (string) {
     1275        _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string);
     1276
     1277        if (!usingLegacyCocoaTextInput)
     1278            _data->_pluginComplexTextInputState = PluginComplexTextInputDisabled;
     1279    }
     1280
     1281    return didHandleEvent;
     1282}
     1283
     1284- (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event
     1285{
     1286    if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled)
     1287        return NO;
     1288
     1289    // Check if the text input window has been dismissed and let the plug-in process know.
     1290    // This is only valid with the updated Cocoa text input spec.
     1291    [self _disableComplexTextInputIfNecessary];
     1292
     1293    // Try feeding the keyboard event directly to the plug-in.
     1294    if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
     1295        return [self _handlePluginComplexTextInputKeyDown:event];
     1296
     1297    return NO;
     1298}
     1299
     1300static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
     1301{
     1302    int length = [[string string] length];
     1303   
     1304    int i = 0;
     1305    while (i < length) {
     1306        NSRange range;
     1307        NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
     1308       
     1309        if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
     1310            Color color = Color::black;
     1311            if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
     1312                color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
     1313            result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
     1314        }
     1315       
     1316        i = range.location + range.length;
     1317    }
     1318}
     1319
     1320#if USE(ASYNC_NSTEXTINPUTCLIENT)
     1321
     1322- (void)_collectKeyboardLayoutCommandsForEvent:(NSEvent *)event to:(Vector<KeypressCommand>&)commands
     1323{
     1324    if ([event type] != NSKeyDown)
     1325        return;
     1326
     1327    ASSERT(!_data->_collectedKeypressCommands);
     1328    _data->_collectedKeypressCommands = &commands;
     1329
     1330    if (NSTextInputContext *context = [self inputContext])
     1331        [context handleEventByKeyboardLayout:event];
     1332    else
     1333        [self interpretKeyEvents:[NSArray arrayWithObject:event]];
     1334
     1335    _data->_collectedKeypressCommands = nullptr;
     1336}
     1337
     1338- (void)_interpretKeyEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled, const Vector<KeypressCommand>& commands))completionHandler
     1339{
     1340    if (![self inputContext]) {
     1341        Vector<KeypressCommand> commands;
     1342        [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
     1343        completionHandler(NO, commands);
     1344        return;
     1345    }
     1346
     1347    LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
     1348    [[self inputContext] handleEventByInputMethod:event completionHandler:^(BOOL handled) {
     1349       
     1350        LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
     1351        if (handled) {
     1352            completionHandler(YES, Vector<KeypressCommand>());
     1353            return;
     1354        }
     1355
     1356        Vector<KeypressCommand> commands;
     1357        [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
     1358        completionHandler(NO, commands);
     1359    }];
     1360}
     1361
     1362- (void)doCommandBySelector:(SEL)selector
     1363{
     1364    LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
     1365
     1366    Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
     1367
     1368    if (keypressCommands) {
     1369        KeypressCommand command(NSStringFromSelector(selector));
     1370        keypressCommands->append(command);
     1371        LOG(TextInput, "...stored");
     1372        _data->_page->registerKeypressCommandName(command.commandName);
     1373    } else {
     1374        // FIXME: Send the command to Editor synchronously and only send it along the
     1375        // responder chain if it's a selector that does not correspond to an editing command.
     1376        [super doCommandBySelector:selector];
     1377    }
     1378}
     1379
     1380- (void)insertText:(id)string
     1381{
     1382    // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
     1383    // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
     1384    // command ensures that a keypress event is dispatched as appropriate.
     1385    [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
     1386}
     1387
     1388- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
     1389{
     1390    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
     1391    ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
     1392
     1393    if (replacementRange.location != NSNotFound)
     1394        LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
     1395    else
     1396        LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
     1397
     1398    NSString *text;
     1399    Vector<TextAlternativeWithRange> dictationAlternatives;
     1400
     1401    if (isAttributedString) {
     1402#if USE(DICTATION_ALTERNATIVES)
     1403        collectDictationTextAlternatives(string, dictationAlternatives);
     1404#endif
     1405        // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
     1406        text = [string string];
     1407    } else
     1408        text = string;
     1409
     1410    // insertText can be called for several reasons:
     1411    // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
     1412    // - If it's from an input method, then we should go ahead and insert the text now.
     1413    // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
     1414    // then we also execute it immediately, as there will be no other chance.
     1415    Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
     1416    if (keypressCommands) {
     1417        ASSERT(replacementRange.location == NSNotFound);
     1418        KeypressCommand command("insertText:", text);
     1419        keypressCommands->append(command);
     1420        LOG(TextInput, "...stored");
     1421        _data->_page->registerKeypressCommandName(command.commandName);
     1422        return;
     1423    }
     1424
     1425    String eventText = text;
     1426    eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
     1427    if (!dictationAlternatives.isEmpty())
     1428        _data->_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives);
     1429    else
     1430        _data->_page->insertTextAsync(eventText, replacementRange);
     1431}
     1432
     1433- (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
     1434{
     1435    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1436
     1437    LOG(TextInput, "selectedRange");
     1438    _data->_page->getSelectedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange& editingRangeResult) {
     1439        void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
     1440        if (error) {
     1441            LOG(TextInput, "    ...selectedRange failed.");
     1442            completionHandlerBlock(NSMakeRange(NSNotFound, 0));
     1443            return;
     1444        }
     1445        NSRange result = editingRangeResult;
     1446        if (result.location == NSNotFound)
     1447            LOG(TextInput, "    -> selectedRange returned (NSNotFound, %llu)", result.length);
     1448        else
     1449            LOG(TextInput, "    -> selectedRange returned (%llu, %llu)", result.location, result.length);
     1450        completionHandlerBlock(result);
     1451    }));
     1452}
     1453
     1454- (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
     1455{
     1456    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1457
     1458    LOG(TextInput, "markedRange");
     1459    _data->_page->getMarkedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange& editingRangeResult) {
     1460        void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
     1461        if (error) {
     1462            LOG(TextInput, "    ...markedRange failed.");
     1463            completionHandlerBlock(NSMakeRange(NSNotFound, 0));
     1464            return;
     1465        }
     1466        NSRange result = editingRangeResult;
     1467        if (result.location == NSNotFound)
     1468            LOG(TextInput, "    -> markedRange returned (NSNotFound, %llu)", result.length);
     1469        else
     1470            LOG(TextInput, "    -> markedRange returned (%llu, %llu)", result.location, result.length);
     1471        completionHandlerBlock(result);
     1472    }));
     1473}
     1474
     1475- (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
     1476{
     1477    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1478
     1479    LOG(TextInput, "hasMarkedText");
     1480    _data->_page->getMarkedRangeAsync(EditingRangeCallback::create([completionHandler](bool error, const EditingRange& editingRangeResult) {
     1481        void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
     1482        if (error) {
     1483            LOG(TextInput, "    ...hasMarkedText failed.");
     1484            completionHandlerBlock(NO);
     1485            return;
     1486        }
     1487        BOOL hasMarkedText = editingRangeResult.location != notFound;
     1488        LOG(TextInput, "    -> hasMarkedText returned %u", hasMarkedText);
     1489        completionHandlerBlock(hasMarkedText);
     1490    }));
     1491}
     1492
     1493- (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
     1494{
     1495    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1496
     1497    LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", nsRange.location, nsRange.length);
     1498    _data->_page->attributedSubstringForCharacterRangeAsync(nsRange, AttributedStringForCharacterRangeCallback::create([completionHandler](bool error, const AttributedString& string, const EditingRange& actualRange) {
     1499        void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
     1500        if (error) {
     1501            LOG(TextInput, "    ...attributedSubstringFromRange failed.");
     1502            completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
     1503            return;
     1504        }
     1505        LOG(TextInput, "    -> attributedSubstringFromRange returned %@", [string.string.get() string]);
     1506        completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
     1507    }));
     1508}
     1509
     1510- (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
     1511{
     1512    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1513
     1514    LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", theRange.location, theRange.length);
     1515
     1516    // Just to match NSTextView's behavior. Regression tests cannot detect this;
     1517    // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
     1518    // (type something; try ranges (1, -1) and (2, -1).
     1519    if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
     1520        theRange.length = 0;
     1521
     1522    _data->_page->firstRectForCharacterRangeAsync(theRange, RectForCharacterRangeCallback::create([self, completionHandler](bool error, const IntRect& rect, const EditingRange& actualRange) {
     1523        void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get();
     1524        if (error) {
     1525            LOG(TextInput, "    ...firstRectForCharacterRange failed.");
     1526            completionHandlerBlock(NSMakeRect(0, 0, 0, 0), NSMakeRange(NSNotFound, 0));
     1527            return;
     1528        }
     1529
     1530        NSRect resultRect = [self convertRect:rect toView:nil];
     1531        resultRect = [self.window convertRectToScreen:resultRect];
     1532
     1533        LOG(TextInput, "    -> firstRectForCharacterRange returned (%f, %f, %f, %f)", resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
     1534        completionHandlerBlock(resultRect, actualRange);
     1535    }));
     1536}
     1537
     1538- (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
     1539{
     1540    RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
     1541
     1542    LOG(TextInput, "characterIndexForPoint:(%f, %f)", thePoint.x, thePoint.y);
     1543
     1544    NSWindow *window = [self window];
     1545
     1546#pragma clang diagnostic push
     1547#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     1548    if (window)
     1549        thePoint = [window convertScreenToBase:thePoint];
     1550#pragma clang diagnostic pop
     1551    thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
     1552
     1553    _data->_page->characterIndexForPointAsync(IntPoint(thePoint), UnsignedCallback::create([completionHandler](bool error, uint64_t result) {
     1554        void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get();
     1555        if (error) {
     1556            LOG(TextInput, "    ...characterIndexForPoint failed.");
     1557            completionHandlerBlock(0);
     1558            return;
     1559        }
     1560        if (result == notFound)
     1561            result = NSNotFound;
     1562        LOG(TextInput, "    -> characterIndexForPoint returned %lu", result);
     1563        completionHandlerBlock(result);
     1564    }));
     1565}
     1566
     1567- (NSTextInputContext *)inputContext
     1568{
     1569    bool collectingKeypressCommands = _data->_collectedKeypressCommands;
     1570
     1571    if (_data->_pluginComplexTextInputIdentifier && !collectingKeypressCommands)
     1572        return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
     1573
     1574    // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
     1575    if (!_data->_page->editorState().isContentEditable)
     1576        return nil;
     1577
     1578    return [super inputContext];
     1579}
     1580
     1581- (void)unmarkText
     1582{
     1583    LOG(TextInput, "unmarkText");
     1584
     1585    _data->_page->confirmCompositionAsync();
     1586}
     1587
     1588- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
     1589{
     1590    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
     1591    ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
     1592
     1593    LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u) replacementRange:(%u, %u)", isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);
     1594
     1595    Vector<CompositionUnderline> underlines;
     1596    NSString *text;
     1597
     1598    if (isAttributedString) {
     1599        // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
     1600        text = [string string];
     1601        extractUnderlines(string, underlines);
     1602    } else
     1603        text = string;
     1604
     1605    if (_data->_inSecureInputState) {
     1606        // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
     1607        // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
     1608        ASSERT(!_data->_page->editorState().hasComposition);
     1609        [self _notifyInputContextAboutDiscardedComposition];
     1610        // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition.
     1611        if ([text length] == 1 && isASCII([text characterAtIndex:0]))
     1612            _data->_page->insertTextAsync(text, replacementRange);
     1613        else
     1614            NSBeep();
     1615        return;
     1616    }
     1617
     1618    _data->_page->setCompositionAsync(text, underlines, selectedRange, replacementRange);
     1619}
     1620
     1621// Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed.
     1622
     1623- (NSRange)selectedRange NO_RETURN_DUE_TO_ASSERT
     1624{
     1625    ASSERT_NOT_REACHED();
     1626    return NSMakeRange(NSNotFound, 0);
     1627}
     1628
     1629- (BOOL)hasMarkedText NO_RETURN_DUE_TO_ASSERT
     1630{
     1631    ASSERT_NOT_REACHED();
     1632    return NO;
     1633}
     1634
     1635- (NSRange)markedRange NO_RETURN_DUE_TO_ASSERT
     1636{
     1637    ASSERT_NOT_REACHED();
     1638    return NSMakeRange(NSNotFound, 0);
     1639}
     1640
     1641- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
     1642{
     1643    ASSERT_NOT_REACHED();
     1644    return nil;
     1645}
     1646
     1647- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint NO_RETURN_DUE_TO_ASSERT
     1648{
     1649    ASSERT_NOT_REACHED();
     1650    return 0;
     1651}
     1652
     1653- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
     1654{
     1655    ASSERT_NOT_REACHED();
     1656    return NSMakeRect(0, 0, 0, 0);
     1657}
     1658
     1659- (BOOL)performKeyEquivalent:(NSEvent *)event
     1660{
     1661    // There's a chance that responding to this event will run a nested event loop, and
     1662    // fetching a new event might release the old one. Retaining and then autoreleasing
     1663    // the current event prevents that from causing a problem inside WebKit or AppKit code.
     1664    [[event retain] autorelease];
     1665
     1666    // 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,
     1667    // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
     1668    // Don't interpret this event again, avoiding re-entrancy and infinite loops.
     1669    if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
     1670        return [super performKeyEquivalent:event];
     1671
     1672    // If we are already re-sending the event, then WebCore has already seen it, no need for custom processing.
     1673    BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
     1674    if (eventWasSentToWebCore)
     1675        return [super performKeyEquivalent:event];
     1676
     1677    ASSERT(event == [NSApp currentEvent]);
     1678
     1679    [self _disableComplexTextInputIfNecessary];
     1680
     1681    // Pass key combos through WebCore if there is a key binding available for
     1682    // this event. This lets web pages have a crack at intercepting key-modified keypresses.
     1683    // FIXME: Why is the firstResponder check needed?
     1684    if (self == [[self window] firstResponder]) {
     1685        [self _interpretKeyEvent:event completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
     1686            _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
     1687        }];
     1688        return YES;
     1689    }
     1690   
     1691    return [super performKeyEquivalent:event];
     1692}
     1693
     1694- (void)keyUp:(NSEvent *)theEvent
     1695{
     1696    LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
     1697
     1698    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
     1699        ASSERT(!handledByInputMethod || commands.isEmpty());
     1700        _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
     1701    }];
     1702}
     1703
     1704- (void)keyDown:(NSEvent *)theEvent
     1705{
     1706    LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
     1707
     1708    if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
     1709        LOG(TextInput, "...handled by plug-in");
     1710        return;
     1711    }
     1712
     1713    // We could be receiving a key down from AppKit if we have re-sent an event
     1714    // that maps to an action that is currently unavailable (for example a copy when
     1715    // there is no range selection).
     1716    // If this is the case we should ignore the key down.
     1717    if (_data->_keyDownEventBeingResent == theEvent) {
     1718        [super keyDown:theEvent];
     1719        return;
     1720    }
     1721
     1722    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
     1723        ASSERT(!handledByInputMethod || commands.isEmpty());
     1724        _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
     1725    }];
     1726}
     1727
     1728- (void)flagsChanged:(NSEvent *)theEvent
     1729{
     1730    LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
     1731
     1732    unsigned short keyCode = [theEvent keyCode];
     1733
     1734    // Don't make an event from the num lock and function keys
     1735    if (!keyCode || keyCode == 10 || keyCode == 63)
     1736        return;
     1737
     1738    [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
     1739        _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
     1740    }];
     1741}
     1742
     1743#else // USE(ASYNC_NSTEXTINPUTCLIENT)
     1744
     1745- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands
     1746{
     1747    ASSERT(!_data->_interpretKeyEventsParameters);
     1748    ASSERT(commands.isEmpty());
     1749
     1750    if ([event type] == NSFlagsChanged)
     1751        return NO;
     1752
     1753    WKViewInterpretKeyEventsParameters parameters;
     1754    parameters.eventInterpretationHadSideEffects = false;
     1755    parameters.executingSavedKeypressCommands = false;
     1756    // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
     1757    // We assume the IM will *not* consume hotkey sequences.
     1758    parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask);
     1759    parameters.commands = &commands;
     1760    _data->_interpretKeyEventsParameters = &parameters;
     1761
     1762    LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event);
     1763    [self interpretKeyEvents:[NSArray arrayWithObject:event]];
     1764
     1765    _data->_interpretKeyEventsParameters = nullptr;
     1766
     1767    // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
     1768    // in which case we should not bubble the event up the DOM.
     1769    if (parameters.consumedByIM) {
     1770        ASSERT(commands.isEmpty());
     1771        LOG(TextInput, "...event %p was consumed by an input method", event);
     1772        return YES;
     1773    }
     1774
     1775    LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects);
     1776
     1777    // If we have already executed all or some of the commands, the event is "handled". Note that there are additional checks on web process side.
     1778    return parameters.eventInterpretationHadSideEffects;
     1779}
     1780
     1781- (void)_executeSavedKeypressCommands
     1782{
     1783    WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
     1784    if (!parameters || parameters->commands->isEmpty())
     1785        return;
     1786
     1787    // We could be called again if the execution of one command triggers a call to selectedRange.
     1788    // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result.
     1789    if (parameters->executingSavedKeypressCommands)
     1790        return;
     1791
     1792    LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size());
     1793
     1794    parameters->executingSavedKeypressCommands = true;
     1795    parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands);
     1796    parameters->commands->clear();
     1797    parameters->executingSavedKeypressCommands = false;
     1798
     1799    LOG(TextInput, "...done executing saved keypress commands.");
     1800}
     1801
    12161802- (void)doCommandBySelector:(SEL)selector
    12171803{
     
    12391825- (void)insertText:(id)string
    12401826{
    1241     // Unlike and NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
     1827    // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
    12421828    // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
    12431829    // command ensures that a keypress event is dispatched as appropriate.
     
    12991885}
    13001886
    1301 - (BOOL)performKeyEquivalent:(NSEvent *)event
    1302 {
    1303     // There's a chance that responding to this event will run a nested event loop, and
    1304     // fetching a new event might release the old one. Retaining and then autoreleasing
    1305     // the current event prevents that from causing a problem inside WebKit or AppKit code.
    1306     [[event retain] autorelease];
    1307 
    1308     // 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,
    1309     // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
    1310     // Don't interpret this event again, avoiding re-entrancy and infinite loops.
    1311     if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
    1312         return [super performKeyEquivalent:event];
    1313 
    1314     // If we are already re-sending the event, then WebCore has already seen it, no need for custom processing.
    1315     BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
    1316     if (eventWasSentToWebCore)
    1317         return [super performKeyEquivalent:event];
    1318 
    1319     ASSERT(event == [NSApp currentEvent]);
    1320 
    1321     [self _disableComplexTextInputIfNecessary];
    1322 
    1323     // Pass key combos through WebCore if there is a key binding available for
    1324     // this event. This lets web pages have a crack at intercepting key-modified keypresses.
    1325     // FIXME: Why is the firstResponder check needed?
    1326     if (self == [[self window] firstResponder]) {
    1327         Vector<KeypressCommand> commands;
    1328         BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
    1329         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
    1330         return YES;
    1331     }
    1332    
    1333     return [super performKeyEquivalent:event];
    1334 }
    1335 
    1336 - (void)keyUp:(NSEvent *)theEvent
    1337 {
    1338     LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
    1339     // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>).
    1340     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
    1341 }
    1342 
    1343 - (void)_disableComplexTextInputIfNecessary
    1344 {
    1345     if (!_data->_pluginComplexTextInputIdentifier)
    1346         return;
    1347 
    1348     if (_data->_pluginComplexTextInputState != PluginComplexTextInputEnabled)
    1349         return;
    1350 
    1351     // Check if the text input window has been dismissed.
    1352     if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
    1353         [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled];
    1354 }
    1355 
    1356 - (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event
    1357 {
    1358     ASSERT(_data->_pluginComplexTextInputIdentifier);
    1359     ASSERT(_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled);
    1360 
    1361     BOOL usingLegacyCocoaTextInput = _data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
    1362 
    1363     NSString *string = nil;
    1364     BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
    1365 
    1366     if (string) {
    1367         _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string);
    1368 
    1369         if (!usingLegacyCocoaTextInput)
    1370             _data->_pluginComplexTextInputState = PluginComplexTextInputDisabled;
    1371     }
    1372 
    1373     return didHandleEvent;
    1374 }
    1375 
    1376 - (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event
    1377 {
    1378     if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled)
    1379         return NO;
    1380 
    1381     // Check if the text input window has been dismissed and let the plug-in process know.
    1382     // This is only valid with the updated Cocoa text input spec.
    1383     [self _disableComplexTextInputIfNecessary];
    1384 
    1385     // Try feeding the keyboard event directly to the plug-in.
    1386     if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
    1387         return [self _handlePluginComplexTextInputKeyDown:event];
    1388 
    1389     return NO;
    1390 }
    1391 
    1392 - (void)keyDown:(NSEvent *)theEvent
    1393 {
    1394     LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
    1395 
    1396     // There's a chance that responding to this event will run a nested event loop, and
    1397     // fetching a new event might release the old one. Retaining and then autoreleasing
    1398     // the current event prevents that from causing a problem inside WebKit or AppKit code.
    1399     [[theEvent retain] autorelease];
    1400 
    1401     if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
    1402         LOG(TextInput, "...handled by plug-in");
    1403         return;
    1404     }
    1405 
    1406     // We could be receiving a key down from AppKit if we have re-sent an event
    1407     // that maps to an action that is currently unavailable (for example a copy when
    1408     // there is no range selection).
    1409     // If this is the case we should ignore the key down.
    1410     if (_data->_keyDownEventBeingResent == theEvent) {
    1411         [super keyDown:theEvent];
    1412         return;
    1413     }
    1414 
    1415     Vector<KeypressCommand> commands;
    1416     BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
    1417     if (!commands.isEmpty()) {
    1418         // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
    1419         // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
    1420         // should be handled like normal text input after DOM event dispatch.
    1421         handledByInputMethod = NO;
    1422     }
    1423 
    1424     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
    1425 }
    1426 
    1427 - (void)flagsChanged:(NSEvent *)theEvent
    1428 {
    1429     LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
    1430 
    1431     // There's a chance that responding to this event will run a nested event loop, and
    1432     // fetching a new event might release the old one. Retaining and then autoreleasing
    1433     // the current event prevents that from causing a problem inside WebKit or AppKit code.
    1434     [[theEvent retain] autorelease];
    1435 
    1436     unsigned short keyCode = [theEvent keyCode];
    1437 
    1438     // Don't make an event from the num lock and function keys
    1439     if (!keyCode || keyCode == 10 || keyCode == 63)
    1440         return;
    1441 
    1442     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
    1443 }
    1444 
    1445 - (void)_executeSavedKeypressCommands
    1446 {
    1447     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
    1448     if (!parameters || parameters->commands->isEmpty())
    1449         return;
    1450 
    1451     // We could be called again if the execution of one command triggers a call to selectedRange.
    1452     // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result.
    1453     if (parameters->executingSavedKeypressCommands)
    1454         return;
    1455 
    1456     LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size());
    1457 
    1458     parameters->executingSavedKeypressCommands = true;
    1459     parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands);
    1460     parameters->commands->clear();
    1461     parameters->executingSavedKeypressCommands = false;
    1462 
    1463     LOG(TextInput, "...done executing saved keypress commands.");
    1464 }
    1465 
    1466 - (NSTextInputContext *)inputContext
    1467 {
    1468     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
    1469 
    1470     if (_data->_pluginComplexTextInputIdentifier && !parameters)
    1471         return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
    1472 
    1473     // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
    1474     if (!_data->_page->editorState().isContentEditable)
    1475         return nil;
    1476 
    1477     return [super inputContext];
    1478 }
    1479 
    14801887- (NSRange)selectedRange
    14811888{
     
    15321939    _data->_page->confirmComposition();
    15331940}
     1941
     1942- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
     1943{
     1944    [self _executeSavedKeypressCommands];
     1945
     1946    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
     1947    ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
     1948
     1949    LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length);
     1950
     1951    // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
     1952    WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
     1953
     1954    if (parameters) {
     1955        parameters->eventInterpretationHadSideEffects = true;
     1956        parameters->consumedByIM = false;
     1957    }
     1958   
     1959    Vector<CompositionUnderline> underlines;
     1960    NSString *text;
     1961
     1962    if (isAttributedString) {
     1963        // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
     1964        text = [string string];
     1965        extractUnderlines(string, underlines);
     1966    } else
     1967        text = string;
     1968
     1969    if (_data->_page->editorState().isInPasswordField) {
     1970        // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
     1971        // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
     1972        ASSERT(!_data->_page->editorState().hasComposition);
     1973        [self _notifyInputContextAboutDiscardedComposition];
     1974        if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) {
     1975            _data->_page->insertText(text, replacementRange);
     1976        } else
     1977            NSBeep();
     1978        return;
     1979    }
     1980
     1981    _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange);
     1982}
     1983
     1984- (NSRange)markedRange
     1985{
     1986    [self _executeSavedKeypressCommands];
     1987
     1988    EditingRange markedRange;
     1989    _data->_page->getMarkedRange(markedRange);
     1990
     1991    NSRange result = markedRange;
     1992    if (result.location == NSNotFound)
     1993        LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length);
     1994    else
     1995        LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
     1996
     1997    return result;
     1998}
     1999
     2000- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
     2001{
     2002    [self _executeSavedKeypressCommands];
     2003
     2004    if (!_data->_page->editorState().isContentEditable) {
     2005        LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
     2006        return nil;
     2007    }
     2008
     2009    if (_data->_page->editorState().isInPasswordField)
     2010        return nil;
     2011
     2012    AttributedString result;
     2013    _data->_page->getAttributedSubstringFromRange(nsRange, result);
     2014
     2015    if (actualRange) {
     2016        *actualRange = nsRange;
     2017        actualRange->length = [result.string length];
     2018    }
     2019
     2020    LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string string]);
     2021    return [[result.string retain] autorelease];
     2022}
     2023
     2024- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
     2025{
     2026    [self _executeSavedKeypressCommands];
     2027
     2028    NSWindow *window = [self window];
     2029   
     2030#pragma clang diagnostic push
     2031#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     2032    if (window)
     2033        thePoint = [window convertScreenToBase:thePoint];
     2034#pragma clang diagnostic pop
     2035    thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
     2036   
     2037    uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
     2038    if (result == notFound)
     2039        result = NSNotFound;
     2040    LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
     2041    return result;
     2042}
     2043
     2044- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
     2045{
     2046    [self _executeSavedKeypressCommands];
     2047
     2048    // Just to match NSTextView's behavior. Regression tests cannot detect this;
     2049    // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
     2050    // (type something; try ranges (1, -1) and (2, -1).
     2051    if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
     2052        theRange.length = 0;
     2053   
     2054    NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange);
     2055    resultRect = [self convertRect:resultRect toView:nil];
     2056    resultRect = [self.window convertRectToScreen:resultRect];
     2057
     2058    if (actualRange) {
     2059        // FIXME: Update actualRange to match the range of first rect.
     2060        *actualRange = theRange;
     2061    }
     2062
     2063    LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
     2064    return resultRect;
     2065}
     2066
     2067- (BOOL)performKeyEquivalent:(NSEvent *)event
     2068{
     2069    // There's a chance that responding to this event will run a nested event loop, and
     2070    // fetching a new event might release the old one. Retaining and then autoreleasing
     2071    // the current event prevents that from causing a problem inside WebKit or AppKit code.
     2072    [[event retain] autorelease];
     2073
     2074    // 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,
     2075    // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
     2076    // Don't interpret this event again, avoiding re-entrancy and infinite loops.
     2077    if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
     2078        return [super performKeyEquivalent:event];
     2079
     2080    // If we are already re-sending the event, then WebCore has already seen it, no need for custom processing.
     2081    BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
     2082    if (eventWasSentToWebCore)
     2083        return [super performKeyEquivalent:event];
     2084
     2085    ASSERT(event == [NSApp currentEvent]);
     2086
     2087    [self _disableComplexTextInputIfNecessary];
     2088
     2089    // Pass key combos through WebCore if there is a key binding available for
     2090    // this event. This lets web pages have a crack at intercepting key-modified keypresses.
     2091    // FIXME: Why is the firstResponder check needed?
     2092    if (self == [[self window] firstResponder]) {
     2093        Vector<KeypressCommand> commands;
     2094        BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
     2095        _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
     2096        return YES;
     2097    }
     2098   
     2099    return [super performKeyEquivalent:event];
     2100}
     2101
     2102- (void)keyUp:(NSEvent *)theEvent
     2103{
     2104    LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
     2105    // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>).
     2106    _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
     2107}
     2108
     2109- (void)keyDown:(NSEvent *)theEvent
     2110{
     2111    LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
     2112
     2113    // There's a chance that responding to this event will run a nested event loop, and
     2114    // fetching a new event might release the old one. Retaining and then autoreleasing
     2115    // the current event prevents that from causing a problem inside WebKit or AppKit code.
     2116    [[theEvent retain] autorelease];
     2117
     2118    if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
     2119        LOG(TextInput, "...handled by plug-in");
     2120        return;
     2121    }
     2122
     2123    // We could be receiving a key down from AppKit if we have re-sent an event
     2124    // that maps to an action that is currently unavailable (for example a copy when
     2125    // there is no range selection).
     2126    // If this is the case we should ignore the key down.
     2127    if (_data->_keyDownEventBeingResent == theEvent) {
     2128        [super keyDown:theEvent];
     2129        return;
     2130    }
     2131
     2132    Vector<KeypressCommand> commands;
     2133    BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
     2134    if (!commands.isEmpty()) {
     2135        // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
     2136        // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
     2137        // should be handled like normal text input after DOM event dispatch.
     2138        handledByInputMethod = NO;
     2139    }
     2140
     2141    _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
     2142}
     2143
     2144- (void)flagsChanged:(NSEvent *)theEvent
     2145{
     2146    LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
     2147
     2148    // There's a chance that responding to this event will run a nested event loop, and
     2149    // fetching a new event might release the old one. Retaining and then autoreleasing
     2150    // the current event prevents that from causing a problem inside WebKit or AppKit code.
     2151    [[theEvent retain] autorelease];
     2152
     2153    unsigned short keyCode = [theEvent keyCode];
     2154
     2155    // Don't make an event from the num lock and function keys
     2156    if (!keyCode || keyCode == 10 || keyCode == 63)
     2157        return;
     2158
     2159    _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
     2160}
     2161
     2162#endif // USE(ASYNC_NSTEXTINPUTCLIENT)
    15342163
    15352164- (NSArray *)validAttributesForMarkedText
     
    15532182    LOG(TextInput, "validAttributesForMarkedText -> (...)");
    15542183    return validAttributes;
    1555 }
    1556 
    1557 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
    1558 {
    1559     int length = [[string string] length];
    1560    
    1561     int i = 0;
    1562     while (i < length) {
    1563         NSRange range;
    1564         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
    1565        
    1566         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
    1567             Color color = Color::black;
    1568             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
    1569                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
    1570             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
    1571         }
    1572        
    1573         i = range.location + range.length;
    1574     }
    1575 }
    1576 
    1577 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
    1578 {
    1579     [self _executeSavedKeypressCommands];
    1580 
    1581     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
    1582     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
    1583 
    1584     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length);
    1585 
    1586     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
    1587     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
    1588 
    1589     if (parameters) {
    1590         parameters->eventInterpretationHadSideEffects = true;
    1591         parameters->consumedByIM = false;
    1592     }
    1593    
    1594     Vector<CompositionUnderline> underlines;
    1595     NSString *text;
    1596 
    1597     if (isAttributedString) {
    1598         // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
    1599         text = [string string];
    1600         extractUnderlines(string, underlines);
    1601     } else
    1602         text = string;
    1603 
    1604     if (_data->_page->editorState().isInPasswordField) {
    1605         // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
    1606         // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
    1607         ASSERT(!_data->_page->editorState().hasComposition);
    1608         [self _notifyInputContextAboutDiscardedComposition];
    1609         if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) {
    1610             _data->_page->insertText(text, replacementRange);
    1611         } else
    1612             NSBeep();
    1613         return;
    1614     }
    1615 
    1616     _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange);
    1617 }
    1618 
    1619 - (NSRange)markedRange
    1620 {
    1621     [self _executeSavedKeypressCommands];
    1622 
    1623     EditingRange markedRange;
    1624     _data->_page->getMarkedRange(markedRange);
    1625 
    1626     NSRange result = markedRange;
    1627     if (result.location == NSNotFound)
    1628         LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length);
    1629     else
    1630         LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
    1631 
    1632     return result;
    1633 }
    1634 
    1635 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
    1636 {
    1637     [self _executeSavedKeypressCommands];
    1638 
    1639     if (!_data->_page->editorState().isContentEditable) {
    1640         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
    1641         return nil;
    1642     }
    1643 
    1644     if (_data->_page->editorState().isInPasswordField)
    1645         return nil;
    1646 
    1647     AttributedString result;
    1648     _data->_page->getAttributedSubstringFromRange(nsRange, result);
    1649 
    1650     if (actualRange) {
    1651         *actualRange = nsRange;
    1652         actualRange->length = [result.string length];
    1653     }
    1654 
    1655     LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string string]);
    1656     return [[result.string retain] autorelease];
    1657 }
    1658 
    1659 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
    1660 {
    1661     [self _executeSavedKeypressCommands];
    1662 
    1663     NSWindow *window = [self window];
    1664    
    1665 #pragma clang diagnostic push
    1666 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    1667     if (window)
    1668         thePoint = [window convertScreenToBase:thePoint];
    1669 #pragma clang diagnostic pop
    1670     thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
    1671    
    1672     uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
    1673     if (result == notFound)
    1674         result = NSNotFound;
    1675     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
    1676     return result;
    1677 }
    1678 
    1679 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
    1680 {
    1681     [self _executeSavedKeypressCommands];
    1682 
    1683     // Just to match NSTextView's behavior. Regression tests cannot detect this;
    1684     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
    1685     // (type something; try ranges (1, -1) and (2, -1).
    1686     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
    1687         theRange.length = 0;
    1688    
    1689     NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange);
    1690     resultRect = [self convertRect:resultRect toView:nil];
    1691     resultRect = [self.window convertRectToScreen:resultRect];
    1692 
    1693     if (actualRange) {
    1694         // FIXME: Update actualRange to match the range of first rect.
    1695         *actualRange = theRange;
    1696     }
    1697 
    1698     LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
    1699     return resultRect;
    17002184}
    17012185
     
    23202804}
    23212805
    2322 - (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands
    2323 {
    2324     ASSERT(!_data->_interpretKeyEventsParameters);
    2325     ASSERT(commands.isEmpty());
    2326 
    2327     if ([event type] == NSFlagsChanged)
    2328         return NO;
    2329 
    2330     WKViewInterpretKeyEventsParameters parameters;
    2331     parameters.eventInterpretationHadSideEffects = false;
    2332     parameters.executingSavedKeypressCommands = false;
    2333     // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
    2334     // We assume the IM will *not* consume hotkey sequences.
    2335     parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask);
    2336     parameters.commands = &commands;
    2337     _data->_interpretKeyEventsParameters = &parameters;
    2338 
    2339     LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event);
    2340     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
    2341 
    2342     _data->_interpretKeyEventsParameters = nullptr;
    2343 
    2344     // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
    2345     // in which case we should not bubble the event up the DOM.
    2346     if (parameters.consumedByIM) {
    2347         ASSERT(commands.isEmpty());
    2348         LOG(TextInput, "...event %p was consumed by an input method", event);
    2349         return YES;
    2350     }
    2351 
    2352     LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects);
    2353 
    2354     // If we have already executed all or some of the commands, the event is "handled". Note that there are additional checks on web process side.
    2355     return parameters.eventInterpretationHadSideEffects;
    2356 }
    2357 
    23582806- (NSRect)_convertToDeviceSpace:(NSRect)rect
    23592807{
  • trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h

    r165758 r165972  
    7373- (void)_setCursor:(NSCursor *)cursor;
    7474- (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState;
    75 - (BOOL)_interpretKeyEvent:(NSEvent *)theEvent savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands;
    7675- (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled;
    7776- (bool)_executeSavedCommandBySelector:(SEL)selector;
  • trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp

    r165968 r165972  
    10921092
    10931093    uint64_t callbackID = callback->callbackID();
    1094     m_validateCommandCallbacks.set(callbackID, callback.get());
     1094    m_validateCommandCallbacks.set(callbackID, callback);
    10951095    m_process->send(Messages::WebPage::ValidateCommand(commandName, callbackID), m_pageID);
    10961096}
     
    19471947
    19481948    uint64_t callbackID = callback->callbackID();
    1949     m_scriptValueCallbacks.set(callbackID, callback.get());
     1949    m_scriptValueCallbacks.set(callbackID, callback);
    19501950    m_process->send(Messages::WebPage::RunJavaScriptInMainFrame(script, callbackID), m_pageID);
    19511951}
     
    19601960   
    19611961    uint64_t callbackID = callback->callbackID();
    1962     m_stringCallbacks.set(callbackID, callback.get());
     1962    m_stringCallbacks.set(callbackID, callback);
    19631963    m_process->send(Messages::WebPage::GetRenderTreeExternalRepresentation(callbackID), m_pageID);
    19641964}
     
    19741974    uint64_t callbackID = callback->callbackID();
    19751975    m_loadDependentStringCallbackIDs.add(callbackID);
    1976     m_stringCallbacks.set(callbackID, callback.get());
     1976    m_stringCallbacks.set(callbackID, callback);
    19771977    m_process->send(Messages::WebPage::GetSourceForFrame(frame->frameID(), callbackID), m_pageID);
    19781978}
     
    19881988    uint64_t callbackID = callback->callbackID();
    19891989    m_loadDependentStringCallbackIDs.add(callbackID);
    1990     m_stringCallbacks.set(callbackID, callback.get());
     1990    m_stringCallbacks.set(callbackID, callback);
    19911991    m_process->send(Messages::WebPage::GetContentsAsString(callbackID), m_pageID);
    19921992}
     
    20022002    uint64_t callbackID = callback->callbackID();
    20032003    m_loadDependentStringCallbackIDs.add(callbackID);
    2004     m_stringCallbacks.set(callbackID, callback.get());
     2004    m_stringCallbacks.set(callbackID, callback);
    20052005    m_process->send(Messages::WebPage::GetBytecodeProfile(callbackID), m_pageID);
    20062006}
     
    20162016
    20172017    uint64_t callbackID = callback->callbackID();
    2018     m_dataCallbacks.set(callbackID, callback.get());
     2018    m_dataCallbacks.set(callbackID, callback);
    20192019    m_process->send(Messages::WebPage::GetContentsAsMHTMLData(callbackID, useBinaryEncoding), m_pageID);
    20202020}
     
    20302030   
    20312031    uint64_t callbackID = callback->callbackID();
    2032     m_stringCallbacks.set(callbackID, callback.get());
     2032    m_stringCallbacks.set(callbackID, callback);
    20332033    m_process->send(Messages::WebPage::GetSelectionOrContentsAsString(callbackID), m_pageID);
    20342034}
     
    20432043   
    20442044    uint64_t callbackID = callback->callbackID();
    2045     m_dataCallbacks.set(callbackID, callback.get());
     2045    m_dataCallbacks.set(callbackID, callback);
    20462046    m_process->send(Messages::WebPage::GetSelectionAsWebArchiveData(callbackID), m_pageID);
    20472047}
     
    20562056   
    20572057    uint64_t callbackID = callback->callbackID();
    2058     m_dataCallbacks.set(callbackID, callback.get());
     2058    m_dataCallbacks.set(callbackID, callback);
    20592059    m_process->send(Messages::WebPage::GetMainResourceDataOfFrame(frame->frameID(), callbackID), m_pageID);
    20602060}
     
    20692069   
    20702070    uint64_t callbackID = callback->callbackID();
    2071     m_dataCallbacks.set(callbackID, callback.get());
     2071    m_dataCallbacks.set(callbackID, callback);
    20722072    m_process->send(Messages::WebPage::GetResourceDataFromFrame(frame->frameID(), resourceURL->string(), callbackID), m_pageID);
    20732073}
     
    20822082   
    20832083    uint64_t callbackID = callback->callbackID();
    2084     m_dataCallbacks.set(callbackID, callback.get());
     2084    m_dataCallbacks.set(callbackID, callback);
    20852085    m_process->send(Messages::WebPage::GetWebArchiveOfFrame(frame->frameID(), callbackID), m_pageID);
    20862086}
     
    20952095
    20962096    uint64_t callbackID = callback->callbackID();
    2097     m_voidCallbacks.set(callbackID, callback.get());
     2097    m_voidCallbacks.set(callbackID, callback);
    20982098    m_drawingArea->waitForBackingStoreUpdateOnNextPaint();
    20992099    m_process->send(Messages::WebPage::ForceRepaint(callbackID), m_pageID);
     
    37923792}
    37933793
     3794void WebPageProxy::unsignedCallback(uint64_t result, uint64_t callbackID)
     3795{
     3796    RefPtr<UnsignedCallback> callback = m_unsignedCallbacks.take(callbackID);
     3797    if (!callback) {
     3798        // FIXME: Log error or assert.
     3799        // this can validly happen if a load invalidated the callback, though
     3800        return;
     3801    }
     3802
     3803    callback->performCallbackWithReturnValue(result);
     3804}
     3805
     3806void WebPageProxy::editingRangeCallback(const EditingRange& range, uint64_t callbackID)
     3807{
     3808    MESSAGE_CHECK(range.isValid());
     3809
     3810    RefPtr<EditingRangeCallback> callback = m_editingRangeCallbacks.take(callbackID);
     3811    if (!callback) {
     3812        // FIXME: Log error or assert.
     3813        // this can validly happen if a load invalidated the callback, though
     3814        return;
     3815    }
     3816
     3817    callback->performCallbackWithReturnValue(range);
     3818}
     3819
     3820void WebPageProxy::rectForCharacterRangeCallback(const IntRect& rect, const EditingRange& actualRange, uint64_t callbackID)
     3821{
     3822    MESSAGE_CHECK(actualRange.isValid());
     3823
     3824    RefPtr<RectForCharacterRangeCallback> callback = m_rectForCharacterRangeCallbacks.take(callbackID);
     3825    if (!callback) {
     3826        // FIXME: Log error or assert.
     3827        // this can validly happen if a load invalidated the callback, though
     3828        return;
     3829    }
     3830
     3831    callback->performCallbackWithReturnValue(rect, actualRange);
     3832}
     3833
    37943834#if PLATFORM(GTK)
    37953835void WebPageProxy::printFinishedCallback(const ResourceError& printError, uint64_t callbackID)
     
    39343974    invalidateCallbackMap(m_computedPagesCallbacks);
    39353975    invalidateCallbackMap(m_validateCommandCallbacks);
     3976    invalidateCallbackMap(m_unsignedCallbacks);
     3977    invalidateCallbackMap(m_editingRangeCallbacks);
     3978    invalidateCallbackMap(m_rectForCharacterRangeCallbacks);
     3979#if PLATFORM(MAC)
     3980    invalidateCallbackMap(m_attributedStringForCharacterRangeCallbacks);
     3981#endif
    39363982#if PLATFORM(IOS)
    39373983    invalidateCallbackMap(m_gestureCallbacks);
     
    43414387
    43424388    uint64_t callbackID = callback->callbackID();
    4343     m_computedPagesCallbacks.set(callbackID, callback.get());
     4389    m_computedPagesCallbacks.set(callbackID, callback);
    43444390    m_isInPrintingMode = true;
    43454391    m_process->send(Messages::WebPage::ComputePagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
     
    43564402   
    43574403    uint64_t callbackID = callback->callbackID();
    4358     m_imageCallbacks.set(callbackID, callback.get());
     4404    m_imageCallbacks.set(callbackID, callback);
    43594405    m_process->send(Messages::WebPage::DrawRectToImage(frame->frameID(), printInfo, rect, imageSize, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
    43604406}
     
    43694415   
    43704416    uint64_t callbackID = callback->callbackID();
    4371     m_dataCallbacks.set(callbackID, callback.get());
     4417    m_dataCallbacks.set(callbackID, callback);
    43724418    m_process->send(Messages::WebPage::DrawPagesToPDF(frame->frameID(), printInfo, first, count, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
    43734419}
     
    43824428
    43834429    uint64_t callbackID = callback->callbackID();
    4384     m_printFinishedCallbacks.set(callbackID, callback.get());
     4430    m_printFinishedCallbacks.set(callbackID, callback);
    43854431    m_isInPrintingMode = true;
    43864432    m_process->send(Messages::WebPage::DrawPagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0);
     
    46014647}
    46024648
    4603 void WebPageProxy::takeSnapshot(IntRect rect, IntSize bitmapSize, SnapshotOptions options, ImageCallback::CallbackFunction callbackFunction)
    4604 {
    4605     RefPtr<ImageCallback> callback = ImageCallback::create(callbackFunction);
    4606 
     4649#if PLATFORM(COCOA)
     4650
     4651void WebPageProxy::insertTextAsync(const String& text, const EditingRange& replacementRange)
     4652{
     4653    if (!isValid())
     4654        return;
     4655
     4656    process().send(Messages::WebPage::InsertTextAsync(text, replacementRange), m_pageID);
     4657}
     4658
     4659void WebPageProxy::getMarkedRangeAsync(PassRefPtr<EditingRangeCallback> callback)
     4660{
    46074661    if (!isValid()) {
    46084662        callback->invalidate();
     
    46114665
    46124666    uint64_t callbackID = callback->callbackID();
     4667    m_editingRangeCallbacks.set(callbackID, callback);
     4668
     4669    process().send(Messages::WebPage::GetMarkedRangeAsync(callbackID), m_pageID);
     4670}
     4671
     4672void WebPageProxy::getSelectedRangeAsync(PassRefPtr<EditingRangeCallback> callback)
     4673{
     4674    if (!isValid()) {
     4675        callback->invalidate();
     4676        return;
     4677    }
     4678
     4679    uint64_t callbackID = callback->callbackID();
     4680    m_editingRangeCallbacks.set(callbackID, callback);
     4681
     4682    process().send(Messages::WebPage::GetSelectedRangeAsync(callbackID), m_pageID);
     4683}
     4684
     4685void WebPageProxy::characterIndexForPointAsync(const WebCore::IntPoint& point, PassRefPtr<UnsignedCallback> callback)
     4686{
     4687    if (!isValid()) {
     4688        callback->invalidate();
     4689        return;
     4690    }
     4691
     4692    uint64_t callbackID = callback->callbackID();
     4693    m_unsignedCallbacks.set(callbackID, callback);
     4694
     4695    process().send(Messages::WebPage::CharacterIndexForPointAsync(point, callbackID), m_pageID);
     4696}
     4697
     4698void WebPageProxy::firstRectForCharacterRangeAsync(const EditingRange& range, PassRefPtr<RectForCharacterRangeCallback> callback)
     4699{
     4700    if (!isValid()) {
     4701        callback->invalidate();
     4702        return;
     4703    }
     4704
     4705    uint64_t callbackID = callback->callbackID();
     4706    m_rectForCharacterRangeCallbacks.set(callbackID, callback);
     4707
     4708    process().send(Messages::WebPage::FirstRectForCharacterRangeAsync(range, callbackID), m_pageID);
     4709}
     4710
     4711void WebPageProxy::setCompositionAsync(const String& text, Vector<CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange)
     4712{
     4713    if (!isValid()) {
     4714        // If this fails, we should call -discardMarkedText on input context to notify the input method.
     4715        // This will happen naturally later, as part of reloading the page.
     4716        return;
     4717    }
     4718
     4719    process().send(Messages::WebPage::SetCompositionAsync(text, underlines, selectionRange, replacementRange), m_pageID);
     4720}
     4721
     4722void WebPageProxy::confirmCompositionAsync()
     4723{
     4724    if (!isValid())
     4725        return;
     4726
     4727    process().send(Messages::WebPage::ConfirmCompositionAsync(), m_pageID);
     4728}
     4729
     4730#endif
     4731
     4732void WebPageProxy::takeSnapshot(IntRect rect, IntSize bitmapSize, SnapshotOptions options, ImageCallback::CallbackFunction callbackFunction)
     4733{
     4734    RefPtr<ImageCallback> callback = ImageCallback::create(callbackFunction);
     4735
     4736    if (!isValid()) {
     4737        callback->invalidate();
     4738        return;
     4739    }
     4740
     4741    uint64_t callbackID = callback->callbackID();
    46134742    m_imageCallbacks.set(callbackID, callback.get());
    46144743
  • trunk/Source/WebKit2/UIProcess/WebPageProxy.h

    r165872 r165972  
    3535#include "DragControllerAction.h"
    3636#include "DrawingAreaProxy.h"
     37#include "EditingRange.h"
    3738#include "EditorState.h"
    3839#include "GeolocationPermissionRequestManagerProxy.h"
     
    99100#endif
    100101
     102#if PLATFORM(MAC)
     103#include "AttributedString.h"
     104#endif
     105
    101106namespace API {
    102107class LoaderClient;
     
    171176#endif
    172177
     178typedef GenericCallback<uint64_t> UnsignedCallback;
     179typedef GenericCallback<EditingRange> EditingRangeCallback;
    173180typedef GenericCallback<StringImpl*> StringCallback;
    174181typedef GenericCallback<WebSerializedScriptValue*> ScriptValueCallback;
     
    231238    CallbackFunction m_callback;
    232239};
     240
     241// FIXME: Make a version of CallbackBase with two arguments, and define RectForCharacterRangeCallback as a specialization.
     242class RectForCharacterRangeCallback : public CallbackBase {
     243public:
     244    typedef std::function<void (bool, const WebCore::IntRect&, const EditingRange&)> CallbackFunction;
     245
     246    static PassRefPtr<RectForCharacterRangeCallback> create(CallbackFunction callback)
     247    {
     248        return adoptRef(new RectForCharacterRangeCallback(callback));
     249    }
     250
     251    virtual ~RectForCharacterRangeCallback()
     252    {
     253        ASSERT(!m_callback);
     254    }
     255
     256    void performCallbackWithReturnValue(const WebCore::IntRect& rect, const EditingRange& range)
     257    {
     258        ASSERT(m_callback);
     259
     260        m_callback(false, rect, range);
     261
     262        m_callback = 0;
     263    }
     264   
     265    void invalidate()
     266    {
     267        ASSERT(m_callback);
     268
     269        m_callback(true, WebCore::IntRect(), EditingRange());
     270
     271        m_callback = 0;
     272    }
     273
     274private:
     275
     276    RectForCharacterRangeCallback(CallbackFunction callback)
     277        : m_callback(callback)
     278    {
     279    }
     280
     281    CallbackFunction m_callback;
     282};
     283
     284#if PLATFORM(MAC)
     285
     286// FIXME: Make a version of CallbackBase with two arguments, and define AttributedStringForCharacterRangeCallback as a specialization.
     287class AttributedStringForCharacterRangeCallback : public CallbackBase {
     288public:
     289    typedef std::function<void (bool, const AttributedString&, const EditingRange&)> CallbackFunction;
     290
     291    static PassRefPtr<AttributedStringForCharacterRangeCallback> create(CallbackFunction callback)
     292    {
     293        return adoptRef(new AttributedStringForCharacterRangeCallback(callback));
     294    }
     295
     296    virtual ~AttributedStringForCharacterRangeCallback()
     297    {
     298        ASSERT(!m_callback);
     299    }
     300
     301    void performCallbackWithReturnValue(const AttributedString& string, const EditingRange& range)
     302    {
     303        ASSERT(m_callback);
     304
     305        m_callback(false, string, range);
     306
     307        m_callback = 0;
     308    }
     309   
     310    void invalidate()
     311    {
     312        ASSERT(m_callback);
     313
     314        m_callback(true, AttributedString(), EditingRange());
     315
     316        m_callback = 0;
     317    }
     318
     319private:
     320
     321    AttributedStringForCharacterRangeCallback(CallbackFunction callback)
     322        : m_callback(callback)
     323    {
     324    }
     325
     326    CallbackFunction m_callback;
     327};
     328
     329#endif
    233330
    234331#if PLATFORM(IOS)
     
    536633    void cancelComposition();
    537634    bool insertText(const String& text, const EditingRange& replacementRange);
    538     bool insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::TextAlternativeWithRange>& dictationAlternatives);
    539635    void getMarkedRange(EditingRange&);
    540636    void getSelectedRange(EditingRange&);
    541     void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);
    542637    uint64_t characterIndexForPoint(const WebCore::IntPoint);
    543638    WebCore::IntRect firstRectForCharacterRange(const EditingRange&);
     
    551646    LayerOrView* acceleratedCompositingRootLayer() const;
    552647
    553 #if USE(APPKIT)
     648    void insertTextAsync(const String& text, const EditingRange& replacementRange);
     649    void getMarkedRangeAsync(PassRefPtr<EditingRangeCallback>);
     650    void getSelectedRangeAsync(PassRefPtr<EditingRangeCallback>);
     651    void characterIndexForPointAsync(const WebCore::IntPoint&, PassRefPtr<UnsignedCallback>);
     652    void firstRectForCharacterRangeAsync(const EditingRange&, PassRefPtr<RectForCharacterRangeCallback>);
     653    void setCompositionAsync(const String& text, Vector<WebCore::CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange);
     654    void confirmCompositionAsync();
     655
     656#if PLATFORM(MAC)
     657    bool insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::TextAlternativeWithRange>& dictationAlternatives);
     658    void insertDictatedTextAsync(const String& text, const EditingRange& replacementRange, const Vector<WebCore::TextAlternativeWithRange>& dictationAlternatives);
     659    void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);
     660    void attributedSubstringForCharacterRangeAsync(const EditingRange&, PassRefPtr<AttributedStringForCharacterRangeCallback>);
     661
    554662    WKView* wkView() const;
    555663    void intrinsicContentSizeDidChange(const WebCore::IntSize& intrinsicContentSize);
     
    11561264    void computedPagesCallback(const Vector<WebCore::IntRect>&, double totalScaleFactorForPrinting, uint64_t);
    11571265    void validateCommandCallback(const String&, bool, int, uint64_t);
     1266    void unsignedCallback(uint64_t, uint64_t);
     1267    void editingRangeCallback(const EditingRange&, uint64_t);
     1268    void rectForCharacterRangeCallback(const WebCore::IntRect&, const EditingRange&, uint64_t);
     1269#if PLATFORM(MAC)
     1270    void attributedStringForCharacterRangeCallback(const AttributedString&, const EditingRange&, uint64_t);
     1271#endif
    11581272#if PLATFORM(IOS)
    11591273    void gestureCallback(const WebCore::IntPoint&, uint32_t, uint32_t, uint32_t, uint64_t);
     
    12931407    HashMap<uint64_t, RefPtr<ComputedPagesCallback>> m_computedPagesCallbacks;
    12941408    HashMap<uint64_t, RefPtr<ValidateCommandCallback>> m_validateCommandCallbacks;
     1409    HashMap<uint64_t, RefPtr<UnsignedCallback>> m_unsignedCallbacks;
     1410    HashMap<uint64_t, RefPtr<EditingRangeCallback>> m_editingRangeCallbacks;
     1411    HashMap<uint64_t, RefPtr<RectForCharacterRangeCallback>> m_rectForCharacterRangeCallbacks;
     1412#if PLATFORM(MAC)
     1413    HashMap<uint64_t, RefPtr<AttributedStringForCharacterRangeCallback>> m_attributedStringForCharacterRangeCallbacks;
     1414#endif
    12951415#if PLATFORM(IOS)
    12961416    HashMap<uint64_t, RefPtr<GestureCallback>> m_gestureCallbacks;
  • trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in

    r165356 r165972  
    152152    ComputedPagesCallback(Vector<WebCore::IntRect> pageRects, double totalScaleFactorForPrinting, uint64_t callbackID)
    153153    ValidateCommandCallback(String command, bool isEnabled, int32_t state, uint64_t callbackID)
     154    EditingRangeCallback(WebKit::EditingRange range, uint64_t callbackID)
     155    UnsignedCallback(uint64_t result, uint64_t callbackID)
     156    RectForCharacterRangeCallback(WebCore::IntRect rect, WebKit::EditingRange actualRange, uint64_t callbackID)
     157#if PLATFORM(MAC)
     158    AttributedStringForCharacterRangeCallback(WebKit::AttributedString string, WebKit::EditingRange actualRange, uint64_t callbackID)
     159#endif
    154160#if PLATFORM(IOS)
    155161    GestureCallback(WebCore::IntPoint point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, uint64_t callbackID)
  • trunk/Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm

    r165823 r165972  
    131131}
    132132
    133 bool WebPageProxy::insertDictatedText(const String&, const EditingRange&, const Vector<WebCore::TextAlternativeWithRange>&)
    134 {
    135     notImplemented();
    136     return false;
    137 }
    138 
    139133void WebPageProxy::getMarkedRange(EditingRange&)
    140134{
     
    143137
    144138void WebPageProxy::getSelectedRange(EditingRange&)
    145 {
    146     notImplemented();
    147 }
    148 
    149 void WebPageProxy::getAttributedSubstringFromRange(const EditingRange&, AttributedString&)
    150139{
    151140    notImplemented();
  • trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm

    r165823 r165972  
    221221}
    222222
     223void WebPageProxy::insertDictatedTextAsync(const String& text, const EditingRange& replacementRange, const Vector<TextAlternativeWithRange>& dictationAlternativesWithRange)
     224{
     225#if USE(DICTATION_ALTERNATIVES)
     226    if (!isValid())
     227        return;
     228
     229    Vector<DictationAlternative> dictationAlternatives;
     230
     231    for (const TextAlternativeWithRange& alternativeWithRange : dictationAlternativesWithRange) {
     232        uint64_t dictationContext = m_pageClient.addDictationAlternatives(alternativeWithRange.alternatives);
     233        if (dictationContext)
     234            dictationAlternatives.append(DictationAlternative(alternativeWithRange.range.location, alternativeWithRange.range.length, dictationContext));
     235    }
     236
     237    if (dictationAlternatives.isEmpty()) {
     238        insertTextAsync(text, replacementRange);
     239        return;
     240    }
     241
     242    process().send(Messages::WebPage::InsertDictatedTextAsync(text, replacementRange, dictationAlternatives), m_pageID);
     243#else
     244    insertTextAsync(text, replacementRange);
     245#endif
     246}
     247
    223248void WebPageProxy::getMarkedRange(EditingRange& result)
    224249{
     
    248273        return;
    249274    process().sendSync(Messages::WebPage::GetAttributedSubstringFromRange(range), Messages::WebPage::GetAttributedSubstringFromRange::Reply(result), m_pageID);
     275}
     276
     277void WebPageProxy::attributedSubstringForCharacterRangeAsync(const EditingRange& range, PassRefPtr<AttributedStringForCharacterRangeCallback> callback)
     278{
     279    if (!isValid()) {
     280        callback->invalidate();
     281        return;
     282    }
     283
     284    uint64_t callbackID = callback->callbackID();
     285    m_attributedStringForCharacterRangeCallbacks.set(callbackID, callback);
     286
     287    process().send(Messages::WebPage::AttributedSubstringForCharacterRangeAsync(range, callbackID), m_pageID);
     288}
     289
     290void WebPageProxy::attributedStringForCharacterRangeCallback(const AttributedString& string, const EditingRange& actualRange, uint64_t callbackID)
     291{
     292    MESSAGE_CHECK(actualRange.isValid());
     293
     294    RefPtr<AttributedStringForCharacterRangeCallback> callback = m_attributedStringForCharacterRangeCallbacks.take(callbackID);
     295    if (!callback) {
     296        // FIXME: Log error or assert.
     297        // this can validly happen if a load invalidated the callback, though
     298        return;
     299    }
     300
     301    callback->performCallbackWithReturnValue(string, actualRange);
    250302}
    251303
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp

    r165968 r165972  
    38973897}
    38983898
     3899#if PLATFORM(COCOA)
     3900
     3901void WebPage::insertTextAsync(const String& text, const EditingRange& replacementEditingRange)
     3902{
     3903    Frame& frame = m_page->focusController().focusedOrMainFrame();
     3904
     3905    if (replacementEditingRange.location != notFound) {
     3906        RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
     3907        if (replacementRange)
     3908            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
     3909    }
     3910
     3911    if (!frame.editor().hasComposition()) {
     3912        // An insertText: might be handled by other responders in the chain if we don't handle it.
     3913        // One example is space bar that results in scrolling down the page.
     3914        frame.editor().insertText(text, nullptr);
     3915    } else
     3916        frame.editor().confirmComposition(text);
     3917}
     3918
     3919void WebPage::getMarkedRangeAsync(uint64_t callbackID)
     3920{
     3921    Frame& frame = m_page->focusController().focusedOrMainFrame();
     3922
     3923    RefPtr<Range> range = frame.editor().compositionRange();
     3924    size_t location;
     3925    size_t length;
     3926    if (!range || !TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), location, length)) {
     3927        location = notFound;
     3928        length = 0;
     3929    }
     3930
     3931    send(Messages::WebPageProxy::EditingRangeCallback(EditingRange(location, length), callbackID));
     3932}
     3933
     3934void WebPage::getSelectedRangeAsync(uint64_t callbackID)
     3935{
     3936    Frame& frame = m_page->focusController().focusedOrMainFrame();
     3937
     3938    size_t location;
     3939    size_t length;
     3940    RefPtr<Range> range = frame.selection().toNormalizedRange();
     3941    if (!range || !TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), location, length)) {
     3942        location = notFound;
     3943        length = 0;
     3944    }
     3945
     3946    send(Messages::WebPageProxy::EditingRangeCallback(EditingRange(location, length), callbackID));
     3947}
     3948
     3949void WebPage::characterIndexForPointAsync(const WebCore::IntPoint& point, uint64_t callbackID)
     3950{
     3951    uint64_t index = notFound;
     3952
     3953    HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(point);
     3954    Frame* frame = result.innerNonSharedNode() ? result.innerNodeFrame() : &m_page->focusController().focusedOrMainFrame();
     3955   
     3956    RefPtr<Range> range = frame->rangeForPoint(result.roundedPointInInnerNodeFrame());
     3957    if (range) {
     3958        size_t location;
     3959        size_t length;
     3960        if (TextIterator::getLocationAndLengthFromRange(frame->selection().rootEditableElementOrDocumentElement(), range.get(), location, length))
     3961            index = static_cast<uint64_t>(location);
     3962    }
     3963
     3964    send(Messages::WebPageProxy::UnsignedCallback(index, callbackID));
     3965}
     3966
     3967void WebPage::firstRectForCharacterRangeAsync(const EditingRange& editingRange, uint64_t callbackID)
     3968{
     3969    Frame& frame = m_page->focusController().focusedOrMainFrame();
     3970    IntRect result(IntPoint(0, 0), IntSize(0, 0));
     3971   
     3972    RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
     3973    if (!range) {
     3974        send(Messages::WebPageProxy::RectForCharacterRangeCallback(result, EditingRange(notFound, 0), callbackID));
     3975        return;
     3976    }
     3977
     3978    ASSERT(range->startContainer());
     3979    ASSERT(range->endContainer());
     3980
     3981    result = frame.view()->contentsToWindow(frame.editor().firstRectForRange(range.get()));
     3982
     3983    // FIXME: Update actualRange to match the range of first rect.
     3984    send(Messages::WebPageProxy::RectForCharacterRangeCallback(result, editingRange, callbackID));
     3985}
     3986
     3987void WebPage::setCompositionAsync(const String& text, Vector<CompositionUnderline> underlines, const EditingRange& selection, const EditingRange& replacementEditingRange)
     3988{
     3989    Frame& frame = m_page->focusController().focusedOrMainFrame();
     3990
     3991    if (frame.selection().selection().isContentEditable()) {
     3992        RefPtr<Range> replacementRange;
     3993        if (replacementEditingRange.location != notFound) {
     3994            replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
     3995            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
     3996        }
     3997
     3998        frame.editor().setComposition(text, underlines, selection.location, selection.location + selection.length);
     3999    }
     4000}
     4001
     4002void WebPage::confirmCompositionAsync()
     4003{
     4004    Frame& frame = m_page->focusController().focusedOrMainFrame();
     4005    frame.editor().confirmComposition();
     4006}
     4007
     4008#endif // PLATFORM(COCOA)
     4009
    38994010#if PLATFORM(GTK)
    39004011static Frame* targetFrameForEditing(WebPage* page)
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h

    r165872 r165972  
    539539    void sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput);
    540540
    541     void cancelComposition(EditorState& newState);
    542 #if !PLATFORM(IOS)
     541#if PLATFORM(MAC)
    543542    void insertText(const String& text, const EditingRange& replacementRange, bool& handled, EditorState& newState);
    544543    void setComposition(const String& text, Vector<WebCore::CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange, EditorState& newState);
    545544    void confirmComposition(EditorState& newState);
    546 #endif
     545    void insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState);
     546    void insertDictatedTextAsync(const String& text, const EditingRange& replacementRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations);
     547    void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);
     548    void attributedSubstringForCharacterRangeAsync(const EditingRange&, uint64_t callbackID);
     549#endif
     550
     551    void insertTextAsync(const String& text, const EditingRange& replacementRange);
     552    void getMarkedRangeAsync(uint64_t callbackID);
     553    void getSelectedRangeAsync(uint64_t callbackID);
     554    void characterIndexForPointAsync(const WebCore::IntPoint&, uint64_t callbackID);
     555    void firstRectForCharacterRangeAsync(const EditingRange&, uint64_t callbackID);
     556    void setCompositionAsync(const String& text, Vector<WebCore::CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange);
     557    void confirmCompositionAsync();
     558
     559    void cancelComposition(EditorState& newState);
    547560    void getMarkedRange(EditingRange&);
    548561    void getSelectedRange(EditingRange&);
    549     void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);
    550562    void characterIndexForPoint(const WebCore::IntPoint point, uint64_t& result);
    551563    void firstRectForCharacterRange(const EditingRange&, WebCore::IntRect& resultRect);
     
    557569    void acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent&, bool& result);
    558570    bool performNonEditingBehaviorForSelector(const String&, WebCore::KeyboardEvent*);
    559     void insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState);
    560571#elif PLATFORM(EFL)
    561572    void confirmComposition(const String& compositionString);
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in

    r165872 r165972  
    5757    RequestAutocorrectionContext(uint64_t callbackID)
    5858    GetAutocorrectionContext() -> (String beforeContext, String markedText, String selectedText, String afterContext, uint64_t location, uint64_t length)
     59    # FIXME: Use shared COCOA text input methods like insertTextAsync.
    5960    InsertText(String text, WebKit::EditingRange replacementRange)
    6061    SetComposition(String text, Vector<WebCore::CompositionUnderline> underlines, WebKit::EditingRange selectionRange)
     
    299300    GetMarkedRange() -> (WebKit::EditingRange range)
    300301    GetSelectedRange() -> (WebKit::EditingRange range)
    301     GetAttributedSubstringFromRange(WebKit::EditingRange range) -> (WebKit::AttributedString result)
    302302    CharacterIndexForPoint(WebCore::IntPoint point) -> (uint64_t result)
    303303    FirstRectForCharacterRange(WebKit::EditingRange range) -> (WebCore::IntRect resultRect)
     
    305305    ShouldDelayWindowOrderingEvent(WebKit::WebMouseEvent event) -> (bool result)
    306306    AcceptsFirstMouse(int eventNumber, WebKit::WebMouseEvent event) -> (bool result)
    307     InsertDictatedText(String text, WebKit::EditingRange replacementRange, Vector<WebCore::DictationAlternative> dictationAlternatives) -> (bool handled, WebKit::EditorState newState)
     307
     308    InsertTextAsync(String text, WebKit::EditingRange replacementRange)
     309    GetMarkedRangeAsync(uint64_t callbackID)
     310    GetSelectedRangeAsync(uint64_t callbackID)
     311    CharacterIndexForPointAsync(WebCore::IntPoint point, uint64_t callbackID);
     312    FirstRectForCharacterRangeAsync(WebKit::EditingRange range, uint64_t callbackID);
     313    SetCompositionAsync(String text, Vector<WebCore::CompositionUnderline> underlines, WebKit::EditingRange selectionRange, WebKit::EditingRange replacementRange)
     314    ConfirmCompositionAsync()
    308315#endif
    309316#if PLATFORM(MAC)
     
    311318    SetComposition(String text, Vector<WebCore::CompositionUnderline> underlines, WebKit::EditingRange selectionRange, WebKit::EditingRange replacementRange) -> (WebKit::EditorState newState)
    312319    ConfirmComposition() -> (WebKit::EditorState newState)
     320    InsertDictatedText(String text, WebKit::EditingRange replacementRange, Vector<WebCore::DictationAlternative> dictationAlternatives) -> (bool handled, WebKit::EditorState newState)
     321    InsertDictatedTextAsync(String text, WebKit::EditingRange replacementRange, Vector<WebCore::DictationAlternative> dictationAlternatives)
     322    GetAttributedSubstringFromRange(WebKit::EditingRange range) -> (WebKit::AttributedString result)
     323    AttributedSubstringForCharacterRangeAsync(WebKit::EditingRange range, uint64_t callbackID);
    313324#endif
    314325    SetMinimumLayoutSize(WebCore::IntSize minimumLayoutSize)
  • trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm

    r165823 r165972  
    205205}
    206206
    207 void WebPage::insertDictatedText(const String&, const EditingRange&, const Vector<WebCore::DictationAlternative>&, bool&, EditorState&)
    208 {
    209     notImplemented();
    210 }
    211 
    212207void WebPage::getMarkedRange(EditingRange&)
    213208{
     
    216211
    217212void WebPage::getSelectedRange(EditingRange&)
    218 {
    219     notImplemented();
    220 }
    221 
    222 void WebPage::getAttributedSubstringFromRange(const EditingRange&, AttributedString&)
    223213{
    224214    notImplemented();
  • trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm

    r165823 r165972  
    315315}
    316316
     317void WebPage::insertDictatedTextAsync(const String& text, const EditingRange& replacementEditingRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations)
     318{
     319    Frame& frame = m_page->focusController().focusedOrMainFrame();
     320
     321    if (replacementEditingRange.location != notFound) {
     322        RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
     323        if (replacementRange)
     324            frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
     325    }
     326
     327    ASSERT(!frame.editor().hasComposition());
     328    frame.editor().insertDictatedText(text, dictationAlternativeLocations, nullptr);
     329}
     330
    317331void WebPage::getMarkedRange(EditingRange& result)
    318332{
     
    364378        result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
    365379    }
     380}
     381
     382void WebPage::attributedSubstringForCharacterRangeAsync(const EditingRange& editingRange, uint64_t callbackID)
     383{
     384    AttributedString result;
     385
     386    Frame& frame = m_page->focusController().focusedOrMainFrame();
     387
     388    const VisibleSelection& selection = frame.selection().selection();
     389    if (selection.isNone() || !selection.isContentEditable() || selection.isInPasswordField()) {
     390        send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
     391        return;
     392    }
     393
     394    RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
     395    if (!range) {
     396        send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
     397        return;
     398    }
     399
     400    result.string = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
     401    NSAttributedString* attributedString = result.string.get();
     402   
     403    // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing
     404    // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
     405    // To work around this we truncate the resultant string to the correct length.
     406    if ([attributedString length] > editingRange.length) {
     407        ASSERT([attributedString length] == editingRange.length + 1);
     408        ASSERT([[attributedString string] characterAtIndex:editingRange.length] == '\n' || [[attributedString string] characterAtIndex:editingRange.length] == ' ');
     409        result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
     410    }
     411
     412    send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(editingRange.location, [result.string length]), callbackID));
    366413}
    367414
Note: See TracChangeset for help on using the changeset viewer.