Automated Commit: Committing new LKGM version 7479.0.0 for chromeos.
[chromium-blink-merge.git] / ui / views / cocoa / bridged_content_view.mm
blob43a95a873bec0735900f429cd1f7069a307ea6ed
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 "skia/ext/skia_utils_mac.h"
11 #include "ui/base/ime/input_method.h"
12 #include "ui/base/ime/text_input_client.h"
13 #include "ui/compositor/canvas_painter.h"
14 #import "ui/events/cocoa/cocoa_event_utils.h"
15 #include "ui/events/keycodes/dom/dom_code.h"
16 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
17 #include "ui/gfx/canvas_paint_mac.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/strings/grit/ui_strings.h"
20 #include "ui/views/controls/menu/menu_config.h"
21 #include "ui/views/controls/menu/menu_controller.h"
22 #include "ui/views/view.h"
23 #include "ui/views/widget/widget.h"
25 using views::MenuController;
27 namespace {
29 // Convert a |point| in |source_window|'s AppKit coordinate system (origin at
30 // the bottom left of the window) to |target_window|'s content rect, with the
31 // origin at the top left of the content area.
32 // If |source_window| is nil, |point| will be treated as screen coordinates.
33 gfx::Point MovePointToWindow(const NSPoint& point,
34                              NSWindow* source_window,
35                              NSWindow* target_window) {
36   NSPoint point_in_screen = source_window
37       ? [source_window convertBaseToScreen:point]
38       : point;
40   NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen];
41   NSRect content_rect =
42       [target_window contentRectForFrameRect:[target_window frame]];
43   return gfx::Point(point_in_window.x,
44                     NSHeight(content_rect) - point_in_window.y);
47 // Checks if there's an active MenuController during key event dispatch. If
48 // there is one, it gets preference, and it will likely swallow the event.
49 bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) {
50   MenuController* menuController = MenuController::GetActiveInstance();
51   if (menuController && menuController->owner() == widget) {
52     if (menuController->OnWillDispatchKeyEvent(0, key_code) ==
53         ui::POST_DISPATCH_NONE)
54       return true;
55   }
56   return false;
59 }  // namespace
61 @interface BridgedContentView ()
63 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes
64 // the event to the InputMethod for dispatch.
65 - (void)handleKeyEvent:(NSEvent*)theEvent;
67 // Handles an NSResponder Action Message by mapping it to a corresponding text
68 // editing command from ui_strings.grd and, when not being sent to a
69 // TextInputClient, the keyCode that toolkit-views expects internally.
70 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales
71 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
72 // This approach also allows action messages a user
73 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
74 // catered for.
75 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict
76 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do
77 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to
78 // get something readable.
79 - (void)handleAction:(int)commandId
80              keyCode:(ui::KeyboardCode)keyCode
81              domCode:(ui::DomCode)domCode
82           eventFlags:(int)eventFlags;
84 // Menu action handlers.
85 - (void)undo:(id)sender;
86 - (void)redo:(id)sender;
87 - (void)cut:(id)sender;
88 - (void)copy:(id)sender;
89 - (void)paste:(id)sender;
90 - (void)selectAll:(id)sender;
92 @end
94 @implementation BridgedContentView
96 @synthesize hostedView = hostedView_;
97 @synthesize textInputClient = textInputClient_;
98 @synthesize drawMenuBackgroundForBlur = drawMenuBackgroundForBlur_;
99 @synthesize mouseDownCanMoveWindow = mouseDownCanMoveWindow_;
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     cursorTrackingArea_.reset([[CrTrackingArea alloc]
114         initWithRect:NSZeroRect
115              options:NSTrackingMouseMoved | NSTrackingCursorUpdate |
116                      NSTrackingActiveInActiveApp | NSTrackingInVisibleRect
117                owner:self
118             userInfo:nil]);
119     [self addTrackingArea:cursorTrackingArea_.get()];
120   }
121   return self;
124 - (void)clearView {
125   hostedView_ = NULL;
126   [cursorTrackingArea_.get() clearOwner];
127   [self removeTrackingArea:cursorTrackingArea_.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 mouseEvent: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 - (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent {
151   DCHECK(hostedView_);
152   base::string16 newTooltipText;
154   views::View* view = hostedView_->GetTooltipHandlerForPoint(locationInContent);
155   if (view) {
156     gfx::Point viewPoint = locationInContent;
157     views::View::ConvertPointToTarget(hostedView_, view, &viewPoint);
158     if (!view->GetTooltipText(viewPoint, &newTooltipText))
159       DCHECK(newTooltipText.empty());
160   }
161   if (newTooltipText != lastTooltipText_) {
162     std::swap(newTooltipText, lastTooltipText_);
163     [self setToolTipAtMousePoint:base::SysUTF16ToNSString(lastTooltipText_)];
164   }
167 // BridgedContentView private implementation.
169 - (void)handleKeyEvent:(NSEvent*)theEvent {
170   if (!hostedView_)
171     return;
173   DCHECK(theEvent);
174   ui::KeyEvent event(theEvent);
175   if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code()))
176     return;
178   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event);
181 - (void)handleAction:(int)commandId
182              keyCode:(ui::KeyboardCode)keyCode
183              domCode:(ui::DomCode)domCode
184           eventFlags:(int)eventFlags {
185   if (!hostedView_)
186     return;
188   if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode))
189     return;
191   // If there's an active TextInputClient, schedule the editing command to be
192   // performed.
193   if (commandId && textInputClient_ &&
194       textInputClient_->IsEditCommandEnabled(commandId))
195     textInputClient_->SetEditCommandForNextKeyEvent(commandId);
197   // Generate a synthetic event with the keycode toolkit-views expects.
198   ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
199   hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event);
202 - (void)undo:(id)sender {
203   // This DCHECK is more strict than a similar check in handleAction:. It can be
204   // done here because the actors sending these actions should be calling
205   // validateUserInterfaceItem: before enabling UI that allows these messages to
206   // be sent. Checking it here would be too late to provide correct UI feedback
207   // (e.g. there will be no "beep").
208   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
209   [self handleAction:IDS_APP_UNDO
210              keyCode:ui::VKEY_Z
211              domCode:ui::DomCode::KEY_Z
212           eventFlags:ui::EF_CONTROL_DOWN];
215 - (void)redo:(id)sender {
216   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
217   [self handleAction:IDS_APP_REDO
218              keyCode:ui::VKEY_Z
219              domCode:ui::DomCode::KEY_Z
220           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
223 - (void)cut:(id)sender {
224   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
225   [self handleAction:IDS_APP_CUT
226              keyCode:ui::VKEY_X
227              domCode:ui::DomCode::KEY_X
228           eventFlags:ui::EF_CONTROL_DOWN];
231 - (void)copy:(id)sender {
232   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_COPY));
233   [self handleAction:IDS_APP_COPY
234              keyCode:ui::VKEY_C
235              domCode:ui::DomCode::KEY_C
236           eventFlags:ui::EF_CONTROL_DOWN];
239 - (void)paste:(id)sender {
240   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE));
241   [self handleAction:IDS_APP_PASTE
242              keyCode:ui::VKEY_V
243              domCode:ui::DomCode::KEY_V
244           eventFlags:ui::EF_CONTROL_DOWN];
247 - (void)selectAll:(id)sender {
248   DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL));
249   [self handleAction:IDS_APP_SELECT_ALL
250              keyCode:ui::VKEY_A
251              domCode:ui::DomCode::KEY_A
252           eventFlags:ui::EF_CONTROL_DOWN];
255 // BaseView implementation.
257 // Don't use tracking areas from BaseView. BridgedContentView's tracks
258 // NSTrackingCursorUpdate and Apple's documentation suggests it's incompatible.
259 - (void)enableTracking {
262 // Translates the location of |theEvent| to toolkit-views coordinates and passes
263 // the event to NativeWidgetMac for handling.
264 - (void)mouseEvent:(NSEvent*)theEvent {
265   if (!hostedView_)
266     return;
268   ui::MouseEvent event(theEvent);
270   // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
271   // Mac hooks in here.
272   [self updateTooltipIfRequiredAt:event.location()];
274   hostedView_->GetWidget()->OnMouseEvent(&event);
277 // NSView implementation.
279 - (BOOL)acceptsFirstResponder {
280   return YES;
283 - (void)viewDidMoveToWindow {
284   // When this view is added to a window, AppKit calls setFrameSize before it is
285   // added to the window, so the behavior in setFrameSize is not triggered.
286   NSWindow* window = [self window];
287   if (window)
288     [self setFrameSize:NSZeroSize];
291 - (void)setFrameSize:(NSSize)newSize {
292   // The size passed in here does not always use
293   // -[NSWindow contentRectForFrameRect]. The following ensures that the
294   // contentView for a frameless window can extend over the titlebar of the new
295   // window containing it, since AppKit requires a titlebar to give frameless
296   // windows correct shadows and rounded corners.
297   NSWindow* window = [self window];
298   if (window)
299     newSize = [window contentRectForFrameRect:[window frame]].size;
301   [super setFrameSize:newSize];
302   if (!hostedView_)
303     return;
305   hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
308 - (void)drawRect:(NSRect)dirtyRect {
309   // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to
310   // suppress calls to this when the window is known to be hidden.
311   if (!hostedView_)
312     return;
314   if (drawMenuBackgroundForBlur_) {
315     const CGFloat radius = views::MenuConfig::instance(nullptr).corner_radius;
316     [gfx::SkColorToSRGBNSColor(0x01000000) set];
317     [[NSBezierPath bezierPathWithRoundedRect:[self bounds]
318                                      xRadius:radius
319                                      yRadius:radius] fill];
320   }
322   // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
323   if (hostedView_->GetWidget()->GetLayer())
324     return;
326   gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
327   hostedView_->GetWidget()->OnNativeWidgetPaint(
328       ui::CanvasPainter(&canvas, 1.f).context());
331 - (NSTextInputContext*)inputContext {
332   if (!hostedView_)
333     return [super inputContext];
335   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
336   // input context, return nil. This ensures the action message is sent to
337   // the view, rather than any NSTextInputClient a subview has installed.
338   MenuController* menuController = MenuController::GetActiveInstance();
339   if (menuController && menuController->owner() == hostedView_->GetWidget())
340     return nil;
342   return [super inputContext];
345 // NSResponder implementation.
347 - (void)keyDown:(NSEvent*)theEvent {
348   // Convert the event into an action message, according to OSX key mappings.
349   inKeyDown_ = YES;
350   [self interpretKeyEvents:@[ theEvent ]];
351   inKeyDown_ = NO;
354 - (void)scrollWheel:(NSEvent*)theEvent {
355   if (!hostedView_)
356     return;
358   ui::MouseWheelEvent event(theEvent);
359   hostedView_->GetWidget()->OnMouseEvent(&event);
362 ////////////////////////////////////////////////////////////////////////////////
363 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
364 // 10.9 SDK). The list should eventually be complete. Anything not defined will
365 // beep when interpretKeyEvents: would otherwise call it.
366 // TODO(tapted): Make this list complete, except for insert* methods which are
367 // dispatched as regular key events in doCommandBySelector:.
369 // The insertText action message forwards to the TextInputClient unless a menu
370 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't
371 // direct insertText: through doCommandBySelector:, so this is still needed to
372 // handle the case when inputContext: is nil. When inputContext: returns non-nil
373 // text goes directly to insertText:replacementRange:.
374 - (void)insertText:(id)text {
375   [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)];
378 // Selection movement and scrolling.
380 - (void)moveRight:(id)sender {
381   [self handleAction:IDS_MOVE_RIGHT
382              keyCode:ui::VKEY_RIGHT
383              domCode:ui::DomCode::ARROW_RIGHT
384           eventFlags:0];
387 - (void)moveLeft:(id)sender {
388   [self handleAction:IDS_MOVE_LEFT
389              keyCode:ui::VKEY_LEFT
390              domCode:ui::DomCode::ARROW_LEFT
391           eventFlags:0];
394 - (void)moveUp:(id)sender {
395   [self handleAction:0
396              keyCode:ui::VKEY_UP
397              domCode:ui::DomCode::ARROW_UP
398           eventFlags:0];
401 - (void)moveDown:(id)sender {
402   [self handleAction:0
403              keyCode:ui::VKEY_DOWN
404              domCode:ui::DomCode::ARROW_DOWN
405           eventFlags:0];
408 - (void)moveWordRight:(id)sender {
409   [self handleAction:IDS_MOVE_WORD_RIGHT
410              keyCode:ui::VKEY_RIGHT
411              domCode:ui::DomCode::ARROW_RIGHT
412           eventFlags:ui::EF_CONTROL_DOWN];
415 - (void)moveWordLeft:(id)sender {
416   [self handleAction:IDS_MOVE_WORD_LEFT
417              keyCode:ui::VKEY_LEFT
418              domCode:ui::DomCode::ARROW_LEFT
419           eventFlags:ui::EF_CONTROL_DOWN];
422 - (void)moveLeftAndModifySelection:(id)sender {
423   [self handleAction:IDS_MOVE_LEFT_AND_MODIFY_SELECTION
424              keyCode:ui::VKEY_LEFT
425              domCode:ui::DomCode::ARROW_LEFT
426           eventFlags:ui::EF_SHIFT_DOWN];
429 - (void)moveRightAndModifySelection:(id)sender {
430   [self handleAction:IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
431              keyCode:ui::VKEY_RIGHT
432              domCode:ui::DomCode::ARROW_RIGHT
433           eventFlags:ui::EF_SHIFT_DOWN];
436 - (void)moveWordRightAndModifySelection:(id)sender {
437   [self handleAction:IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
438              keyCode:ui::VKEY_RIGHT
439              domCode:ui::DomCode::ARROW_RIGHT
440           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
443 - (void)moveWordLeftAndModifySelection:(id)sender {
444   [self handleAction:IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
445              keyCode:ui::VKEY_LEFT
446              domCode:ui::DomCode::ARROW_LEFT
447           eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
450 - (void)moveToLeftEndOfLine:(id)sender {
451   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE
452              keyCode:ui::VKEY_HOME
453              domCode:ui::DomCode::HOME
454           eventFlags:0];
457 - (void)moveToRightEndOfLine:(id)sender {
458   [self handleAction:IDS_MOVE_TO_END_OF_LINE
459              keyCode:ui::VKEY_END
460              domCode:ui::DomCode::END
461           eventFlags:0];
464 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
465   [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
466              keyCode:ui::VKEY_HOME
467              domCode:ui::DomCode::HOME
468           eventFlags:ui::EF_SHIFT_DOWN];
471 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
472   [self handleAction:IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
473              keyCode:ui::VKEY_END
474              domCode:ui::DomCode::END
475           eventFlags:ui::EF_SHIFT_DOWN];
478 // Deletions.
480 - (void)deleteForward:(id)sender {
481   [self handleAction:IDS_DELETE_FORWARD
482              keyCode:ui::VKEY_DELETE
483              domCode:ui::DomCode::DEL
484           eventFlags:0];
487 - (void)deleteBackward:(id)sender {
488   [self handleAction:IDS_DELETE_BACKWARD
489              keyCode:ui::VKEY_BACK
490              domCode:ui::DomCode::BACKSPACE
491           eventFlags:0];
494 - (void)deleteWordForward:(id)sender {
495   [self handleAction:IDS_DELETE_WORD_FORWARD
496              keyCode:ui::VKEY_DELETE
497              domCode:ui::DomCode::DEL
498           eventFlags:ui::EF_CONTROL_DOWN];
501 - (void)deleteWordBackward:(id)sender {
502   [self handleAction:IDS_DELETE_WORD_BACKWARD
503              keyCode:ui::VKEY_BACK
504              domCode:ui::DomCode::BACKSPACE
505           eventFlags:ui::EF_CONTROL_DOWN];
508 // Cancellation.
510 - (void)cancelOperation:(id)sender {
511   [self handleAction:0
512              keyCode:ui::VKEY_ESCAPE
513              domCode:ui::DomCode::ESCAPE
514           eventFlags:0];
517 // Support for Services in context menus.
518 // Currently we only support reading and writing plain strings.
519 - (id)validRequestorForSendType:(NSString*)sendType
520                      returnType:(NSString*)returnType {
521   BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
522                   [self selectedRange].length > 0;
523   BOOL canRead = [returnType isEqualToString:NSStringPboardType];
524   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
525   // or (string, string).
526   BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
527                                     (canRead && (canWrite || !sendType)));
528   return valid ? self : [super validRequestorForSendType:sendType
529                                               returnType:returnType];
532 // NSServicesRequests informal protocol.
534 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
535   DCHECK([types containsObject:NSStringPboardType]);
536   if (!textInputClient_)
537     return NO;
539   gfx::Range selectionRange;
540   if (!textInputClient_->GetSelectionRange(&selectionRange))
541     return NO;
543   base::string16 text;
544   textInputClient_->GetTextFromRange(selectionRange, &text);
545   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
548 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
549   NSArray* objects =
550       [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
551   DCHECK([objects count] == 1);
552   [self insertText:[objects lastObject]];
553   return YES;
556 // NSTextInputClient protocol implementation.
558 - (NSAttributedString*)
559     attributedSubstringForProposedRange:(NSRange)range
560                             actualRange:(NSRangePointer)actualRange {
561   base::string16 substring;
562   if (textInputClient_) {
563     gfx::Range textRange;
564     textInputClient_->GetTextRange(&textRange);
565     gfx::Range subrange = textRange.Intersect(gfx::Range(range));
566     textInputClient_->GetTextFromRange(subrange, &substring);
567     if (actualRange)
568       *actualRange = subrange.ToNSRange();
569   }
570   return [[[NSAttributedString alloc]
571       initWithString:base::SysUTF16ToNSString(substring)] autorelease];
574 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
575   NOTIMPLEMENTED();
576   return 0;
579 - (void)doCommandBySelector:(SEL)selector {
580   // Like the renderer, handle insert action messages as a regular key dispatch.
581   // This ensures, e.g., insertTab correctly changes focus between fields.
582   if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) {
583     [self handleKeyEvent:[NSApp currentEvent]];
584     return;
585   }
587   if ([self respondsToSelector:selector])
588     [self performSelector:selector withObject:nil];
589   else
590     [[self nextResponder] doCommandBySelector:selector];
593 - (NSRect)firstRectForCharacterRange:(NSRange)range
594                          actualRange:(NSRangePointer)actualRange {
595   NOTIMPLEMENTED();
596   return NSZeroRect;
599 - (BOOL)hasMarkedText {
600   return textInputClient_ && textInputClient_->HasCompositionText();
603 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
604   if (!hostedView_)
605     return;
607   if ([text isKindOfClass:[NSAttributedString class]])
608     text = [text string];
610   MenuController* menuController = MenuController::GetActiveInstance();
611   if (menuController && menuController->owner() == hostedView_->GetWidget()) {
612     // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single-
613     // characters and input from IME. For IME, swallow the entire string unless
614     // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT.
615     bool swallowedAny = false;
616     for (NSUInteger i = 0; i < [text length]; ++i) {
617       if (!menuController ||
618           menuController->OnWillDispatchKeyEvent([text characterAtIndex:i],
619                                                  ui::VKEY_UNKNOWN) ==
620               ui::POST_DISPATCH_PERFORM_DEFAULT) {
621         if (swallowedAny)
622           return;  // Swallow remainder.
623         break;
624       }
625       swallowedAny = true;
626       // Ensure the menu remains active.
627       menuController = MenuController::GetActiveInstance();
628     }
629   }
631   if (!textInputClient_)
632     return;
634   textInputClient_->DeleteRange(gfx::Range(replacementRange));
636   // If a single character is inserted by keyDown's call to interpretKeyEvents:
637   // then use InsertChar() to allow editing events to be merged. The second
638   // argument is the key modifier, which interpretKeyEvents: will have already
639   // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
640   // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
641   // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
642   if (inKeyDown_ && [text length] == 1)
643     textInputClient_->InsertChar([text characterAtIndex:0], 0);
644   else
645     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
648 - (NSRange)markedRange {
649   if (!textInputClient_)
650     return NSMakeRange(NSNotFound, 0);
652   gfx::Range range;
653   textInputClient_->GetCompositionTextRange(&range);
654   return range.ToNSRange();
657 - (NSRange)selectedRange {
658   if (!textInputClient_)
659     return NSMakeRange(NSNotFound, 0);
661   gfx::Range range;
662   textInputClient_->GetSelectionRange(&range);
663   return range.ToNSRange();
666 - (void)setMarkedText:(id)text
667         selectedRange:(NSRange)selectedRange
668      replacementRange:(NSRange)replacementRange {
669   if (!textInputClient_)
670     return;
672   if ([text isKindOfClass:[NSAttributedString class]])
673     text = [text string];
674   ui::CompositionText composition;
675   composition.text = base::SysNSStringToUTF16(text);
676   composition.selection = gfx::Range(selectedRange);
677   textInputClient_->SetCompositionText(composition);
680 - (void)unmarkText {
681   if (textInputClient_)
682     textInputClient_->ConfirmCompositionText();
685 - (NSArray*)validAttributesForMarkedText {
686   return @[];
689 // NSUserInterfaceValidations protocol implementation.
691 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
692   if (!textInputClient_)
693     return NO;
695   SEL action = [item action];
697   if (action == @selector(undo:))
698     return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
699   if (action == @selector(redo:))
700     return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
701   if (action == @selector(cut:))
702     return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
703   if (action == @selector(copy:))
704     return textInputClient_->IsEditCommandEnabled(IDS_APP_COPY);
705   if (action == @selector(paste:))
706     return textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE);
707   if (action == @selector(selectAll:))
708     return textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL);
710   return NO;
713 // NSAccessibility informal protocol implementation.
715 - (id)accessibilityAttributeValue:(NSString*)attribute {
716   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
717     return @[ hostedView_->GetNativeViewAccessible() ];
718   }
720   return [super accessibilityAttributeValue:attribute];
723 - (id)accessibilityHitTest:(NSPoint)point {
724   return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
727 @end