Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ui / views / cocoa / bridged_content_view.mm
blobfa0731c8ae3ffcbc017d794080f911c56297c6d2
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/dom/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 keycodes and modifiers on |theEvent| to ui::KeyEvents and passes
62 // the event to the InputMethod for dispatch.
63 - (void)handleKeyEvent:(NSEvent*)theEvent;
65 // Handles an NSResponder Action Message by mapping it to a corresponding text
66 // editing command from ui_strings.grd and, when not being sent to a
67 // TextInputClient, the keyCode that toolkit-views expects internally.
68 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales
69 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
70 // This approach also allows action messages a user
71 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
72 // catered for.
73 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict
74 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do
75 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to
76 // get something readable.
77 - (void)handleAction:(int)commandId
78              keyCode:(ui::KeyboardCode)keyCode
79              domCode:(ui::DomCode)domCode
80           eventFlags:(int)eventFlags;
82 // Menu action handlers.
83 - (void)undo:(id)sender;
84 - (void)redo:(id)sender;
85 - (void)cut:(id)sender;
86 - (void)copy:(id)sender;
87 - (void)paste:(id)sender;
88 - (void)selectAll:(id)sender;
90 @end
92 @implementation BridgedContentView
94 @synthesize hostedView = hostedView_;
95 @synthesize textInputClient = textInputClient_;
97 - (id)initWithView:(views::View*)viewToHost {
98   DCHECK(viewToHost);
99   gfx::Rect bounds = viewToHost->bounds();
100   // To keep things simple, assume the origin is (0, 0) until there exists a use
101   // case for something other than that.
102   DCHECK(bounds.origin().IsOrigin());
103   NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
104   if ((self = [super initWithFrame:initialFrame])) {
105     hostedView_ = viewToHost;
107     // Apple's documentation says that NSTrackingActiveAlways is incompatible
108     // with NSTrackingCursorUpdate, so use NSTrackingActiveInActiveApp.
109     cursorTrackingArea_.reset([[CrTrackingArea alloc]
110         initWithRect:NSZeroRect
111              options:NSTrackingMouseMoved | NSTrackingCursorUpdate |
112                      NSTrackingActiveInActiveApp | NSTrackingInVisibleRect
113                owner:self
114             userInfo:nil]);
115     [self addTrackingArea:cursorTrackingArea_.get()];
116   }
117   return self;
120 - (void)clearView {
121   hostedView_ = NULL;
122   [cursorTrackingArea_.get() clearOwner];
123   [self removeTrackingArea:cursorTrackingArea_.get()];
126 - (void)processCapturedMouseEvent:(NSEvent*)theEvent {
127   if (!hostedView_)
128     return;
130   NSWindow* source = [theEvent window];
131   NSWindow* target = [self window];
132   DCHECK(target);
134   // If it's the view's window, process normally.
135   if ([target isEqual:source]) {
136     [self mouseEvent:theEvent];
137     return;
138   }
140   ui::MouseEvent event(theEvent);
141   event.set_location(
142       MovePointToWindow([theEvent locationInWindow], source, target));
143   hostedView_->GetWidget()->OnMouseEvent(&event);
146 - (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent {
147   DCHECK(hostedView_);
148   base::string16 newTooltipText;
150   views::View* view = hostedView_->GetTooltipHandlerForPoint(locationInContent);
151   if (view) {
152     gfx::Point viewPoint = locationInContent;
153     views::View::ConvertPointToTarget(hostedView_, view, &viewPoint);
154     if (!view->GetTooltipText(viewPoint, &newTooltipText))
155       DCHECK(newTooltipText.empty());
156   }
157   if (newTooltipText != lastTooltipText_) {
158     std::swap(newTooltipText, lastTooltipText_);
159     [self setToolTipAtMousePoint:base::SysUTF16ToNSString(lastTooltipText_)];
160   }
163 // BridgedContentView private implementation.
165 - (void)handleKeyEvent:(NSEvent*)theEvent {
166   if (!hostedView_)
167     return;
169   DCHECK(theEvent);
170   ui::KeyEvent event(theEvent);
171   if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code()))
172     return;
174   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
177 - (void)handleAction:(int)commandId
178              keyCode:(ui::KeyboardCode)keyCode
179              domCode:(ui::DomCode)domCode
180           eventFlags:(int)eventFlags {
181   if (!hostedView_)
182     return;
184   if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode))
185     return;
187   // If there's an active TextInputClient, schedule the editing command to be
188   // performed.
189   if (commandId && textInputClient_ &&
190       textInputClient_->IsEditCommandEnabled(commandId))
191     textInputClient_->SetEditCommandForNextKeyEvent(commandId);
193   // Generate a synthetic event with the keycode toolkit-views expects.
194   ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
195   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
198 - (void)undo:(id)sender {
199   // This DCHECK is more strict than a similar check in handleAction:. It can be
200   // done here because the actors sending these actions should be calling
201   // validateUserInterfaceItem: before enabling UI that allows these messages to
202   // be sent. Checking it here would be too late to provide correct UI feedback
203   // (e.g. there will be no "beep").
204   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
205   [self handleAction:IDS_APP_UNDO
206              keyCode:ui::VKEY_Z
207              domCode:ui::DomCode::KEY_Z
208           eventFlags:ui::EF_CONTROL_DOWN];
211 - (void)redo:(id)sender {
212   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
213   [self handleAction:IDS_APP_REDO
214              keyCode:ui::VKEY_Z
215              domCode:ui::DomCode::KEY_Z
216           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
219 - (void)cut:(id)sender {
220   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
221   [self handleAction:IDS_APP_CUT
222              keyCode:ui::VKEY_X
223              domCode:ui::DomCode::KEY_X
224           eventFlags:ui::EF_CONTROL_DOWN];
227 - (void)copy:(id)sender {
228   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_COPY));
229   [self handleAction:IDS_APP_COPY
230              keyCode:ui::VKEY_C
231              domCode:ui::DomCode::KEY_C
232           eventFlags:ui::EF_CONTROL_DOWN];
235 - (void)paste:(id)sender {
236   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE));
237   [self handleAction:IDS_APP_PASTE
238              keyCode:ui::VKEY_V
239              domCode:ui::DomCode::KEY_V
240           eventFlags:ui::EF_CONTROL_DOWN];
243 - (void)selectAll:(id)sender {
244   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL));
245   [self handleAction:IDS_APP_SELECT_ALL
246              keyCode:ui::VKEY_A
247              domCode:ui::DomCode::KEY_A
248           eventFlags:ui::EF_CONTROL_DOWN];
251 // BaseView implementation.
253 // Don't use tracking areas from BaseView. BridgedContentView's tracks
254 // NSTrackingCursorUpdate and Apple's documentation suggests it's incompatible.
255 - (void)enableTracking {
258 // Translates the location of |theEvent| to toolkit-views coordinates and passes
259 // the event to NativeWidgetMac for handling.
260 - (void)mouseEvent:(NSEvent*)theEvent {
261   if (!hostedView_)
262     return;
264   ui::MouseEvent event(theEvent);
266   // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
267   // Mac hooks in here.
268   [self updateTooltipIfRequiredAt:event.location()];
270   hostedView_->GetWidget()->OnMouseEvent(&event);
273 // NSView implementation.
275 - (BOOL)acceptsFirstResponder {
276   return YES;
279 - (void)viewDidMoveToWindow {
280   // When this view is added to a window, AppKit calls setFrameSize before it is
281   // added to the window, so the behavior in setFrameSize is not triggered.
282   NSWindow* window = [self window];
283   if (window)
284     [self setFrameSize:NSZeroSize];
287 - (void)setFrameSize:(NSSize)newSize {
288   // The size passed in here does not always use
289   // -[NSWindow contentRectForFrameRect]. The following ensures that the
290   // contentView for a frameless window can extend over the titlebar of the new
291   // window containing it, since AppKit requires a titlebar to give frameless
292   // windows correct shadows and rounded corners.
293   NSWindow* window = [self window];
294   if (window)
295     newSize = [window contentRectForFrameRect:[window frame]].size;
297   [super setFrameSize:newSize];
298   if (!hostedView_)
299     return;
301   hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
304 - (void)drawRect:(NSRect)dirtyRect {
305   // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to
306   // suppress calls to this when the window is known to be hidden.
307   if (!hostedView_)
308     return;
310   // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
311   if (hostedView_->GetWidget()->GetLayer())
312     return;
314   gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
315   hostedView_->GetWidget()->OnNativeWidgetPaint(ui::PaintContext(&canvas));
318 - (NSTextInputContext*)inputContext {
319   if (!hostedView_)
320     return [super inputContext];
322   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
323   // input context, return nil. This ensures the action message is sent to
324   // the view, rather than any NSTextInputClient a subview has installed.
325   MenuController* menuController = MenuController::GetActiveInstance();
326   if (menuController && menuController->owner() == hostedView_->GetWidget())
327     return nil;
329   return [super inputContext];
332 // NSResponder implementation.
334 - (void)keyDown:(NSEvent*)theEvent {
335   // Convert the event into an action message, according to OSX key mappings.
336   inKeyDown_ = YES;
337   [self interpretKeyEvents:@[ theEvent ]];
338   inKeyDown_ = NO;
341 - (void)scrollWheel:(NSEvent*)theEvent {
342   if (!hostedView_)
343     return;
345   ui::MouseWheelEvent event(theEvent);
346   hostedView_->GetWidget()->OnMouseEvent(&event);
349 ////////////////////////////////////////////////////////////////////////////////
350 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
351 // 10.9 SDK). The list should eventually be complete. Anything not defined will
352 // beep when interpretKeyEvents: would otherwise call it.
353 // TODO(tapted): Make this list complete, except for insert* methods which are
354 // dispatched as regular key events in doCommandBySelector:.
356 // The insertText action message forwards to the TextInputClient unless a menu
357 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't
358 // direct insertText: through doCommandBySelector:, so this is still needed to
359 // handle the case when inputContext: is nil. When inputContext: returns non-nil
360 // text goes directly to insertText:replacementRange:.
361 - (void)insertText:(id)text {
362   [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)];
365 // Selection movement and scrolling.
367 - (void)moveRight:(id)sender {
368   [self handleAction:IDS_MOVE_RIGHT
369              keyCode:ui::VKEY_RIGHT
370              domCode:ui::DomCode::ARROW_RIGHT
371           eventFlags:0];
374 - (void)moveLeft:(id)sender {
375   [self handleAction:IDS_MOVE_LEFT
376              keyCode:ui::VKEY_LEFT
377              domCode:ui::DomCode::ARROW_LEFT
378           eventFlags:0];
381 - (void)moveUp:(id)sender {
382   [self handleAction:0
383              keyCode:ui::VKEY_UP
384              domCode:ui::DomCode::ARROW_UP
385           eventFlags:0];
388 - (void)moveDown:(id)sender {
389   [self handleAction:0
390              keyCode:ui::VKEY_DOWN
391              domCode:ui::DomCode::ARROW_DOWN
392           eventFlags:0];
395 - (void)moveWordRight:(id)sender {
396   [self handleAction:IDS_MOVE_WORD_RIGHT
397              keyCode:ui::VKEY_RIGHT
398              domCode:ui::DomCode::ARROW_RIGHT
399           eventFlags:ui::EF_CONTROL_DOWN];
402 - (void)moveWordLeft:(id)sender {
403   [self handleAction:IDS_MOVE_WORD_LEFT
404              keyCode:ui::VKEY_LEFT
405              domCode:ui::DomCode::ARROW_LEFT
406           eventFlags:ui::EF_CONTROL_DOWN];
409 - (void)moveLeftAndModifySelection:(id)sender {
410   [self handleAction:IDS_MOVE_LEFT_AND_MODIFY_SELECTION
411              keyCode:ui::VKEY_LEFT
412              domCode:ui::DomCode::ARROW_LEFT
413           eventFlags:ui::EF_SHIFT_DOWN];
416 - (void)moveRightAndModifySelection:(id)sender {
417   [self handleAction:IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
418              keyCode:ui::VKEY_RIGHT
419              domCode:ui::DomCode::ARROW_RIGHT
420           eventFlags:ui::EF_SHIFT_DOWN];
423 - (void)moveWordRightAndModifySelection:(id)sender {
424   [self handleAction:IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
425              keyCode:ui::VKEY_RIGHT
426              domCode:ui::DomCode::ARROW_RIGHT
427           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
430 - (void)moveWordLeftAndModifySelection:(id)sender {
431   [self handleAction:IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
432              keyCode:ui::VKEY_LEFT
433              domCode:ui::DomCode::ARROW_LEFT
434           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
437 - (void)moveToLeftEndOfLine:(id)sender {
438   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE
439              keyCode:ui::VKEY_HOME
440              domCode:ui::DomCode::HOME
441           eventFlags:0];
444 - (void)moveToRightEndOfLine:(id)sender {
445   [self handleAction:IDS_MOVE_TO_END_OF_LINE
446              keyCode:ui::VKEY_END
447              domCode:ui::DomCode::END
448           eventFlags:0];
451 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
452   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
453              keyCode:ui::VKEY_HOME
454              domCode:ui::DomCode::HOME
455           eventFlags:ui::EF_SHIFT_DOWN];
458 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
459   [self handleAction:IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
460              keyCode:ui::VKEY_END
461              domCode:ui::DomCode::END
462           eventFlags:ui::EF_SHIFT_DOWN];
465 // Deletions.
467 - (void)deleteForward:(id)sender {
468   [self handleAction:IDS_DELETE_FORWARD
469              keyCode:ui::VKEY_DELETE
470              domCode:ui::DomCode::DEL
471           eventFlags:0];
474 - (void)deleteBackward:(id)sender {
475   [self handleAction:IDS_DELETE_BACKWARD
476              keyCode:ui::VKEY_BACK
477              domCode:ui::DomCode::BACKSPACE
478           eventFlags:0];
481 - (void)deleteWordForward:(id)sender {
482   [self handleAction:IDS_DELETE_WORD_FORWARD
483              keyCode:ui::VKEY_DELETE
484              domCode:ui::DomCode::DEL
485           eventFlags:ui::EF_CONTROL_DOWN];
488 - (void)deleteWordBackward:(id)sender {
489   [self handleAction:IDS_DELETE_WORD_BACKWARD
490              keyCode:ui::VKEY_BACK
491              domCode:ui::DomCode::BACKSPACE
492           eventFlags:ui::EF_CONTROL_DOWN];
495 // Cancellation.
497 - (void)cancelOperation:(id)sender {
498   [self handleAction:0
499              keyCode:ui::VKEY_ESCAPE
500              domCode:ui::DomCode::ESCAPE
501           eventFlags:0];
504 // Support for Services in context menus.
505 // Currently we only support reading and writing plain strings.
506 - (id)validRequestorForSendType:(NSString*)sendType
507                      returnType:(NSString*)returnType {
508   BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
509                   [self selectedRange].length > 0;
510   BOOL canRead = [returnType isEqualToString:NSStringPboardType];
511   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
512   // or (string, string).
513   BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
514                                     (canRead && (canWrite || !sendType)));
515   return valid ? self : [super validRequestorForSendType:sendType
516                                               returnType:returnType];
519 // NSServicesRequests informal protocol.
521 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
522   DCHECK([types containsObject:NSStringPboardType]);
523   if (!textInputClient_)
524     return NO;
526   gfx::Range selectionRange;
527   if (!textInputClient_->GetSelectionRange(&selectionRange))
528     return NO;
530   base::string16 text;
531   textInputClient_->GetTextFromRange(selectionRange, &text);
532   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
535 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
536   NSArray* objects =
537       [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
538   DCHECK([objects count] == 1);
539   [self insertText:[objects lastObject]];
540   return YES;
543 // NSTextInputClient protocol implementation.
545 - (NSAttributedString*)
546     attributedSubstringForProposedRange:(NSRange)range
547                             actualRange:(NSRangePointer)actualRange {
548   base::string16 substring;
549   if (textInputClient_) {
550     gfx::Range textRange;
551     textInputClient_->GetTextRange(&textRange);
552     gfx::Range subrange = textRange.Intersect(gfx::Range(range));
553     textInputClient_->GetTextFromRange(subrange, &substring);
554     if (actualRange)
555       *actualRange = subrange.ToNSRange();
556   }
557   return [[[NSAttributedString alloc]
558       initWithString:base::SysUTF16ToNSString(substring)] autorelease];
561 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
562   NOTIMPLEMENTED();
563   return 0;
566 - (void)doCommandBySelector:(SEL)selector {
567   // Like the renderer, handle insert action messages as a regular key dispatch.
568   // This ensures, e.g., insertTab correctly changes focus between fields.
569   if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) {
570     [self handleKeyEvent:[NSApp currentEvent]];
571     return;
572   }
574   if ([self respondsToSelector:selector])
575     [self performSelector:selector withObject:nil];
576   else
577     [[self nextResponder] doCommandBySelector:selector];
580 - (NSRect)firstRectForCharacterRange:(NSRange)range
581                          actualRange:(NSRangePointer)actualRange {
582   NOTIMPLEMENTED();
583   return NSZeroRect;
586 - (BOOL)hasMarkedText {
587   return textInputClient_ && textInputClient_->HasCompositionText();
590 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
591   if (!hostedView_)
592     return;
594   if ([text isKindOfClass:[NSAttributedString class]])
595     text = [text string];
597   MenuController* menuController = MenuController::GetActiveInstance();
598   if (menuController && menuController->owner() == hostedView_->GetWidget()) {
599     // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single-
600     // characters and input from IME. For IME, swallow the entire string unless
601     // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT.
602     bool swallowedAny = false;
603     for (NSUInteger i = 0; i < [text length]; ++i) {
604       if (!menuController ||
605           menuController->OnWillDispatchKeyEvent([text characterAtIndex:i],
606                                                  ui::VKEY_UNKNOWN) ==
607               ui::POST_DISPATCH_PERFORM_DEFAULT) {
608         if (swallowedAny)
609           return;  // Swallow remainder.
610         break;
611       }
612       swallowedAny = true;
613       // Ensure the menu remains active.
614       menuController = MenuController::GetActiveInstance();
615     }
616   }
618   if (!textInputClient_)
619     return;
621   textInputClient_->DeleteRange(gfx::Range(replacementRange));
623   // If a single character is inserted by keyDown's call to interpretKeyEvents:
624   // then use InsertChar() to allow editing events to be merged. The second
625   // argument is the key modifier, which interpretKeyEvents: will have already
626   // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
627   // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
628   // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
629   if (inKeyDown_ && [text length] == 1)
630     textInputClient_->InsertChar([text characterAtIndex:0], 0);
631   else
632     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
635 - (NSRange)markedRange {
636   if (!textInputClient_)
637     return NSMakeRange(NSNotFound, 0);
639   gfx::Range range;
640   textInputClient_->GetCompositionTextRange(&range);
641   return range.ToNSRange();
644 - (NSRange)selectedRange {
645   if (!textInputClient_)
646     return NSMakeRange(NSNotFound, 0);
648   gfx::Range range;
649   textInputClient_->GetSelectionRange(&range);
650   return range.ToNSRange();
653 - (void)setMarkedText:(id)text
654         selectedRange:(NSRange)selectedRange
655      replacementRange:(NSRange)replacementRange {
656   if (!textInputClient_)
657     return;
659   if ([text isKindOfClass:[NSAttributedString class]])
660     text = [text string];
661   ui::CompositionText composition;
662   composition.text = base::SysNSStringToUTF16(text);
663   composition.selection = gfx::Range(selectedRange);
664   textInputClient_->SetCompositionText(composition);
667 - (void)unmarkText {
668   if (textInputClient_)
669     textInputClient_->ConfirmCompositionText();
672 - (NSArray*)validAttributesForMarkedText {
673   return @[];
676 // NSUserInterfaceValidations protocol implementation.
678 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
679   if (!textInputClient_)
680     return NO;
682   SEL action = [item action];
684   if (action == @selector(undo:))
685     return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
686   if (action == @selector(redo:))
687     return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
688   if (action == @selector(cut:))
689     return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
690   if (action == @selector(copy:))
691     return textInputClient_->IsEditCommandEnabled(IDS_APP_COPY);
692   if (action == @selector(paste:))
693     return textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE);
694   if (action == @selector(selectAll:))
695     return textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL);
697   return NO;
700 // NSAccessibility informal protocol implementation.
702 - (id)accessibilityAttributeValue:(NSString*)attribute {
703   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
704     return @[ hostedView_->GetNativeViewAccessible() ];
705   }
707   return [super accessibilityAttributeValue:attribute];
710 - (id)accessibilityHitTest:(NSPoint)point {
711   return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
714 @end