Diff
Modified: trunk/Source/WebKit2/ChangeLog (191790 => 191791)
--- trunk/Source/WebKit2/ChangeLog 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/ChangeLog 2015-10-30 17:02:36 UTC (rev 191791)
@@ -1,3 +1,72 @@
+2015-10-30 Tim Horton <[email protected]>
+
+ WKView being inside WKWebView leads to weird API issues
+ https://bugs.webkit.org/show_bug.cgi?id=150174
+
+ Reviewed by Darin Adler.
+
+ * UIProcess/API/mac/WKView.mm:
+ (-[WKView doCommandBySelector:]):
+ (-[WKView insertText:]):
+ (-[WKView insertText:replacementRange:]):
+ (-[WKView inputContext]):
+ (-[WKView performKeyEquivalent:]):
+ (-[WKView keyUp:]):
+ (-[WKView keyDown:]):
+ (-[WKView flagsChanged:]):
+ (-[WKView setMarkedText:selectedRange:replacementRange:]):
+ (-[WKView unmarkText]):
+ (-[WKView selectedRange]):
+ (-[WKView hasMarkedText]):
+ (-[WKView markedRange]):
+ (-[WKView attributedSubstringForProposedRange:actualRange:]):
+ (-[WKView characterIndexForPoint:]):
+ (-[WKView firstRectForCharacterRange:actualRange:]):
+ (-[WKView selectedRangeWithCompletionHandler:]):
+ (-[WKView markedRangeWithCompletionHandler:]):
+ (-[WKView hasMarkedTextWithCompletionHandler:]):
+ (-[WKView attributedSubstringForProposedRange:completionHandler:]):
+ (-[WKView firstRectForCharacterRange:completionHandler:]):
+ (-[WKView characterIndexForPoint:completionHandler:]):
+ (-[WKView _superPerformKeyEquivalent:]):
+ (-[WKView _superKeyDown:]):
+ (extractUnderlines): Deleted.
+ (-[WKView _collectKeyboardLayoutCommandsForEvent:to:]): Deleted.
+ (-[WKView _interpretKeyEvent:completionHandler:]): Deleted.
+ (-[WKView NO_RETURN_DUE_TO_ASSERT]): Deleted.
+ (-[WKView _interpretKeyEvent:savingCommandsTo:]): Deleted.
+ (-[WKView _executeSavedKeypressCommands]): Deleted.
+ (-[WKView _doneWithKeyEvent:eventWasHandled:]): Deleted.
+ * UIProcess/API/mac/WKViewInternal.h:
+ * UIProcess/Cocoa/WebViewImpl.h:
+ * UIProcess/Cocoa/WebViewImpl.mm:
+ (WebKit::WebViewImpl::doneWithKeyEvent):
+ (WebKit::extractUnderlines):
+ (WebKit::WebViewImpl::collectKeyboardLayoutCommandsForEvent):
+ (WebKit::WebViewImpl::interpretKeyEvent):
+ (WebKit::WebViewImpl::doCommandBySelector):
+ (WebKit::WebViewImpl::insertText):
+ (WebKit::WebViewImpl::selectedRangeWithCompletionHandler):
+ (WebKit::WebViewImpl::markedRangeWithCompletionHandler):
+ (WebKit::WebViewImpl::hasMarkedTextWithCompletionHandler):
+ (WebKit::WebViewImpl::attributedSubstringForProposedRange):
+ (WebKit::WebViewImpl::firstRectForCharacterRange):
+ (WebKit::WebViewImpl::characterIndexForPoint):
+ (WebKit::WebViewImpl::inputContext):
+ (WebKit::WebViewImpl::unmarkText):
+ (WebKit::WebViewImpl::setMarkedText):
+ (WebKit::WebViewImpl::selectedRange):
+ (WebKit::WebViewImpl::hasMarkedText):
+ (WebKit::WebViewImpl::markedRange):
+ (WebKit::WebViewImpl::performKeyEquivalent):
+ (WebKit::WebViewImpl::keyUp):
+ (WebKit::WebViewImpl::keyDown):
+ (WebKit::WebViewImpl::flagsChanged):
+ (WebKit::WebViewImpl::executeSavedKeypressCommands):
+ * UIProcess/mac/PageClientImpl.mm:
+ (WebKit::PageClientImpl::doneWithKeyEvent):
+ Move NSTextInputClient implementation.
+
2015-10-30 Carlos Garcia Campos <[email protected]>
[GTK] Move the socket polling off the WorkQueue
Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm (191790 => 191791)
--- trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm 2015-10-30 17:02:36 UTC (rev 191791)
@@ -110,41 +110,14 @@
#import "WKBrowsingContextGroupPrivate.h"
#import "WKProcessGroupPrivate.h"
-#if USE(ASYNC_NSTEXTINPUTCLIENT)
-@interface NSTextInputContext (WKNSTextInputContextDetails)
-- (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
-- (void)handleEventByInputMethod:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
-- (BOOL)handleEventByKeyboardLayout:(NSEvent *)theEvent;
-@end
-#endif
-
using namespace WebKit;
using namespace WebCore;
-#if !USE(ASYNC_NSTEXTINPUTCLIENT)
-struct WKViewInterpretKeyEventsParameters {
- bool eventInterpretationHadSideEffects;
- bool consumedByIM;
- bool executingSavedKeypressCommands;
- Vector<KeypressCommand>* commands;
-};
-#endif
-
@interface WKViewData : NSObject {
@public
std::unique_ptr<PageClientImpl> _pageClient;
RefPtr<WebPageProxy> _page;
std::unique_ptr<WebViewImpl> _impl;
-
- // We keep here the event when resending it to
- // the application to distinguish the case of a new event from one
- // that has been already sent to WebCore.
- RetainPtr<NSEvent> _keyDownEventBeingResent;
-#if USE(ASYNC_NSTEXTINPUTCLIENT)
- Vector<KeypressCommand>* _collectedKeypressCommands;
-#else
- WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters;
-#endif
}
@end
@@ -758,932 +731,116 @@
return result;
}
-static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
-{
- int length = [[string string] length];
-
- int i = 0;
- while (i < length) {
- NSRange range;
- NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
-
- if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
- Color color = Color::black;
- if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
- color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
- result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
- }
-
- i = range.location + range.length;
- }
-}
-
-#if USE(ASYNC_NSTEXTINPUTCLIENT)
-
-- (void)_collectKeyboardLayoutCommandsForEvent:(NSEvent *)event to:(Vector<KeypressCommand>&)commands
-{
- if ([event type] != NSKeyDown)
- return;
-
- ASSERT(!_data->_collectedKeypressCommands);
- _data->_collectedKeypressCommands = &commands;
-
- if (NSTextInputContext *context = [self inputContext])
- [context handleEventByKeyboardLayout:event];
- else
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-
- _data->_collectedKeypressCommands = nullptr;
-}
-
-- (void)_interpretKeyEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled, const Vector<KeypressCommand>& commands))completionHandler
-{
- // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
- // There is no need to collect commands, as the plug-in cannot execute them.
- if (_data->_impl->pluginComplexTextInputIdentifier()) {
- completionHandler(NO, Vector<KeypressCommand>());
- return;
- }
-
- if (![self inputContext]) {
- Vector<KeypressCommand> commands;
- [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
- completionHandler(NO, commands);
- return;
- }
-
- LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
- [[self inputContext] handleEventByInputMethod:event completionHandler:^(BOOL handled) {
-
- LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
- if (handled) {
- completionHandler(YES, Vector<KeypressCommand>());
- return;
- }
-
- Vector<KeypressCommand> commands;
- [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
- completionHandler(NO, commands);
- }];
-}
-
- (void)doCommandBySelector:(SEL)selector
{
- LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
-
- Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
-
- if (keypressCommands) {
- KeypressCommand command(NSStringFromSelector(selector));
- keypressCommands->append(command);
- LOG(TextInput, "...stored");
- _data->_page->registerKeypressCommandName(command.commandName);
- } else {
- // FIXME: Send the command to Editor synchronously and only send it along the
- // responder chain if it's a selector that does not correspond to an editing command.
- [super doCommandBySelector:selector];
- }
+ _data->_impl->doCommandBySelector(selector);
}
- (void)insertText:(id)string
{
- // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
- // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
- // command ensures that a keypress event is dispatched as appropriate.
- [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
+ _data->_impl->insertText(string);
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
- ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
-
- if (replacementRange.location != NSNotFound)
- LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
- else
- LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
-
- NSString *text;
- Vector<TextAlternativeWithRange> dictationAlternatives;
-
- bool registerUndoGroup = false;
- if (isAttributedString) {
-#if USE(DICTATION_ALTERNATIVES)
- collectDictationTextAlternatives(string, dictationAlternatives);
-#endif
-#if USE(INSERTION_UNDO_GROUPING)
- registerUndoGroup = shouldRegisterInsertionUndoGroup(string);
-#endif
- // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
- text = [string string];
- } else
- text = string;
-
- // insertText can be called for several reasons:
- // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
- // - If it's from an input method, then we should insert the text now.
- // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
- // then we also execute it immediately, as there will be no other chance.
- Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
- if (keypressCommands) {
- ASSERT(replacementRange.location == NSNotFound);
- KeypressCommand command("insertText:", text);
- keypressCommands->append(command);
- LOG(TextInput, "...stored");
- _data->_page->registerKeypressCommandName(command.commandName);
- return;
- }
-
- String eventText = text;
- eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
- if (!dictationAlternatives.isEmpty())
- _data->_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
- else
- _data->_page->insertTextAsync(eventText, replacementRange, registerUndoGroup);
+ _data->_impl->insertText(string, replacementRange);
}
-- (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "selectedRange");
- _data->_page->getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...selectedRange failed.");
- completionHandlerBlock(NSMakeRange(NSNotFound, 0));
- return;
- }
- NSRange result = editingRangeResult;
- if (result.location == NSNotFound)
- LOG(TextInput, " -> selectedRange returned (NSNotFound, %llu)", result.length);
- else
- LOG(TextInput, " -> selectedRange returned (%llu, %llu)", result.location, result.length);
- completionHandlerBlock(result);
- });
-}
-
-- (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "markedRange");
- _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...markedRange failed.");
- completionHandlerBlock(NSMakeRange(NSNotFound, 0));
- return;
- }
- NSRange result = editingRangeResult;
- if (result.location == NSNotFound)
- LOG(TextInput, " -> markedRange returned (NSNotFound, %llu)", result.length);
- else
- LOG(TextInput, " -> markedRange returned (%llu, %llu)", result.location, result.length);
- completionHandlerBlock(result);
- });
-}
-
-- (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "hasMarkedText");
- _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...hasMarkedText failed.");
- completionHandlerBlock(NO);
- return;
- }
- BOOL hasMarkedText = editingRangeResult.location != notFound;
- LOG(TextInput, " -> hasMarkedText returned %u", hasMarkedText);
- completionHandlerBlock(hasMarkedText);
- });
-}
-
-- (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", nsRange.location, nsRange.length);
- _data->_page->attributedSubstringForCharacterRangeAsync(nsRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...attributedSubstringFromRange failed.");
- completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
- return;
- }
- LOG(TextInput, " -> attributedSubstringFromRange returned %@", [string.string.get() string]);
- completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
- });
-}
-
-- (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", theRange.location, theRange.length);
-
- // Just to match NSTextView's behavior. Regression tests cannot detect this;
- // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
- // (type something; try ranges (1, -1) and (2, -1).
- if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
- theRange.length = 0;
-
- if (theRange.location == NSNotFound) {
- LOG(TextInput, " -> NSZeroRect");
- completionHandlerPtr(NSZeroRect, theRange);
- return;
- }
-
- _data->_page->firstRectForCharacterRangeAsync(theRange, [self, completionHandler](const IntRect& rect, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...firstRectForCharacterRange failed.");
- completionHandlerBlock(NSZeroRect, NSMakeRange(NSNotFound, 0));
- return;
- }
-
- NSRect resultRect = [self convertRect:rect toView:nil];
- resultRect = [self.window convertRectToScreen:resultRect];
-
- LOG(TextInput, " -> firstRectForCharacterRange returned (%f, %f, %f, %f)", resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
- completionHandlerBlock(resultRect, actualRange);
- });
-}
-
-- (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
-{
- RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
-
- LOG(TextInput, "characterIndexForPoint:(%f, %f)", thePoint.x, thePoint.y);
-
- NSWindow *window = [self window];
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (window)
- thePoint = [window convertScreenToBase:thePoint];
-#pragma clang diagnostic pop
- thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame
-
- _data->_page->characterIndexForPointAsync(IntPoint(thePoint), [completionHandler](uint64_t result, WebKit::CallbackBase::Error error) {
- void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get();
- if (error != WebKit::CallbackBase::Error::None) {
- LOG(TextInput, " ...characterIndexForPoint failed.");
- completionHandlerBlock(0);
- return;
- }
- if (result == notFound)
- result = NSNotFound;
- LOG(TextInput, " -> characterIndexForPoint returned %lu", result);
- completionHandlerBlock(result);
- });
-}
-
- (NSTextInputContext *)inputContext
{
- if (_data->_impl->pluginComplexTextInputIdentifier()) {
- ASSERT(!_data->_collectedKeypressCommands); // Should not get here from -_interpretKeyEvent:completionHandler:, we only use WKTextInputWindowController after giving the plug-in a chance to handle keydown natively.
- return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
- }
-
- // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
- if (!_data->_page->editorState().isContentEditable)
- return nil;
-
- return [super inputContext];
+ return _data->_impl->inputContext();
}
-- (void)unmarkText
-{
- LOG(TextInput, "unmarkText");
-
- _data->_page->confirmCompositionAsync();
-}
-
-- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
-{
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
- ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
-
- LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u) replacementRange:(%u, %u)", isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);
-
- Vector<CompositionUnderline> underlines;
- NSString *text;
-
- if (isAttributedString) {
- // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
- text = [string string];
- extractUnderlines(string, underlines);
- } else
- text = string;
-
- if (_data->_impl->inSecureInputState()) {
- // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
- // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
- ASSERT(!_data->_page->editorState().hasComposition);
- _data->_impl->notifyInputContextAboutDiscardedComposition();
- // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition.
- if ([text length] == 1 && isASCII([text characterAtIndex:0]))
- _data->_page->insertTextAsync(text, replacementRange);
- else
- NSBeep();
- return;
- }
-
- _data->_page->setCompositionAsync(text, underlines, selectedRange, replacementRange);
-}
-
-// Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed.
-
-- (NSRange)selectedRange NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return NSMakeRange(NSNotFound, 0);
-}
-
-- (BOOL)hasMarkedText NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return NO;
-}
-
-- (NSRange)markedRange NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return NSMakeRange(NSNotFound, 0);
-}
-
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return nil;
-}
-
-- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return 0;
-}
-
-- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
-{
- ASSERT_NOT_REACHED();
- return NSMakeRect(0, 0, 0, 0);
-}
-
- (BOOL)performKeyEquivalent:(NSEvent *)event
{
- if (_data->_impl->ignoresNonWheelEvents())
- return NO;
-
- // There's a chance that responding to this event will run a nested event loop, and
- // fetching a new event might release the old one. Retaining and then autoreleasing
- // the current event prevents that from causing a problem inside WebKit or AppKit code.
- [[event retain] autorelease];
-
- // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
- // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
- // Don't interpret this event again, avoiding re-entrancy and infinite loops.
- if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
- return [super performKeyEquivalent:event];
-
- if (_data->_keyDownEventBeingResent) {
- // WebCore has already seen the event, no need for custom processing.
- // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
- // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
- return [super performKeyEquivalent:event];
- }
-
- ASSERT(event == [NSApp currentEvent]);
-
- _data->_impl->disableComplexTextInputIfNecessary();
-
- // Pass key combos through WebCore if there is a key binding available for
- // this event. This lets webpages have a crack at intercepting key-modified keypresses.
- // FIXME: Why is the firstResponder check needed?
- if (self == [[self window] firstResponder]) {
- [self _interpretKeyEvent:event completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
- }];
- return YES;
- }
-
- return [super performKeyEquivalent:event];
+ return _data->_impl->performKeyEquivalent(event);
}
- (void)keyUp:(NSEvent *)theEvent
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
-
- LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
-
- [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
- ASSERT(!handledByInputMethod || commands.isEmpty());
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
- }];
+ _data->_impl->keyUp(theEvent);
}
- (void)keyDown:(NSEvent *)theEvent
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
-
- LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
-
- if (_data->_impl->tryHandlePluginComplexTextInputKeyDown(theEvent)) {
- LOG(TextInput, "...handled by plug-in");
- return;
- }
-
- // We could be receiving a key down from AppKit if we have re-sent an event
- // that maps to an action that is currently unavailable (for example a copy when
- // there is no range selection).
- // If this is the case we should ignore the key down.
- if (_data->_keyDownEventBeingResent == theEvent) {
- [super keyDown:theEvent];
- return;
- }
-
- [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
- ASSERT(!handledByInputMethod || commands.isEmpty());
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
- }];
+ _data->_impl->keyDown(theEvent);
}
- (void)flagsChanged:(NSEvent *)theEvent
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
-
- LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
-
- unsigned short keyCode = [theEvent keyCode];
-
- // Don't make an event from the num lock and function keys
- if (!keyCode || keyCode == 10 || keyCode == 63)
- return;
-
- [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
- }];
+ _data->_impl->flagsChanged(theEvent);
}
-#else // USE(ASYNC_NSTEXTINPUTCLIENT)
-
-- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands
+- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
{
- ASSERT(!_data->_interpretKeyEventsParameters);
- ASSERT(commands.isEmpty());
-
- if ([event type] == NSFlagsChanged)
- return NO;
-
- WKViewInterpretKeyEventsParameters parameters;
- parameters.eventInterpretationHadSideEffects = false;
- parameters.executingSavedKeypressCommands = false;
- // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
- // We assume the IM will *not* consume hotkey sequences.
- parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask);
- parameters.commands = &commands;
- _data->_interpretKeyEventsParameters = ¶meters;
-
- LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event);
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-
- _data->_interpretKeyEventsParameters = nullptr;
-
- // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
- // in which case we should not bubble the event up the DOM.
- if (parameters.consumedByIM) {
- ASSERT(commands.isEmpty());
- LOG(TextInput, "...event %p was consumed by an input method", event);
- return YES;
- }
-
- LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects);
-
- // 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.
- return parameters.eventInterpretationHadSideEffects;
+ _data->_impl->setMarkedText(string, newSelectedRange, replacementRange);
}
-- (void)_executeSavedKeypressCommands
+- (void)unmarkText
{
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
- if (!parameters || parameters->commands->isEmpty())
- return;
-
- // We could be called again if the execution of one command triggers a call to selectedRange.
- // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result.
- if (parameters->executingSavedKeypressCommands)
- return;
-
- LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size());
-
- parameters->executingSavedKeypressCommands = true;
- parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands);
- parameters->commands->clear();
- parameters->executingSavedKeypressCommands = false;
-
- LOG(TextInput, "...done executing saved keypress commands.");
+ _data->_impl->unmarkText();
}
-- (void)doCommandBySelector:(SEL)selector
-{
- LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
-
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
- if (parameters)
- parameters->consumedByIM = false;
-
- // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text.
- bool isFromInputMethod = _data->_page->editorState().hasComposition;
-
- if (parameters && !isFromInputMethod) {
- KeypressCommand command(NSStringFromSelector(selector));
- parameters->commands->append(command);
- LOG(TextInput, "...stored");
- _data->_page->registerKeypressCommandName(command.commandName);
- } else {
- // FIXME: Send the command to Editor synchronously and only send it along the
- // responder chain if it's a selector that does not correspond to an editing command.
- [super doCommandBySelector:selector];
- }
-}
-
-- (void)insertText:(id)string
-{
- // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
- // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
- // command ensures that a keypress event is dispatched as appropriate.
- [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
-}
-
-- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
-{
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
- ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
-
- if (replacementRange.location != NSNotFound)
- LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
- else
- LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
- if (parameters)
- parameters->consumedByIM = false;
-
- NSString *text;
- bool isFromInputMethod = _data->_page->editorState().hasComposition;
-
- Vector<TextAlternativeWithRange> dictationAlternatives;
-
- if (isAttributedString) {
-#if USE(DICTATION_ALTERNATIVES)
- collectDictationTextAlternatives(string, dictationAlternatives);
-#endif
- // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
- text = [string string];
- } else
- text = string;
-
- // insertText can be called for several reasons:
- // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
- // - If it's from an input method, then we should insert the text now. We assume it's from the input method if we have marked text.
- // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
- // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
- // then we also execute it immediately, as there will be no other chance.
- if (parameters && !isFromInputMethod) {
- // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see <rdar://11940670>).
- ASSERT(replacementRange.location == NSNotFound);
- KeypressCommand command("insertText:", text);
- parameters->commands->append(command);
- _data->_page->registerKeypressCommandName(command.commandName);
- return;
- }
-
- String eventText = text;
- eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
- bool eventHandled;
- if (!dictationAlternatives.isEmpty())
- eventHandled = _data->_page->insertDictatedText(eventText, replacementRange, dictationAlternatives);
- else
- eventHandled = _data->_page->insertText(eventText, replacementRange);
-
- if (parameters)
- parameters->eventInterpretationHadSideEffects |= eventHandled;
-}
-
-- (NSTextInputContext *)inputContext
-{
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
-
- if (_data->_impl->pluginComplexTextInputIdentifier() && !parameters)
- return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
-
- // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
- if (!_data->_page->editorState().isContentEditable)
- return nil;
-
- return [super inputContext];
-}
-
- (NSRange)selectedRange
{
- [self _executeSavedKeypressCommands];
-
- EditingRange selectedRange;
- _data->_page->getSelectedRange(selectedRange);
-
- NSRange result = selectedRange;
- if (result.location == NSNotFound)
- LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length);
- else
- LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
-
- return result;
+ return _data->_impl->selectedRange();
}
- (BOOL)hasMarkedText
{
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
-
- BOOL result;
- if (parameters) {
- result = _data->_page->editorState().hasComposition;
- if (result) {
- // A saved command can confirm a composition, but it cannot start a new one.
- [self _executeSavedKeypressCommands];
- result = _data->_page->editorState().hasComposition;
- }
- } else {
- EditingRange markedRange;
- _data->_page->getMarkedRange(markedRange);
- result = markedRange.location != notFound;
- }
-
- LOG(TextInput, "hasMarkedText -> %u", result);
- return result;
+ return _data->_impl->hasMarkedText();
}
-- (void)unmarkText
-{
- [self _executeSavedKeypressCommands];
-
- LOG(TextInput, "unmarkText");
-
- // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
-
- if (parameters) {
- parameters->eventInterpretationHadSideEffects = true;
- parameters->consumedByIM = false;
- }
-
- _data->_page->confirmComposition();
-}
-
-- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
-{
- [self _executeSavedKeypressCommands];
-
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
- ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
-
- LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length);
-
- // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
- WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
-
- if (parameters) {
- parameters->eventInterpretationHadSideEffects = true;
- parameters->consumedByIM = false;
- }
-
- Vector<CompositionUnderline> underlines;
- NSString *text;
-
- if (isAttributedString) {
- // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
- text = [string string];
- extractUnderlines(string, underlines);
- } else
- text = string;
-
- if (_data->_page->editorState().isInPasswordField) {
- // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
- // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
- ASSERT(!_data->_page->editorState().hasComposition);
- _data->_impl->notifyInputContextAboutDiscardedComposition();
- if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) {
- _data->_page->insertText(text, replacementRange);
- } else
- NSBeep();
- return;
- }
-
- _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange);
-}
-
- (NSRange)markedRange
{
- [self _executeSavedKeypressCommands];
-
- EditingRange markedRange;
- _data->_page->getMarkedRange(markedRange);
-
- NSRange result = markedRange;
- if (result.location == NSNotFound)
- LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length);
- else
- LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
-
- return result;
+ return _data->_impl->markedRange();
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
{
- [self _executeSavedKeypressCommands];
-
- if (!_data->_page->editorState().isContentEditable) {
- LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
- return nil;
- }
-
- if (_data->_page->editorState().isInPasswordField)
- return nil;
-
- AttributedString result;
- _data->_page->getAttributedSubstringFromRange(nsRange, result);
-
- if (actualRange) {
- *actualRange = nsRange;
- actualRange->length = [result.string length];
- }
-
- LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string string]);
- return [[result.string retain] autorelease];
+ return _data->_impl->attributedSubstringForProposedRange(nsRange, actualRange);
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
- [self _executeSavedKeypressCommands];
-
- NSWindow *window = [self window];
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (window)
- thePoint = [window convertScreenToBase:thePoint];
-#pragma clang diagnostic pop
- thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame
-
- uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
- if (result == notFound)
- result = NSNotFound;
- LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
- return result;
+ return _data->_impl->characterIndexForPoint(thePoint);
}
- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
-{
- [self _executeSavedKeypressCommands];
+{
+ return _data->_impl->firstRectForCharacterRange(theRange, actualRange);
+}
- // Just to match NSTextView's behavior. Regression tests cannot detect this;
- // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
- // (type something; try ranges (1, -1) and (2, -1).
- if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
- theRange.length = 0;
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
- if (theRange.location == NSNotFound) {
- if (actualRange)
- *actualRange = theRange;
- LOG(TextInput, "firstRectForCharacterRange:(NSNotFound, %u) -> NSZeroRect", theRange.length);
- return NSZeroRect;
- }
-
- NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange);
- resultRect = [self convertRect:resultRect toView:nil];
- resultRect = [self.window convertRectToScreen:resultRect];
-
- if (actualRange) {
- // FIXME: Update actualRange to match the range of first rect.
- *actualRange = theRange;
- }
-
- LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
- return resultRect;
+- (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
+{
+ _data->_impl->selectedRangeWithCompletionHandler(completionHandlerPtr);
}
-- (BOOL)performKeyEquivalent:(NSEvent *)event
+- (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
{
- if (_data->_impl->ignoresNonWheelEvents())
- return NO;
-
- // There's a chance that responding to this event will run a nested event loop, and
- // fetching a new event might release the old one. Retaining and then autoreleasing
- // the current event prevents that from causing a problem inside WebKit or AppKit code.
- [[event retain] autorelease];
-
- // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
- // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
- // Don't interpret this event again, avoiding re-entrancy and infinite loops.
- if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
- return [super performKeyEquivalent:event];
-
- if (_data->_keyDownEventBeingResent) {
- // WebCore has already seen the event, no need for custom processing.
- // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
- // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
- return [super performKeyEquivalent:event];
- }
-
- ASSERT(event == [NSApp currentEvent]);
-
- _data->_impl->disableComplexTextInputIfNecessary();
-
- // Pass key combos through WebCore if there is a key binding available for
- // this event. This lets webpages have a crack at intercepting key-modified keypresses.
- // FIXME: Why is the firstResponder check needed?
- if (self == [[self window] firstResponder]) {
- Vector<KeypressCommand> commands;
- BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
- return YES;
- }
-
- return [super performKeyEquivalent:event];
+ _data->_impl->markedRangeWithCompletionHandler(completionHandlerPtr);
}
-- (void)keyUp:(NSEvent *)theEvent
+- (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
-
- LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
- // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>).
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
+ _data->_impl->hasMarkedTextWithCompletionHandler(completionHandlerPtr);
}
-- (void)keyDown:(NSEvent *)theEvent
+- (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
-
- LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
-
- // There's a chance that responding to this event will run a nested event loop, and
- // fetching a new event might release the old one. Retaining and then autoreleasing
- // the current event prevents that from causing a problem inside WebKit or AppKit code.
- [[theEvent retain] autorelease];
-
- if (_data->_impl->tryHandlePluginComplexTextInputKeyDown(theEvent)) {
- LOG(TextInput, "...handled by plug-in");
- return;
- }
-
- // We could be receiving a key down from AppKit if we have re-sent an event
- // that maps to an action that is currently unavailable (for example a copy when
- // there is no range selection).
- // If this is the case we should ignore the key down.
- if (_data->_keyDownEventBeingResent == theEvent) {
- [super keyDown:theEvent];
- return;
- }
-
- Vector<KeypressCommand> commands;
- BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
- if (!commands.isEmpty()) {
- // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
- // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
- // should be handled like normal text input after DOM event dispatch.
- handledByInputMethod = NO;
- }
-
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
+ _data->_impl->attributedSubstringForProposedRange(nsRange, completionHandlerPtr);
}
-- (void)flagsChanged:(NSEvent *)theEvent
+- (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
{
- if (_data->_impl->ignoresNonWheelEvents())
- return;
+ _data->_impl->firstRectForCharacterRange(theRange, completionHandlerPtr);
+}
- LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
-
- // There's a chance that responding to this event will run a nested event loop, and
- // fetching a new event might release the old one. Retaining and then autoreleasing
- // the current event prevents that from causing a problem inside WebKit or AppKit code.
- [[theEvent retain] autorelease];
-
- unsigned short keyCode = [theEvent keyCode];
-
- // Don't make an event from the num lock and function keys
- if (!keyCode || keyCode == 10 || keyCode == 63)
- return;
-
- _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
+- (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
+{
+ _data->_impl->characterIndexForPoint(thePoint, completionHandlerPtr);
}
#endif // USE(ASYNC_NSTEXTINPUTCLIENT)
@@ -1731,6 +888,16 @@
[super doCommandBySelector:selector];
}
+- (BOOL)_superPerformKeyEquivalent:(NSEvent *)event
+{
+ return [super performKeyEquivalent:event];
+}
+
+- (void)_superKeyDown:(NSEvent *)event
+{
+ [super keyDown:event];
+}
+
- (NSArray *)validAttributesForMarkedText
{
static NSArray *validAttributes;
@@ -1892,30 +1059,6 @@
_data->_impl->quickLookWithEvent(event);
}
-- (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled
-{
- if ([event type] != NSKeyDown)
- return;
-
- if (_data->_impl->tryPostProcessPluginComplexTextInputKeyDown(event))
- return;
-
- if (eventWasHandled) {
- [NSCursor setHiddenUntilMouseMoves:YES];
- return;
- }
-
- // resending the event may destroy this WKView
- RetainPtr<WKView> protector(self);
-
- ASSERT(!_data->_keyDownEventBeingResent);
- _data->_keyDownEventBeingResent = event;
- [NSApp _setCurrentEvent:event];
- [NSApp sendEvent:event];
-
- _data->_keyDownEventBeingResent = nullptr;
-}
-
- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
{
return _data->_impl->addTrackingRect(NSRectToCGRect(rect), owner, data, assumeInside);
Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h (191790 => 191791)
--- trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h 2015-10-30 17:02:36 UTC (rev 191791)
@@ -50,7 +50,6 @@
@property (nonatomic, setter=_setThumbnailView:) _WKThumbnailView *_thumbnailView;
#endif
-- (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled;
- (void)_addFontPanelObserver;
@end
Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h (191790 => 191791)
--- trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h 2015-10-30 17:02:36 UTC (rev 191791)
@@ -59,6 +59,8 @@
- (void)_superSmartMagnifyWithEvent:(NSEvent *)event;
- (id)_superAccessibilityAttributeValue:(NSString *)attribute;
- (void)_superDoCommandBySelector:(SEL)selector;
+- (BOOL)_superPerformKeyEquivalent:(NSEvent *)event;
+- (void)_superKeyDown:(NSEvent *)event;
// This is a hack; these things live can live on a category (e.g. WKView (Private)) but WKView itself conforms to this protocol.
// They're not actually optional.
@@ -74,6 +76,10 @@
@end
+namespace WebCore {
+struct KeyPressCommand;
+}
+
namespace WebKit {
class DrawingAreaProxy;
@@ -86,6 +92,15 @@
typedef Vector<RetainPtr<ValidationItem>> ValidationVector;
typedef HashMap<String, ValidationVector> ValidationMap;
+#if !USE(ASYNC_NSTEXTINPUTCLIENT)
+struct WKViewInterpretKeyEventsParameters {
+ bool eventInterpretationHadSideEffects;
+ bool consumedByIM;
+ bool executingSavedKeypressCommands;
+ Vector<WebCore::KeypressCommand>* commands;
+};
+#endif
+
class WebViewImpl {
WTF_MAKE_FAST_ALLOCATED;
WTF_MAKE_NONCOPYABLE(WebViewImpl);
@@ -392,6 +407,33 @@
void setTotalHeightOfBanners(CGFloat totalHeightOfBanners) { m_totalHeightOfBanners = totalHeightOfBanners; }
CGFloat totalHeightOfBanners() const { return m_totalHeightOfBanners; }
+ void doneWithKeyEvent(NSEvent *, bool eventWasHandled);
+ void doCommandBySelector(SEL);
+ void insertText(id string);
+ void insertText(id string, NSRange replacementRange);
+ NSTextInputContext *inputContext();
+ void unmarkText();
+ void setMarkedText(id string, NSRange selectedRange, NSRange replacementRange);
+ NSRange selectedRange();
+ bool hasMarkedText();
+ NSRange markedRange();
+ NSAttributedString *attributedSubstringForProposedRange(NSRange, NSRangePointer actualRange);
+ NSUInteger characterIndexForPoint(NSPoint);
+ NSRect firstRectForCharacterRange(NSRange, NSRangePointer actualRange);
+ bool performKeyEquivalent(NSEvent *);
+ void keyUp(NSEvent *);
+ void keyDown(NSEvent *);
+ void flagsChanged(NSEvent *);
+
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+ void selectedRangeWithCompletionHandler(void(^)(NSRange));
+ void hasMarkedTextWithCompletionHandler(void(^)(BOOL hasMarkedText));
+ void markedRangeWithCompletionHandler(void(^)(NSRange));
+ void attributedSubstringForProposedRange(NSRange, void(^)(NSAttributedString *attrString, NSRange actualRange));
+ void firstRectForCharacterRange(NSRange, void(^)(NSRect firstRect, NSRange actualRange));
+ void characterIndexForPoint(NSPoint, void(^)(NSUInteger));
+#endif // USE(ASYNC_NSTEXTINPUTCLIENT)
+
private:
WeakPtr<WebViewImpl> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
@@ -411,6 +453,14 @@
void setUserInterfaceItemState(NSString *commandName, bool enabled, int state);
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+ void collectKeyboardLayoutCommandsForEvent(NSEvent *, Vector<WebCore::KeypressCommand>&);
+ void interpretKeyEvent(NSEvent *, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>&));
+#else
+ void executeSavedKeypressCommands();
+ bool interpretKeyEvent(NSEvent *, Vector<WebCore::KeypressCommand>&);
+#endif
+
NSView <WebViewImplDelegate> *m_view;
WebPageProxy& m_page;
PageClient& m_pageClient;
@@ -516,6 +566,16 @@
CGFloat m_totalHeightOfBanners { 0 };
RetainPtr<NSView> m_inspectorAttachmentView;
+
+ // We keep here the event when resending it to
+ // the application to distinguish the case of a new event from one
+ // that has been already sent to WebCore.
+ RetainPtr<NSEvent> m_keyDownEventBeingResent;
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+ Vector<WebCore::KeypressCommand>* m_collectedKeypressCommands;
+#else
+ WKViewInterpretKeyEventsParameters* m_interpretKeyEventsParameters;
+#endif
};
} // namespace WebKit
Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm (191790 => 191791)
--- trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm 2015-10-30 17:02:36 UTC (rev 191791)
@@ -74,6 +74,8 @@
#import <WebCore/NSWindowSPI.h>
#import <WebCore/PlatformEventFactoryMac.h>
#import <WebCore/SoftLinking.h>
+#import <WebCore/TextAlternativeWithRange.h>
+#import <WebCore/TextUndoInsertionMarkupMac.h>
#import <WebCore/ViewState.h>
#import <WebCore/WebActionDisablingCALayerDelegate.h>
#import <WebCore/WebCoreCALayerExtras.h>
@@ -85,6 +87,15 @@
SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
+// FIXME: Move to an SPI header.
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+@interface NSTextInputContext (WKNSTextInputContextDetails)
+- (void)handleEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
+- (void)handleEventByInputMethod:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
+- (BOOL)handleEventByKeyboardLayout:(NSEvent *)event;
+@end
+#endif
+
@interface WKWindowVisibilityObserver : NSObject {
NSView *_view;
WebKit::WebViewImpl *_impl;
@@ -532,7 +543,7 @@
NSEvent *keyboardEvent = nil;
if ([event type] == NSKeyDown || [event type] == NSKeyUp)
keyboardEvent = event;
- m_page.setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, Vector<WebCore::KeypressCommand>()), [](WebKit::CallbackBase::Error) { });
+ m_page.setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, { }), [](WebKit::CallbackBase::Error) { });
}
return true;
}
@@ -3068,6 +3079,966 @@
#endif
}
+void WebViewImpl::doneWithKeyEvent(NSEvent *event, bool eventWasHandled)
+{
+ if ([event type] != NSKeyDown)
+ return;
+
+ if (tryPostProcessPluginComplexTextInputKeyDown(event))
+ return;
+
+ if (eventWasHandled) {
+ [NSCursor setHiddenUntilMouseMoves:YES];
+ return;
+ }
+
+ // resending the event may destroy this WKView
+ RetainPtr<NSView> protector(m_view);
+
+ ASSERT(!m_keyDownEventBeingResent);
+ m_keyDownEventBeingResent = event;
+ [NSApp _setCurrentEvent:event];
+ [NSApp sendEvent:event];
+
+ m_keyDownEventBeingResent = nullptr;
+}
+
+static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string)
+{
+ Vector<WebCore::CompositionUnderline> result;
+ int length = string.string.length;
+
+ for (int i = 0; i < length;) {
+ NSRange range;
+ NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
+
+ if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
+ WebCore::Color color = WebCore::Color::black;
+ if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
+ color = WebCore::colorFromNSColor(colorAttr);
+ result.append(WebCore::CompositionUnderline(range.location, NSMaxRange(range), color, style.intValue > 1));
+ }
+
+ i = range.location + range.length;
+ }
+
+ return result;
+}
+
+static bool eventKeyCodeIsZeroOrNumLockOrFn(NSEvent *event)
+{
+ unsigned short keyCode = [event keyCode];
+ return !keyCode || keyCode == 10 || keyCode == 63;
+}
+
+#if USE(ASYNC_NSTEXTINPUTCLIENT)
+
+Vector<WebCore::KeypressCommand> WebViewImpl::collectKeyboardLayoutCommandsForEvent(NSEvent *event)
+{
+ Vector<WebCore::KeypressCommand>& commands;
+
+ if ([event type] != NSKeyDown)
+ return;
+
+ ASSERT(!m_collectedKeypressCommands);
+ m_collectedKeypressCommands = &commands;
+
+ if (NSTextInputContext *context = inputContext())
+ [context handleEventByKeyboardLayout:event];
+ else
+ [m_view interpretKeyEvents:[NSArray arrayWithObject:event]];
+
+ m_collectedKeypressCommands = nullptr;
+
+ return commands;
+}
+
+void WebViewImpl::interpretKeyEvent(NSEvent *event, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>& commands))
+{
+ // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
+ // There is no need to collect commands, as the plug-in cannot execute them.
+ if (pluginComplexTextInputIdentifier()) {
+ completionHandler(NO, { });
+ return;
+ }
+
+ if (!inputContext()) {
+ auto commands = collectKeyboardLayoutCommandsForEvent(event);
+ completionHandler(NO, commands);
+ return;
+ }
+
+ LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
+ [inputContext() handleEventByInputMethod:event completionHandler:^(BOOL handled) {
+
+ LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
+ if (handled) {
+ completionHandler(YES, { });
+ return;
+ }
+
+ auto commands = collectKeyboardLayoutCommandsForEvent(event);
+ completionHandler(NO, commands);
+ }];
+}
+
+void WebViewImpl::doCommandBySelector(SEL selector)
+{
+ LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
+
+ if (auto* keypressCommands = m_collectedKeypressCommands) {
+ WebCore::KeypressCommand command(NSStringFromSelector(selector));
+ keypressCommands->append(command);
+ LOG(TextInput, "...stored");
+ m_page.registerKeypressCommandName(command.commandName);
+ } else {
+ // FIXME: Send the command to Editor synchronously and only send it along the
+ // responder chain if it's a selector that does not correspond to an editing command.
+ [m_view _superDoCommandBySelector:selector];
+ }
+}
+
+void WebViewImpl::insertText(id string)
+{
+ // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
+ // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
+ // command ensures that a keypress event is dispatched as appropriate.
+ insertText(string, NSMakeRange(NSNotFound, 0));
+}
+
+void WebViewImpl::insertText(id string, NSRange replacementRange)
+{
+ BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+ if (replacementRange.location != NSNotFound)
+ LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
+ else
+ LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
+
+ NSString *text;
+ Vector<WebCore::TextAlternativeWithRange> dictationAlternatives;
+
+ bool registerUndoGroup = false;
+ if (isAttributedString) {
+#if USE(DICTATION_ALTERNATIVES)
+ WebCore::collectDictationTextAlternatives(string, dictationAlternatives);
+#endif
+#if USE(INSERTION_UNDO_GROUPING)
+ registerUndoGroup = WebCore::shouldRegisterInsertionUndoGroup(string);
+#endif
+ // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
+ text = [string string];
+ } else
+ text = string;
+
+ // insertText can be called for several reasons:
+ // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
+ // - If it's from an input method, then we should insert the text now.
+ // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
+ // then we also execute it immediately, as there will be no other chance.
+ Vector<WebCore::KeypressCommand>* keypressCommands = m_collectedKeypressCommands;
+ if (keypressCommands) {
+ ASSERT(replacementRange.location == NSNotFound);
+ WebCore::KeypressCommand command("insertText:", text);
+ keypressCommands->append(command);
+ LOG(TextInput, "...stored");
+ m_page.registerKeypressCommandName(command.commandName);
+ return;
+ }
+
+ String eventText = text;
+ eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
+ if (!dictationAlternatives.isEmpty())
+ m_page.insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
+ else
+ m_page.insertTextAsync(eventText, replacementRange, registerUndoGroup);
+}
+
+void WebViewImpl::selectedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange selectedRange))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "selectedRange");
+ m_page.getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
+ void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...selectedRange failed.");
+ completionHandlerBlock(NSMakeRange(NSNotFound, 0));
+ return;
+ }
+ NSRange result = editingRangeResult;
+ if (result.location == NSNotFound)
+ LOG(TextInput, " -> selectedRange returned (NSNotFound, %llu)", result.length);
+ else
+ LOG(TextInput, " -> selectedRange returned (%llu, %llu)", result.location, result.length);
+ completionHandlerBlock(result);
+ });
+}
+
+void WebViewImpl::markedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange markedRange))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "markedRange");
+ m_page.getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
+ void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...markedRange failed.");
+ completionHandlerBlock(NSMakeRange(NSNotFound, 0));
+ return;
+ }
+ NSRange result = editingRangeResult;
+ if (result.location == NSNotFound)
+ LOG(TextInput, " -> markedRange returned (NSNotFound, %llu)", result.length);
+ else
+ LOG(TextInput, " -> markedRange returned (%llu, %llu)", result.location, result.length);
+ completionHandlerBlock(result);
+ });
+}
+
+void WebViewImpl::hasMarkedTextWithCompletionHandler(void(^completionHandlerPtr)(BOOL hasMarkedText))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "hasMarkedText");
+ m_page.getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
+ void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...hasMarkedText failed.");
+ completionHandlerBlock(NO);
+ return;
+ }
+ BOOL hasMarkedText = editingRangeResult.location != notFound;
+ LOG(TextInput, " -> hasMarkedText returned %u", hasMarkedText);
+ completionHandlerBlock(hasMarkedText);
+ });
+}
+
+void WebViewImpl::attributedSubstringForProposedRange(NSRange proposedRange, void(^completionHandlerPtr)(NSAttributedString *attrString, NSRange actualRange))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", proposedRange.location, proposedRange.length);
+ m_page.attributedSubstringForCharacterRangeAsync(proposedRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
+ void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...attributedSubstringFromRange failed.");
+ completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
+ return;
+ }
+ LOG(TextInput, " -> attributedSubstringFromRange returned %@", [string.string.get() string]);
+ completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
+ });
+}
+
+void WebViewImpl::firstRectForCharacterRange(NSRange range, void(^completionHandlerPtr)(NSRect firstRect, NSRange actualRange))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", range.location, range.length);
+
+ // Just to match NSTextView's behavior. Regression tests cannot detect this;
+ // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
+ // (type something; try ranges (1, -1) and (2, -1).
+ if ((range.location + range.length < range.location) && (range.location + range.length != 0))
+ range.length = 0;
+
+ if (range.location == NSNotFound) {
+ LOG(TextInput, " -> NSZeroRect");
+ completionHandlerPtr(NSZeroRect, range);
+ return;
+ }
+
+ auto weakThis = createWeakPtr();
+ m_page.firstRectForCharacterRangeAsync(range, [weakThis, completionHandler](const WebCore::IntRect& rect, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
+ if (!weakThis)
+ return;
+
+ void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...firstRectForCharacterRange failed.");
+ completionHandlerBlock(NSZeroRect, NSMakeRange(NSNotFound, 0));
+ return;
+ }
+
+ NSRect resultRect = [weakThis->m_view convertRect:rect toView:nil];
+ resultRect = [weakThis->m_view.window convertRectToScreen:resultRect];
+
+ LOG(TextInput, " -> firstRectForCharacterRange returned (%f, %f, %f, %f)", resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
+ completionHandlerBlock(resultRect, actualRange);
+ });
+}
+
+void WebViewImpl::characterIndexForPoint(NSPoint point, void(^completionHandlerPtr)(NSUInteger))
+{
+ auto completionHandler = adoptNS([completionHandlerPtr copy]);
+
+ LOG(TextInput, "characterIndexForPoint:(%f, %f)", point.x, point.y);
+
+ NSWindow *window = m_view.window;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ if (window)
+ point = [window convertScreenToBase:point];
+#pragma clang diagnostic pop
+ point = [m_view convertPoint:point fromView:nil]; // the point is relative to the main frame
+
+ m_page.characterIndexForPointAsync(WebCore::IntPoint(point), [completionHandler](uint64_t result, WebKit::CallbackBase::Error error) {
+ void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get();
+ if (error != WebKit::CallbackBase::Error::None) {
+ LOG(TextInput, " ...characterIndexForPoint failed.");
+ completionHandlerBlock(0);
+ return;
+ }
+ if (result == notFound)
+ result = NSNotFound;
+ LOG(TextInput, " -> characterIndexForPoint returned %lu", result);
+ completionHandlerBlock(result);
+ });
+}
+
+NSTextInputContext *WebViewImpl::inputContext()
+{
+ if (pluginComplexTextInputIdentifier()) {
+ ASSERT(!m_collectedKeypressCommands); // Should not get here from -_interpretKeyEvent:completionHandler:, we only use WKTextInputWindowController after giving the plug-in a chance to handle keydown natively.
+ return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
+ }
+
+ // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
+ if (!m_page.editorState().isContentEditable)
+ return nil;
+
+ return [m_view _superInputContext];
+}
+
+void WebViewImpl::unmarkText()
+{
+ LOG(TextInput, "unmarkText");
+
+ m_page.confirmCompositionAsync();
+}
+
+void WebViewImpl::setMarkedText(id string, NSRange selectedRange, NSRange replacementRange)
+{
+ BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+ LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u) replacementRange:(%u, %u)", isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);
+
+ Vector<WebCore::CompositionUnderline> underlines;
+ NSString *text;
+
+ if (isAttributedString) {
+ // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
+ text = [string string];
+ underlines = extractUnderlines(string);
+ } else
+ text = string;
+
+ if (inSecureInputState()) {
+ // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
+ // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
+ ASSERT(!m_page.editorState().hasComposition);
+ notifyInputContextAboutDiscardedComposition();
+ // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition.
+ if ([text length] == 1 && isASCII([text characterAtIndex:0]))
+ m_page.insertTextAsync(text, replacementRange);
+ else
+ NSBeep();
+ return;
+ }
+
+ m_page.setCompositionAsync(text, underlines, selectedRange, replacementRange);
+}
+
+// Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed.
+
+NSRange WebViewImpl::selectedRange()
+{
+ ASSERT_NOT_REACHED();
+ return NSMakeRange(NSNotFound, 0);
+}
+
+bool WebViewImpl::hasMarkedText()
+{
+ ASSERT_NOT_REACHED();
+ return NO;
+}
+
+NSRange WebViewImpl::markedRange()
+{
+ ASSERT_NOT_REACHED();
+ return NSMakeRange(NSNotFound, 0);
+}
+
+NSAttributedString *WebViewImpl::attributedSubstringForProposedRange(NSRange nsRange, NSRangePointer actualRange)
+{
+ ASSERT_NOT_REACHED();
+ return nil;
+}
+
+NSUInteger WebViewImpl::characterIndexForPoint(NSPoint point)
+{
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+NSRect WebViewImpl::firstRectForCharacterRange(NSRange range, NSRangePointer actualRange)
+{
+ ASSERT_NOT_REACHED();
+ return NSZeroRect;
+}
+
+bool WebViewImpl::performKeyEquivalent(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return NO;
+
+ // There's a chance that responding to this event will run a nested event loop, and
+ // fetching a new event might release the old one. Retaining and then autoreleasing
+ // the current event prevents that from causing a problem inside WebKit or AppKit code.
+ [[event retain] autorelease];
+
+ // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
+ // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
+ // Don't interpret this event again, avoiding re-entrancy and infinite loops.
+ if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
+ return [m_view _superPerformKeyEquivalent:event];
+
+ if (m_keyDownEventBeingResent) {
+ // WebCore has already seen the event, no need for custom processing.
+ // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
+ // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
+ return [m_view _superPerformKeyEquivalent:event];
+ }
+
+ ASSERT(event == [NSApp currentEvent]);
+
+ disableComplexTextInputIfNecessary();
+
+ // Pass key combos through WebCore if there is a key binding available for
+ // this event. This lets webpages have a crack at intercepting key-modified keypresses.
+ // FIXME: Why is the firstResponder check needed?
+ if (m_view == m_view.window.firstResponder) {
+ interpretKeyEvent(event, ^(BOOL handledByInputMethod, const Vector<WebCore::KeypressCommand>& commands) {
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+ });
+ return YES;
+ }
+
+ return [m_view _superPerformKeyEquivalent:event];
+}
+
+void WebViewImpl::keyUp(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "keyUp:%p %@", event, event);
+
+ interpretKeyEvent(event, ^(BOOL handledByInputMethod, const Vector<WebCore::KeypressCommand>& commands) {
+ ASSERT(!handledByInputMethod || commands.isEmpty());
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+ });
+}
+
+void WebViewImpl::keyDown(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "keyDown:%p %@%s", event, event, (event == m_keyDownEventBeingResent) ? " (re-sent)" : "");
+
+ if (tryHandlePluginComplexTextInputKeyDown(event)) {
+ LOG(TextInput, "...handled by plug-in");
+ return;
+ }
+
+ // We could be receiving a key down from AppKit if we have re-sent an event
+ // that maps to an action that is currently unavailable (for example a copy when
+ // there is no range selection).
+ // If this is the case we should ignore the key down.
+ if (m_keyDownEventBeingResent == event) {
+ [m_view _superKeyDown:event];
+ return;
+ }
+
+ interpretKeyEvent(event, ^(BOOL handledByInputMethod, const Vector<WebCore::KeypressCommand>& commands) {
+ ASSERT(!handledByInputMethod || commands.isEmpty());
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+ });
+}
+
+void WebViewImpl::flagsChanged(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "flagsChanged:%p %@", event, event);
+
+ // Don't make an event from the num lock and function keys
+ if (eventKeyCodeIsZeroOrNumLockOrFn(event))
+ return;
+
+ interpretKeyEvent(event, ^(BOOL handledByInputMethod, const Vector<WebCore::KeypressCommand>& commands) {
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+ });
+}
+
+#else // USE(ASYNC_NSTEXTINPUTCLIENT)
+
+bool WebViewImpl::interpretKeyEvent(NSEvent *event, Vector<WebCore::KeypressCommand>& commands)
+{
+ ASSERT(!m_interpretKeyEventsParameters);
+ ASSERT(commands.isEmpty());
+
+ if ([event type] == NSFlagsChanged)
+ return NO;
+
+ WKViewInterpretKeyEventsParameters parameters;
+ parameters.eventInterpretationHadSideEffects = false;
+ parameters.executingSavedKeypressCommands = false;
+ // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
+ // We assume the IM will *not* consume hotkey sequences.
+ parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask);
+ parameters.commands = &commands;
+ m_interpretKeyEventsParameters = ¶meters;
+
+ LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event);
+ [m_view interpretKeyEvents:[NSArray arrayWithObject:event]];
+
+ m_interpretKeyEventsParameters = nullptr;
+
+ // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
+ // in which case we should not bubble the event up the DOM.
+ if (parameters.consumedByIM) {
+ ASSERT(commands.isEmpty());
+ LOG(TextInput, "...event %p was consumed by an input method", event);
+ return YES;
+ }
+
+ LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects);
+
+ // 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.
+ return parameters.eventInterpretationHadSideEffects;
+}
+
+void WebViewImpl::executeSavedKeypressCommands()
+{
+ auto* parameters = m_interpretKeyEventsParameters;
+ if (!parameters || parameters->commands->isEmpty())
+ return;
+
+ // We could be called again if the execution of one command triggers a call to selectedRange.
+ // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result.
+ if (parameters->executingSavedKeypressCommands)
+ return;
+
+ LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size());
+
+ parameters->executingSavedKeypressCommands = true;
+ parameters->eventInterpretationHadSideEffects |= m_page.executeKeypressCommands(*parameters->commands);
+ parameters->commands->clear();
+ parameters->executingSavedKeypressCommands = false;
+
+ LOG(TextInput, "...done executing saved keypress commands.");
+}
+
+void WebViewImpl::doCommandBySelector(SEL selector)
+{
+ LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
+
+ auto* parameters = m_interpretKeyEventsParameters;
+ if (parameters)
+ parameters->consumedByIM = false;
+
+ // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text.
+ bool isFromInputMethod = m_page.editorState().hasComposition;
+
+ if (parameters && !isFromInputMethod) {
+ WebCore::KeypressCommand command(NSStringFromSelector(selector));
+ parameters->commands->append(command);
+ LOG(TextInput, "...stored");
+ m_page.registerKeypressCommandName(command.commandName);
+ } else {
+ // FIXME: Send the command to Editor synchronously and only send it along the
+ // responder chain if it's a selector that does not correspond to an editing command.
+ [m_view _superDoCommandBySelector:selector];
+ }
+}
+
+void WebViewImpl::insertText(id string)
+{
+ // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
+ // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
+ // command ensures that a keypress event is dispatched as appropriate.
+ insertText(string, NSMakeRange(NSNotFound, 0));
+}
+
+void WebViewImpl::insertText(id string, NSRange replacementRange)
+{
+ bool isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+ if (replacementRange.location != NSNotFound)
+ LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
+ else
+ LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
+ auto* parameters = m_interpretKeyEventsParameters;
+ if (parameters)
+ parameters->consumedByIM = false;
+
+ NSString *text;
+ bool isFromInputMethod = m_page.editorState().hasComposition;
+
+ Vector<WebCore::TextAlternativeWithRange> dictationAlternatives;
+
+ if (isAttributedString) {
+#if USE(DICTATION_ALTERNATIVES)
+ WebCore::collectDictationTextAlternatives(string, dictationAlternatives);
+#endif
+ // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
+ text = [string string];
+ } else
+ text = string;
+
+ // insertText can be called for several reasons:
+ // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
+ // - If it's from an input method, then we should insert the text now. We assume it's from the input method if we have marked text.
+ // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
+ // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
+ // then we also execute it immediately, as there will be no other chance.
+ if (parameters && !isFromInputMethod) {
+ // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see <rdar://11940670>).
+ ASSERT(replacementRange.location == NSNotFound);
+ WebCore::KeypressCommand command("insertText:", text);
+ parameters->commands->append(command);
+ m_page.registerKeypressCommandName(command.commandName);
+ return;
+ }
+
+ String eventText = text;
+ eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
+ bool eventHandled;
+ if (!dictationAlternatives.isEmpty())
+ eventHandled = m_page.insertDictatedText(eventText, replacementRange, dictationAlternatives);
+ else
+ eventHandled = m_page.insertText(eventText, replacementRange);
+
+ if (parameters)
+ parameters->eventInterpretationHadSideEffects |= eventHandled;
+}
+
+NSTextInputContext *WebViewImpl::inputContext()
+{
+ auto* parameters = m_interpretKeyEventsParameters;
+
+ if (pluginComplexTextInputIdentifier() && !parameters)
+ return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
+
+ // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
+ if (!m_page.editorState().isContentEditable)
+ return nil;
+
+ return [m_view _superInputContext];
+}
+
+NSRange WebViewImpl::selectedRange()
+{
+ executeSavedKeypressCommands();
+
+ EditingRange selectedRange;
+ m_page.getSelectedRange(selectedRange);
+
+ NSRange result = selectedRange;
+ if (result.location == NSNotFound)
+ LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length);
+ else
+ LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
+
+ return result;
+}
+
+bool WebViewImpl::hasMarkedText()
+{
+ auto* parameters = m_interpretKeyEventsParameters;
+
+ BOOL result;
+ if (parameters) {
+ result = m_page.editorState().hasComposition;
+ if (result) {
+ // A saved command can confirm a composition, but it cannot start a new one.
+ executeSavedKeypressCommands();
+ result = m_page.editorState().hasComposition;
+ }
+ } else {
+ EditingRange markedRange;
+ m_page.getMarkedRange(markedRange);
+ result = markedRange.location != notFound;
+ }
+
+ LOG(TextInput, "hasMarkedText -> %u", result);
+ return result;
+}
+
+void WebViewImpl::unmarkText()
+{
+ executeSavedKeypressCommands();
+
+ LOG(TextInput, "unmarkText");
+
+ auto* parameters = m_interpretKeyEventsParameters;
+
+ if (parameters) {
+ parameters->eventInterpretationHadSideEffects = true;
+ parameters->consumedByIM = false;
+ }
+
+ m_page.confirmComposition();
+}
+
+void WebViewImpl::setMarkedText(id string, NSRange newSelectedRange, NSRange replacementRange)
+{
+ executeSavedKeypressCommands();
+
+ BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
+
+ LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length);
+
+ auto* parameters = m_interpretKeyEventsParameters;
+
+ if (parameters) {
+ parameters->eventInterpretationHadSideEffects = true;
+ parameters->consumedByIM = false;
+ }
+
+ Vector<WebCore::CompositionUnderline> underlines;
+ NSString *text;
+
+ if (isAttributedString) {
+ // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
+ text = [string string];
+ underlines = extractUnderlines(string);
+ } else
+ text = string;
+
+ if (m_page.editorState().isInPasswordField) {
+ // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
+ // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
+ ASSERT(!m_page.editorState().hasComposition);
+ notifyInputContextAboutDiscardedComposition();
+ if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) {
+ m_page.insertText(text, replacementRange);
+ } else
+ NSBeep();
+ return;
+ }
+
+ m_page.setComposition(text, underlines, newSelectedRange, replacementRange);
+}
+
+NSRange WebViewImpl::markedRange()
+{
+ executeSavedKeypressCommands();
+
+ EditingRange markedRange;
+ m_page.getMarkedRange(markedRange);
+
+ NSRange result = markedRange;
+ if (result.location == NSNotFound)
+ LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length);
+ else
+ LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
+
+ return result;
+}
+
+NSAttributedString *WebViewImpl::attributedSubstringForProposedRange(NSRange proposedRange, NSRangePointer actualRange)
+{
+ executeSavedKeypressCommands();
+
+ if (!m_page.editorState().isContentEditable) {
+ LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", proposedRange.location, proposedRange.length);
+ return nil;
+ }
+
+ if (m_page.editorState().isInPasswordField)
+ return nil;
+
+ AttributedString result;
+ m_page.getAttributedSubstringFromRange(proposedRange, result);
+
+ if (actualRange) {
+ *actualRange = proposedRange;
+ actualRange->length = [result.string length];
+ }
+
+ LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", proposedRange.location, proposedRange.length, [result.string string]);
+ return [[result.string retain] autorelease];
+}
+
+NSUInteger WebViewImpl::characterIndexForPoint(NSPoint point)
+{
+ executeSavedKeypressCommands();
+
+ NSWindow *window = m_view.window;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ if (window)
+ point = [window convertScreenToBase:point];
+#pragma clang diagnostic pop
+ point = [m_view convertPoint:point fromView:nil]; // the point is relative to the main frame
+
+ uint64_t result = m_page.characterIndexForPoint(WebCore::IntPoint(point));
+ if (result == notFound)
+ result = NSNotFound;
+ LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", point.x, point.y, result);
+ return result;
+}
+
+NSRect WebViewImpl::firstRectForCharacterRange(NSRange range, NSRangePointer actualRange)
+{
+ executeSavedKeypressCommands();
+
+ // Just to match NSTextView's behavior. Regression tests cannot detect this;
+ // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
+ // (type something; try ranges (1, -1) and (2, -1).
+ if ((range.location + range.length < range.location) && (range.location + range.length != 0))
+ range.length = 0;
+
+ if (range.location == NSNotFound) {
+ if (actualRange)
+ *actualRange = range;
+ LOG(TextInput, "firstRectForCharacterRange:(NSNotFound, %u) -> NSZeroRect", range.length);
+ return NSZeroRect;
+ }
+
+ NSRect resultRect = m_page.firstRectForCharacterRange(range);
+ resultRect = [m_view convertRect:resultRect toView:nil];
+ resultRect = [m_view.window convertRectToScreen:resultRect];
+
+ if (actualRange) {
+ // FIXME: Update actualRange to match the range of first rect.
+ *actualRange = range;
+ }
+
+ LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", range.location, range.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
+ return resultRect;
+}
+
+bool WebViewImpl::performKeyEquivalent(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return false;
+
+ // There's a chance that responding to this event will run a nested event loop, and
+ // fetching a new event might release the old one. Retaining and then autoreleasing
+ // the current event prevents that from causing a problem inside WebKit or AppKit code.
+ [[event retain] autorelease];
+
+ // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
+ // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
+ // Don't interpret this event again, avoiding re-entrancy and infinite loops.
+ if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
+ return [m_view _superPerformKeyEquivalent:event];
+
+ if (m_keyDownEventBeingResent) {
+ // WebCore has already seen the event, no need for custom processing.
+ // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
+ // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
+ return [m_view _superPerformKeyEquivalent:event];
+ }
+
+ ASSERT(event == [NSApp currentEvent]);
+
+ disableComplexTextInputIfNecessary();
+
+ // Pass key combos through WebCore if there is a key binding available for
+ // this event. This lets webpages have a crack at intercepting key-modified keypresses.
+ // FIXME: Why is the firstResponder check needed?
+ if (m_view == m_view.window.firstResponder) {
+ Vector<WebCore::KeypressCommand> commands;
+ bool handledByInputMethod = interpretKeyEvent(event, commands);
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+ return true;
+ }
+
+ return [m_view _superPerformKeyEquivalent:event];
+}
+
+void WebViewImpl::keyUp(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "keyUp:%p %@", event, event);
+ // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>).
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, false, { }));
+}
+
+void WebViewImpl::keyDown(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "keyDown:%p %@%s", event, event, (event == m_keyDownEventBeingResent) ? " (re-sent)" : "");
+
+ // There's a chance that responding to this event will run a nested event loop, and
+ // fetching a new event might release the old one. Retaining and then autoreleasing
+ // the current event prevents that from causing a problem inside WebKit or AppKit code.
+ [[event retain] autorelease];
+
+ if (tryHandlePluginComplexTextInputKeyDown(event)) {
+ LOG(TextInput, "...handled by plug-in");
+ return;
+ }
+
+ // We could be receiving a key down from AppKit if we have re-sent an event
+ // that maps to an action that is currently unavailable (for example a copy when
+ // there is no range selection).
+ // If this is the case we should ignore the key down.
+ if (m_keyDownEventBeingResent == event) {
+ [m_view _superKeyDown:event];
+ return;
+ }
+
+ Vector<WebCore::KeypressCommand> commands;
+ bool handledByInputMethod = interpretKeyEvent(event, commands);
+ if (!commands.isEmpty()) {
+ // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
+ // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
+ // should be handled like normal text input after DOM event dispatch.
+ handledByInputMethod = false;
+ }
+
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
+}
+
+void WebViewImpl::flagsChanged(NSEvent *event)
+{
+ if (ignoresNonWheelEvents())
+ return;
+
+ LOG(TextInput, "flagsChanged:%p %@", event, event);
+
+ // There's a chance that responding to this event will run a nested event loop, and
+ // fetching a new event might release the old one. Retaining and then autoreleasing
+ // the current event prevents that from causing a problem inside WebKit or AppKit code.
+ [[event retain] autorelease];
+
+ // Don't make an event from the num lock and function keys
+ if (eventKeyCodeIsZeroOrNumLockOrFn(event))
+ return;
+
+ m_page.handleKeyboardEvent(NativeWebKeyboardEvent(event, false, { }));
+}
+
+#endif // USE(ASYNC_NSTEXTINPUTCLIENT)
+
} // namespace WebKit
#endif // PLATFORM(MAC)
Modified: trunk/Source/WebKit2/UIProcess/mac/PageClientImpl.mm (191790 => 191791)
--- trunk/Source/WebKit2/UIProcess/mac/PageClientImpl.mm 2015-10-30 16:51:19 UTC (rev 191790)
+++ trunk/Source/WebKit2/UIProcess/mac/PageClientImpl.mm 2015-10-30 17:02:36 UTC (rev 191791)
@@ -435,7 +435,7 @@
void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool eventWasHandled)
{
- [m_wkView _doneWithKeyEvent:event.nativeEvent() eventWasHandled:eventWasHandled];
+ m_impl->doneWithKeyEvent(event.nativeEvent(), eventWasHandled);
}
RefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy& page)