ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / ui / views / cocoa / bridged_content_view.mm
blob58d2972393fb4a50bdef72df2e69a4326d8cf4ca
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 #import "ui/events/cocoa/cocoa_event_utils.h"
12 #include "ui/events/keycodes/dom3/dom_code.h"
13 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
14 #include "ui/gfx/canvas_paint_mac.h"
15 #include "ui/gfx/geometry/rect.h"
16 #include "ui/strings/grit/ui_strings.h"
17 #include "ui/views/controls/menu/menu_controller.h"
18 #include "ui/views/ime/input_method.h"
19 #include "ui/views/view.h"
20 #include "ui/views/widget/widget.h"
22 using views::MenuController;
24 namespace {
26 // Convert a |point| in |source_window|'s AppKit coordinate system (origin at
27 // the bottom left of the window) to |target_window|'s content rect, with the
28 // origin at the top left of the content area.
29 // If |source_window| is nil, |point| will be treated as screen coordinates.
30 gfx::Point MovePointToWindow(const NSPoint& point,
31                              NSWindow* source_window,
32                              NSWindow* target_window) {
33   NSPoint point_in_screen = source_window
34       ? [source_window convertBaseToScreen:point]
35       : point;
37   NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen];
38   NSRect content_rect =
39       [target_window contentRectForFrameRect:[target_window frame]];
40   return gfx::Point(point_in_window.x,
41                     NSHeight(content_rect) - point_in_window.y);
44 // Checks if there's an active MenuController during key event dispatch. If
45 // there is one, it gets preference, and it will likely swallow the event.
46 bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) {
47   MenuController* menuController = MenuController::GetActiveInstance();
48   if (menuController && menuController->owner() == widget) {
49     if (menuController->OnWillDispatchKeyEvent(0, key_code) ==
50         ui::POST_DISPATCH_NONE)
51       return true;
52   }
53   return false;
56 }  // namespace
58 @interface BridgedContentView ()
60 // Translates the location of |theEvent| to toolkit-views coordinates and passes
61 // the event to NativeWidgetMac for handling.
62 - (void)handleMouseEvent:(NSEvent*)theEvent;
64 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes
65 // the event to the InputMethod for dispatch.
66 - (void)handleKeyEvent:(NSEvent*)theEvent;
68 // Handles an NSResponder Action Message by mapping it to a corresponding text
69 // editing command from ui_strings.grd and, when not being sent to a
70 // TextInputClient, the keyCode that toolkit-views expects internally.
71 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales
72 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
73 // This approach also allows action messages a user
74 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
75 // catered for.
76 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict
77 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do
78 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to
79 // get something readable.
80 - (void)handleAction:(int)commandId
81              keyCode:(ui::KeyboardCode)keyCode
82              domCode:(ui::DomCode)domCode
83           eventFlags:(int)eventFlags;
85 // Menu action handlers.
86 - (void)undo:(id)sender;
87 - (void)redo:(id)sender;
88 - (void)cut:(id)sender;
89 - (void)copy:(id)sender;
90 - (void)paste:(id)sender;
91 - (void)selectAll:(id)sender;
93 @end
95 @implementation BridgedContentView
97 @synthesize hostedView = hostedView_;
98 @synthesize textInputClient = textInputClient_;
100 - (id)initWithView:(views::View*)viewToHost {
101   DCHECK(viewToHost);
102   gfx::Rect bounds = viewToHost->bounds();
103   // To keep things simple, assume the origin is (0, 0) until there exists a use
104   // case for something other than that.
105   DCHECK(bounds.origin().IsOrigin());
106   NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
107   if ((self = [super initWithFrame:initialFrame])) {
108     hostedView_ = viewToHost;
110     // Apple's documentation says that NSTrackingActiveAlways is incompatible
111     // with NSTrackingCursorUpdate, so use NSTrackingActiveInActiveApp.
112     trackingArea_.reset([[CrTrackingArea alloc]
113         initWithRect:NSZeroRect
114              options:NSTrackingMouseMoved | NSTrackingCursorUpdate |
115                      NSTrackingActiveInActiveApp | NSTrackingInVisibleRect
116                owner:self
117             userInfo:nil]);
118     [self addTrackingArea:trackingArea_.get()];
119   }
120   return self;
123 - (void)clearView {
124   hostedView_ = NULL;
125   [trackingArea_.get() clearOwner];
126   [self removeTrackingArea:trackingArea_.get()];
129 - (void)processCapturedMouseEvent:(NSEvent*)theEvent {
130   if (!hostedView_)
131     return;
133   NSWindow* source = [theEvent window];
134   NSWindow* target = [self window];
135   DCHECK(target);
137   // If it's the view's window, process normally.
138   if ([target isEqual:source]) {
139     [self handleMouseEvent:theEvent];
140     return;
141   }
143   ui::MouseEvent event(theEvent);
144   event.set_location(
145       MovePointToWindow([theEvent locationInWindow], source, target));
146   hostedView_->GetWidget()->OnMouseEvent(&event);
149 // BridgedContentView private implementation.
151 - (void)handleMouseEvent:(NSEvent*)theEvent {
152   if (!hostedView_)
153     return;
155   ui::MouseEvent event(theEvent);
156   hostedView_->GetWidget()->OnMouseEvent(&event);
159 - (void)handleKeyEvent:(NSEvent*)theEvent {
160   if (!hostedView_)
161     return;
163   DCHECK(theEvent);
164   ui::KeyEvent event(theEvent);
165   if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code()))
166     return;
168   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
171 - (void)handleAction:(int)commandId
172              keyCode:(ui::KeyboardCode)keyCode
173              domCode:(ui::DomCode)domCode
174           eventFlags:(int)eventFlags {
175   if (!hostedView_)
176     return;
178   if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode))
179     return;
181   // If there's an active TextInputClient, schedule the editing command to be
182   // performed.
183   if (commandId && textInputClient_ &&
184       textInputClient_->IsEditCommandEnabled(commandId))
185     textInputClient_->SetEditCommandForNextKeyEvent(commandId);
187   // Generate a synthetic event with the keycode toolkit-views expects.
188   ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
189   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
192 - (void)undo:(id)sender {
193   // This DCHECK is more strict than a similar check in handleAction:. It can be
194   // done here because the actors sending these actions should be calling
195   // validateUserInterfaceItem: before enabling UI that allows these messages to
196   // be sent. Checking it here would be too late to provide correct UI feedback
197   // (e.g. there will be no "beep").
198   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
199   [self handleAction:IDS_APP_UNDO
200              keyCode:ui::VKEY_Z
201              domCode:ui::DomCode::KEY_Z
202           eventFlags:ui::EF_CONTROL_DOWN];
205 - (void)redo:(id)sender {
206   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
207   [self handleAction:IDS_APP_REDO
208              keyCode:ui::VKEY_Z
209              domCode:ui::DomCode::KEY_Z
210           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
213 - (void)cut:(id)sender {
214   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
215   [self handleAction:IDS_APP_CUT
216              keyCode:ui::VKEY_X
217              domCode:ui::DomCode::KEY_X
218           eventFlags:ui::EF_CONTROL_DOWN];
221 - (void)copy:(id)sender {
222   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_COPY));
223   [self handleAction:IDS_APP_COPY
224              keyCode:ui::VKEY_C
225              domCode:ui::DomCode::KEY_C
226           eventFlags:ui::EF_CONTROL_DOWN];
229 - (void)paste:(id)sender {
230   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE));
231   [self handleAction:IDS_APP_PASTE
232              keyCode:ui::VKEY_V
233              domCode:ui::DomCode::KEY_V
234           eventFlags:ui::EF_CONTROL_DOWN];
237 - (void)selectAll:(id)sender {
238   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL));
239   [self handleAction:IDS_APP_SELECT_ALL
240              keyCode:ui::VKEY_A
241              domCode:ui::DomCode::KEY_A
242           eventFlags:ui::EF_CONTROL_DOWN];
245 // NSView implementation.
247 - (BOOL)acceptsFirstResponder {
248   return YES;
251 - (void)setFrameSize:(NSSize)newSize {
252   [super setFrameSize:newSize];
253   if (!hostedView_)
254     return;
256   hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
259 - (void)drawRect:(NSRect)dirtyRect {
260   // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to
261   // suppress calls to this when the window is known to be hidden.
262   if (!hostedView_)
263     return;
265   // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
266   if (hostedView_->GetWidget()->GetLayer())
267     return;
269   gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
270   hostedView_->GetWidget()->OnNativeWidgetPaint(&canvas);
273 - (NSTextInputContext*)inputContext {
274   if (!hostedView_)
275     return [super inputContext];
277   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
278   // input context, return nil. This ensures the action message is sent to
279   // the view, rather than any NSTextInputClient a subview has installed.
280   MenuController* menuController = MenuController::GetActiveInstance();
281   if (menuController && menuController->owner() == hostedView_->GetWidget())
282     return nil;
284   return [super inputContext];
287 // NSResponder implementation.
289 - (void)keyDown:(NSEvent*)theEvent {
290   // Convert the event into an action message, according to OSX key mappings.
291   inKeyDown_ = YES;
292   [self interpretKeyEvents:@[ theEvent ]];
293   inKeyDown_ = NO;
296 - (void)mouseDown:(NSEvent*)theEvent {
297   [self handleMouseEvent:theEvent];
300 - (void)rightMouseDown:(NSEvent*)theEvent {
301   [self handleMouseEvent:theEvent];
304 - (void)otherMouseDown:(NSEvent*)theEvent {
305   [self handleMouseEvent:theEvent];
308 - (void)mouseUp:(NSEvent*)theEvent {
309   [self handleMouseEvent:theEvent];
312 - (void)rightMouseUp:(NSEvent*)theEvent {
313   [self handleMouseEvent:theEvent];
316 - (void)otherMouseUp:(NSEvent*)theEvent {
317   [self handleMouseEvent:theEvent];
320 - (void)mouseDragged:(NSEvent*)theEvent {
321   [self handleMouseEvent:theEvent];
324 - (void)rightMouseDragged:(NSEvent*)theEvent {
325   [self handleMouseEvent:theEvent];
328 - (void)otherMouseDragged:(NSEvent*)theEvent {
329   [self handleMouseEvent:theEvent];
332 - (void)mouseMoved:(NSEvent*)theEvent {
333   // Note: mouseEntered: and mouseExited: are not handled separately.
334   // |hostedView_| is responsible for converting the move events into entered
335   // and exited events for the view heirarchy.
336   [self handleMouseEvent:theEvent];
339 - (void)scrollWheel:(NSEvent*)theEvent {
340   if (!hostedView_)
341     return;
343   ui::MouseWheelEvent event(theEvent);
344   hostedView_->GetWidget()->OnMouseEvent(&event);
347 ////////////////////////////////////////////////////////////////////////////////
348 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
349 // 10.9 SDK). The list should eventually be complete. Anything not defined will
350 // beep when interpretKeyEvents: would otherwise call it.
351 // TODO(tapted): Make this list complete, except for insert* methods which are
352 // dispatched as regular key events in doCommandBySelector:.
354 // The insertText action message forwards to the TextInputClient unless a menu
355 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't
356 // direct insertText: through doCommandBySelector:, so this is still needed to
357 // handle the case when inputContext: is nil. When inputContext: returns non-nil
358 // text goes directly to insertText:replacementRange:.
359 - (void)insertText:(id)text {
360   [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)];
363 // Selection movement and scrolling.
365 - (void)moveRight:(id)sender {
366   [self handleAction:IDS_MOVE_RIGHT
367              keyCode:ui::VKEY_RIGHT
368              domCode:ui::DomCode::ARROW_RIGHT
369           eventFlags:0];
372 - (void)moveLeft:(id)sender {
373   [self handleAction:IDS_MOVE_LEFT
374              keyCode:ui::VKEY_LEFT
375              domCode:ui::DomCode::ARROW_LEFT
376           eventFlags:0];
379 - (void)moveUp:(id)sender {
380   [self handleAction:0
381              keyCode:ui::VKEY_UP
382              domCode:ui::DomCode::ARROW_UP
383           eventFlags:0];
386 - (void)moveDown:(id)sender {
387   [self handleAction:0
388              keyCode:ui::VKEY_DOWN
389              domCode:ui::DomCode::ARROW_DOWN
390           eventFlags:0];
393 - (void)moveWordRight:(id)sender {
394   [self handleAction:IDS_MOVE_WORD_RIGHT
395              keyCode:ui::VKEY_RIGHT
396              domCode:ui::DomCode::ARROW_RIGHT
397           eventFlags:ui::EF_CONTROL_DOWN];
400 - (void)moveWordLeft:(id)sender {
401   [self handleAction:IDS_MOVE_WORD_LEFT
402              keyCode:ui::VKEY_LEFT
403              domCode:ui::DomCode::ARROW_LEFT
404           eventFlags:ui::EF_CONTROL_DOWN];
407 - (void)moveLeftAndModifySelection:(id)sender {
408   [self handleAction:IDS_MOVE_LEFT_AND_MODIFY_SELECTION
409              keyCode:ui::VKEY_LEFT
410              domCode:ui::DomCode::ARROW_LEFT
411           eventFlags:ui::EF_SHIFT_DOWN];
414 - (void)moveRightAndModifySelection:(id)sender {
415   [self handleAction:IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
416              keyCode:ui::VKEY_RIGHT
417              domCode:ui::DomCode::ARROW_RIGHT
418           eventFlags:ui::EF_SHIFT_DOWN];
421 - (void)moveWordRightAndModifySelection:(id)sender {
422   [self handleAction:IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
423              keyCode:ui::VKEY_RIGHT
424              domCode:ui::DomCode::ARROW_RIGHT
425           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
428 - (void)moveWordLeftAndModifySelection:(id)sender {
429   [self handleAction:IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
430              keyCode:ui::VKEY_LEFT
431              domCode:ui::DomCode::ARROW_LEFT
432           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
435 - (void)moveToLeftEndOfLine:(id)sender {
436   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE
437              keyCode:ui::VKEY_HOME
438              domCode:ui::DomCode::HOME
439           eventFlags:0];
442 - (void)moveToRightEndOfLine:(id)sender {
443   [self handleAction:IDS_MOVE_TO_END_OF_LINE
444              keyCode:ui::VKEY_END
445              domCode:ui::DomCode::END
446           eventFlags:0];
449 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
450   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
451              keyCode:ui::VKEY_HOME
452              domCode:ui::DomCode::HOME
453           eventFlags:ui::EF_SHIFT_DOWN];
456 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
457   [self handleAction:IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
458              keyCode:ui::VKEY_END
459              domCode:ui::DomCode::END
460           eventFlags:ui::EF_SHIFT_DOWN];
463 // Deletions.
465 - (void)deleteForward:(id)sender {
466   [self handleAction:IDS_DELETE_FORWARD
467              keyCode:ui::VKEY_DELETE
468              domCode:ui::DomCode::DEL
469           eventFlags:0];
472 - (void)deleteBackward:(id)sender {
473   [self handleAction:IDS_DELETE_BACKWARD
474              keyCode:ui::VKEY_BACK
475              domCode:ui::DomCode::BACKSPACE
476           eventFlags:0];
479 - (void)deleteWordForward:(id)sender {
480   [self handleAction:IDS_DELETE_WORD_FORWARD
481              keyCode:ui::VKEY_DELETE
482              domCode:ui::DomCode::DEL
483           eventFlags:ui::EF_CONTROL_DOWN];
486 - (void)deleteWordBackward:(id)sender {
487   [self handleAction:IDS_DELETE_WORD_BACKWARD
488              keyCode:ui::VKEY_BACK
489              domCode:ui::DomCode::BACKSPACE
490           eventFlags:ui::EF_CONTROL_DOWN];
493 // Cancellation.
495 - (void)cancelOperation:(id)sender {
496   [self handleAction:0
497              keyCode:ui::VKEY_ESCAPE
498              domCode:ui::DomCode::ESCAPE
499           eventFlags:0];
502 // Support for Services in context menus.
503 // Currently we only support reading and writing plain strings.
504 - (id)validRequestorForSendType:(NSString*)sendType
505                      returnType:(NSString*)returnType {
506   BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
507                   [self selectedRange].length > 0;
508   BOOL canRead = [returnType isEqualToString:NSStringPboardType];
509   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
510   // or (string, string).
511   BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
512                                     (canRead && (canWrite || !sendType)));
513   return valid ? self : [super validRequestorForSendType:sendType
514                                               returnType:returnType];
517 // NSServicesRequests informal protocol.
519 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
520   DCHECK([types containsObject:NSStringPboardType]);
521   if (!textInputClient_)
522     return NO;
524   gfx::Range selectionRange;
525   if (!textInputClient_->GetSelectionRange(&selectionRange))
526     return NO;
528   base::string16 text;
529   textInputClient_->GetTextFromRange(selectionRange, &text);
530   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
533 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
534   NSArray* objects =
535       [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
536   DCHECK([objects count] == 1);
537   [self insertText:[objects lastObject]];
538   return YES;
541 // NSTextInputClient protocol implementation.
543 - (NSAttributedString*)
544     attributedSubstringForProposedRange:(NSRange)range
545                             actualRange:(NSRangePointer)actualRange {
546   base::string16 substring;
547   if (textInputClient_) {
548     gfx::Range textRange;
549     textInputClient_->GetTextRange(&textRange);
550     gfx::Range subrange = textRange.Intersect(gfx::Range(range));
551     textInputClient_->GetTextFromRange(subrange, &substring);
552     if (actualRange)
553       *actualRange = subrange.ToNSRange();
554   }
555   return [[[NSAttributedString alloc]
556       initWithString:base::SysUTF16ToNSString(substring)] autorelease];
559 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
560   NOTIMPLEMENTED();
561   return 0;
564 - (void)doCommandBySelector:(SEL)selector {
565   // Like the renderer, handle insert action messages as a regular key dispatch.
566   // This ensures, e.g., insertTab correctly changes focus between fields.
567   if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) {
568     [self handleKeyEvent:[NSApp currentEvent]];
569     return;
570   }
572   if ([self respondsToSelector:selector])
573     [self performSelector:selector withObject:nil];
574   else
575     [[self nextResponder] doCommandBySelector:selector];
578 - (NSRect)firstRectForCharacterRange:(NSRange)range
579                          actualRange:(NSRangePointer)actualRange {
580   NOTIMPLEMENTED();
581   return NSZeroRect;
584 - (BOOL)hasMarkedText {
585   return textInputClient_ && textInputClient_->HasCompositionText();
588 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
589   if (!hostedView_)
590     return;
592   if ([text isKindOfClass:[NSAttributedString class]])
593     text = [text string];
595   MenuController* menuController = MenuController::GetActiveInstance();
596   if (menuController && menuController->owner() == hostedView_->GetWidget()) {
597     // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single-
598     // characters and input from IME. For IME, swallow the entire string unless
599     // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT.
600     bool swallowedAny = false;
601     for (NSUInteger i = 0; i < [text length]; ++i) {
602       if (!menuController ||
603           menuController->OnWillDispatchKeyEvent([text characterAtIndex:i],
604                                                  ui::VKEY_UNKNOWN) ==
605               ui::POST_DISPATCH_PERFORM_DEFAULT) {
606         if (swallowedAny)
607           return;  // Swallow remainder.
608         break;
609       }
610       swallowedAny = true;
611       // Ensure the menu remains active.
612       menuController = MenuController::GetActiveInstance();
613     }
614   }
616   if (!textInputClient_)
617     return;
619   textInputClient_->DeleteRange(gfx::Range(replacementRange));
621   // If a single character is inserted by keyDown's call to interpretKeyEvents:
622   // then use InsertChar() to allow editing events to be merged. The second
623   // argument is the key modifier, which interpretKeyEvents: will have already
624   // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
625   // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
626   // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
627   if (inKeyDown_ && [text length] == 1)
628     textInputClient_->InsertChar([text characterAtIndex:0], 0);
629   else
630     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
633 - (NSRange)markedRange {
634   if (!textInputClient_)
635     return NSMakeRange(NSNotFound, 0);
637   gfx::Range range;
638   textInputClient_->GetCompositionTextRange(&range);
639   return range.ToNSRange();
642 - (NSRange)selectedRange {
643   if (!textInputClient_)
644     return NSMakeRange(NSNotFound, 0);
646   gfx::Range range;
647   textInputClient_->GetSelectionRange(&range);
648   return range.ToNSRange();
651 - (void)setMarkedText:(id)text
652         selectedRange:(NSRange)selectedRange
653      replacementRange:(NSRange)replacementRange {
654   if (!textInputClient_)
655     return;
657   if ([text isKindOfClass:[NSAttributedString class]])
658     text = [text string];
659   ui::CompositionText composition;
660   composition.text = base::SysNSStringToUTF16(text);
661   composition.selection = gfx::Range(selectedRange);
662   textInputClient_->SetCompositionText(composition);
665 - (void)unmarkText {
666   if (textInputClient_)
667     textInputClient_->ConfirmCompositionText();
670 - (NSArray*)validAttributesForMarkedText {
671   return @[];
674 // NSUserInterfaceValidations protocol implementation.
676 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
677   if (!textInputClient_)
678     return NO;
680   SEL action = [item action];
682   if (action == @selector(undo:))
683     return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
684   if (action == @selector(redo:))
685     return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
686   if (action == @selector(cut:))
687     return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
688   if (action == @selector(copy:))
689     return textInputClient_->IsEditCommandEnabled(IDS_APP_COPY);
690   if (action == @selector(paste:))
691     return textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE);
692   if (action == @selector(selectAll:))
693     return textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL);
695   return NO;
698 // NSAccessibility informal protocol implementation.
700 - (id)accessibilityAttributeValue:(NSString*)attribute {
701   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
702     return @[ hostedView_->GetNativeViewAccessible() ];
703   }
705   return [super accessibilityAttributeValue:attribute];
708 - (id)accessibilityHitTest:(NSPoint)point {
709   return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
712 @end