Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / views / cocoa / bridged_content_view.mm
blobee758e2133d22d09fc5a7cd482e8caee054639be
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ui/views/cocoa/bridged_content_view.h"
7 #include "base/logging.h"
8 #import "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "ui/base/ime/text_input_client.h"
11 #include "ui/compositor/paint_context.h"
12 #import "ui/events/cocoa/cocoa_event_utils.h"
13 #include "ui/events/keycodes/dom3/dom_code.h"
14 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
15 #include "ui/gfx/canvas_paint_mac.h"
16 #include "ui/gfx/geometry/rect.h"
17 #include "ui/strings/grit/ui_strings.h"
18 #include "ui/views/controls/menu/menu_controller.h"
19 #include "ui/views/ime/input_method.h"
20 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h"
23 using views::MenuController;
25 namespace {
27 // Convert a |point| in |source_window|'s AppKit coordinate system (origin at
28 // the bottom left of the window) to |target_window|'s content rect, with the
29 // origin at the top left of the content area.
30 // If |source_window| is nil, |point| will be treated as screen coordinates.
31 gfx::Point MovePointToWindow(const NSPoint& point,
32                              NSWindow* source_window,
33                              NSWindow* target_window) {
34   NSPoint point_in_screen = source_window
35       ? [source_window convertBaseToScreen:point]
36       : point;
38   NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen];
39   NSRect content_rect =
40       [target_window contentRectForFrameRect:[target_window frame]];
41   return gfx::Point(point_in_window.x,
42                     NSHeight(content_rect) - point_in_window.y);
45 // Checks if there's an active MenuController during key event dispatch. If
46 // there is one, it gets preference, and it will likely swallow the event.
47 bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) {
48   MenuController* menuController = MenuController::GetActiveInstance();
49   if (menuController && menuController->owner() == widget) {
50     if (menuController->OnWillDispatchKeyEvent(0, key_code) ==
51         ui::POST_DISPATCH_NONE)
52       return true;
53   }
54   return false;
57 }  // namespace
59 @interface BridgedContentView ()
61 // Translates the location of |theEvent| to toolkit-views coordinates and passes
62 // the event to NativeWidgetMac for handling.
63 - (void)handleMouseEvent:(NSEvent*)theEvent;
65 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes
66 // the event to the InputMethod for dispatch.
67 - (void)handleKeyEvent:(NSEvent*)theEvent;
69 // Handles an NSResponder Action Message by mapping it to a corresponding text
70 // editing command from ui_strings.grd and, when not being sent to a
71 // TextInputClient, the keyCode that toolkit-views expects internally.
72 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales
73 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
74 // This approach also allows action messages a user
75 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
76 // catered for.
77 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict
78 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do
79 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to
80 // get something readable.
81 - (void)handleAction:(int)commandId
82              keyCode:(ui::KeyboardCode)keyCode
83              domCode:(ui::DomCode)domCode
84           eventFlags:(int)eventFlags;
86 // Menu action handlers.
87 - (void)undo:(id)sender;
88 - (void)redo:(id)sender;
89 - (void)cut:(id)sender;
90 - (void)copy:(id)sender;
91 - (void)paste:(id)sender;
92 - (void)selectAll:(id)sender;
94 @end
96 @implementation BridgedContentView
98 @synthesize hostedView = hostedView_;
99 @synthesize textInputClient = textInputClient_;
101 - (id)initWithView:(views::View*)viewToHost {
102   DCHECK(viewToHost);
103   gfx::Rect bounds = viewToHost->bounds();
104   // To keep things simple, assume the origin is (0, 0) until there exists a use
105   // case for something other than that.
106   DCHECK(bounds.origin().IsOrigin());
107   NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
108   if ((self = [super initWithFrame:initialFrame])) {
109     hostedView_ = viewToHost;
111     // Apple's documentation says that NSTrackingActiveAlways is incompatible
112     // with NSTrackingCursorUpdate, so use NSTrackingActiveInActiveApp.
113     trackingArea_.reset([[CrTrackingArea alloc]
114         initWithRect:NSZeroRect
115              options:NSTrackingMouseMoved | NSTrackingCursorUpdate |
116                      NSTrackingActiveInActiveApp | NSTrackingInVisibleRect
117                owner:self
118             userInfo:nil]);
119     [self addTrackingArea:trackingArea_.get()];
120   }
121   return self;
124 - (void)clearView {
125   hostedView_ = NULL;
126   [trackingArea_.get() clearOwner];
127   [self removeTrackingArea:trackingArea_.get()];
130 - (void)processCapturedMouseEvent:(NSEvent*)theEvent {
131   if (!hostedView_)
132     return;
134   NSWindow* source = [theEvent window];
135   NSWindow* target = [self window];
136   DCHECK(target);
138   // If it's the view's window, process normally.
139   if ([target isEqual:source]) {
140     [self handleMouseEvent:theEvent];
141     return;
142   }
144   ui::MouseEvent event(theEvent);
145   event.set_location(
146       MovePointToWindow([theEvent locationInWindow], source, target));
147   hostedView_->GetWidget()->OnMouseEvent(&event);
150 // BridgedContentView private implementation.
152 - (void)handleMouseEvent:(NSEvent*)theEvent {
153   if (!hostedView_)
154     return;
156   ui::MouseEvent event(theEvent);
157   hostedView_->GetWidget()->OnMouseEvent(&event);
160 - (void)handleKeyEvent:(NSEvent*)theEvent {
161   if (!hostedView_)
162     return;
164   DCHECK(theEvent);
165   ui::KeyEvent event(theEvent);
166   if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code()))
167     return;
169   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
172 - (void)handleAction:(int)commandId
173              keyCode:(ui::KeyboardCode)keyCode
174              domCode:(ui::DomCode)domCode
175           eventFlags:(int)eventFlags {
176   if (!hostedView_)
177     return;
179   if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode))
180     return;
182   // If there's an active TextInputClient, schedule the editing command to be
183   // performed.
184   if (commandId && textInputClient_ &&
185       textInputClient_->IsEditCommandEnabled(commandId))
186     textInputClient_->SetEditCommandForNextKeyEvent(commandId);
188   // Generate a synthetic event with the keycode toolkit-views expects.
189   ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
190   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
193 - (void)undo:(id)sender {
194   // This DCHECK is more strict than a similar check in handleAction:. It can be
195   // done here because the actors sending these actions should be calling
196   // validateUserInterfaceItem: before enabling UI that allows these messages to
197   // be sent. Checking it here would be too late to provide correct UI feedback
198   // (e.g. there will be no "beep").
199   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
200   [self handleAction:IDS_APP_UNDO
201              keyCode:ui::VKEY_Z
202              domCode:ui::DomCode::KEY_Z
203           eventFlags:ui::EF_CONTROL_DOWN];
206 - (void)redo:(id)sender {
207   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
208   [self handleAction:IDS_APP_REDO
209              keyCode:ui::VKEY_Z
210              domCode:ui::DomCode::KEY_Z
211           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
214 - (void)cut:(id)sender {
215   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
216   [self handleAction:IDS_APP_CUT
217              keyCode:ui::VKEY_X
218              domCode:ui::DomCode::KEY_X
219           eventFlags:ui::EF_CONTROL_DOWN];
222 - (void)copy:(id)sender {
223   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_COPY));
224   [self handleAction:IDS_APP_COPY
225              keyCode:ui::VKEY_C
226              domCode:ui::DomCode::KEY_C
227           eventFlags:ui::EF_CONTROL_DOWN];
230 - (void)paste:(id)sender {
231   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE));
232   [self handleAction:IDS_APP_PASTE
233              keyCode:ui::VKEY_V
234              domCode:ui::DomCode::KEY_V
235           eventFlags:ui::EF_CONTROL_DOWN];
238 - (void)selectAll:(id)sender {
239   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL));
240   [self handleAction:IDS_APP_SELECT_ALL
241              keyCode:ui::VKEY_A
242              domCode:ui::DomCode::KEY_A
243           eventFlags:ui::EF_CONTROL_DOWN];
246 // NSView implementation.
248 - (BOOL)acceptsFirstResponder {
249   return YES;
252 - (void)viewDidMoveToWindow {
253   // When this view is added to a window, AppKit calls setFrameSize before it is
254   // added to the window, so the behavior in setFrameSize is not triggered.
255   NSWindow* window = [self window];
256   if (window)
257     [self setFrameSize:NSZeroSize];
260 - (void)setFrameSize:(NSSize)newSize {
261   // The size passed in here does not always use
262   // -[NSWindow contentRectForFrameRect]. The following ensures that the
263   // contentView for a frameless window can extend over the titlebar of the new
264   // window containing it, since AppKit requires a titlebar to give frameless
265   // windows correct shadows and rounded corners.
266   NSWindow* window = [self window];
267   if (window)
268     newSize = [window contentRectForFrameRect:[window frame]].size;
270   [super setFrameSize:newSize];
271   if (!hostedView_)
272     return;
274   hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
277 - (void)drawRect:(NSRect)dirtyRect {
278   // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to
279   // suppress calls to this when the window is known to be hidden.
280   if (!hostedView_)
281     return;
283   // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
284   if (hostedView_->GetWidget()->GetLayer())
285     return;
287   gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
288   hostedView_->GetWidget()->OnNativeWidgetPaint(ui::PaintContext(&canvas));
291 - (NSTextInputContext*)inputContext {
292   if (!hostedView_)
293     return [super inputContext];
295   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
296   // input context, return nil. This ensures the action message is sent to
297   // the view, rather than any NSTextInputClient a subview has installed.
298   MenuController* menuController = MenuController::GetActiveInstance();
299   if (menuController && menuController->owner() == hostedView_->GetWidget())
300     return nil;
302   return [super inputContext];
305 // NSResponder implementation.
307 - (void)keyDown:(NSEvent*)theEvent {
308   // Convert the event into an action message, according to OSX key mappings.
309   inKeyDown_ = YES;
310   [self interpretKeyEvents:@[ theEvent ]];
311   inKeyDown_ = NO;
314 - (void)mouseDown:(NSEvent*)theEvent {
315   [self handleMouseEvent:theEvent];
318 - (void)rightMouseDown:(NSEvent*)theEvent {
319   [self handleMouseEvent:theEvent];
322 - (void)otherMouseDown:(NSEvent*)theEvent {
323   [self handleMouseEvent:theEvent];
326 - (void)mouseUp:(NSEvent*)theEvent {
327   [self handleMouseEvent:theEvent];
330 - (void)rightMouseUp:(NSEvent*)theEvent {
331   [self handleMouseEvent:theEvent];
334 - (void)otherMouseUp:(NSEvent*)theEvent {
335   [self handleMouseEvent:theEvent];
338 - (void)mouseDragged:(NSEvent*)theEvent {
339   [self handleMouseEvent:theEvent];
342 - (void)rightMouseDragged:(NSEvent*)theEvent {
343   [self handleMouseEvent:theEvent];
346 - (void)otherMouseDragged:(NSEvent*)theEvent {
347   [self handleMouseEvent:theEvent];
350 - (void)mouseMoved:(NSEvent*)theEvent {
351   // Note: mouseEntered: and mouseExited: are not handled separately.
352   // |hostedView_| is responsible for converting the move events into entered
353   // and exited events for the view heirarchy.
354   [self handleMouseEvent:theEvent];
357 - (void)scrollWheel:(NSEvent*)theEvent {
358   if (!hostedView_)
359     return;
361   ui::MouseWheelEvent event(theEvent);
362   hostedView_->GetWidget()->OnMouseEvent(&event);
365 ////////////////////////////////////////////////////////////////////////////////
366 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
367 // 10.9 SDK). The list should eventually be complete. Anything not defined will
368 // beep when interpretKeyEvents: would otherwise call it.
369 // TODO(tapted): Make this list complete, except for insert* methods which are
370 // dispatched as regular key events in doCommandBySelector:.
372 // The insertText action message forwards to the TextInputClient unless a menu
373 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't
374 // direct insertText: through doCommandBySelector:, so this is still needed to
375 // handle the case when inputContext: is nil. When inputContext: returns non-nil
376 // text goes directly to insertText:replacementRange:.
377 - (void)insertText:(id)text {
378   [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)];
381 // Selection movement and scrolling.
383 - (void)moveRight:(id)sender {
384   [self handleAction:IDS_MOVE_RIGHT
385              keyCode:ui::VKEY_RIGHT
386              domCode:ui::DomCode::ARROW_RIGHT
387           eventFlags:0];
390 - (void)moveLeft:(id)sender {
391   [self handleAction:IDS_MOVE_LEFT
392              keyCode:ui::VKEY_LEFT
393              domCode:ui::DomCode::ARROW_LEFT
394           eventFlags:0];
397 - (void)moveUp:(id)sender {
398   [self handleAction:0
399              keyCode:ui::VKEY_UP
400              domCode:ui::DomCode::ARROW_UP
401           eventFlags:0];
404 - (void)moveDown:(id)sender {
405   [self handleAction:0
406              keyCode:ui::VKEY_DOWN
407              domCode:ui::DomCode::ARROW_DOWN
408           eventFlags:0];
411 - (void)moveWordRight:(id)sender {
412   [self handleAction:IDS_MOVE_WORD_RIGHT
413              keyCode:ui::VKEY_RIGHT
414              domCode:ui::DomCode::ARROW_RIGHT
415           eventFlags:ui::EF_CONTROL_DOWN];
418 - (void)moveWordLeft:(id)sender {
419   [self handleAction:IDS_MOVE_WORD_LEFT
420              keyCode:ui::VKEY_LEFT
421              domCode:ui::DomCode::ARROW_LEFT
422           eventFlags:ui::EF_CONTROL_DOWN];
425 - (void)moveLeftAndModifySelection:(id)sender {
426   [self handleAction:IDS_MOVE_LEFT_AND_MODIFY_SELECTION
427              keyCode:ui::VKEY_LEFT
428              domCode:ui::DomCode::ARROW_LEFT
429           eventFlags:ui::EF_SHIFT_DOWN];
432 - (void)moveRightAndModifySelection:(id)sender {
433   [self handleAction:IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
434              keyCode:ui::VKEY_RIGHT
435              domCode:ui::DomCode::ARROW_RIGHT
436           eventFlags:ui::EF_SHIFT_DOWN];
439 - (void)moveWordRightAndModifySelection:(id)sender {
440   [self handleAction:IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
441              keyCode:ui::VKEY_RIGHT
442              domCode:ui::DomCode::ARROW_RIGHT
443           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
446 - (void)moveWordLeftAndModifySelection:(id)sender {
447   [self handleAction:IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
448              keyCode:ui::VKEY_LEFT
449              domCode:ui::DomCode::ARROW_LEFT
450           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
453 - (void)moveToLeftEndOfLine:(id)sender {
454   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE
455              keyCode:ui::VKEY_HOME
456              domCode:ui::DomCode::HOME
457           eventFlags:0];
460 - (void)moveToRightEndOfLine:(id)sender {
461   [self handleAction:IDS_MOVE_TO_END_OF_LINE
462              keyCode:ui::VKEY_END
463              domCode:ui::DomCode::END
464           eventFlags:0];
467 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
468   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
469              keyCode:ui::VKEY_HOME
470              domCode:ui::DomCode::HOME
471           eventFlags:ui::EF_SHIFT_DOWN];
474 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
475   [self handleAction:IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
476              keyCode:ui::VKEY_END
477              domCode:ui::DomCode::END
478           eventFlags:ui::EF_SHIFT_DOWN];
481 // Deletions.
483 - (void)deleteForward:(id)sender {
484   [self handleAction:IDS_DELETE_FORWARD
485              keyCode:ui::VKEY_DELETE
486              domCode:ui::DomCode::DEL
487           eventFlags:0];
490 - (void)deleteBackward:(id)sender {
491   [self handleAction:IDS_DELETE_BACKWARD
492              keyCode:ui::VKEY_BACK
493              domCode:ui::DomCode::BACKSPACE
494           eventFlags:0];
497 - (void)deleteWordForward:(id)sender {
498   [self handleAction:IDS_DELETE_WORD_FORWARD
499              keyCode:ui::VKEY_DELETE
500              domCode:ui::DomCode::DEL
501           eventFlags:ui::EF_CONTROL_DOWN];
504 - (void)deleteWordBackward:(id)sender {
505   [self handleAction:IDS_DELETE_WORD_BACKWARD
506              keyCode:ui::VKEY_BACK
507              domCode:ui::DomCode::BACKSPACE
508           eventFlags:ui::EF_CONTROL_DOWN];
511 // Cancellation.
513 - (void)cancelOperation:(id)sender {
514   [self handleAction:0
515              keyCode:ui::VKEY_ESCAPE
516              domCode:ui::DomCode::ESCAPE
517           eventFlags:0];
520 // Support for Services in context menus.
521 // Currently we only support reading and writing plain strings.
522 - (id)validRequestorForSendType:(NSString*)sendType
523                      returnType:(NSString*)returnType {
524   BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
525                   [self selectedRange].length > 0;
526   BOOL canRead = [returnType isEqualToString:NSStringPboardType];
527   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
528   // or (string, string).
529   BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
530                                     (canRead && (canWrite || !sendType)));
531   return valid ? self : [super validRequestorForSendType:sendType
532                                               returnType:returnType];
535 // NSServicesRequests informal protocol.
537 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
538   DCHECK([types containsObject:NSStringPboardType]);
539   if (!textInputClient_)
540     return NO;
542   gfx::Range selectionRange;
543   if (!textInputClient_->GetSelectionRange(&selectionRange))
544     return NO;
546   base::string16 text;
547   textInputClient_->GetTextFromRange(selectionRange, &text);
548   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
551 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
552   NSArray* objects =
553       [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
554   DCHECK([objects count] == 1);
555   [self insertText:[objects lastObject]];
556   return YES;
559 // NSTextInputClient protocol implementation.
561 - (NSAttributedString*)
562     attributedSubstringForProposedRange:(NSRange)range
563                             actualRange:(NSRangePointer)actualRange {
564   base::string16 substring;
565   if (textInputClient_) {
566     gfx::Range textRange;
567     textInputClient_->GetTextRange(&textRange);
568     gfx::Range subrange = textRange.Intersect(gfx::Range(range));
569     textInputClient_->GetTextFromRange(subrange, &substring);
570     if (actualRange)
571       *actualRange = subrange.ToNSRange();
572   }
573   return [[[NSAttributedString alloc]
574       initWithString:base::SysUTF16ToNSString(substring)] autorelease];
577 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
578   NOTIMPLEMENTED();
579   return 0;
582 - (void)doCommandBySelector:(SEL)selector {
583   // Like the renderer, handle insert action messages as a regular key dispatch.
584   // This ensures, e.g., insertTab correctly changes focus between fields.
585   if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) {
586     [self handleKeyEvent:[NSApp currentEvent]];
587     return;
588   }
590   if ([self respondsToSelector:selector])
591     [self performSelector:selector withObject:nil];
592   else
593     [[self nextResponder] doCommandBySelector:selector];
596 - (NSRect)firstRectForCharacterRange:(NSRange)range
597                          actualRange:(NSRangePointer)actualRange {
598   NOTIMPLEMENTED();
599   return NSZeroRect;
602 - (BOOL)hasMarkedText {
603   return textInputClient_ && textInputClient_->HasCompositionText();
606 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
607   if (!hostedView_)
608     return;
610   if ([text isKindOfClass:[NSAttributedString class]])
611     text = [text string];
613   MenuController* menuController = MenuController::GetActiveInstance();
614   if (menuController && menuController->owner() == hostedView_->GetWidget()) {
615     // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single-
616     // characters and input from IME. For IME, swallow the entire string unless
617     // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT.
618     bool swallowedAny = false;
619     for (NSUInteger i = 0; i < [text length]; ++i) {
620       if (!menuController ||
621           menuController->OnWillDispatchKeyEvent([text characterAtIndex:i],
622                                                  ui::VKEY_UNKNOWN) ==
623               ui::POST_DISPATCH_PERFORM_DEFAULT) {
624         if (swallowedAny)
625           return;  // Swallow remainder.
626         break;
627       }
628       swallowedAny = true;
629       // Ensure the menu remains active.
630       menuController = MenuController::GetActiveInstance();
631     }
632   }
634   if (!textInputClient_)
635     return;
637   textInputClient_->DeleteRange(gfx::Range(replacementRange));
639   // If a single character is inserted by keyDown's call to interpretKeyEvents:
640   // then use InsertChar() to allow editing events to be merged. The second
641   // argument is the key modifier, which interpretKeyEvents: will have already
642   // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
643   // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
644   // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
645   if (inKeyDown_ && [text length] == 1)
646     textInputClient_->InsertChar([text characterAtIndex:0], 0);
647   else
648     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
651 - (NSRange)markedRange {
652   if (!textInputClient_)
653     return NSMakeRange(NSNotFound, 0);
655   gfx::Range range;
656   textInputClient_->GetCompositionTextRange(&range);
657   return range.ToNSRange();
660 - (NSRange)selectedRange {
661   if (!textInputClient_)
662     return NSMakeRange(NSNotFound, 0);
664   gfx::Range range;
665   textInputClient_->GetSelectionRange(&range);
666   return range.ToNSRange();
669 - (void)setMarkedText:(id)text
670         selectedRange:(NSRange)selectedRange
671      replacementRange:(NSRange)replacementRange {
672   if (!textInputClient_)
673     return;
675   if ([text isKindOfClass:[NSAttributedString class]])
676     text = [text string];
677   ui::CompositionText composition;
678   composition.text = base::SysNSStringToUTF16(text);
679   composition.selection = gfx::Range(selectedRange);
680   textInputClient_->SetCompositionText(composition);
683 - (void)unmarkText {
684   if (textInputClient_)
685     textInputClient_->ConfirmCompositionText();
688 - (NSArray*)validAttributesForMarkedText {
689   return @[];
692 // NSUserInterfaceValidations protocol implementation.
694 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
695   if (!textInputClient_)
696     return NO;
698   SEL action = [item action];
700   if (action == @selector(undo:))
701     return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
702   if (action == @selector(redo:))
703     return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
704   if (action == @selector(cut:))
705     return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
706   if (action == @selector(copy:))
707     return textInputClient_->IsEditCommandEnabled(IDS_APP_COPY);
708   if (action == @selector(paste:))
709     return textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE);
710   if (action == @selector(selectAll:))
711     return textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL);
713   return NO;
716 // NSAccessibility informal protocol implementation.
718 - (id)accessibilityAttributeValue:(NSString*)attribute {
719   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
720     return @[ hostedView_->GetNativeViewAccessible() ];
721   }
723   return [super accessibilityAttributeValue:attribute];
726 - (id)accessibilityHitTest:(NSPoint)point {
727   return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
730 @end