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

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

Issue 150206: Implement the NSTextInput protocol.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 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 "chrome/browser/renderer_host/render_widget_host_view_mac.h" 5 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
6 6
7 #include "base/histogram.h" 7 #include "base/histogram.h"
8 #include "base/string_util.h"
8 #include "base/sys_string_conversions.h" 9 #include "base/sys_string_conversions.h"
9 #include "chrome/browser/browser_trial.h" 10 #include "chrome/browser/browser_trial.h"
10 #import "chrome/browser/cocoa/rwhvm_editcommand_helper.h" 11 #import "chrome/browser/cocoa/rwhvm_editcommand_helper.h"
11 #include "chrome/browser/renderer_host/backing_store.h" 12 #include "chrome/browser/renderer_host/backing_store.h"
12 #include "chrome/browser/renderer_host/render_process_host.h" 13 #include "chrome/browser/renderer_host/render_process_host.h"
13 #include "chrome/browser/renderer_host/render_widget_host.h" 14 #include "chrome/browser/renderer_host/render_widget_host.h"
14 #include "chrome/common/native_web_keyboard_event.h" 15 #include "chrome/common/native_web_keyboard_event.h"
15 #include "skia/ext/platform_canvas.h" 16 #include "skia/ext/platform_canvas.h"
16 #include "webkit/api/public/mac/WebInputEventFactory.h" 17 #include "webkit/api/public/mac/WebInputEventFactory.h"
17 #include "webkit/api/public/WebInputEvent.h" 18 #include "webkit/api/public/WebInputEvent.h"
(...skipping 22 matching lines...) Expand all
40 RenderWidgetHost* widget) { 41 RenderWidgetHost* widget) {
41 return new RenderWidgetHostViewMac(widget); 42 return new RenderWidgetHostViewMac(widget);
42 } 43 }
43 44
44 /////////////////////////////////////////////////////////////////////////////// 45 ///////////////////////////////////////////////////////////////////////////////
45 // RenderWidgetHostViewMac, public: 46 // RenderWidgetHostViewMac, public:
46 47
47 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) 48 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
48 : render_widget_host_(widget), 49 : render_widget_host_(widget),
49 about_to_validate_and_paint_(false), 50 about_to_validate_and_paint_(false),
51 im_attributes_(NULL),
52 im_composing_(false),
50 is_loading_(false), 53 is_loading_(false),
51 is_hidden_(false), 54 is_hidden_(false),
52 shutdown_factory_(this), 55 shutdown_factory_(this),
53 parent_view_(NULL) { 56 parent_view_(NULL) {
54 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc] 57 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
55 initWithRenderWidgetHostViewMac:this] autorelease]; 58 initWithRenderWidgetHostViewMac:this] autorelease];
56 render_widget_host_->set_view(this); 59 render_widget_host_->set_view(this);
57 } 60 }
58 61
59 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() { 62 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 [ns_cursor set]; 204 [ns_cursor set];
202 } 205 }
203 206
204 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) { 207 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
205 is_loading_ = is_loading; 208 is_loading_ = is_loading;
206 UpdateCursorIfOverSelf(); 209 UpdateCursorIfOverSelf();
207 } 210 }
208 211
209 void RenderWidgetHostViewMac::IMEUpdateStatus(int control, 212 void RenderWidgetHostViewMac::IMEUpdateStatus(int control,
210 const gfx::Rect& caret_rect) { 213 const gfx::Rect& caret_rect) {
211 NOTIMPLEMENTED(); 214 // The renderer updates its IME status.
215 // We need to control the input method according to the given message.
216
217 // We need to convert the coordinate of the cursor rectangle sent from the
218 // renderer and save it. Our IME backend uses a coordinate system whose
219 // origin is the upper-left corner of this view. On the other hand, Cocoa
220 // uses a coordinate system whose origin is the lower-left corner of this
221 // view. So, we convert the cursor rectangle and save it.
222 NSRect view_rect = [cocoa_view_ bounds];
223 const int y_offset = static_cast<int>(view_rect.size.height);
224 im_caret_rect_ = NSMakeRect(caret_rect.x(),
225 y_offset - caret_rect.y() - caret_rect.height(),
226 caret_rect.width(), caret_rect.height());
212 } 227 }
213 228
214 void RenderWidgetHostViewMac::DidPaintRect(const gfx::Rect& rect) { 229 void RenderWidgetHostViewMac::DidPaintRect(const gfx::Rect& rect) {
215 if (is_hidden_) 230 if (is_hidden_)
216 return; 231 return;
217 232
218 NSRect ns_rect = [cocoa_view_ RectToNSRect:rect]; 233 NSRect ns_rect = [cocoa_view_ RectToNSRect:rect];
219 234
220 if (about_to_validate_and_paint_) { 235 if (about_to_validate_and_paint_) {
221 // As much as we'd like to use -setNeedsDisplayInRect: here, we can't. We're 236 // As much as we'd like to use -setNeedsDisplayInRect: here, we can't. We're
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 } 466 }
452 467
453 - (void)keyEvent:(NSEvent *)theEvent { 468 - (void)keyEvent:(NSEvent *)theEvent {
454 // TODO(avi): Possibly kill self? See RenderWidgetHostViewWin::OnKeyEvent and 469 // TODO(avi): Possibly kill self? See RenderWidgetHostViewWin::OnKeyEvent and
455 // http://b/issue?id=1192881 . 470 // http://b/issue?id=1192881 .
456 471
457 // Don't cancel child popups; the key events are probably what's triggering 472 // Don't cancel child popups; the key events are probably what's triggering
458 // the popup in the first place. 473 // the popup in the first place.
459 474
460 NativeWebKeyboardEvent event(theEvent); 475 NativeWebKeyboardEvent event(theEvent);
476
477 // Save the modifier keys so the insertText method can use it when it sends
478 // a Char event, which is dispatched as an onkeypress() event of JavaScript.
479 renderWidgetHostView_->im_modifiers_ = event.modifiers;
480
481 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
482 // while an input method is composing a text.
483 // Gmail checks this code in its onkeydown handler to stop auto-completing
484 // e-mail addresses while composing a CJK text.
485 if ([theEvent type] == NSKeyDown && renderWidgetHostView_->im_composing_)
486 event.windowsKeyCode = 0xE5;
487
488 // Dispatch this keyboard event to the renderer.
461 if (renderWidgetHostView_->render_widget_host_) 489 if (renderWidgetHostView_->render_widget_host_)
462 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event); 490 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event);
491
492 // Dispatch a NSKeyDown event to an input method.
493 // To send an onkeydown() event before an onkeypress() event, we should
494 // dispatch this NSKeyDown event AFTER sending it to the renderer.
495 // (See <https://bugs.webkit.org/show_bug.cgi?id=25119>).
496 if ([theEvent type] == NSKeyDown)
497 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
463 } 498 }
464 499
465 - (void)scrollWheel:(NSEvent *)theEvent { 500 - (void)scrollWheel:(NSEvent *)theEvent {
466 [self cancelChildPopups]; 501 [self cancelChildPopups];
467 502
468 const WebMouseWheelEvent& event = 503 const WebMouseWheelEvent& event =
469 WebInputEventFactory::mouseWheelEvent(theEvent, self); 504 WebInputEventFactory::mouseWheelEvent(theEvent, self);
470 if (renderWidgetHostView_->render_widget_host_) 505 if (renderWidgetHostView_->render_widget_host_)
471 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event); 506 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event);
472 } 507 }
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
812 } 847 }
813 848
814 // NSView calls this to get the text when displaying the tooltip. 849 // NSView calls this to get the text when displaying the tooltip.
815 - (NSString *)view:(NSView *)view 850 - (NSString *)view:(NSView *)view
816 stringForToolTip:(NSToolTipTag)tag 851 stringForToolTip:(NSToolTipTag)tag
817 point:(NSPoint)point 852 point:(NSPoint)point
818 userData:(void *)data { 853 userData:(void *)data {
819 return [[toolTip_ copy] autorelease]; 854 return [[toolTip_ copy] autorelease];
820 } 855 }
821 856
857 // Below is our NSTextInput implementation.
858 //
859 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
860 // functions to process this event.
861 //
862 // [WebHTMLView keyDown] ->
863 // EventHandler::keyEvent() ->
864 // ...
865 // [WebEditorClient handleKeyboardEvent] ->
866 // [WebHTMLView _interceptEditingKeyEvent] ->
867 // [NSResponder interpretKeyEvents] ->
868 // [WebHTMLView insertText] ->
869 // Editor::insertText()
870 //
871 // Unfortunately, it is hard for Chromium to use this implementation because
872 // it causes key-typing jank.
873 // RenderWidgetHostViewMac is running in a browser process. On the other
874 // hand, Editor and EventHandler are running in a renderer process.
875 // So, if we used this implementation, a NSKeyDown event is dispatched to
876 // the following functions of Chromium.
877 //
878 // [RenderWidgetHostViewMac keyEvent] (browser) ->
879 // |Sync IPC (KeyDown)| (*1) ->
880 // EventHandler::keyEvent() (renderer) ->
881 // ...
882 // EditorClientImpl::handleKeyboardEvent() (renderer) ->
883 // |Sync IPC| (*2) ->
884 // [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
885 // [self interpretKeyEvents] ->
886 // [RenderWidgetHostViewMac insertText] (browser) ->
887 // |Async IPC| ->
888 // Editor::insertText() (renderer)
889 //
890 // (*1) we need to wait until this call finishes since WebHTMLView uses the
891 // result of EventHandler::keyEvent().
892 // (*2) we need to wait until this call finishes since WebEditorClient uses
893 // the result of [WebHTMLView _interceptEditingKeyEvent].
894 //
895 // This needs many sync IPC messages sent between a browser and a renderer for
896 // each key event, which would probably result in key-typing jank.
897 // To avoid this problem, this implementation processes key events (and IME
898 // events) totally in a browser process and sends asynchronous input events,
899 // almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
900 // renderer process.
901 //
902 // [RenderWidgetHostViewMac keyEvent] (browser) ->
903 // |Async IPC (RawKeyDown)| ->
904 // [self interpretKeyEvents] ->
905 // [RenderWidgetHostViewMac insertText] (browser) ->
906 // |Async IPC (Char)| ->
907 // Editor::insertText() (renderer)
908 //
909 // Since this implementation doesn't have to wait any IPC calls, this doesn't
910 // make any key-typing jank. --hbono 7/23/09
911 //
912 extern "C" {
913 extern NSString *NSTextInputReplacementRangeAttributeName;
914 }
915
916 - (NSArray *)validAttributesForMarkedText {
917 // This code is just copied from WebKit except renaming variables.
918 if (!renderWidgetHostView_->im_attributes_) {
919 renderWidgetHostView_->im_attributes_ = [[NSArray alloc] initWithObjects:
920 NSUnderlineStyleAttributeName,
921 NSUnderlineColorAttributeName,
922 NSMarkedClauseSegmentAttributeName,
923 NSTextInputReplacementRangeAttributeName,
924 nil];
925 }
926 return renderWidgetHostView_->im_attributes_;
927 }
928
929 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
930 NOTIMPLEMENTED();
931 return NSNotFound;
932 }
933
934 - (NSRect)firstRectForCharacterRange:(NSRange)theRange {
935 // An input method requests a cursor rectangle to display its candidate
936 // window.
937 // Calculate the screen coordinate of the cursor rectangle saved in
938 // RenderWidgetHostViewMac::IMEUpdateStatus() and send it to the IME.
939 // Since this window may be moved since we receive the cursor rectangle last
940 // time we sent the cursor rectangle to the IME, so we should map from the
941 // view coordinate to the screen coordinate every time when an IME need it.
942 NSRect resultRect = renderWidgetHostView_->im_caret_rect_;
943 resultRect = [self convertRect:resultRect toView:nil];
944 NSWindow* window = [self window];
945 if (window)
946 resultRect.origin = [window convertBaseToScreen:resultRect.origin];
947 return resultRect;
948 }
949
950 - (NSRange)selectedRange {
951 // Return the selected range saved in the setMarkedText method.
952 return renderWidgetHostView_->im_selected_range_;
953 }
954
955 - (NSRange)markedRange {
956 // An input method calls this method to check if an application really has
957 // a text being composed when hasMarkedText call returns true.
958 // Returns the range saved in the setMarkedText method so the input method
959 // calls the setMarkedText method and we can update the composition node
960 // there. (When this method returns an empty range, the input method doesn't
961 // call the setMarkedText method.)
962 return renderWidgetHostView_->im_marked_range_;
963 }
964
965 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange {
966 // TODO(hbono): Even though many input method works without implementing
967 // this method, we need to save a copy of the string in the setMarkedText
968 // method and create a NSAttributedString with the given range.
969 NOTIMPLEMENTED();
970 return nil;
971 }
972
973 - (NSInteger)conversationIdentifier {
974 return reinterpret_cast<NSInteger>(self);
975 }
976
977 - (BOOL)hasMarkedText {
978 // An input method calls this function to figure out whether or not an
979 // application is really composing a text. If it is composing, it calls
980 // the markedRange method, and maybe calls the setMarkedTest method.
981 // It seems an input method usually calls this function when it is about to
982 // cancel an ongoing composition. If an application has a non-empty marked
983 // range, it calls the setMarkedText method to delete the range.
984 return renderWidgetHostView_->im_composing_ ? YES : NO;
985 }
986
987 - (void)unmarkText {
988 // Delete the composition node of the renderer and finish an ongoing
989 // composition.
990 // It seems an input method calls the setMarkedText method and set an empty
991 // text when it cancels an ongoing composition, i.e. I have never seen an
992 // input method calls this method.
993 renderWidgetHostView_->render_widget_host_->ImeCancelComposition();
994 renderWidgetHostView_->im_composing_ = false;
995 }
996
997 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange {
998 // An input method updates the composition string.
999 // We send the given text and range to the renderer so it can update the
1000 // composition node of WebKit.
1001 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
1002 NSString* im_text = isAttributedString ? [string string] : string;
1003 int length = [im_text length];
1004 int cursor;
1005 int target_start;
1006 int target_end;
1007 if (!newSelRange.length) {
1008 // The given text doesn't have any range to be highlighted.
1009 // Put the cursor to the end of this text and clear the selection range.
1010 cursor = length;
1011 target_start = 0;
1012 target_end = 0;
1013 } else {
1014 // The given text has a range to be highlighted.
1015 // Set the selection range to the given one and put the cursor at the
1016 // beginning of the selection range.
1017 cursor = newSelRange.location;
1018 target_start = newSelRange.location;
1019 target_end = newSelRange.location + newSelRange.length;
1020 }
1021
1022 // Dispatch this IME event to the renderer and update the IME state of this
1023 // object.
1024 // Input methods of Mac use setMarkedText calls with an empty text to cancel
1025 // an ongoing composition. So, we should check whether or not the given text
1026 // is empty to update the IME state. (Our IME backend can automatically
1027 // cancels an ongoing composition when we send an empty text. So, it is OK
1028 // to send an empty text to the renderer.)
1029 renderWidgetHostView_->render_widget_host_->ImeSetComposition(
1030 UTF8ToUTF16([im_text UTF8String]), cursor, target_start, target_end);
1031 renderWidgetHostView_->GetRenderWidgetHost()->ImeSetInputMode(true);
1032 renderWidgetHostView_->im_composing_ = length > 0;
1033 renderWidgetHostView_->im_marked_range_.location = 0;
1034 renderWidgetHostView_->im_marked_range_.length = length;
1035 renderWidgetHostView_->im_selected_range_.location = newSelRange.location;
1036 renderWidgetHostView_->im_selected_range_.length = newSelRange.length;
1037 }
1038
1039 - (void)doCommandBySelector:(SEL)selector {
1040 // An input method calls this function to dispatch an editing command to be
1041 // handled by this view.
1042 // Even though most editing commands has been already handled by the
1043 // RWHVMEditCommandHelper object, we need to handle an insertNewline: command
1044 // and send a '\r' character to WebKit so that WebKit dispatches this
1045 // character to onkeypress() event handlers.
1046 // TODO(hbono): need to handle more commands?
1047 if (selector == @selector(insertNewline:)) {
1048 NativeWebKeyboardEvent event('\r', renderWidgetHostView_->im_modifiers_,
1049 base::Time::Now().ToDoubleT());
1050 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event);
1051 }
1052 }
1053
1054 - (void)insertText:(id)string {
1055 // An input method has characters to be inserted.
1056 // Same as Linux, Mac calls this method not only:
1057 // * when an input method finishs composing text, but also;
1058 // * when we type an ASCII character (without using input methods).
1059 // When we aren't using input methods, we should send the given character as
1060 // a Char event so it is dispatched to an onkeypress() event handler of
1061 // JavaScript.
1062 // On the other hand, when we are using input methods, we should send the
1063 // given characters as an IME event and prevent the characters from being
1064 // dispatched to onkeypress() event handlers.
1065 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
1066 NSString* im_text = isAttributedString ? [string string] : string;
1067 if (!renderWidgetHostView_->im_composing_ && [im_text length] == 1) {
1068 NativeWebKeyboardEvent event([im_text characterAtIndex:0],
1069 renderWidgetHostView_->im_modifiers_,
1070 base::Time::Now().ToDoubleT());
1071 renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event);
1072 } else {
1073 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
1074 UTF8ToUTF16([im_text UTF8String]));
1075 }
1076 renderWidgetHostView_->im_composing_ = false;
1077 }
1078
822 @end 1079 @end
823 1080
OLDNEW
« no previous file with comments | « chrome/browser/renderer_host/render_widget_host_view_mac.h ('k') | chrome/common/native_web_keyboard_event.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698