OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <QuartzCore/QuartzCore.h> | 5 #include <QuartzCore/QuartzCore.h> |
6 | 6 |
7 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" | 7 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" |
8 | 8 |
9 #include "app/surface/io_surface_support_mac.h" | 9 #include "app/surface/io_surface_support_mac.h" |
10 #import "base/chrome_application_mac.h" | 10 #import "base/chrome_application_mac.h" |
(...skipping 788 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
799 if (text_input_type_ == WebKit::WebTextInputTypePassword) | 799 if (text_input_type_ == WebKit::WebTextInputTypePassword) |
800 EnablePasswordInput(); | 800 EnablePasswordInput(); |
801 else | 801 else |
802 DisablePasswordInput(); | 802 DisablePasswordInput(); |
803 } else { | 803 } else { |
804 if (text_input_type_ == WebKit::WebTextInputTypePassword) | 804 if (text_input_type_ == WebKit::WebTextInputTypePassword) |
805 DisablePasswordInput(); | 805 DisablePasswordInput(); |
806 } | 806 } |
807 } | 807 } |
808 | 808 |
809 // EditCommandMatcher --------------------------------------------------------- | |
810 | |
811 // This class is used to capture the shortcuts that a given key event maps to. | |
812 // We instantiate a vanilla NSResponder, call interpretKeyEvents on it, and | |
813 // record all of the selectors passed into doCommandBySelector while | |
814 // interpreting the key event. The selectors are converted into edit commands | |
815 // which can be passed to the render process. | |
816 // | |
817 // Caveats: | |
818 // - Shortcuts involving a sequence of key combinations (chords) don't work, | |
819 // because we instantiate a new responder for each event. | |
820 // - We ignore key combinations that don't include a modifier (ctrl, cmd, alt) | |
821 // because this was causing strange behavior (e.g. tab always inserted a tab | |
822 // rather than moving to the next field on the page). | |
823 | |
824 @interface EditCommandMatcher : NSResponder { | |
825 EditCommands* edit_commands_; | |
826 } | |
827 @end | |
828 | |
829 @implementation EditCommandMatcher | |
830 | |
831 - (id)initWithEditCommands:(EditCommands*)edit_commands { | |
832 if ((self = [super init]) != nil) { | |
833 edit_commands_ = edit_commands; | |
834 } | |
835 return self; | |
836 } | |
837 | |
838 - (void)doCommandBySelector:(SEL)selector { | |
839 NSString* editCommand = | |
840 RWHVMEditCommandHelper::CommandNameForSelector(selector); | |
841 edit_commands_->push_back( | |
842 EditCommand(base::SysNSStringToUTF8(editCommand), "")); | |
843 } | |
844 | |
845 - (void)insertText:(id)string { | |
846 // If we don't ignore this, then sometimes we get a bell. | |
847 } | |
848 | |
849 + (void)matchEditCommands:(EditCommands*)edit_commands | |
850 forEvent:(NSEvent*)theEvent { | |
851 if ([theEvent type] != NSKeyDown) | |
852 return; | |
853 // Don't interpret plain key presses. This screws up things like <Tab>. | |
854 NSUInteger flags = [theEvent modifierFlags]; | |
855 flags &= (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask); | |
856 if (!flags) | |
857 return; | |
858 scoped_nsobject<EditCommandMatcher> matcher( | |
859 [[EditCommandMatcher alloc] initWithEditCommands:edit_commands]); | |
860 [matcher.get() interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; | |
861 } | |
862 | |
863 @end | |
864 | |
865 // RenderWidgetHostViewCocoa --------------------------------------------------- | 809 // RenderWidgetHostViewCocoa --------------------------------------------------- |
866 | 810 |
867 @implementation RenderWidgetHostViewCocoa | 811 @implementation RenderWidgetHostViewCocoa |
868 | 812 |
869 @synthesize caretRect = caretRect_; | 813 @synthesize caretRect = caretRect_; |
870 | 814 |
871 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { | 815 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { |
872 self = [super initWithFrame:NSZeroRect]; | 816 self = [super initWithFrame:NSZeroRect]; |
873 if (self) { | 817 if (self) { |
874 editCommand_helper_.reset(new RWHVMEditCommandHelper); | 818 editCommand_helper_.reset(new RWHVMEditCommandHelper); |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1008 // text was deleted or not after handling the key down event. | 952 // text was deleted or not after handling the key down event. |
1009 BOOL oldHasMarkedText = hasMarkedText_; | 953 BOOL oldHasMarkedText = hasMarkedText_; |
1010 | 954 |
1011 // This method should not be called recursively. | 955 // This method should not be called recursively. |
1012 DCHECK(!handlingKeyDown_); | 956 DCHECK(!handlingKeyDown_); |
1013 | 957 |
1014 // Tells insertText: and doCommandBySelector: that we are handling a key | 958 // Tells insertText: and doCommandBySelector: that we are handling a key |
1015 // down event. | 959 // down event. |
1016 handlingKeyDown_ = YES; | 960 handlingKeyDown_ = YES; |
1017 | 961 |
1018 // These two variables might be set when handling the keyboard event. | 962 // These variables might be set when handling the keyboard event. |
1019 // Clear them here so that we can know whether they have changed afterwards. | 963 // Clear them here so that we can know whether they have changed afterwards. |
1020 textToBeInserted_.clear(); | 964 textToBeInserted_.clear(); |
1021 markedText_.clear(); | 965 markedText_.clear(); |
1022 underlines_.clear(); | 966 underlines_.clear(); |
1023 unmarkTextCalled_ = NO; | 967 unmarkTextCalled_ = NO; |
| 968 hasEditCommands_ = NO; |
| 969 editCommands_.clear(); |
1024 | 970 |
1025 // Sends key down events to input method first, then we can decide what should | 971 // Sends key down events to input method first, then we can decide what should |
1026 // be done according to input method's feedback. | 972 // be done according to input method's feedback. |
1027 if (renderWidgetHostView_->text_input_type_ == WebKit::WebTextInputTypeText) | 973 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
1028 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; | |
1029 | 974 |
1030 handlingKeyDown_ = NO; | 975 handlingKeyDown_ = NO; |
1031 | 976 |
| 977 // Indicates if we should send the key event and corresponding editor commands |
| 978 // after processing the input method result. |
| 979 BOOL delayEventUntilAfterImeCompostion = NO; |
| 980 |
1032 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY | 981 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY |
1033 // while an input method is composing or inserting a text. | 982 // while an input method is composing or inserting a text. |
1034 // Gmail checks this code in its onkeydown handler to stop auto-completing | 983 // Gmail checks this code in its onkeydown handler to stop auto-completing |
1035 // e-mail addresses while composing a CJK text. | 984 // e-mail addresses while composing a CJK text. |
1036 // If the text to be inserted has only one character, then we don't need this | 985 // If the text to be inserted has only one character, then we don't need this |
1037 // trick, because we'll send the text as a key press event instead. | 986 // trick, because we'll send the text as a key press event instead. |
1038 if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) { | 987 if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) { |
1039 event.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY | 988 NativeWebKeyboardEvent fakeEvent = event; |
1040 event.setKeyIdentifierFromWindowsKeyCode(); | 989 fakeEvent.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY |
1041 event.skip_in_browser = true; | 990 fakeEvent.setKeyIdentifierFromWindowsKeyCode(); |
| 991 fakeEvent.skip_in_browser = true; |
| 992 widgetHost->ForwardKeyboardEvent(fakeEvent); |
| 993 // If this key event was handled by the input method, but |
| 994 // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above) |
| 995 // enqueued edit commands, then in order to let webkit handle them |
| 996 // correctly, we need to send the real key event and corresponding edit |
| 997 // commands after processing the input method result. |
| 998 // We shouldn't do this if a new marked text was set by the input method, |
| 999 // otherwise the new marked text might be cancelled by webkit. |
| 1000 if (hasEditCommands_ && !hasMarkedText_) |
| 1001 delayEventUntilAfterImeCompostion = YES; |
1042 } else { | 1002 } else { |
1043 // Look up shortcut, if any, for this key combination. | 1003 if (!editCommands_.empty()) |
1044 EditCommands editCommands; | 1004 widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_); |
1045 [EditCommandMatcher matchEditCommands:&editCommands forEvent:theEvent]; | 1005 widgetHost->ForwardKeyboardEvent(event); |
1046 if (!editCommands.empty()) | |
1047 widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands); | |
1048 } | 1006 } |
1049 | 1007 |
1050 // Forward the key down event first. | |
1051 widgetHost->ForwardKeyboardEvent(event); | |
1052 | |
1053 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the | 1008 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the |
1054 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will | 1009 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will |
1055 // be set to NULL. So we check it here and return immediately if it's NULL. | 1010 // be set to NULL. So we check it here and return immediately if it's NULL. |
1056 if (!renderWidgetHostView_->render_widget_host_) | 1011 if (!renderWidgetHostView_->render_widget_host_) |
1057 return; | 1012 return; |
1058 | 1013 |
1059 // Then send keypress and/or composition related events. | 1014 // Then send keypress and/or composition related events. |
1060 // If there was a marked text or the text to be inserted is longer than 1 | 1015 // If there was a marked text or the text to be inserted is longer than 1 |
1061 // character, then we send the text by calling ConfirmComposition(). | 1016 // character, then we send the text by calling ConfirmComposition(). |
1062 // Otherwise, if the text to be inserted only contains 1 character, then we | 1017 // Otherwise, if the text to be inserted only contains 1 character, then we |
1063 // can just send a keypress event which is fabricated by changing the type of | 1018 // can just send a keypress event which is fabricated by changing the type of |
1064 // the keydown event, so that we can retain all necessary informations, such | 1019 // the keydown event, so that we can retain all necessary informations, such |
1065 // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to | 1020 // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to |
1066 // prevent the browser from handling it again. | 1021 // prevent the browser from handling it again. |
1067 // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only | 1022 // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only |
1068 // handle BMP characters here, as we can always insert non-BMP characters as | 1023 // handle BMP characters here, as we can always insert non-BMP characters as |
1069 // text. | 1024 // text. |
1070 BOOL textInserted = NO; | 1025 BOOL textInserted = NO; |
1071 if (textToBeInserted_.length() > (oldHasMarkedText ? 0 : 1)) { | 1026 if (textToBeInserted_.length() > |
| 1027 ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) { |
1072 widgetHost->ImeConfirmComposition(textToBeInserted_); | 1028 widgetHost->ImeConfirmComposition(textToBeInserted_); |
1073 textInserted = YES; | 1029 textInserted = YES; |
1074 } else if (textToBeInserted_.length() == 1) { | |
1075 event.type = WebKit::WebInputEvent::Char; | |
1076 event.text[0] = textToBeInserted_[0]; | |
1077 event.text[1] = 0; | |
1078 event.skip_in_browser = true; | |
1079 widgetHost->ForwardKeyboardEvent(event); | |
1080 } else if (!hasMarkedText_ && !oldHasMarkedText && | |
1081 [[theEvent characters] length] > 0) { | |
1082 // We don't get insertText: calls if ctrl is down and not even a keyDown: | |
1083 // call if cmd is down, or in password input mode, so synthesize a keypress | |
1084 // event for these cases. | |
1085 event.type = WebKit::WebInputEvent::Char; | |
1086 event.skip_in_browser = true; | |
1087 widgetHost->ForwardKeyboardEvent(event); | |
1088 } | 1030 } |
1089 | 1031 |
1090 // Updates or cancels the composition. If some text has been inserted, then | 1032 // Updates or cancels the composition. If some text has been inserted, then |
1091 // we don't need to cancel the composition explicitly. | 1033 // we don't need to cancel the composition explicitly. |
1092 if (hasMarkedText_ && markedText_.length()) { | 1034 if (hasMarkedText_ && markedText_.length()) { |
1093 // Sends the updated marked text to the renderer so it can update the | 1035 // Sends the updated marked text to the renderer so it can update the |
1094 // composition node in WebKit. | 1036 // composition node in WebKit. |
1095 // When marked text is available, |selectedRange_| will be the range being | 1037 // When marked text is available, |selectedRange_| will be the range being |
1096 // selected inside the marked text. | 1038 // selected inside the marked text. |
1097 widgetHost->ImeSetComposition(markedText_, underlines_, | 1039 widgetHost->ImeSetComposition(markedText_, underlines_, |
1098 selectedRange_.location, | 1040 selectedRange_.location, |
1099 NSMaxRange(selectedRange_)); | 1041 NSMaxRange(selectedRange_)); |
1100 } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) { | 1042 } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) { |
1101 if (unmarkTextCalled_) | 1043 if (unmarkTextCalled_) |
1102 widgetHost->ImeConfirmComposition(); | 1044 widgetHost->ImeConfirmComposition(); |
1103 else | 1045 else |
1104 widgetHost->ImeCancelComposition(); | 1046 widgetHost->ImeCancelComposition(); |
1105 } | 1047 } |
1106 | 1048 |
| 1049 // If the key event was handled by the input method but it also generated some |
| 1050 // edit commands, then we need to send the real key event and corresponding |
| 1051 // edit commands here. This usually occurs when the input method wants to |
| 1052 // finish current composition session but still wants the application to |
| 1053 // handle the key event. See http://crbug.com/48161 for reference. |
| 1054 if (delayEventUntilAfterImeCompostion) { |
| 1055 // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event |
| 1056 // with windowsKeyCode == 0xE5 has already been sent to webkit. |
| 1057 // So before sending the real key down event, we need to send a fake key up |
| 1058 // event to balance it. |
| 1059 NativeWebKeyboardEvent fakeEvent = event; |
| 1060 fakeEvent.type = WebKit::WebInputEvent::KeyUp; |
| 1061 fakeEvent.skip_in_browser = true; |
| 1062 widgetHost->ForwardKeyboardEvent(fakeEvent); |
| 1063 // Not checking |renderWidgetHostView_->render_widget_host_| here because |
| 1064 // a key event with |skip_in_browser| == true won't be handled by browser, |
| 1065 // thus it won't destroy the widget. |
| 1066 |
| 1067 if (!editCommands_.empty()) |
| 1068 widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_); |
| 1069 widgetHost->ForwardKeyboardEvent(event); |
| 1070 |
| 1071 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the |
| 1072 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will |
| 1073 // be set to NULL. So we check it here and return immediately if it's NULL. |
| 1074 if (!renderWidgetHostView_->render_widget_host_) |
| 1075 return; |
| 1076 } |
| 1077 |
| 1078 // Only send a corresponding key press event if there is no marked text. |
| 1079 if (!hasMarkedText_) { |
| 1080 if (!textInserted && textToBeInserted_.length() == 1) { |
| 1081 // If a single character was inserted, then we just send it as a keypress |
| 1082 // event. |
| 1083 event.type = WebKit::WebInputEvent::Char; |
| 1084 event.text[0] = textToBeInserted_[0]; |
| 1085 event.text[1] = 0; |
| 1086 event.skip_in_browser = true; |
| 1087 widgetHost->ForwardKeyboardEvent(event); |
| 1088 } else if ((!textInserted || delayEventUntilAfterImeCompostion) && |
| 1089 editCommands_.empty() && [[theEvent characters] length] > 0) { |
| 1090 // We don't get insertText: calls if ctrl or cmd is down, or the key event |
| 1091 // generates an insert command. So synthesize a keypress event for these |
| 1092 // cases, unless the key event generated any other command. |
| 1093 event.type = WebKit::WebInputEvent::Char; |
| 1094 event.skip_in_browser = true; |
| 1095 widgetHost->ForwardKeyboardEvent(event); |
| 1096 } |
| 1097 } |
| 1098 |
1107 // Possibly autohide the cursor. | 1099 // Possibly autohide the cursor. |
1108 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) | 1100 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) |
1109 [NSCursor setHiddenUntilMouseMoves:YES]; | 1101 [NSCursor setHiddenUntilMouseMoves:YES]; |
1110 } | 1102 } |
1111 | 1103 |
1112 - (void)scrollWheel:(NSEvent *)theEvent { | 1104 - (void)scrollWheel:(NSEvent *)theEvent { |
1113 [self cancelChildPopups]; | 1105 [self cancelChildPopups]; |
1114 | 1106 |
1115 const WebMouseWheelEvent& event = | 1107 const WebMouseWheelEvent& event = |
1116 WebInputEventFactory::mouseWheelEvent(theEvent, self); | 1108 WebInputEventFactory::mouseWheelEvent(theEvent, self); |
(...skipping 784 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1901 if (!handlingKeyDown_) { | 1893 if (!handlingKeyDown_) { |
1902 renderWidgetHostView_->render_widget_host_->ImeSetComposition( | 1894 renderWidgetHostView_->render_widget_host_->ImeSetComposition( |
1903 markedText_, underlines_, | 1895 markedText_, underlines_, |
1904 newSelRange.location, NSMaxRange(newSelRange)); | 1896 newSelRange.location, NSMaxRange(newSelRange)); |
1905 } | 1897 } |
1906 } | 1898 } |
1907 | 1899 |
1908 - (void)doCommandBySelector:(SEL)selector { | 1900 - (void)doCommandBySelector:(SEL)selector { |
1909 // An input method calls this function to dispatch an editing command to be | 1901 // An input method calls this function to dispatch an editing command to be |
1910 // handled by this view. | 1902 // handled by this view. |
1911 // Even though most editing commands has been already handled by the | 1903 if (selector == @selector(noop:)) |
1912 // RWHVMEditCommandHelper object, we need to handle an insertNewline: command | 1904 return; |
1913 // and send a '\r' character to WebKit so that WebKit dispatches this | 1905 |
1914 // character to onkeypress() event handlers. | 1906 std::string command( |
1915 // TODO(hbono): need to handle more commands? | 1907 [RWHVMEditCommandHelper::CommandNameForSelector(selector) UTF8String]); |
1916 if (selector == @selector(insertNewline:)) { | 1908 |
1917 if (handlingKeyDown_) { | 1909 // If this method is called when handling a key down event, then we need to |
1918 // If we are handling a key down event, then we just need to append a '\r' | 1910 // handle the command in the key event handler. Otherwise we can just handle |
1919 // character to |textToBeInserted_| which will then be handled by | 1911 // it here. |
1920 // keyEvent: method. | 1912 if (handlingKeyDown_) { |
1921 textToBeInserted_.push_back('\r'); | 1913 hasEditCommands_ = YES; |
1922 } else { | 1914 // We ignore commands that insert characters, because this was causing |
1923 // This call is not initiated by a key event, so just executed the | 1915 // strange behavior (e.g. tab always inserted a tab rather than moving to |
1924 // corresponding editor command. | 1916 // the next field on the page). |
1925 renderWidgetHostView_->render_widget_host_->ForwardEditCommand( | 1917 if (!StartsWithASCII(command, "insert", false)) |
1926 "InsertNewline", ""); | 1918 editCommands_.push_back(EditCommand(command, "")); |
1927 } | 1919 } else { |
| 1920 renderWidgetHostView_->render_widget_host_->ForwardEditCommand(command, ""); |
1928 } | 1921 } |
1929 } | 1922 } |
1930 | 1923 |
1931 - (void)insertText:(id)string { | 1924 - (void)insertText:(id)string { |
1932 // An input method has characters to be inserted. | 1925 // An input method has characters to be inserted. |
1933 // Same as Linux, Mac calls this method not only: | 1926 // Same as Linux, Mac calls this method not only: |
1934 // * when an input method finishs composing text, but also; | 1927 // * when an input method finishs composing text, but also; |
1935 // * when we type an ASCII character (without using input methods). | 1928 // * when we type an ASCII character (without using input methods). |
1936 // When we aren't using input methods, we should send the given character as | 1929 // When we aren't using input methods, we should send the given character as |
1937 // a Char event so it is dispatched to an onkeypress() event handler of | 1930 // a Char event so it is dispatched to an onkeypress() event handler of |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2098 if (!hasMarkedText_) | 2091 if (!hasMarkedText_) |
2099 return; | 2092 return; |
2100 | 2093 |
2101 if (renderWidgetHostView_->render_widget_host_) | 2094 if (renderWidgetHostView_->render_widget_host_) |
2102 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(); | 2095 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(); |
2103 | 2096 |
2104 [self cancelComposition]; | 2097 [self cancelComposition]; |
2105 } | 2098 } |
2106 | 2099 |
2107 @end | 2100 @end |
OLD | NEW |