Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(458)

Side by Side Diff: chrome/browser/renderer_host/render_widget_host_view_mac.mm

Issue 1908006: [Mac]Refactor input method related code. (Closed)
Patch Set: Update CL according to review feedback. Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/renderer_host/render_widget_host_view_mac.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/renderer_host/render_widget_host_view_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698