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 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 return NULL; | 124 return NULL; |
125 } | 125 } |
126 | 126 |
127 /////////////////////////////////////////////////////////////////////////////// | 127 /////////////////////////////////////////////////////////////////////////////// |
128 // RenderWidgetHostViewMac, public: | 128 // RenderWidgetHostViewMac, public: |
129 | 129 |
130 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) | 130 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) |
131 : render_widget_host_(widget), | 131 : render_widget_host_(widget), |
132 about_to_validate_and_paint_(false), | 132 about_to_validate_and_paint_(false), |
133 call_set_needs_display_in_rect_pending_(false), | 133 call_set_needs_display_in_rect_pending_(false), |
134 im_attributes_(nil), | |
135 im_composing_(false), | |
136 is_loading_(false), | 134 is_loading_(false), |
137 is_hidden_(false), | 135 is_hidden_(false), |
138 is_popup_menu_(false), | 136 is_popup_menu_(false), |
139 shutdown_factory_(this), | 137 shutdown_factory_(this), |
140 parent_view_(NULL) { | 138 parent_view_(NULL) { |
141 // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away. | 139 // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away. |
142 // Since we autorelease it, our caller must put |native_view()| into the view | 140 // Since we autorelease it, our caller must put |native_view()| into the view |
143 // hierarchy right after calling us. | 141 // hierarchy right after calling us. |
144 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc] | 142 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc] |
145 initWithRenderWidgetHostViewMac:this] autorelease]; | 143 initWithRenderWidgetHostViewMac:this] autorelease]; |
146 render_widget_host_->set_view(this); | 144 render_widget_host_->set_view(this); |
147 } | 145 } |
148 | 146 |
149 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() { | 147 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() { |
150 [im_attributes_ release]; | |
151 } | 148 } |
152 | 149 |
153 /////////////////////////////////////////////////////////////////////////////// | 150 /////////////////////////////////////////////////////////////////////////////// |
154 // RenderWidgetHostViewMac, RenderWidgetHostView implementation: | 151 // RenderWidgetHostViewMac, RenderWidgetHostView implementation: |
155 | 152 |
156 void RenderWidgetHostViewMac::InitAsPopup( | 153 void RenderWidgetHostViewMac::InitAsPopup( |
157 RenderWidgetHostView* parent_host_view, | 154 RenderWidgetHostView* parent_host_view, |
158 const gfx::Rect& pos) { | 155 const gfx::Rect& pos) { |
159 bool activatable = popup_type_ == WebKit::WebPopupTypeNone; | 156 bool activatable = popup_type_ == WebKit::WebPopupTypeNone; |
160 [cocoa_view_ setCloseOnDeactivate:YES]; | 157 [cocoa_view_ setCloseOnDeactivate:YES]; |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 | 311 |
315 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) { | 312 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) { |
316 is_loading_ = is_loading; | 313 is_loading_ = is_loading; |
317 UpdateCursorIfOverSelf(); | 314 UpdateCursorIfOverSelf(); |
318 } | 315 } |
319 | 316 |
320 void RenderWidgetHostViewMac::IMEUpdateStatus(int control, | 317 void RenderWidgetHostViewMac::IMEUpdateStatus(int control, |
321 const gfx::Rect& caret_rect) { | 318 const gfx::Rect& caret_rect) { |
322 // Reset the IME state and finish an ongoing composition in the renderer. | 319 // Reset the IME state and finish an ongoing composition in the renderer. |
323 if (control == IME_DISABLE || control == IME_COMPLETE_COMPOSITION) | 320 if (control == IME_DISABLE || control == IME_COMPLETE_COMPOSITION) |
324 IMECleanupComposition(); | 321 [cocoa_view_ cancelComposition]; |
325 | 322 |
326 // We need to convert the coordinate of the cursor rectangle sent from the | 323 // We need to convert the coordinate of the cursor rectangle sent from the |
327 // renderer and save it. Our IME backend uses a coordinate system whose | 324 // renderer and save it. Our IME backend uses a coordinate system whose |
328 // origin is the upper-left corner of this view. On the other hand, Cocoa | 325 // origin is the upper-left corner of this view. On the other hand, Cocoa |
329 // uses a coordinate system whose origin is the lower-left corner of this | 326 // uses a coordinate system whose origin is the lower-left corner of this |
330 // view. So, we convert the cursor rectangle and save it. | 327 // view. So, we convert the cursor rectangle and save it. |
331 NSRect view_rect = [cocoa_view_ bounds]; | 328 [cocoa_view_ setCaretRect:[cocoa_view_ RectToNSRect:caret_rect]]; |
332 const int y_offset = static_cast<int>(view_rect.size.height); | |
333 im_caret_rect_ = NSMakeRect(caret_rect.x(), | |
334 y_offset - caret_rect.y() - caret_rect.height(), | |
335 caret_rect.width(), caret_rect.height()); | |
336 } | 329 } |
337 | 330 |
338 void RenderWidgetHostViewMac::DidPaintBackingStoreRects( | 331 void RenderWidgetHostViewMac::DidPaintBackingStoreRects( |
339 const std::vector<gfx::Rect>& rects) { | 332 const std::vector<gfx::Rect>& rects) { |
340 if (is_hidden_) | 333 if (is_hidden_) |
341 return; | 334 return; |
342 | 335 |
343 for (size_t i = 0; i < rects.size(); ++i) { | 336 for (size_t i = 0; i < rects.size(); ++i) { |
344 NSRect ns_rect = [cocoa_view_ RectToNSRect:rects[i]]; | 337 NSRect ns_rect = [cocoa_view_ RectToNSRect:rects[i]]; |
345 | 338 |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 | 530 |
538 void RenderWidgetHostViewMac::KillSelf() { | 531 void RenderWidgetHostViewMac::KillSelf() { |
539 if (shutdown_factory_.empty()) { | 532 if (shutdown_factory_.empty()) { |
540 [cocoa_view_ setHidden:YES]; | 533 [cocoa_view_ setHidden:YES]; |
541 MessageLoop::current()->PostTask(FROM_HERE, | 534 MessageLoop::current()->PostTask(FROM_HERE, |
542 shutdown_factory_.NewRunnableMethod( | 535 shutdown_factory_.NewRunnableMethod( |
543 &RenderWidgetHostViewMac::ShutdownHost)); | 536 &RenderWidgetHostViewMac::ShutdownHost)); |
544 } | 537 } |
545 } | 538 } |
546 | 539 |
547 void RenderWidgetHostViewMac::IMECleanupComposition() { | |
548 if (!im_composing_) | |
549 return; | |
550 | |
551 // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:] | |
552 // doesn't call any NSTextInput functions, such as setMarkedText or | |
553 // insertText. So, we need to send an IPC message to a renderer so it can | |
554 // delete the composition node. | |
555 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; | |
556 [currentInputManager markedTextAbandoned:[cocoa_view_ self]]; | |
557 | |
558 render_widget_host_->ImeCancelComposition(); | |
559 im_text_.clear(); | |
560 im_composing_ = false; | |
561 } | |
562 | |
563 gfx::PluginWindowHandle | 540 gfx::PluginWindowHandle |
564 RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque) { | 541 RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque) { |
565 // Make sure we have a layer for the plugin to draw into. | 542 // Make sure we have a layer for the plugin to draw into. |
566 [cocoa_view_ ensureAcceleratedPluginLayer]; | 543 [cocoa_view_ ensureAcceleratedPluginLayer]; |
567 | 544 |
568 return plugin_container_manager_.AllocateFakePluginWindowHandle(opaque); | 545 return plugin_container_manager_.AllocateFakePluginWindowHandle(opaque); |
569 } | 546 } |
570 | 547 |
571 void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle( | 548 void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle( |
572 gfx::PluginWindowHandle window) { | 549 gfx::PluginWindowHandle window) { |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
773 [[EditCommandMatcher alloc] initWithEditCommands:edit_commands]); | 750 [[EditCommandMatcher alloc] initWithEditCommands:edit_commands]); |
774 [matcher.get() interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; | 751 [matcher.get() interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
775 } | 752 } |
776 | 753 |
777 @end | 754 @end |
778 | 755 |
779 // RenderWidgetHostViewCocoa --------------------------------------------------- | 756 // RenderWidgetHostViewCocoa --------------------------------------------------- |
780 | 757 |
781 @implementation RenderWidgetHostViewCocoa | 758 @implementation RenderWidgetHostViewCocoa |
782 | 759 |
| 760 @synthesize caretRect = caretRect_; |
| 761 |
783 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { | 762 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { |
784 self = [super initWithFrame:NSZeroRect]; | 763 self = [super initWithFrame:NSZeroRect]; |
785 if (self != nil) { | 764 if (self != nil) { |
786 editCommand_helper_.reset(new RWHVMEditCommandHelper); | 765 editCommand_helper_.reset(new RWHVMEditCommandHelper); |
787 editCommand_helper_->AddEditingSelectorsToClass([self class]); | 766 editCommand_helper_->AddEditingSelectorsToClass([self class]); |
788 | 767 |
789 renderWidgetHostView_.reset(r); | 768 renderWidgetHostView_.reset(r); |
790 canBeKeyView_ = YES; | 769 canBeKeyView_ = YES; |
791 closeOnDeactivate_ = NO; | 770 closeOnDeactivate_ = NO; |
792 } | 771 } |
(...skipping 14 matching lines...) Expand all Loading... |
807 // popup. A click outside the text field would cause the text field to drop | 786 // popup. A click outside the text field would cause the text field to drop |
808 // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel | 787 // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel |
809 // the popup anyway, so we're OK. | 788 // the popup anyway, so we're OK. |
810 | 789 |
811 const WebMouseEvent& event = | 790 const WebMouseEvent& event = |
812 WebInputEventFactory::mouseEvent(theEvent, self); | 791 WebInputEventFactory::mouseEvent(theEvent, self); |
813 if (renderWidgetHostView_->render_widget_host_) | 792 if (renderWidgetHostView_->render_widget_host_) |
814 renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event); | 793 renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event); |
815 | 794 |
816 if ([theEvent type] == NSLeftMouseDown) { | 795 if ([theEvent type] == NSLeftMouseDown) { |
817 renderWidgetHostView_->IMECleanupComposition(); | 796 [self cancelComposition]; |
818 | 797 |
819 hasOpenMouseDown_ = YES; | 798 hasOpenMouseDown_ = YES; |
820 } | 799 } |
821 | 800 |
822 if ([theEvent type] == NSLeftMouseUp) { | 801 if ([theEvent type] == NSLeftMouseUp) { |
823 hasOpenMouseDown_ = NO; | 802 hasOpenMouseDown_ = NO; |
824 } | 803 } |
825 } | 804 } |
826 | 805 |
827 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent { | 806 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
874 !equiv == !([theEvent modifierFlags] & NSCommandKeyMask)); | 853 !equiv == !([theEvent modifierFlags] & NSCommandKeyMask)); |
875 | 854 |
876 if ([theEvent type] == NSFlagsChanged) { | 855 if ([theEvent type] == NSFlagsChanged) { |
877 // Ignore NSFlagsChanged events from the NumLock and Fn keys as | 856 // Ignore NSFlagsChanged events from the NumLock and Fn keys as |
878 // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm"). | 857 // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm"). |
879 int keyCode = [theEvent keyCode]; | 858 int keyCode = [theEvent keyCode]; |
880 if (!keyCode || keyCode == 10 || keyCode == 63) | 859 if (!keyCode || keyCode == 10 || keyCode == 63) |
881 return; | 860 return; |
882 } | 861 } |
883 | 862 |
| 863 // Don't cancel child popups; the key events are probably what's triggering |
| 864 // the popup in the first place. |
| 865 |
| 866 RenderWidgetHost* widgetHost = renderWidgetHostView_->render_widget_host_; |
| 867 DCHECK(widgetHost); |
| 868 |
| 869 NativeWebKeyboardEvent event(theEvent); |
| 870 |
| 871 // We only handle key down events and just simply forward other events. |
| 872 if ([theEvent type] != NSKeyDown) { |
| 873 widgetHost->ForwardKeyboardEvent(event); |
| 874 |
| 875 // Possibly autohide the cursor. |
| 876 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) |
| 877 [NSCursor setHiddenUntilMouseMoves:YES]; |
| 878 |
| 879 return; |
| 880 } |
| 881 |
884 scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]); | 882 scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]); |
885 | 883 |
886 // Don't cancel child popups; the key events are probably what's triggering | 884 // Records the current marked text state, so that we can know if the marked |
887 // the popup in the first place. | 885 // text was deleted or not after handling the key down event. |
| 886 BOOL oldHasMarkedText = hasMarkedText_; |
888 | 887 |
889 NativeWebKeyboardEvent event(theEvent); | 888 // This method should not be called recursively. |
| 889 DCHECK(!handlingKeyDown_); |
890 | 890 |
891 // Save the modifier keys so the insertText method can use it when it sends | 891 // Tells insertText: and doCommandBySelector: that we are handling a key |
892 // a Char event, which is dispatched as an onkeypress() event of JavaScript. | 892 // down event. |
893 renderWidgetHostView_->im_modifiers_ = event.modifiers; | 893 handlingKeyDown_ = YES; |
| 894 |
| 895 // These two variables might be set when handling the keyboard event. |
| 896 // Clear them here so that we can know whether they have changed afterwards. |
| 897 textToBeInserted_.clear(); |
| 898 newMarkedText_.clear(); |
| 899 |
| 900 // Sends key down events to input method first, then we can decide what should |
| 901 // be done according to input method's feedback. |
| 902 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
| 903 |
| 904 handlingKeyDown_ = NO; |
894 | 905 |
895 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY | 906 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY |
896 // while an input method is composing a text. | 907 // while an input method is composing or inserting a text. |
897 // Gmail checks this code in its onkeydown handler to stop auto-completing | 908 // Gmail checks this code in its onkeydown handler to stop auto-completing |
898 // e-mail addresses while composing a CJK text. | 909 // e-mail addresses while composing a CJK text. |
899 if ([theEvent type] == NSKeyDown && renderWidgetHostView_->im_composing_) { | 910 // If the text to be inserted has only one character, then we don't need this |
| 911 // trick, because we'll send the text as a key press event instead. |
| 912 if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) { |
900 event.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY | 913 event.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY |
901 event.setKeyIdentifierFromWindowsKeyCode(); | 914 event.setKeyIdentifierFromWindowsKeyCode(); |
902 event.skip_in_browser = true; | 915 event.skip_in_browser = true; |
903 } | 916 } else { |
904 | |
905 // Dispatch this keyboard event to the renderer. | |
906 if (renderWidgetHostView_->render_widget_host_) { | |
907 RenderWidgetHost* widgetHost = renderWidgetHostView_->render_widget_host_; | |
908 // Look up shortcut, if any, for this key combination. | 917 // Look up shortcut, if any, for this key combination. |
909 EditCommands editCommands; | 918 EditCommands editCommands; |
910 [EditCommandMatcher matchEditCommands:&editCommands forEvent:theEvent]; | 919 [EditCommandMatcher matchEditCommands:&editCommands forEvent:theEvent]; |
911 if (!editCommands.empty()) | 920 if (!editCommands.empty()) |
912 widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands); | 921 widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands); |
913 widgetHost->ForwardKeyboardEvent(event); | |
914 } | 922 } |
915 | 923 |
916 currentKeyEvent_.reset([theEvent retain]); | 924 // Forward the key down event first. |
917 textInserted_ = NO; | 925 widgetHost->ForwardKeyboardEvent(event); |
918 | 926 |
919 // Dispatch a NSKeyDown event to an input method. | 927 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the |
920 // To send an onkeydown() event before an onkeypress() event, we should | 928 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will |
921 // dispatch this NSKeyDown event AFTER sending it to the renderer. | 929 // be set to NULL. So we check it here and return immediately if it's NULL. |
922 // (See <https://bugs.webkit.org/show_bug.cgi?id=25119>). | 930 if (!renderWidgetHostView_->render_widget_host_) |
923 // | 931 return; |
924 // If this object's retainCount is 1, the only reference is the one held by | |
925 // keepSelfAlive. All other references may have been destroyed in the | |
926 // RenderWidgetHost::ForwardKeyboardEvent call above if it resulted in tab | |
927 // closure. Were it not for that single reference, this object would | |
928 // already be deallocated. In that case, there's no point in calling | |
929 // -interpretKeyEvents:. | |
930 if ([self retainCount] > 1 && [theEvent type] == NSKeyDown) { | |
931 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; | |
932 | 932 |
| 933 // Then send keypress and/or composition related events. |
| 934 // If there was a marked text or the text to be inserted is longer than 1 |
| 935 // character, then we send the text by calling ImeConfirmComposition(). |
| 936 // Otherwise, if the text to be inserted only contains 1 character, then we |
| 937 // can just send a keypress event which is fabricated by changing the type of |
| 938 // the keydown event, so that we can retain all necessary informations, such |
| 939 // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to |
| 940 // prevent the browser from handling it again. |
| 941 // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only |
| 942 // handle BMP characters here, as we can always insert non-BMP characters as |
| 943 // text. |
| 944 const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask; |
| 945 BOOL textInserted = NO; |
| 946 if (textToBeInserted_.length() > (oldHasMarkedText ? 0 : 1)) { |
| 947 widgetHost->ImeConfirmComposition(textToBeInserted_); |
| 948 textInserted = YES; |
| 949 } else if (textToBeInserted_.length() == 1) { |
| 950 event.type = WebKit::WebInputEvent::Char; |
| 951 event.text[0] = textToBeInserted_[0]; |
| 952 event.text[1] = 0; |
| 953 event.skip_in_browser = true; |
| 954 widgetHost->ForwardKeyboardEvent(event); |
| 955 } else if (([theEvent modifierFlags] & kCtrlCmdKeyMask) && |
| 956 [[theEvent characters] length] > 0) { |
933 // We don't get insertText: calls if ctrl is down and not even a keyDown: | 957 // We don't get insertText: calls if ctrl is down and not even a keyDown: |
934 // call if cmd is down, so synthesize a keypress event for these cases. | 958 // call if cmd is down, so synthesize a keypress event for these cases. |
935 // Note that this makes our behavior deviate from the windows and linux | 959 // Note that this makes our behavior deviate from the windows and linux |
936 // versions of chrome (however, see http://crbug.com/13891 ), but it makes | 960 // versions of chrome (however, see http://crbug.com/13891 ), but it makes |
937 // us behave similar to how Safari behaves. | 961 // us behave similar to how Safari behaves. |
938 if ([theEvent modifierFlags] & (NSControlKeyMask | NSCommandKeyMask) && | 962 event.type = WebKit::WebInputEvent::Char; |
939 !textInserted_ && [[theEvent characters] length] > 0 && | 963 event.skip_in_browser = true; |
940 renderWidgetHostView_->render_widget_host_) { | 964 widgetHost->ForwardKeyboardEvent(event); |
941 // Just fabricate a Char event by changing the type of the RawKeyDown | |
942 // event, to retain all necessary informations, such as unmodifiedText. | |
943 event.type = WebKit::WebInputEvent::Char; | |
944 // We fire menu items on keydown, we don't want to activate menu items | |
945 // twice. | |
946 event.skip_in_browser = true; | |
947 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event); | |
948 } | |
949 } | 965 } |
950 | 966 |
951 currentKeyEvent_.reset(); | 967 // Updates or cancels the composition. If some text has been inserted, then |
| 968 // we don't need to cancel the composition explicitly. |
| 969 if (hasMarkedText_ && newMarkedText_.length()) { |
| 970 // Sends the updated marked text to the renderer so it can update the |
| 971 // composition node in WebKit. |
| 972 // When marked text is available, |selectedRange_| will be the range being |
| 973 // selected inside the marked text. We put the cursor at the beginning of |
| 974 // the selected range. |
| 975 widgetHost->ImeSetComposition(newMarkedText_, |
| 976 selectedRange_.location, |
| 977 selectedRange_.location, |
| 978 NSMaxRange(selectedRange_)); |
| 979 } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) { |
| 980 widgetHost->ImeCancelComposition(); |
| 981 } |
952 | 982 |
953 // Possibly autohide the cursor. | 983 // Possibly autohide the cursor. |
954 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) | 984 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) |
955 [NSCursor setHiddenUntilMouseMoves:YES]; | 985 [NSCursor setHiddenUntilMouseMoves:YES]; |
956 } | 986 } |
957 | 987 |
958 - (void)scrollWheel:(NSEvent *)theEvent { | 988 - (void)scrollWheel:(NSEvent *)theEvent { |
959 [self cancelChildPopups]; | 989 [self cancelChildPopups]; |
960 | 990 |
961 const WebMouseWheelEvent& event = | 991 const WebMouseWheelEvent& event = |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1141 return NO; | 1171 return NO; |
1142 | 1172 |
1143 return canBeKeyView_; | 1173 return canBeKeyView_; |
1144 } | 1174 } |
1145 | 1175 |
1146 - (BOOL)becomeFirstResponder { | 1176 - (BOOL)becomeFirstResponder { |
1147 if (!renderWidgetHostView_->render_widget_host_) | 1177 if (!renderWidgetHostView_->render_widget_host_) |
1148 return NO; | 1178 return NO; |
1149 | 1179 |
1150 renderWidgetHostView_->render_widget_host_->Focus(); | 1180 renderWidgetHostView_->render_widget_host_->Focus(); |
| 1181 renderWidgetHostView_->render_widget_host_->ImeSetInputMode(true); |
1151 return YES; | 1182 return YES; |
1152 } | 1183 } |
1153 | 1184 |
1154 - (BOOL)resignFirstResponder { | 1185 - (BOOL)resignFirstResponder { |
1155 if (!renderWidgetHostView_->render_widget_host_) | 1186 if (!renderWidgetHostView_->render_widget_host_) |
1156 return YES; | 1187 return YES; |
1157 | 1188 |
1158 if (closeOnDeactivate_) | 1189 if (closeOnDeactivate_) |
1159 renderWidgetHostView_->KillSelf(); | 1190 renderWidgetHostView_->KillSelf(); |
1160 | 1191 |
| 1192 renderWidgetHostView_->render_widget_host_->ImeSetInputMode(false); |
1161 renderWidgetHostView_->render_widget_host_->Blur(); | 1193 renderWidgetHostView_->render_widget_host_->Blur(); |
1162 | |
1163 return YES; | 1194 return YES; |
1164 } | 1195 } |
1165 | 1196 |
1166 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { | 1197 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
1167 SEL action = [item action]; | 1198 SEL action = [item action]; |
1168 | 1199 |
1169 // For now, these actions are always enabled for render view, | 1200 // For now, these actions are always enabled for render view, |
1170 // this is sub-optimal. | 1201 // this is sub-optimal. |
1171 // TODO(suzhe): Plumb the "can*" methods up from WebCore. | 1202 // TODO(suzhe): Plumb the "can*" methods up from WebCore. |
1172 if (action == @selector(undo:) || | 1203 if (action == @selector(undo:) || |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1487 // | 1518 // |
1488 // Since this implementation doesn't have to wait any IPC calls, this doesn't | 1519 // Since this implementation doesn't have to wait any IPC calls, this doesn't |
1489 // make any key-typing jank. --hbono 7/23/09 | 1520 // make any key-typing jank. --hbono 7/23/09 |
1490 // | 1521 // |
1491 extern "C" { | 1522 extern "C" { |
1492 extern NSString *NSTextInputReplacementRangeAttributeName; | 1523 extern NSString *NSTextInputReplacementRangeAttributeName; |
1493 } | 1524 } |
1494 | 1525 |
1495 - (NSArray *)validAttributesForMarkedText { | 1526 - (NSArray *)validAttributesForMarkedText { |
1496 // This code is just copied from WebKit except renaming variables. | 1527 // This code is just copied from WebKit except renaming variables. |
1497 if (!renderWidgetHostView_->im_attributes_) { | 1528 if (!validAttributesForMarkedText_) { |
1498 renderWidgetHostView_->im_attributes_ = [[NSArray alloc] initWithObjects: | 1529 validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects: |
1499 NSUnderlineStyleAttributeName, | 1530 NSUnderlineStyleAttributeName, |
1500 NSUnderlineColorAttributeName, | 1531 NSUnderlineColorAttributeName, |
1501 NSMarkedClauseSegmentAttributeName, | 1532 NSMarkedClauseSegmentAttributeName, |
1502 NSTextInputReplacementRangeAttributeName, | 1533 NSTextInputReplacementRangeAttributeName, |
1503 nil]; | 1534 nil]); |
1504 } | 1535 } |
1505 return renderWidgetHostView_->im_attributes_; | 1536 return validAttributesForMarkedText_.get(); |
1506 } | 1537 } |
1507 | 1538 |
1508 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint { | 1539 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint { |
1509 NOTIMPLEMENTED(); | 1540 NOTIMPLEMENTED(); |
1510 return NSNotFound; | 1541 return NSNotFound; |
1511 } | 1542 } |
1512 | 1543 |
1513 - (NSRect)firstRectForCharacterRange:(NSRange)theRange { | 1544 - (NSRect)firstRectForCharacterRange:(NSRange)theRange { |
1514 // An input method requests a cursor rectangle to display its candidate | 1545 // An input method requests a cursor rectangle to display its candidate |
1515 // window. | 1546 // window. |
1516 // Calculate the screen coordinate of the cursor rectangle saved in | 1547 // Calculate the screen coordinate of the cursor rectangle saved in |
1517 // RenderWidgetHostViewMac::IMEUpdateStatus() and send it to the IME. | 1548 // RenderWidgetHostViewMac::IMEUpdateStatus() and send it to the IME. |
1518 // Since this window may be moved since we receive the cursor rectangle last | 1549 // Since this window may be moved since we receive the cursor rectangle last |
1519 // time we sent the cursor rectangle to the IME, so we should map from the | 1550 // time we sent the cursor rectangle to the IME, so we should map from the |
1520 // view coordinate to the screen coordinate every time when an IME need it. | 1551 // view coordinate to the screen coordinate every time when an IME need it. |
1521 NSRect resultRect = renderWidgetHostView_->im_caret_rect_; | 1552 NSRect resultRect = [self convertRect:caretRect_ toView:nil]; |
1522 resultRect = [self convertRect:resultRect toView:nil]; | |
1523 NSWindow* window = [self window]; | 1553 NSWindow* window = [self window]; |
1524 if (window) | 1554 if (window) |
1525 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; | 1555 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; |
1526 return resultRect; | 1556 return resultRect; |
1527 } | 1557 } |
1528 | 1558 |
1529 - (NSRange)selectedRange { | 1559 - (NSRange)selectedRange { |
1530 // Return the selected range saved in the setMarkedText method. | 1560 // Return the selected range saved in the setMarkedText method. |
1531 return renderWidgetHostView_->im_selected_range_; | 1561 return hasMarkedText_ ? selectedRange_ : NSMakeRange(NSNotFound, 0); |
1532 } | 1562 } |
1533 | 1563 |
1534 - (NSRange)markedRange { | 1564 - (NSRange)markedRange { |
1535 // An input method calls this method to check if an application really has | 1565 // An input method calls this method to check if an application really has |
1536 // a text being composed when hasMarkedText call returns true. | 1566 // a text being composed when hasMarkedText call returns true. |
1537 // Returns the range saved in the setMarkedText method so the input method | 1567 // Returns the range saved in the setMarkedText method so the input method |
1538 // calls the setMarkedText method and we can update the composition node | 1568 // calls the setMarkedText method and we can update the composition node |
1539 // there. (When this method returns an empty range, the input method doesn't | 1569 // there. (When this method returns an empty range, the input method doesn't |
1540 // call the setMarkedText method.) | 1570 // call the setMarkedText method.) |
1541 return renderWidgetHostView_->im_marked_range_; | 1571 return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0); |
1542 } | 1572 } |
1543 | 1573 |
1544 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange { | 1574 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange { |
1545 // TODO(hbono): Even though many input method works without implementing | 1575 // TODO(hbono): Even though many input method works without implementing |
1546 // this method, we need to save a copy of the string in the setMarkedText | 1576 // this method, we need to save a copy of the string in the setMarkedText |
1547 // method and create a NSAttributedString with the given range. | 1577 // method and create a NSAttributedString with the given range. |
1548 // http://crbug.com/37715 | 1578 // http://crbug.com/37715 |
1549 return nil; | 1579 return nil; |
1550 } | 1580 } |
1551 | 1581 |
1552 - (NSInteger)conversationIdentifier { | 1582 - (NSInteger)conversationIdentifier { |
1553 return reinterpret_cast<NSInteger>(self); | 1583 return reinterpret_cast<NSInteger>(self); |
1554 } | 1584 } |
1555 | 1585 |
1556 - (BOOL)hasMarkedText { | 1586 - (BOOL)hasMarkedText { |
1557 // An input method calls this function to figure out whether or not an | 1587 // An input method calls this function to figure out whether or not an |
1558 // application is really composing a text. If it is composing, it calls | 1588 // application is really composing a text. If it is composing, it calls |
1559 // the markedRange method, and maybe calls the setMarkedTest method. | 1589 // the markedRange method, and maybe calls the setMarkedText method. |
1560 // It seems an input method usually calls this function when it is about to | 1590 // It seems an input method usually calls this function when it is about to |
1561 // cancel an ongoing composition. If an application has a non-empty marked | 1591 // cancel an ongoing composition. If an application has a non-empty marked |
1562 // range, it calls the setMarkedText method to delete the range. | 1592 // range, it calls the setMarkedText method to delete the range. |
1563 return renderWidgetHostView_->im_composing_ ? YES : NO; | 1593 return hasMarkedText_; |
1564 } | 1594 } |
1565 | 1595 |
1566 - (void)unmarkText { | 1596 - (void)unmarkText { |
1567 // Delete the composition node of the renderer and finish an ongoing | 1597 // Delete the composition node of the renderer and finish an ongoing |
1568 // composition. | 1598 // composition. |
1569 // It seems an input method calls the setMarkedText method and set an empty | 1599 // It seems an input method calls the setMarkedText method and set an empty |
1570 // text when it cancels an ongoing composition, i.e. I have never seen an | 1600 // text when it cancels an ongoing composition, i.e. I have never seen an |
1571 // input method calls this method. | 1601 // input method calls this method. |
1572 renderWidgetHostView_->render_widget_host_->ImeCancelComposition(); | 1602 hasMarkedText_ = NO; |
1573 renderWidgetHostView_->im_composing_ = false; | 1603 |
| 1604 // If we are handling a key down event, then ImeCancelComposition() will be |
| 1605 // called in keyEvent: method. |
| 1606 if (!handlingKeyDown_) |
| 1607 renderWidgetHostView_->render_widget_host_->ImeCancelComposition(); |
1574 } | 1608 } |
1575 | 1609 |
1576 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange { | 1610 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange { |
1577 // An input method updates the composition string. | 1611 // An input method updates the composition string. |
1578 // We send the given text and range to the renderer so it can update the | 1612 // We send the given text and range to the renderer so it can update the |
1579 // composition node of WebKit. | 1613 // composition node of WebKit. |
1580 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; | 1614 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; |
1581 NSString* im_text = isAttributedString ? [string string] : string; | 1615 NSString* im_text = isAttributedString ? [string string] : string; |
1582 int length = [im_text length]; | 1616 int length = [im_text length]; |
1583 int cursor = newSelRange.location; | |
1584 | 1617 |
1585 int target_start; | 1618 markedRange_ = NSMakeRange(0, length); |
1586 int target_end; | 1619 selectedRange_ = newSelRange; |
1587 if (!newSelRange.length) { | 1620 newMarkedText_ = base::SysNSStringToUTF16(im_text); |
1588 // The given text doesn't have any range to be highlighted. Clear the | 1621 hasMarkedText_ = (length > 0); |
1589 // selection range. | |
1590 target_start = 0; | |
1591 target_end = 0; | |
1592 } else { | |
1593 // The given text has a range to be highlighted. | |
1594 // Set the selection range to the given one and put the cursor at the | |
1595 // beginning of the selection range. | |
1596 target_start = newSelRange.location; | |
1597 target_end = NSMaxRange(newSelRange); | |
1598 } | |
1599 | 1622 |
1600 // Dispatch this IME event to the renderer and update the IME state of this | 1623 // If we are handling a key down event, then ImeSetComposition() will be |
1601 // object. | 1624 // called in keyEvent: method. |
1602 // Input methods of Mac use setMarkedText calls with an empty text to cancel | 1625 // Input methods of Mac use setMarkedText calls with an empty text to cancel |
1603 // an ongoing composition. So, we should check whether or not the given text | 1626 // an ongoing composition. So, we should check whether or not the given text |
1604 // is empty to update the IME state. (Our IME backend can automatically | 1627 // is empty to update the IME state. (Our IME backend can automatically |
1605 // cancels an ongoing composition when we send an empty text. So, it is OK | 1628 // cancels an ongoing composition when we send an empty text. So, it is OK |
1606 // to send an empty text to the renderer.) | 1629 // to send an empty text to the renderer.) |
1607 renderWidgetHostView_->im_text_ = UTF8ToUTF16([im_text UTF8String]); | 1630 if (!handlingKeyDown_) { |
1608 renderWidgetHostView_->render_widget_host_->ImeSetComposition( | 1631 renderWidgetHostView_->render_widget_host_->ImeSetComposition( |
1609 renderWidgetHostView_->im_text_, cursor, target_start, target_end); | 1632 newMarkedText_, newSelRange.location, newSelRange.location, |
1610 renderWidgetHostView_->GetRenderWidgetHost()->ImeSetInputMode(true); | 1633 NSMaxRange(newSelRange)); |
1611 renderWidgetHostView_->im_composing_ = length > 0; | 1634 } |
1612 renderWidgetHostView_->im_marked_range_.location = 0; | |
1613 renderWidgetHostView_->im_marked_range_.length = length; | |
1614 renderWidgetHostView_->im_selected_range_.location = newSelRange.location; | |
1615 renderWidgetHostView_->im_selected_range_.length = newSelRange.length; | |
1616 } | 1635 } |
1617 | 1636 |
1618 - (void)doCommandBySelector:(SEL)selector { | 1637 - (void)doCommandBySelector:(SEL)selector { |
1619 // An input method calls this function to dispatch an editing command to be | 1638 // An input method calls this function to dispatch an editing command to be |
1620 // handled by this view. | 1639 // handled by this view. |
1621 // Even though most editing commands has been already handled by the | 1640 // Even though most editing commands has been already handled by the |
1622 // RWHVMEditCommandHelper object, we need to handle an insertNewline: command | 1641 // RWHVMEditCommandHelper object, we need to handle an insertNewline: command |
1623 // and send a '\r' character to WebKit so that WebKit dispatches this | 1642 // and send a '\r' character to WebKit so that WebKit dispatches this |
1624 // character to onkeypress() event handlers. | 1643 // character to onkeypress() event handlers. |
1625 // TODO(hbono): need to handle more commands? | 1644 // TODO(hbono): need to handle more commands? |
1626 if (selector == @selector(insertNewline:)) { | 1645 if (selector == @selector(insertNewline:)) { |
1627 if (currentKeyEvent_.get()) { | 1646 if (handlingKeyDown_) { |
1628 // Create the Char event from the NSEvent object, so that we can retain | 1647 // If we are handling a key down event, then we just need to append a '\r' |
1629 // necessary informations, especially unmodifiedText. | 1648 // character to |textToBeInserted_| which will then be handled by |
1630 NativeWebKeyboardEvent event(currentKeyEvent_.get()); | 1649 // keyEvent: method. |
1631 event.type = WebKit::WebInputEvent::Char; | 1650 textToBeInserted_.push_back('\r'); |
1632 event.text[0] = '\r'; | |
1633 event.text[1] = 0; | |
1634 event.skip_in_browser = true; | |
1635 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event); | |
1636 } else { | 1651 } else { |
1637 // This call is not initiated by a key event, so just executed the | 1652 // This call is not initiated by a key event, so just executed the |
1638 // corresponding editor command. | 1653 // corresponding editor command. |
1639 renderWidgetHostView_->render_widget_host_->ForwardEditCommand( | 1654 renderWidgetHostView_->render_widget_host_->ForwardEditCommand( |
1640 "InsertNewline", ""); | 1655 "InsertNewline", ""); |
1641 } | 1656 } |
1642 textInserted_ = YES; | |
1643 } | 1657 } |
1644 } | 1658 } |
1645 | 1659 |
1646 - (void)insertText:(id)string { | 1660 - (void)insertText:(id)string { |
1647 // An input method has characters to be inserted. | 1661 // An input method has characters to be inserted. |
1648 // Same as Linux, Mac calls this method not only: | 1662 // Same as Linux, Mac calls this method not only: |
1649 // * when an input method finishs composing text, but also; | 1663 // * when an input method finishs composing text, but also; |
1650 // * when we type an ASCII character (without using input methods). | 1664 // * when we type an ASCII character (without using input methods). |
1651 // When we aren't using input methods, we should send the given character as | 1665 // When we aren't using input methods, we should send the given character as |
1652 // a Char event so it is dispatched to an onkeypress() event handler of | 1666 // a Char event so it is dispatched to an onkeypress() event handler of |
1653 // JavaScript. | 1667 // JavaScript. |
1654 // On the other hand, when we are using input methods, we should send the | 1668 // On the other hand, when we are using input methods, we should send the |
1655 // given characters as an IME event and prevent the characters from being | 1669 // given characters as an IME event and prevent the characters from being |
1656 // dispatched to onkeypress() event handlers. | 1670 // dispatched to onkeypress() event handlers. |
1657 // Text inserting might be initiated by other source instead of keyboard | 1671 // Text inserting might be initiated by other source instead of keyboard |
1658 // events, such as the Characters dialog. In this case the text should be | 1672 // events, such as the Characters dialog. In this case the text should be |
1659 // sent as an IME event as well. | 1673 // sent as an IME event as well. |
1660 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; | 1674 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; |
1661 NSString* im_text = isAttributedString ? [string string] : string; | 1675 NSString* im_text = isAttributedString ? [string string] : string; |
1662 if (!renderWidgetHostView_->im_composing_ && [im_text length] == 1 && | 1676 if (handlingKeyDown_) { |
1663 currentKeyEvent_.get()) { | 1677 textToBeInserted_.append(base::SysNSStringToUTF16(im_text)); |
1664 // Create the Char event from the NSEvent object, so that we can retain | |
1665 // necessary informations, especially unmodifiedText. | |
1666 NativeWebKeyboardEvent event(currentKeyEvent_.get()); | |
1667 event.type = WebKit::WebInputEvent::Char; | |
1668 event.text[0] = [im_text characterAtIndex:0]; | |
1669 event.text[1] = 0; | |
1670 event.skip_in_browser = true; | |
1671 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event); | |
1672 } else { | 1678 } else { |
1673 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( | 1679 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( |
1674 UTF8ToUTF16([im_text UTF8String])); | 1680 base::SysNSStringToUTF16(im_text)); |
1675 } | 1681 } |
1676 renderWidgetHostView_->im_text_.clear(); | 1682 |
1677 renderWidgetHostView_->im_composing_ = false; | 1683 // Inserting text will delete all marked text automatically. |
1678 textInserted_ = YES; | 1684 hasMarkedText_ = NO; |
1679 } | 1685 } |
1680 | 1686 |
1681 - (void)viewDidMoveToWindow { | 1687 - (void)viewDidMoveToWindow { |
1682 // If we move into a new window, refresh the frame information. We don't need | 1688 // If we move into a new window, refresh the frame information. We don't need |
1683 // to do it if it was the same window as it used to be in, since that case | 1689 // to do it if it was the same window as it used to be in, since that case |
1684 // is covered by DidBecomeSelected. | 1690 // is covered by DidBecomeSelected. |
1685 // We only want to do this for real browser views, not popups. | 1691 // We only want to do this for real browser views, not popups. |
1686 if (canBeKeyView_) { | 1692 if (canBeKeyView_) { |
1687 NSWindow* newWindow = [self window]; | 1693 NSWindow* newWindow = [self window]; |
1688 // Pointer comparison only, since we don't know if lastWindow_ is still | 1694 // Pointer comparison only, since we don't know if lastWindow_ is still |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1794 | 1800 |
1795 [super setLayer:newLayer]; | 1801 [super setLayer:newLayer]; |
1796 if ([self layer]) | 1802 if ([self layer]) |
1797 [self attachPluginLayer]; | 1803 [self attachPluginLayer]; |
1798 } | 1804 } |
1799 | 1805 |
1800 - (void)drawAcceleratedPluginLayer { | 1806 - (void)drawAcceleratedPluginLayer { |
1801 [acceleratedPluginLayer_.get() setNeedsDisplay]; | 1807 [acceleratedPluginLayer_.get() setNeedsDisplay]; |
1802 } | 1808 } |
1803 | 1809 |
| 1810 - (void)cancelComposition { |
| 1811 if (!hasMarkedText_) |
| 1812 return; |
| 1813 |
| 1814 // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:] |
| 1815 // doesn't call any NSTextInput functions, such as setMarkedText or |
| 1816 // insertText. So, we need to send an IPC message to a renderer so it can |
| 1817 // delete the composition node. |
| 1818 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; |
| 1819 [currentInputManager markedTextAbandoned:self]; |
| 1820 |
| 1821 [self unmarkText]; |
| 1822 } |
| 1823 |
1804 @end | 1824 @end |
OLD | NEW |