Changeset 165972 in webkit
- Timestamp:
- Mar 20, 2014 10:45:04 AM (10 years ago)
- Location:
- trunk/Source/WebKit2
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit2/ChangeLog
r165968 r165972 1 2014-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 1 53 2014-03-20 Martin Robinson <mrobinson@igalia.com> 2 54 -
trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm
r165823 r165972 116 116 @end 117 117 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 118 126 #if defined(__has_include) && __has_include(<CoreGraphics/CoreGraphicsPrivate.h>) 119 127 #import <CoreGraphics/CoreGraphicsPrivate.h> … … 140 148 } 141 149 150 #if !USE(ASYNC_NSTEXTINPUTCLIENT) 142 151 struct WKViewInterpretKeyEventsParameters { 143 152 bool eventInterpretationHadSideEffects; … … 146 155 Vector<KeypressCommand>* commands; 147 156 }; 157 #endif 148 158 149 159 @interface WKViewData : NSObject { … … 174 184 // that has been already sent to WebCore. 175 185 RetainPtr<NSEvent> _keyDownEventBeingResent; 186 #if USE(ASYNC_NSTEXTINPUTCLIENT) 187 Vector<KeypressCommand>* _collectedKeypressCommands; 188 #else 176 189 WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters; 190 #endif 177 191 178 192 NSSize _resizeScrollOffset; … … 1084 1098 } 1085 1099 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 1086 1120 #define NATIVE_MOUSE_EVENT_HANDLER(Selector) \ 1087 1121 - (void)Selector:(NSEvent *)theEvent \ … … 1096 1130 _data->_page->handleMouseEvent(webEvent); \ 1097 1131 } 1132 #endif 1098 1133 1099 1134 NATIVE_MOUSE_EVENT_HANDLER(mouseEntered) … … 1214 1249 } 1215 1250 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 1300 static 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 = ¶meters; 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 1216 1802 - (void)doCommandBySelector:(SEL)selector 1217 1803 { … … 1239 1825 - (void)insertText:(id)string 1240 1826 { 1241 // Unlike an dNSTextInputClient 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, 1242 1828 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText 1243 1829 // command ensures that a keypress event is dispatched as appropriate. … … 1299 1885 } 1300 1886 1301 - (BOOL)performKeyEquivalent:(NSEvent *)event1302 {1303 // There's a chance that responding to this event will run a nested event loop, and1304 // fetching a new event might release the old one. Retaining and then autoreleasing1305 // 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 for1324 // 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 *)theEvent1337 {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)_disableComplexTextInputIfNecessary1344 {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 *)event1357 {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 *)event1377 {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 *)theEvent1393 {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, and1397 // fetching a new event might release the old one. Retaining and then autoreleasing1398 // 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 event1407 // that maps to an action that is currently unavailable (for example a copy when1408 // 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 that1420 // 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 *)theEvent1428 {1429 LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);1430 1431 // There's a chance that responding to this event will run a nested event loop, and1432 // fetching a new event might release the old one. Retaining and then autoreleasing1433 // 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 keys1439 if (!keyCode || keyCode == 10 || keyCode == 63)1440 return;1441 1442 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));1443 }1444 1445 - (void)_executeSavedKeypressCommands1446 {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 *)inputContext1467 {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 1480 1887 - (NSRange)selectedRange 1481 1888 { … … 1532 1939 _data->_page->confirmComposition(); 1533 1940 } 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) 1534 2163 1535 2164 - (NSArray *)validAttributesForMarkedText … … 1553 2182 LOG(TextInput, "validAttributesForMarkedText -> (...)"); 1554 2183 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)replacementRange1578 {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 } else1602 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 } else1612 NSBeep();1613 return;1614 }1615 1616 _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange);1617 }1618 1619 - (NSRange)markedRange1620 {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 else1630 LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);1631 1632 return result;1633 }1634 1635 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange1636 {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)thePoint1660 {1661 [self _executeSavedKeypressCommands];1662 1663 NSWindow *window = [self window];1664 1665 #pragma clang diagnostic push1666 #pragma clang diagnostic ignored "-Wdeprecated-declarations"1667 if (window)1668 thePoint = [window convertScreenToBase:thePoint];1669 #pragma clang diagnostic pop1670 thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame1671 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)actualRange1680 {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=46821685 // (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;1700 2184 } 1701 2185 … … 2320 2804 } 2321 2805 2322 - (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands2323 {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 = ¶meters;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 2358 2806 - (NSRect)_convertToDeviceSpace:(NSRect)rect 2359 2807 { -
trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h
r165758 r165972 73 73 - (void)_setCursor:(NSCursor *)cursor; 74 74 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState; 75 - (BOOL)_interpretKeyEvent:(NSEvent *)theEvent savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands;76 75 - (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled; 77 76 - (bool)_executeSavedCommandBySelector:(SEL)selector; -
trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp
r165968 r165972 1092 1092 1093 1093 uint64_t callbackID = callback->callbackID(); 1094 m_validateCommandCallbacks.set(callbackID, callback .get());1094 m_validateCommandCallbacks.set(callbackID, callback); 1095 1095 m_process->send(Messages::WebPage::ValidateCommand(commandName, callbackID), m_pageID); 1096 1096 } … … 1947 1947 1948 1948 uint64_t callbackID = callback->callbackID(); 1949 m_scriptValueCallbacks.set(callbackID, callback .get());1949 m_scriptValueCallbacks.set(callbackID, callback); 1950 1950 m_process->send(Messages::WebPage::RunJavaScriptInMainFrame(script, callbackID), m_pageID); 1951 1951 } … … 1960 1960 1961 1961 uint64_t callbackID = callback->callbackID(); 1962 m_stringCallbacks.set(callbackID, callback .get());1962 m_stringCallbacks.set(callbackID, callback); 1963 1963 m_process->send(Messages::WebPage::GetRenderTreeExternalRepresentation(callbackID), m_pageID); 1964 1964 } … … 1974 1974 uint64_t callbackID = callback->callbackID(); 1975 1975 m_loadDependentStringCallbackIDs.add(callbackID); 1976 m_stringCallbacks.set(callbackID, callback .get());1976 m_stringCallbacks.set(callbackID, callback); 1977 1977 m_process->send(Messages::WebPage::GetSourceForFrame(frame->frameID(), callbackID), m_pageID); 1978 1978 } … … 1988 1988 uint64_t callbackID = callback->callbackID(); 1989 1989 m_loadDependentStringCallbackIDs.add(callbackID); 1990 m_stringCallbacks.set(callbackID, callback .get());1990 m_stringCallbacks.set(callbackID, callback); 1991 1991 m_process->send(Messages::WebPage::GetContentsAsString(callbackID), m_pageID); 1992 1992 } … … 2002 2002 uint64_t callbackID = callback->callbackID(); 2003 2003 m_loadDependentStringCallbackIDs.add(callbackID); 2004 m_stringCallbacks.set(callbackID, callback .get());2004 m_stringCallbacks.set(callbackID, callback); 2005 2005 m_process->send(Messages::WebPage::GetBytecodeProfile(callbackID), m_pageID); 2006 2006 } … … 2016 2016 2017 2017 uint64_t callbackID = callback->callbackID(); 2018 m_dataCallbacks.set(callbackID, callback .get());2018 m_dataCallbacks.set(callbackID, callback); 2019 2019 m_process->send(Messages::WebPage::GetContentsAsMHTMLData(callbackID, useBinaryEncoding), m_pageID); 2020 2020 } … … 2030 2030 2031 2031 uint64_t callbackID = callback->callbackID(); 2032 m_stringCallbacks.set(callbackID, callback .get());2032 m_stringCallbacks.set(callbackID, callback); 2033 2033 m_process->send(Messages::WebPage::GetSelectionOrContentsAsString(callbackID), m_pageID); 2034 2034 } … … 2043 2043 2044 2044 uint64_t callbackID = callback->callbackID(); 2045 m_dataCallbacks.set(callbackID, callback .get());2045 m_dataCallbacks.set(callbackID, callback); 2046 2046 m_process->send(Messages::WebPage::GetSelectionAsWebArchiveData(callbackID), m_pageID); 2047 2047 } … … 2056 2056 2057 2057 uint64_t callbackID = callback->callbackID(); 2058 m_dataCallbacks.set(callbackID, callback .get());2058 m_dataCallbacks.set(callbackID, callback); 2059 2059 m_process->send(Messages::WebPage::GetMainResourceDataOfFrame(frame->frameID(), callbackID), m_pageID); 2060 2060 } … … 2069 2069 2070 2070 uint64_t callbackID = callback->callbackID(); 2071 m_dataCallbacks.set(callbackID, callback .get());2071 m_dataCallbacks.set(callbackID, callback); 2072 2072 m_process->send(Messages::WebPage::GetResourceDataFromFrame(frame->frameID(), resourceURL->string(), callbackID), m_pageID); 2073 2073 } … … 2082 2082 2083 2083 uint64_t callbackID = callback->callbackID(); 2084 m_dataCallbacks.set(callbackID, callback .get());2084 m_dataCallbacks.set(callbackID, callback); 2085 2085 m_process->send(Messages::WebPage::GetWebArchiveOfFrame(frame->frameID(), callbackID), m_pageID); 2086 2086 } … … 2095 2095 2096 2096 uint64_t callbackID = callback->callbackID(); 2097 m_voidCallbacks.set(callbackID, callback .get());2097 m_voidCallbacks.set(callbackID, callback); 2098 2098 m_drawingArea->waitForBackingStoreUpdateOnNextPaint(); 2099 2099 m_process->send(Messages::WebPage::ForceRepaint(callbackID), m_pageID); … … 3792 3792 } 3793 3793 3794 void 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 3806 void 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 3820 void 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 3794 3834 #if PLATFORM(GTK) 3795 3835 void WebPageProxy::printFinishedCallback(const ResourceError& printError, uint64_t callbackID) … … 3934 3974 invalidateCallbackMap(m_computedPagesCallbacks); 3935 3975 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 3936 3982 #if PLATFORM(IOS) 3937 3983 invalidateCallbackMap(m_gestureCallbacks); … … 4341 4387 4342 4388 uint64_t callbackID = callback->callbackID(); 4343 m_computedPagesCallbacks.set(callbackID, callback .get());4389 m_computedPagesCallbacks.set(callbackID, callback); 4344 4390 m_isInPrintingMode = true; 4345 4391 m_process->send(Messages::WebPage::ComputePagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0); … … 4356 4402 4357 4403 uint64_t callbackID = callback->callbackID(); 4358 m_imageCallbacks.set(callbackID, callback .get());4404 m_imageCallbacks.set(callbackID, callback); 4359 4405 m_process->send(Messages::WebPage::DrawRectToImage(frame->frameID(), printInfo, rect, imageSize, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0); 4360 4406 } … … 4369 4415 4370 4416 uint64_t callbackID = callback->callbackID(); 4371 m_dataCallbacks.set(callbackID, callback .get());4417 m_dataCallbacks.set(callbackID, callback); 4372 4418 m_process->send(Messages::WebPage::DrawPagesToPDF(frame->frameID(), printInfo, first, count, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0); 4373 4419 } … … 4382 4428 4383 4429 uint64_t callbackID = callback->callbackID(); 4384 m_printFinishedCallbacks.set(callbackID, callback .get());4430 m_printFinishedCallbacks.set(callbackID, callback); 4385 4431 m_isInPrintingMode = true; 4386 4432 m_process->send(Messages::WebPage::DrawPagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, m_isPerformingDOMPrintOperation ? IPC::DispatchMessageEvenWhenWaitingForSyncReply : 0); … … 4601 4647 } 4602 4648 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 4651 void 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 4659 void WebPageProxy::getMarkedRangeAsync(PassRefPtr<EditingRangeCallback> callback) 4660 { 4607 4661 if (!isValid()) { 4608 4662 callback->invalidate(); … … 4611 4665 4612 4666 uint64_t callbackID = callback->callbackID(); 4667 m_editingRangeCallbacks.set(callbackID, callback); 4668 4669 process().send(Messages::WebPage::GetMarkedRangeAsync(callbackID), m_pageID); 4670 } 4671 4672 void 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 4685 void 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 4698 void 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 4711 void 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 4722 void WebPageProxy::confirmCompositionAsync() 4723 { 4724 if (!isValid()) 4725 return; 4726 4727 process().send(Messages::WebPage::ConfirmCompositionAsync(), m_pageID); 4728 } 4729 4730 #endif 4731 4732 void 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(); 4613 4742 m_imageCallbacks.set(callbackID, callback.get()); 4614 4743 -
trunk/Source/WebKit2/UIProcess/WebPageProxy.h
r165872 r165972 35 35 #include "DragControllerAction.h" 36 36 #include "DrawingAreaProxy.h" 37 #include "EditingRange.h" 37 38 #include "EditorState.h" 38 39 #include "GeolocationPermissionRequestManagerProxy.h" … … 99 100 #endif 100 101 102 #if PLATFORM(MAC) 103 #include "AttributedString.h" 104 #endif 105 101 106 namespace API { 102 107 class LoaderClient; … … 171 176 #endif 172 177 178 typedef GenericCallback<uint64_t> UnsignedCallback; 179 typedef GenericCallback<EditingRange> EditingRangeCallback; 173 180 typedef GenericCallback<StringImpl*> StringCallback; 174 181 typedef GenericCallback<WebSerializedScriptValue*> ScriptValueCallback; … … 231 238 CallbackFunction m_callback; 232 239 }; 240 241 // FIXME: Make a version of CallbackBase with two arguments, and define RectForCharacterRangeCallback as a specialization. 242 class RectForCharacterRangeCallback : public CallbackBase { 243 public: 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 274 private: 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. 287 class AttributedStringForCharacterRangeCallback : public CallbackBase { 288 public: 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 319 private: 320 321 AttributedStringForCharacterRangeCallback(CallbackFunction callback) 322 : m_callback(callback) 323 { 324 } 325 326 CallbackFunction m_callback; 327 }; 328 329 #endif 233 330 234 331 #if PLATFORM(IOS) … … 536 633 void cancelComposition(); 537 634 bool insertText(const String& text, const EditingRange& replacementRange); 538 bool insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::TextAlternativeWithRange>& dictationAlternatives);539 635 void getMarkedRange(EditingRange&); 540 636 void getSelectedRange(EditingRange&); 541 void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);542 637 uint64_t characterIndexForPoint(const WebCore::IntPoint); 543 638 WebCore::IntRect firstRectForCharacterRange(const EditingRange&); … … 551 646 LayerOrView* acceleratedCompositingRootLayer() const; 552 647 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 554 662 WKView* wkView() const; 555 663 void intrinsicContentSizeDidChange(const WebCore::IntSize& intrinsicContentSize); … … 1156 1264 void computedPagesCallback(const Vector<WebCore::IntRect>&, double totalScaleFactorForPrinting, uint64_t); 1157 1265 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 1158 1272 #if PLATFORM(IOS) 1159 1273 void gestureCallback(const WebCore::IntPoint&, uint32_t, uint32_t, uint32_t, uint64_t); … … 1293 1407 HashMap<uint64_t, RefPtr<ComputedPagesCallback>> m_computedPagesCallbacks; 1294 1408 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 1295 1415 #if PLATFORM(IOS) 1296 1416 HashMap<uint64_t, RefPtr<GestureCallback>> m_gestureCallbacks; -
trunk/Source/WebKit2/UIProcess/WebPageProxy.messages.in
r165356 r165972 152 152 ComputedPagesCallback(Vector<WebCore::IntRect> pageRects, double totalScaleFactorForPrinting, uint64_t callbackID) 153 153 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 154 160 #if PLATFORM(IOS) 155 161 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 131 131 } 132 132 133 bool WebPageProxy::insertDictatedText(const String&, const EditingRange&, const Vector<WebCore::TextAlternativeWithRange>&)134 {135 notImplemented();136 return false;137 }138 139 133 void WebPageProxy::getMarkedRange(EditingRange&) 140 134 { … … 143 137 144 138 void WebPageProxy::getSelectedRange(EditingRange&) 145 {146 notImplemented();147 }148 149 void WebPageProxy::getAttributedSubstringFromRange(const EditingRange&, AttributedString&)150 139 { 151 140 notImplemented(); -
trunk/Source/WebKit2/UIProcess/mac/WebPageProxyMac.mm
r165823 r165972 221 221 } 222 222 223 void 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 223 248 void WebPageProxy::getMarkedRange(EditingRange& result) 224 249 { … … 248 273 return; 249 274 process().sendSync(Messages::WebPage::GetAttributedSubstringFromRange(range), Messages::WebPage::GetAttributedSubstringFromRange::Reply(result), m_pageID); 275 } 276 277 void 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 290 void 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); 250 302 } 251 303 -
trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp
r165968 r165972 3897 3897 } 3898 3898 3899 #if PLATFORM(COCOA) 3900 3901 void 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 3919 void 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 3934 void 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 3949 void 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 3967 void 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 3987 void 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 4002 void WebPage::confirmCompositionAsync() 4003 { 4004 Frame& frame = m_page->focusController().focusedOrMainFrame(); 4005 frame.editor().confirmComposition(); 4006 } 4007 4008 #endif // PLATFORM(COCOA) 4009 3899 4010 #if PLATFORM(GTK) 3900 4011 static Frame* targetFrameForEditing(WebPage* page) -
trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h
r165872 r165972 539 539 void sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput); 540 540 541 void cancelComposition(EditorState& newState); 542 #if !PLATFORM(IOS) 541 #if PLATFORM(MAC) 543 542 void insertText(const String& text, const EditingRange& replacementRange, bool& handled, EditorState& newState); 544 543 void setComposition(const String& text, Vector<WebCore::CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange, EditorState& newState); 545 544 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); 547 560 void getMarkedRange(EditingRange&); 548 561 void getSelectedRange(EditingRange&); 549 void getAttributedSubstringFromRange(const EditingRange&, AttributedString&);550 562 void characterIndexForPoint(const WebCore::IntPoint point, uint64_t& result); 551 563 void firstRectForCharacterRange(const EditingRange&, WebCore::IntRect& resultRect); … … 557 569 void acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent&, bool& result); 558 570 bool performNonEditingBehaviorForSelector(const String&, WebCore::KeyboardEvent*); 559 void insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState);560 571 #elif PLATFORM(EFL) 561 572 void confirmComposition(const String& compositionString); -
trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in
r165872 r165972 57 57 RequestAutocorrectionContext(uint64_t callbackID) 58 58 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. 59 60 InsertText(String text, WebKit::EditingRange replacementRange) 60 61 SetComposition(String text, Vector<WebCore::CompositionUnderline> underlines, WebKit::EditingRange selectionRange) … … 299 300 GetMarkedRange() -> (WebKit::EditingRange range) 300 301 GetSelectedRange() -> (WebKit::EditingRange range) 301 GetAttributedSubstringFromRange(WebKit::EditingRange range) -> (WebKit::AttributedString result)302 302 CharacterIndexForPoint(WebCore::IntPoint point) -> (uint64_t result) 303 303 FirstRectForCharacterRange(WebKit::EditingRange range) -> (WebCore::IntRect resultRect) … … 305 305 ShouldDelayWindowOrderingEvent(WebKit::WebMouseEvent event) -> (bool result) 306 306 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() 308 315 #endif 309 316 #if PLATFORM(MAC) … … 311 318 SetComposition(String text, Vector<WebCore::CompositionUnderline> underlines, WebKit::EditingRange selectionRange, WebKit::EditingRange replacementRange) -> (WebKit::EditorState newState) 312 319 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); 313 324 #endif 314 325 SetMinimumLayoutSize(WebCore::IntSize minimumLayoutSize) -
trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm
r165823 r165972 205 205 } 206 206 207 void WebPage::insertDictatedText(const String&, const EditingRange&, const Vector<WebCore::DictationAlternative>&, bool&, EditorState&)208 {209 notImplemented();210 }211 212 207 void WebPage::getMarkedRange(EditingRange&) 213 208 { … … 216 211 217 212 void WebPage::getSelectedRange(EditingRange&) 218 {219 notImplemented();220 }221 222 void WebPage::getAttributedSubstringFromRange(const EditingRange&, AttributedString&)223 213 { 224 214 notImplemented(); -
trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm
r165823 r165972 315 315 } 316 316 317 void 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 317 331 void WebPage::getMarkedRange(EditingRange& result) 318 332 { … … 364 378 result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)]; 365 379 } 380 } 381 382 void 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)); 366 413 } 367 414
Note: See TracChangeset
for help on using the changeset viewer.