• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2011 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.
4
5#include <QuartzCore/QuartzCore.h>
6
7#include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
8
9#include "base/logging.h"
10#include "base/mac/scoped_cftyperef.h"
11#import "base/mac/scoped_nsautorelease_pool.h"
12#include "base/metrics/histogram.h"
13#import "base/memory/scoped_nsobject.h"
14#include "base/string_util.h"
15#include "base/sys_info.h"
16#include "base/sys_string_conversions.h"
17#import "chrome/browser/accessibility/browser_accessibility_cocoa.h"
18#include "chrome/browser/accessibility/browser_accessibility_state.h"
19#include "chrome/browser/browser_trial.h"
20#include "chrome/browser/gpu_process_host_ui_shim.h"
21#import "chrome/browser/renderer_host/accelerated_plugin_view_mac.h"
22#include "chrome/browser/spellchecker_platform_engine.h"
23#import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h"
24#import "chrome/browser/ui/cocoa/view_id_util.h"
25#include "chrome/common/render_messages.h"
26#include "content/browser/browser_thread.h"
27#include "content/browser/gpu_process_host.h"
28#include "content/browser/plugin_process_host.h"
29#include "content/browser/renderer_host/backing_store_mac.h"
30#include "content/browser/renderer_host/render_process_host.h"
31#include "content/browser/renderer_host/render_view_host.h"
32#include "content/browser/renderer_host/render_widget_host.h"
33#include "content/common/edit_command.h"
34#include "content/common/gpu_messages.h"
35#include "content/common/native_web_keyboard_event.h"
36#include "content/common/plugin_messages.h"
37#include "content/common/view_messages.h"
38#include "skia/ext/platform_canvas.h"
39#import "third_party/mozilla/ComplexTextInputPanel.h"
40#include "third_party/skia/include/core/SkColor.h"
41#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h"
42#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
43#include "ui/gfx/point.h"
44#include "ui/gfx/surface/io_surface_support_mac.h"
45#include "webkit/glue/webaccessibility.h"
46#include "webkit/plugins/npapi/webplugin.h"
47
48using WebKit::WebInputEvent;
49using WebKit::WebInputEventFactory;
50using WebKit::WebMouseEvent;
51using WebKit::WebMouseWheelEvent;
52
53static inline int ToWebKitModifiers(NSUInteger flags) {
54  int modifiers = 0;
55  if (flags & NSControlKeyMask) modifiers |= WebInputEvent::ControlKey;
56  if (flags & NSShiftKeyMask) modifiers |= WebInputEvent::ShiftKey;
57  if (flags & NSAlternateKeyMask) modifiers |= WebInputEvent::AltKey;
58  if (flags & NSCommandKeyMask) modifiers |= WebInputEvent::MetaKey;
59  return modifiers;
60}
61
62@interface RenderWidgetHostViewCocoa (Private)
63+ (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
64- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
65- (void)keyEvent:(NSEvent *)theEvent wasKeyEquivalent:(BOOL)equiv;
66- (void)cancelChildPopups;
67- (void)checkForPluginImeCancellation;
68@end
69
70// This API was published since 10.6. Provide the declaration so it can be
71// // called below when building with the 10.5 SDK.
72#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
73@class NSTextInputContext;
74@interface NSResponder (AppKitDetails)
75- (NSTextInputContext *)inputContext;
76@end
77#endif
78
79namespace {
80
81// Maximum number of characters we allow in a tooltip.
82const size_t kMaxTooltipLength = 1024;
83
84// TODO(suzhe): Upstream this function.
85WebKit::WebColor WebColorFromNSColor(NSColor *color) {
86  CGFloat r, g, b, a;
87  [color getRed:&r green:&g blue:&b alpha:&a];
88
89  return
90      std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
91      std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
92      std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
93      std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
94}
95
96// Extract underline information from an attributed string. Mostly copied from
97// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
98void ExtractUnderlines(
99    NSAttributedString* string,
100    std::vector<WebKit::WebCompositionUnderline>* underlines) {
101  int length = [[string string] length];
102  int i = 0;
103  while (i < length) {
104    NSRange range;
105    NSDictionary* attrs = [string attributesAtIndex:i
106                              longestEffectiveRange:&range
107                                            inRange:NSMakeRange(i, length - i)];
108    if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
109      WebKit::WebColor color = SK_ColorBLACK;
110      if (NSColor *colorAttr =
111          [attrs objectForKey:NSUnderlineColorAttributeName]) {
112        color = WebColorFromNSColor(
113            [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
114      }
115      underlines->push_back(WebKit::WebCompositionUnderline(
116          range.location, NSMaxRange(range), color, [style intValue] > 1));
117    }
118    i = range.location + range.length;
119  }
120}
121
122// EnablePasswordInput() and DisablePasswordInput() are copied from
123// enableSecureTextInput() and disableSecureTextInput() functions in
124// third_party/WebKit/WebCore/platform/SecureTextInput.cpp
125// But we don't call EnableSecureEventInput() and DisableSecureEventInput()
126// here, because they are already called in webkit and they are system wide
127// functions.
128void EnablePasswordInput() {
129  CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
130  TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
131                         sizeof(CFArrayRef), &inputSources);
132  CFRelease(inputSources);
133}
134
135void DisablePasswordInput() {
136  TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
137}
138
139// Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
140// left of the primary screen (Carbon coordinates), and stuffs it into a
141// gfx::Rect.
142gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
143  gfx::Rect new_rect(NSRectToCGRect(rect));
144  if ([[NSScreen screens] count] > 0) {
145    new_rect.set_y([[[NSScreen screens] objectAtIndex:0] frame].size.height -
146                   new_rect.y() - new_rect.height());
147  }
148  return new_rect;
149}
150
151// Returns the window that visually contains the given view. This is different
152// from [view window] in the case of tab dragging, where the view's owning
153// window is a floating panel attached to the actual browser window that the tab
154// is visually part of.
155NSWindow* ApparentWindowForView(NSView* view) {
156  // TODO(shess): In case of !window, the view has been removed from
157  // the view hierarchy because the tab isn't main.  Could retrieve
158  // the information from the main tab for our window.
159  NSWindow* enclosing_window = [view window];
160
161  // See if this is a tab drag window. The width check is to distinguish that
162  // case from extension popup windows.
163  NSWindow* ancestor_window = [enclosing_window parentWindow];
164  if (ancestor_window && (NSWidth([enclosing_window frame]) ==
165                          NSWidth([ancestor_window frame]))) {
166    enclosing_window = ancestor_window;
167  }
168
169  return enclosing_window;
170}
171
172}  // namespace
173
174// RenderWidgetHostView --------------------------------------------------------
175
176// static
177RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
178    RenderWidgetHost* widget) {
179  return new RenderWidgetHostViewMac(widget);
180}
181
182// static
183RenderWidgetHostView* RenderWidgetHostView::
184    GetRenderWidgetHostViewFromNativeView(gfx::NativeView native_view) {
185  // TODO(port)
186  NOTREACHED() <<
187      "RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView not"
188      "implemented";
189  return NULL;
190}
191
192///////////////////////////////////////////////////////////////////////////////
193// RenderWidgetHostViewMac, public:
194
195RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
196    : render_widget_host_(widget),
197      about_to_validate_and_paint_(false),
198      call_set_needs_display_in_rect_pending_(false),
199      text_input_type_(WebKit::WebTextInputTypeNone),
200      is_loading_(false),
201      is_hidden_(false),
202      shutdown_factory_(this),
203      needs_gpu_visibility_update_after_repaint_(false),
204      compositing_surface_(gfx::kNullPluginWindow) {
205  // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away.
206  // Since we autorelease it, our caller must put |native_view()| into the view
207  // hierarchy right after calling us.
208  cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
209                  initWithRenderWidgetHostViewMac:this] autorelease];
210  render_widget_host_->set_view(this);
211
212  // Turn on accessibility only if VoiceOver is running.
213  if (IsVoiceOverRunning()) {
214    BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
215    render_widget_host_->EnableRendererAccessibility();
216  }
217}
218
219RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
220}
221
222///////////////////////////////////////////////////////////////////////////////
223// RenderWidgetHostViewMac, RenderWidgetHostView implementation:
224
225void RenderWidgetHostViewMac::InitAsPopup(
226    RenderWidgetHostView* parent_host_view,
227    const gfx::Rect& pos) {
228  bool activatable = popup_type_ == WebKit::WebPopupTypeNone;
229  [cocoa_view_ setCloseOnDeactivate:YES];
230  [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
231  [parent_host_view->GetNativeView() addSubview:cocoa_view_];
232
233  NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
234  if ([[NSScreen screens] count] > 0) {
235    origin_global.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height -
236        pos.height() - origin_global.y;
237  }
238  NSPoint origin_window =
239      [[cocoa_view_ window] convertScreenToBase:origin_global];
240  NSPoint origin_view =
241      [cocoa_view_ convertPoint:origin_window fromView:nil];
242  NSRect initial_frame = NSMakeRect(origin_view.x,
243                                    origin_view.y,
244                                    pos.width(),
245                                    pos.height());
246  [cocoa_view_ setFrame:initial_frame];
247}
248
249void RenderWidgetHostViewMac::InitAsFullscreen() {
250  NOTIMPLEMENTED() << "Full screen not implemented on Mac";
251}
252
253RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
254  return render_widget_host_;
255}
256
257void RenderWidgetHostViewMac::DidBecomeSelected() {
258  if (!is_hidden_)
259    return;
260
261  if (tab_switch_paint_time_.is_null())
262    tab_switch_paint_time_ = base::TimeTicks::Now();
263  is_hidden_ = false;
264  render_widget_host_->WasRestored();
265}
266
267void RenderWidgetHostViewMac::WasHidden() {
268  if (is_hidden_)
269    return;
270
271  // If we receive any more paint messages while we are hidden, we want to
272  // ignore them so we don't re-allocate the backing store.  We will paint
273  // everything again when we become selected again.
274  is_hidden_ = true;
275
276  // If we have a renderer, then inform it that we are being hidden so it can
277  // reduce its resource utilization.
278  render_widget_host_->WasHidden();
279}
280
281void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
282  gfx::Rect rect = GetViewBounds();
283  rect.set_size(size);
284  SetBounds(rect);
285}
286
287void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
288  // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
289  // TODO(thakis): fix, http://crbug.com/73362
290  if (is_hidden_)
291    return;
292
293  // During the initial creation of the RenderWidgetHostView in
294  // TabContents::CreateRenderViewForRenderManager, SetSize is called with an
295  // empty size. In the Windows code flow, it is not ignored because subsequent
296  // sizing calls from the OS flow through TCVW::WasSized which calls SetSize()
297  // again. On Cocoa, we rely on the Cocoa view struture and resizer flags to
298  // keep things sized properly. On the other hand, if the size is not empty
299  // then this is a valid request for a pop-up.
300  if (rect.size().IsEmpty())
301    return;
302
303  // Ignore the position of |rect| for non-popup rwhvs. This is because
304  // background tabs do not have a window, but the window is required for the
305  // coordinate conversions. Popups are always for a visible tab.
306  if (IsPopup()) {
307    // The position of |rect| is screen coordinate system and we have to
308    // consider Cocoa coordinate system is upside-down and also multi-screen.
309    NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
310    if ([[NSScreen screens] count] > 0) {
311      NSSize size = NSMakeSize(rect.width(), rect.height());
312      size = [cocoa_view_ convertSize:size toView:nil];
313      NSScreen* screen =
314          static_cast<NSScreen*>([[NSScreen screens] objectAtIndex:0]);
315      origin_global.y =
316          NSHeight([screen frame]) - size.height - origin_global.y;
317    }
318
319    // Then |origin_global| is converted to client coordinate system.
320    DCHECK([cocoa_view_ window]);
321    NSPoint origin_window =
322        [[cocoa_view_ window] convertScreenToBase:origin_global];
323    NSPoint origin_view =
324        [[cocoa_view_ superview] convertPoint:origin_window fromView:nil];
325    NSRect frame = NSMakeRect(origin_view.x, origin_view.y,
326                              rect.width(), rect.height());
327    [cocoa_view_ setFrame:frame];
328  } else {
329    DCHECK([[cocoa_view_ superview] isKindOfClass:[BaseView class]]);
330    BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
331    gfx::Rect rect = [superview flipNSRectToRect:[cocoa_view_ frame]];
332    rect.set_width(rect.width());
333    rect.set_height(rect.height());
334    [cocoa_view_ setFrame:[superview flipRectToNSRect:rect]];
335  }
336}
337
338gfx::NativeView RenderWidgetHostViewMac::GetNativeView() {
339  return native_view();
340}
341
342void RenderWidgetHostViewMac::MovePluginWindows(
343    const std::vector<webkit::npapi::WebPluginGeometry>& moves) {
344  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345  // Handle movement of accelerated plugins, which are the only "windowed"
346  // plugins that exist on the Mac.
347  for (std::vector<webkit::npapi::WebPluginGeometry>::const_iterator iter =
348           moves.begin();
349       iter != moves.end();
350       ++iter) {
351    webkit::npapi::WebPluginGeometry geom = *iter;
352
353    AcceleratedPluginView* view = ViewForPluginWindowHandle(geom.window);
354    DCHECK(view);
355    if (!view)
356      continue;
357
358    if (geom.rects_valid) {
359      gfx::Rect rect = geom.window_rect;
360      if (geom.visible) {
361        rect.set_x(rect.x() + geom.clip_rect.x());
362        rect.set_y(rect.y() + geom.clip_rect.y());
363        rect.set_width(geom.clip_rect.width());
364        rect.set_height(geom.clip_rect.height());
365      }
366      NSRect new_rect([cocoa_view_ flipRectToNSRect:rect]);
367      [view setFrame:new_rect];
368      NSMutableArray* cutout_rects =
369          [NSMutableArray arrayWithCapacity:geom.cutout_rects.size()];
370      for (unsigned int i = 0; i < geom.cutout_rects.size(); ++i) {
371        // Convert to NSRect, and flip vertically.
372        NSRect cutout_rect = NSRectFromCGRect(geom.cutout_rects[i].ToCGRect());
373        cutout_rect.origin.y = new_rect.size.height - NSMaxY(cutout_rect);
374        [cutout_rects addObject:[NSValue valueWithRect:cutout_rect]];
375      }
376      [view setCutoutRects:cutout_rects];
377      [view setNeedsDisplay:YES];
378    }
379
380    plugin_container_manager_.SetPluginContainerGeometry(geom);
381
382    BOOL visible =
383        plugin_container_manager_.SurfaceShouldBeVisible(geom.window);
384    [view setHidden:!visible];
385  }
386}
387
388void RenderWidgetHostViewMac::Focus() {
389  [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
390}
391
392void RenderWidgetHostViewMac::Blur() {
393  [[cocoa_view_ window] makeFirstResponder:nil];
394}
395
396bool RenderWidgetHostViewMac::HasFocus() {
397  return [[cocoa_view_ window] firstResponder] == cocoa_view_;
398}
399
400void RenderWidgetHostViewMac::Show() {
401  [cocoa_view_ setHidden:NO];
402
403  DidBecomeSelected();
404}
405
406void RenderWidgetHostViewMac::Hide() {
407  [cocoa_view_ setHidden:YES];
408
409  WasHidden();
410}
411
412bool RenderWidgetHostViewMac::IsShowing() {
413  return ![cocoa_view_ isHidden];
414}
415
416gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
417  // TODO(shess): In case of !window, the view has been removed from
418  // the view hierarchy because the tab isn't main.  Could retrieve
419  // the information from the main tab for our window.
420  NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
421  if (!enclosing_window)
422    return gfx::Rect();
423
424  NSRect bounds = [cocoa_view_ bounds];
425  bounds = [cocoa_view_ convertRect:bounds toView:nil];
426  bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
427  return FlipNSRectToRectScreen(bounds);
428}
429
430void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
431  current_cursor_ = cursor;
432  UpdateCursorIfOverSelf();
433}
434
435void RenderWidgetHostViewMac::UpdateCursorIfOverSelf() {
436  // Do something special (as Win Chromium does) for arrow cursor while loading
437  // a page? TODO(avi): decide
438  // Can we synchronize to the event stream? Switch to -[NSWindow
439  // mouseLocationOutsideOfEventStream] if we cannot. TODO(avi): test and see
440  NSEvent* event = [[cocoa_view_ window] currentEvent];
441  if ([event window] != [cocoa_view_ window])
442    return;
443
444  NSPoint event_location = [event locationInWindow];
445  NSPoint local_point = [cocoa_view_ convertPoint:event_location fromView:nil];
446
447  if (!NSPointInRect(local_point, [cocoa_view_ bounds]))
448    return;
449
450  NSCursor* ns_cursor = current_cursor_.GetCursor();
451  [ns_cursor set];
452}
453
454void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
455  is_loading_ = is_loading;
456  UpdateCursorIfOverSelf();
457}
458
459void RenderWidgetHostViewMac::ImeUpdateTextInputState(
460    WebKit::WebTextInputType type,
461    const gfx::Rect& caret_rect) {
462  if (text_input_type_ != type) {
463    text_input_type_ = type;
464    if (HasFocus())
465      SetTextInputActive(true);
466  }
467
468  // We need to convert the coordinate of the cursor rectangle sent from the
469  // renderer and save it. Our input method backend uses a coordinate system
470  // whose origin is the upper-left corner of this view. On the other hand,
471  // Cocoa uses a coordinate system whose origin is the lower-left corner of
472  // this view. So, we convert the cursor rectangle and save it.
473  [cocoa_view_ setCaretRect:[cocoa_view_ flipRectToNSRect:caret_rect]];
474}
475
476void RenderWidgetHostViewMac::ImeCancelComposition() {
477  [cocoa_view_ cancelComposition];
478}
479
480void RenderWidgetHostViewMac::DidUpdateBackingStore(
481    const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
482    const std::vector<gfx::Rect>& copy_rects) {
483  if (!is_hidden_) {
484    std::vector<gfx::Rect> rects(copy_rects);
485
486    // Because the findbar might be open, we cannot use scrollRect:by: here. For
487    // now, simply mark all of scroll rect as dirty.
488    if (!scroll_rect.IsEmpty())
489      rects.push_back(scroll_rect);
490
491    for (size_t i = 0; i < rects.size(); ++i) {
492      NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]];
493
494      if (about_to_validate_and_paint_) {
495        // As much as we'd like to use -setNeedsDisplayInRect: here, we can't.
496        // We're in the middle of executing a -drawRect:, and as soon as it
497        // returns Cocoa will clear its record of what needs display. We
498        // instead use |performSelector:| to call |setNeedsDisplayInRect:|
499        // after returning to the main loop, at which point |drawRect:| is no
500        // longer on the stack.
501        DCHECK([NSThread isMainThread]);
502        if (!call_set_needs_display_in_rect_pending_) {
503          [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect)
504                       withObject:nil
505                       afterDelay:0];
506          call_set_needs_display_in_rect_pending_ = true;
507          invalid_rect_ = ns_rect;
508        } else {
509          // The old invalid rect is probably invalid now, since the view has
510          // most likely been resized, but there's no harm in dirtying the
511          // union.  In the limit, this becomes equivalent to dirtying the
512          // whole view.
513          invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect);
514        }
515      } else {
516        [cocoa_view_ setNeedsDisplayInRect:ns_rect];
517      }
518    }
519
520    if (!about_to_validate_and_paint_)
521      [cocoa_view_ displayIfNeeded];
522  }
523
524  // If |about_to_validate_and_paint_| is set, then -drawRect: is on the stack
525  // and it's not allowed to call -setHidden on the accelerated view.  In that
526  // case, -callSetNeedsDisplayInRect: will hide it later.
527  // If |about_to_validate_and_paint_| is not set, do it now.
528  if (!about_to_validate_and_paint_)
529    HandleDelayedGpuViewHiding();
530}
531
532void RenderWidgetHostViewMac::RenderViewGone(base::TerminationStatus status,
533                                             int error_code) {
534  // TODO(darin): keep this around, and draw sad-tab into it.
535  UpdateCursorIfOverSelf();
536  Destroy();
537}
538
539void RenderWidgetHostViewMac::Destroy() {
540  // On Windows, popups are implemented with a popup window style, so that when
541  // an event comes in that would "cancel" it, it receives the OnCancelMode
542  // message and can kill itself. Alas, on the Mac, views cannot capture events
543  // outside of themselves. On Windows, if Destroy is being called on a view,
544  // then the event causing the destroy had also cancelled any popups by the
545  // time Destroy() was called. On the Mac we have to destroy all the popups
546  // ourselves.
547
548  // Depth-first destroy all popups. Use ShutdownHost() to enforce
549  // deepest-first ordering.
550  for (NSView* subview in [cocoa_view_ subviews]) {
551    if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) {
552      [static_cast<RenderWidgetHostViewCocoa*>(subview)
553          renderWidgetHostViewMac]->ShutdownHost();
554    } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) {
555      [static_cast<AcceleratedPluginView*>(subview)
556          onRenderWidgetHostViewGone];
557    }
558  }
559
560  // We've been told to destroy.
561  [cocoa_view_ retain];
562  [cocoa_view_ removeFromSuperview];
563  [cocoa_view_ autorelease];
564
565  // We get this call just before |render_widget_host_| deletes
566  // itself.  But we are owned by |cocoa_view_|, which may be retained
567  // by some other code.  Examples are TabContentsViewMac's
568  // |latent_focus_view_| and TabWindowController's
569  // |cachedContentView_|.
570  render_widget_host_ = NULL;
571}
572
573// Called from the renderer to tell us what the tooltip text should be. It
574// calls us frequently so we need to cache the value to prevent doing a lot
575// of repeat work.
576void RenderWidgetHostViewMac::SetTooltipText(const std::wstring& tooltip_text) {
577  if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
578    tooltip_text_ = tooltip_text;
579
580    // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
581    // Windows; we're just trying to be polite. Don't persist the trimmed
582    // string, as then the comparison above will always fail and we'll try to
583    // set it again every single time the mouse moves.
584    std::wstring display_text = tooltip_text_;
585    if (tooltip_text_.length() > kMaxTooltipLength)
586      display_text = tooltip_text_.substr(0, kMaxTooltipLength);
587
588    NSString* tooltip_nsstring = base::SysWideToNSString(display_text);
589    [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
590  }
591}
592
593//
594// RenderWidgetHostViewCocoa uses the stored selection text,
595// which implements NSServicesRequests protocol.
596//
597void RenderWidgetHostViewMac::SelectionChanged(const std::string& text) {
598  selected_text_ = text;
599}
600
601bool RenderWidgetHostViewMac::IsPopup() const {
602  return popup_type_ != WebKit::WebPopupTypeNone;
603}
604
605BackingStore* RenderWidgetHostViewMac::AllocBackingStore(
606    const gfx::Size& size) {
607  return new BackingStoreMac(render_widget_host_, size);
608}
609
610// Sets whether or not to accept first responder status.
611void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
612  [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
613}
614
615void RenderWidgetHostViewMac::KillSelf() {
616  if (shutdown_factory_.empty()) {
617    [cocoa_view_ setHidden:YES];
618    MessageLoop::current()->PostTask(FROM_HERE,
619        shutdown_factory_.NewRunnableMethod(
620            &RenderWidgetHostViewMac::ShutdownHost));
621  }
622}
623
624void RenderWidgetHostViewMac::PluginFocusChanged(bool focused, int plugin_id) {
625  [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
626}
627
628void RenderWidgetHostViewMac::StartPluginIme() {
629  [cocoa_view_ setPluginImeActive:YES];
630}
631
632bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
633    const NativeWebKeyboardEvent& event) {
634  // Check WebInputEvent type since multiple types of events can be sent into
635  // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
636  // necessary to avoid double processing.
637  // Also check the native type, since NSFlagsChanged is considered a key event
638  // for WebKit purposes, but isn't considered a key event by the OS.
639  if (event.type == WebInputEvent::RawKeyDown &&
640      [event.os_event type] == NSKeyDown)
641    return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
642  return false;
643}
644
645void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
646    const string16& text, int plugin_id) {
647  if (render_widget_host_) {
648    render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
649        render_widget_host_->routing_id(), text, plugin_id));
650  }
651}
652
653gfx::PluginWindowHandle
654RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque,
655                                                        bool root) {
656  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
657
658  // |render_widget_host_| is set to NULL when |RWHVMac::Destroy()| has
659  // completed. If |AllocateFakePluginWindowHandle()| is called after that,
660  // we will crash when the AcceleratedPluginView we allocate below is
661  // destroyed.
662  DCHECK(render_widget_host_);
663
664  // Create an NSView to host the plugin's/compositor's pixels.
665  gfx::PluginWindowHandle handle =
666      plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root);
667
668  scoped_nsobject<AcceleratedPluginView> plugin_view(
669      [[AcceleratedPluginView alloc] initWithRenderWidgetHostViewMac:this
670                                                        pluginHandle:handle]);
671  [plugin_view setHidden:YES];
672
673  [cocoa_view_ addSubview:plugin_view];
674  plugin_views_[handle] = plugin_view;
675
676  return handle;
677}
678
679void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle(
680    gfx::PluginWindowHandle window) {
681  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
682  PluginViewMap::iterator it = plugin_views_.find(window);
683  DCHECK(plugin_views_.end() != it);
684  if (plugin_views_.end() == it) {
685    return;
686  }
687  [it->second removeFromSuperview];
688  plugin_views_.erase(it);
689
690  // The view's dealloc will call DeallocFakePluginWindowHandle(), which will
691  // remove the handle from |plugin_container_manager_|. This code path is
692  // taken if a plugin is removed, but the RWHVMac itself stays alive.
693}
694
695// This is called by AcceleratedPluginView's -dealloc.
696void RenderWidgetHostViewMac::DeallocFakePluginWindowHandle(
697    gfx::PluginWindowHandle window) {
698  // When a browser window with a GpuScheduler is closed, the render process
699  // will attempt to finish all GL commands. It will busy-wait on the GPU
700  // process until the command queue is empty. If a paint is pending, the GPU
701  // process won't process any GL commands until the browser sends a paint ack,
702  // but since the browser window is already closed, it will never arrive.
703  // To resolve this we ask the GPU process to destroy the command buffer
704  // associated with the given render widget.  Once the command buffer is
705  // destroyed, all GL commands from the renderer will immediately receive
706  // channel error.
707  if (render_widget_host_ &&
708      plugin_container_manager_.IsRootContainer(window)) {
709    GpuProcessHost::SendOnIO(
710        render_widget_host_->process()->id(),
711        content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
712        new GpuMsg_DestroyCommandBuffer(
713            render_widget_host_->process()->id(),
714            render_widget_host_->routing_id()));
715  }
716
717  plugin_container_manager_.DestroyFakePluginWindowHandle(window);
718}
719
720AcceleratedPluginView* RenderWidgetHostViewMac::ViewForPluginWindowHandle(
721    gfx::PluginWindowHandle window) {
722  PluginViewMap::iterator it = plugin_views_.find(window);
723  DCHECK(plugin_views_.end() != it);
724  if (plugin_views_.end() == it)
725    return nil;
726  return it->second;
727}
728
729void RenderWidgetHostViewMac::AcceleratedSurfaceSetIOSurface(
730    gfx::PluginWindowHandle window,
731    int32 width,
732    int32 height,
733    uint64 io_surface_identifier) {
734  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
735  plugin_container_manager_.SetSizeAndIOSurface(window,
736                                                width,
737                                                height,
738                                                io_surface_identifier);
739
740  if (plugin_container_manager_.IsRootContainer(window)) {
741    // Fake up a WebPluginGeometry for the root window to set the
742    // container's size; we will never get a notification from the
743    // browser about the root window, only plugins.
744    webkit::npapi::WebPluginGeometry geom;
745    gfx::Rect rect(0, 0, width, height);
746    geom.window = window;
747    geom.window_rect = rect;
748    geom.clip_rect = rect;
749    geom.visible = true;
750    geom.rects_valid = true;
751    MovePluginWindows(std::vector<webkit::npapi::WebPluginGeometry>(1, geom));
752  }
753}
754
755void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB(
756    gfx::PluginWindowHandle window,
757    int32 width,
758    int32 height,
759    TransportDIB::Handle transport_dib) {
760  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
761  plugin_container_manager_.SetSizeAndTransportDIB(window,
762                                                   width,
763                                                   height,
764                                                   transport_dib);
765}
766
767void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
768    gfx::PluginWindowHandle window,
769    uint64 surface_id,
770    int renderer_id,
771    int32 route_id,
772    int gpu_host_id,
773    uint64 swap_buffers_count) {
774  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
775  AcceleratedPluginView* view = ViewForPluginWindowHandle(window);
776  DCHECK(view);
777  if (!view)
778    return;
779
780  plugin_container_manager_.SetSurfaceWasPaintedTo(window, surface_id);
781
782  // The surface is hidden until its first paint, to not show gargabe.
783  if (plugin_container_manager_.SurfaceShouldBeVisible(window))
784    [view setHidden:NO];
785  [view updateSwapBuffersCount:swap_buffers_count
786                  fromRenderer:renderer_id
787                       routeId:route_id
788                     gpuHostId:gpu_host_id];
789}
790
791void RenderWidgetHostViewMac::UpdateRootGpuViewVisibility(
792    bool show_gpu_widget) {
793  // Plugins are destroyed on page navigate. The compositor layer on the other
794  // hand is created on demand and then stays alive until its renderer process
795  // dies (usually on cross-domain navigation). Instead, only a flag
796  // |is_accelerated_compositing_active()| is flipped when the compositor output
797  // should be shown/hidden.
798  // Show/hide the view belonging to the compositor here.
799  plugin_container_manager_.set_gpu_rendering_active(show_gpu_widget);
800
801  gfx::PluginWindowHandle root_handle =
802      plugin_container_manager_.root_container_handle();
803  if (root_handle != gfx::kNullPluginWindow) {
804    AcceleratedPluginView* view = ViewForPluginWindowHandle(root_handle);
805    DCHECK(view);
806    bool visible =
807        plugin_container_manager_.SurfaceShouldBeVisible(root_handle);
808    [[view window] disableScreenUpdatesUntilFlush];
809    [view setHidden:!visible];
810  }
811}
812
813void RenderWidgetHostViewMac::HandleDelayedGpuViewHiding() {
814  if (needs_gpu_visibility_update_after_repaint_) {
815    UpdateRootGpuViewVisibility(false);
816    needs_gpu_visibility_update_after_repaint_ = false;
817  }
818}
819
820void RenderWidgetHostViewMac::AcknowledgeSwapBuffers(
821    int renderer_id,
822    int32 route_id,
823    int gpu_host_id,
824    uint64 swap_buffers_count) {
825  // Called on the display link thread. Hand actual work off to the IO thread,
826  // because |GpuProcessHost::Get()| can only be called there.
827  // Currently, this is never called for plugins.
828  if (render_widget_host_) {
829    DCHECK_EQ(render_widget_host_->process()->id(), renderer_id);
830    // |render_widget_host_->routing_id()| and |route_id| are usually not
831    // equal: The former identifies the channel from the RWH in the browser
832    // process to the corresponding render widget in the renderer process, while
833    // the latter identifies the channel from the GpuCommandBufferStub in the
834    // GPU process to the corresponding command buffer client in the renderer.
835  }
836
837  // TODO(apatrick): Send the acknowledgement via the UI thread when running in
838  // single process or in process GPU mode for now. This is bad from a
839  // performance point of view but the plan is to not use AcceleratedSurface at
840  // all in these cases.
841  if (gpu_host_id == 0) {
842    BrowserThread::PostTask(
843        BrowserThread::UI,
844        FROM_HERE,
845        NewRunnableFunction(&GpuProcessHostUIShim::SendToGpuHost,
846                            gpu_host_id,
847                            new GpuMsg_AcceleratedSurfaceBuffersSwappedACK(
848                                renderer_id,
849                                route_id,
850                                swap_buffers_count)));
851  } else {
852    GpuProcessHost::SendOnIO(
853        gpu_host_id,
854        content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
855        new GpuMsg_AcceleratedSurfaceBuffersSwappedACK(
856            renderer_id, route_id, swap_buffers_count));
857  }
858}
859
860void RenderWidgetHostViewMac::GpuRenderingStateDidChange() {
861  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
862  if (GetRenderWidgetHost()->is_accelerated_compositing_active()) {
863    UpdateRootGpuViewVisibility(
864        GetRenderWidgetHost()->is_accelerated_compositing_active());
865  } else {
866    needs_gpu_visibility_update_after_repaint_ = true;
867  }
868}
869
870gfx::PluginWindowHandle RenderWidgetHostViewMac::GetCompositingSurface() {
871  if (compositing_surface_ == gfx::kNullPluginWindow)
872    compositing_surface_ = AllocateFakePluginWindowHandle(
873        /*opaque=*/true, /*root=*/true);
874  return compositing_surface_;
875}
876
877void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance(
878      CGLContextObj context,
879      gfx::PluginWindowHandle plugin_handle,
880      NSSize size) {
881  // Called on the display link thread.
882  CGLSetCurrentContext(context);
883
884  glMatrixMode(GL_PROJECTION);
885  glLoadIdentity();
886  // Note that we place the origin at the upper left corner with +y
887  // going down
888  glOrtho(0, size.width, size.height, 0, -1, 1);
889  glMatrixMode(GL_MODELVIEW);
890  glLoadIdentity();
891
892  plugin_container_manager_.Draw(context, plugin_handle);
893}
894
895void RenderWidgetHostViewMac::ForceTextureReload() {
896  plugin_container_manager_.ForceTextureReload();
897}
898
899void RenderWidgetHostViewMac::SetVisuallyDeemphasized(const SkColor* color,
900                                                      bool animate) {
901  // This is not used on mac.
902}
903
904void RenderWidgetHostViewMac::ShutdownHost() {
905  shutdown_factory_.RevokeAll();
906  render_widget_host_->Shutdown();
907  // Do not touch any members at this point, |this| has been deleted.
908}
909
910bool RenderWidgetHostViewMac::IsVoiceOverRunning() {
911  NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
912  [user_defaults addSuiteNamed:@"com.apple.universalaccess"];
913  return 1 == [user_defaults integerForKey:@"voiceOverOnOffKey"];
914}
915
916gfx::Rect RenderWidgetHostViewMac::GetViewCocoaBounds() const {
917  return gfx::Rect(NSRectToCGRect([cocoa_view_ bounds]));
918}
919
920gfx::Rect RenderWidgetHostViewMac::GetRootWindowRect() {
921  // TODO(shess): In case of !window, the view has been removed from
922  // the view hierarchy because the tab isn't main.  Could retrieve
923  // the information from the main tab for our window.
924  NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
925  if (!enclosing_window)
926    return gfx::Rect();
927
928  NSRect bounds = [enclosing_window frame];
929  return FlipNSRectToRectScreen(bounds);
930}
931
932void RenderWidgetHostViewMac::SetActive(bool active) {
933  if (render_widget_host_)
934    render_widget_host_->SetActive(active);
935  if (HasFocus())
936    SetTextInputActive(active);
937  if (!active)
938    [cocoa_view_ setPluginImeActive:NO];
939}
940
941void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
942  if (render_widget_host_) {
943    render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
944        render_widget_host_->routing_id(), visible));
945  }
946}
947
948void RenderWidgetHostViewMac::WindowFrameChanged() {
949  if (render_widget_host_) {
950    render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
951        render_widget_host_->routing_id(), GetRootWindowRect(),
952        GetViewBounds()));
953  }
954}
955
956void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) {
957  RenderWidgetHostView::SetBackground(background);
958  if (render_widget_host_)
959    render_widget_host_->Send(new ViewMsg_SetBackground(
960        render_widget_host_->routing_id(), background));
961}
962
963bool RenderWidgetHostViewMac::ContainsNativeView(
964    gfx::NativeView native_view) const {
965  // TODO(port)
966  NOTREACHED() <<
967    "RenderWidgetHostViewMac::ContainsNativeView not implemented.";
968  return false;
969}
970
971void RenderWidgetHostViewMac::OnAccessibilityNotifications(
972    const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
973  if (!browser_accessibility_manager_.get()) {
974    // Use empty document to process notifications
975    webkit_glue::WebAccessibility empty_document;
976    empty_document.role = WebAccessibility::ROLE_WEB_AREA;
977    empty_document.state = 0;
978    browser_accessibility_manager_.reset(
979        BrowserAccessibilityManager::Create(cocoa_view_, empty_document, NULL));
980  }
981  browser_accessibility_manager_->OnAccessibilityNotifications(params);
982}
983
984void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
985  if (active) {
986    if (text_input_type_ == WebKit::WebTextInputTypePassword)
987      EnablePasswordInput();
988    else
989      DisablePasswordInput();
990  } else {
991    if (text_input_type_ == WebKit::WebTextInputTypePassword)
992      DisablePasswordInput();
993  }
994}
995
996// RenderWidgetHostViewCocoa ---------------------------------------------------
997
998@implementation RenderWidgetHostViewCocoa
999
1000@synthesize caretRect = caretRect_;
1001
1002- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1003  self = [super initWithFrame:NSZeroRect];
1004  if (self) {
1005    editCommand_helper_.reset(new RWHVMEditCommandHelper);
1006    editCommand_helper_->AddEditingSelectorsToClass([self class]);
1007
1008    renderWidgetHostView_.reset(r);
1009    canBeKeyView_ = YES;
1010    focusedPluginIdentifier_ = -1;
1011  }
1012  return self;
1013}
1014
1015- (void)setCanBeKeyView:(BOOL)can {
1016  canBeKeyView_ = can;
1017}
1018
1019- (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
1020  takesFocusOnlyOnMouseDown_ = b;
1021}
1022
1023- (void)setCloseOnDeactivate:(BOOL)b {
1024  closeOnDeactivate_ = b;
1025}
1026
1027- (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1028  NSWindow* window = [self window];
1029  // If this is a background window, don't handle mouse movement events. This
1030  // is the expected behavior on the Mac as evidenced by other applications.
1031  // Do this only if the window level is NSNormalWindowLevel, as this
1032  // does not necessarily apply in other contexts (e.g. balloons).
1033  if ([theEvent type] == NSMouseMoved &&
1034      [window level] == NSNormalWindowLevel && ![window isKeyWindow]) {
1035    return YES;
1036  }
1037
1038  // Use hitTest to check whether the mouse is over a nonWebContentView - in
1039  // which case the mouse event should not be handled by the render host.
1040  const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1041  NSView* contentView = [window contentView];
1042  NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1043  // Traverse the superview hierarchy as the hitTest will return the frontmost
1044  // view, such as an NSTextView, while nonWebContentView may be specified by
1045  // its parent view.
1046  while (view) {
1047    if ([view respondsToSelector:nonWebContentViewSelector] &&
1048        [view performSelector:nonWebContentViewSelector]) {
1049      // The cursor is over a nonWebContentView - ignore this mouse event.
1050      return YES;
1051    }
1052    view = [view superview];
1053  }
1054  return NO;
1055}
1056
1057- (void)mouseEvent:(NSEvent*)theEvent {
1058  if ([self shouldIgnoreMouseEvent:theEvent]) {
1059    // If this is the first such event, send a mouse exit to the host view.
1060    if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1061      WebMouseEvent exitEvent =
1062          WebInputEventFactory::mouseEvent(theEvent, self);
1063      exitEvent.type = WebInputEvent::MouseLeave;
1064      exitEvent.button = WebMouseEvent::ButtonNone;
1065      renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(exitEvent);
1066    }
1067    mouseEventWasIgnored_ = YES;
1068    return;
1069  }
1070
1071  if (mouseEventWasIgnored_) {
1072    // If this is the first mouse event after a previous event that was ignored
1073    // due to the hitTest, send a mouse enter event to the host view.
1074    if (renderWidgetHostView_->render_widget_host_) {
1075      WebMouseEvent enterEvent =
1076          WebInputEventFactory::mouseEvent(theEvent, self);
1077      enterEvent.type = WebInputEvent::MouseMove;
1078      enterEvent.button = WebMouseEvent::ButtonNone;
1079      renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(enterEvent);
1080    }
1081  }
1082  mouseEventWasIgnored_ = NO;
1083
1084  // TODO(rohitrao): Probably need to handle other mouse down events here.
1085  if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
1086    if (renderWidgetHostView_->render_widget_host_)
1087      renderWidgetHostView_->render_widget_host_->OnMouseActivate();
1088
1089    // Manually take focus after the click but before forwarding it to the
1090    // renderer.
1091    [[self window] makeFirstResponder:self];
1092  }
1093
1094  // Don't cancel child popups; killing them on a mouse click would prevent the
1095  // user from positioning the insertion point in the text field spawning the
1096  // popup. A click outside the text field would cause the text field to drop
1097  // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1098  // the popup anyway, so we're OK.
1099
1100  NSEventType type = [theEvent type];
1101  if (type == NSLeftMouseDown)
1102    hasOpenMouseDown_ = YES;
1103  else if (type == NSLeftMouseUp)
1104    hasOpenMouseDown_ = NO;
1105
1106  // TODO(suzhe): We should send mouse events to the input method first if it
1107  // wants to handle them. But it won't work without implementing method
1108  // - (NSUInteger)characterIndexForPoint:.
1109  // See: http://code.google.com/p/chromium/issues/detail?id=47141
1110  // Instead of sending mouse events to the input method first, we now just
1111  // simply confirm all ongoing composition here.
1112  if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1113      type == NSOtherMouseDown) {
1114    [self confirmComposition];
1115  }
1116
1117  const WebMouseEvent& event =
1118      WebInputEventFactory::mouseEvent(theEvent, self);
1119
1120  if (renderWidgetHostView_->render_widget_host_)
1121    renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
1122}
1123
1124- (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1125  // |performKeyEquivalent:| is sent to all views of a window, not only down the
1126  // responder chain (cf. "Handling Key Equivalents" in
1127  // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1128  // ). We only want to handle key equivalents if we're first responder.
1129  if ([[self window] firstResponder] != self)
1130    return NO;
1131
1132  // If we return |NO| from this function, cocoa will send the key event to
1133  // the menu and only if the menu does not process the event to |keyDown:|. We
1134  // want to send the event to a renderer _before_ sending it to the menu, so
1135  // we need to return |YES| for all events that might be swallowed by the menu.
1136  // We do not return |YES| for every keypress because we don't get |keyDown:|
1137  // events for keys that we handle this way.
1138  NSUInteger modifierFlags = [theEvent modifierFlags];
1139  if ((modifierFlags & NSCommandKeyMask) == 0) {
1140    // Make sure the menu does not contain key equivalents that don't
1141    // contain cmd.
1142    DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1143    return NO;
1144  }
1145
1146  // Command key combinations are sent via performKeyEquivalent rather than
1147  // keyDown:. We just forward this on and if WebCore doesn't want to handle
1148  // it, we let the TabContentsView figure out how to reinject it.
1149  [self keyEvent:theEvent wasKeyEquivalent:YES];
1150  return YES;
1151}
1152
1153- (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1154  // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1155  // returned NO. If this function returns |YES|, Cocoa sends the event to
1156  // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1157  // to us instead of doing key view loop control, ctrl-left/right get handled
1158  // correctly, etc.
1159  // (However, there are still some keys that Cocoa swallows, e.g. the key
1160  // equivalent that Cocoa uses for toggling the input langauge. In this case,
1161  // that's actually a good thing, though -- see http://crbug.com/26115 .)
1162  return YES;
1163}
1164
1165- (void)keyEvent:(NSEvent*)theEvent {
1166  [self keyEvent:theEvent wasKeyEquivalent:NO];
1167}
1168
1169- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1170  DCHECK([theEvent type] != NSKeyDown ||
1171         !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1172
1173  if ([theEvent type] == NSFlagsChanged) {
1174    // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1175    // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1176    int keyCode = [theEvent keyCode];
1177    if (!keyCode || keyCode == 10 || keyCode == 63)
1178      return;
1179  }
1180
1181  // Don't cancel child popups; the key events are probably what's triggering
1182  // the popup in the first place.
1183
1184  RenderWidgetHost* widgetHost = renderWidgetHostView_->render_widget_host_;
1185  DCHECK(widgetHost);
1186
1187  NativeWebKeyboardEvent event(theEvent);
1188
1189  // We only handle key down events and just simply forward other events.
1190  if ([theEvent type] != NSKeyDown) {
1191    widgetHost->ForwardKeyboardEvent(event);
1192
1193    // Possibly autohide the cursor.
1194    if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
1195      [NSCursor setHiddenUntilMouseMoves:YES];
1196
1197    return;
1198  }
1199
1200  scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
1201
1202  // Records the current marked text state, so that we can know if the marked
1203  // text was deleted or not after handling the key down event.
1204  BOOL oldHasMarkedText = hasMarkedText_;
1205
1206  // This method should not be called recursively.
1207  DCHECK(!handlingKeyDown_);
1208
1209  // Tells insertText: and doCommandBySelector: that we are handling a key
1210  // down event.
1211  handlingKeyDown_ = YES;
1212
1213  // These variables might be set when handling the keyboard event.
1214  // Clear them here so that we can know whether they have changed afterwards.
1215  textToBeInserted_.clear();
1216  markedText_.clear();
1217  underlines_.clear();
1218  unmarkTextCalled_ = NO;
1219  hasEditCommands_ = NO;
1220  editCommands_.clear();
1221
1222  // Before doing anything with a key down, check to see if plugin IME has been
1223  // cancelled, since the plugin host needs to be informed of that before
1224  // receiving the keydown.
1225  if ([theEvent type] == NSKeyDown)
1226    [self checkForPluginImeCancellation];
1227
1228  // Sends key down events to input method first, then we can decide what should
1229  // be done according to input method's feedback.
1230  // If a plugin is active, bypass this step since events are forwarded directly
1231  // to the plugin IME.
1232  if (focusedPluginIdentifier_ == -1)
1233    [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
1234
1235  handlingKeyDown_ = NO;
1236
1237  // Indicates if we should send the key event and corresponding editor commands
1238  // after processing the input method result.
1239  BOOL delayEventUntilAfterImeCompostion = NO;
1240
1241  // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
1242  // while an input method is composing or inserting a text.
1243  // Gmail checks this code in its onkeydown handler to stop auto-completing
1244  // e-mail addresses while composing a CJK text.
1245  // If the text to be inserted has only one character, then we don't need this
1246  // trick, because we'll send the text as a key press event instead.
1247  if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
1248    NativeWebKeyboardEvent fakeEvent = event;
1249    fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
1250    fakeEvent.setKeyIdentifierFromWindowsKeyCode();
1251    fakeEvent.skip_in_browser = true;
1252    widgetHost->ForwardKeyboardEvent(fakeEvent);
1253    // If this key event was handled by the input method, but
1254    // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
1255    // enqueued edit commands, then in order to let webkit handle them
1256    // correctly, we need to send the real key event and corresponding edit
1257    // commands after processing the input method result.
1258    // We shouldn't do this if a new marked text was set by the input method,
1259    // otherwise the new marked text might be cancelled by webkit.
1260    if (hasEditCommands_ && !hasMarkedText_)
1261      delayEventUntilAfterImeCompostion = YES;
1262  } else {
1263    if (!editCommands_.empty())
1264      widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_);
1265    widgetHost->ForwardKeyboardEvent(event);
1266  }
1267
1268  // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
1269  // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
1270  // be set to NULL. So we check it here and return immediately if it's NULL.
1271  if (!renderWidgetHostView_->render_widget_host_)
1272    return;
1273
1274  // Then send keypress and/or composition related events.
1275  // If there was a marked text or the text to be inserted is longer than 1
1276  // character, then we send the text by calling ConfirmComposition().
1277  // Otherwise, if the text to be inserted only contains 1 character, then we
1278  // can just send a keypress event which is fabricated by changing the type of
1279  // the keydown event, so that we can retain all necessary informations, such
1280  // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
1281  // prevent the browser from handling it again.
1282  // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
1283  // handle BMP characters here, as we can always insert non-BMP characters as
1284  // text.
1285  BOOL textInserted = NO;
1286  if (textToBeInserted_.length() >
1287      ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
1288    widgetHost->ImeConfirmComposition(textToBeInserted_);
1289    textInserted = YES;
1290  }
1291
1292  // Updates or cancels the composition. If some text has been inserted, then
1293  // we don't need to cancel the composition explicitly.
1294  if (hasMarkedText_ && markedText_.length()) {
1295    // Sends the updated marked text to the renderer so it can update the
1296    // composition node in WebKit.
1297    // When marked text is available, |selectedRange_| will be the range being
1298    // selected inside the marked text.
1299    widgetHost->ImeSetComposition(markedText_, underlines_,
1300                                  selectedRange_.location,
1301                                  NSMaxRange(selectedRange_));
1302  } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
1303    if (unmarkTextCalled_)
1304      widgetHost->ImeConfirmComposition();
1305    else
1306      widgetHost->ImeCancelComposition();
1307  }
1308
1309  // If the key event was handled by the input method but it also generated some
1310  // edit commands, then we need to send the real key event and corresponding
1311  // edit commands here. This usually occurs when the input method wants to
1312  // finish current composition session but still wants the application to
1313  // handle the key event. See http://crbug.com/48161 for reference.
1314  if (delayEventUntilAfterImeCompostion) {
1315    // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
1316    // with windowsKeyCode == 0xE5 has already been sent to webkit.
1317    // So before sending the real key down event, we need to send a fake key up
1318    // event to balance it.
1319    NativeWebKeyboardEvent fakeEvent = event;
1320    fakeEvent.type = WebKit::WebInputEvent::KeyUp;
1321    fakeEvent.skip_in_browser = true;
1322    widgetHost->ForwardKeyboardEvent(fakeEvent);
1323    // Not checking |renderWidgetHostView_->render_widget_host_| here because
1324    // a key event with |skip_in_browser| == true won't be handled by browser,
1325    // thus it won't destroy the widget.
1326
1327    if (!editCommands_.empty())
1328      widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_);
1329    widgetHost->ForwardKeyboardEvent(event);
1330
1331    // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
1332    // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
1333    // be set to NULL. So we check it here and return immediately if it's NULL.
1334    if (!renderWidgetHostView_->render_widget_host_)
1335      return;
1336  }
1337
1338  const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
1339  // Only send a corresponding key press event if there is no marked text.
1340  if (!hasMarkedText_) {
1341    if (!textInserted && textToBeInserted_.length() == 1) {
1342      // If a single character was inserted, then we just send it as a keypress
1343      // event.
1344      event.type = WebKit::WebInputEvent::Char;
1345      event.text[0] = textToBeInserted_[0];
1346      event.text[1] = 0;
1347      event.skip_in_browser = true;
1348      widgetHost->ForwardKeyboardEvent(event);
1349    } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
1350               [[theEvent characters] length] > 0 &&
1351               (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
1352                (hasEditCommands_ && editCommands_.empty()))) {
1353      // We don't get insertText: calls if ctrl or cmd is down, or the key event
1354      // generates an insert command. So synthesize a keypress event for these
1355      // cases, unless the key event generated any other command.
1356      event.type = WebKit::WebInputEvent::Char;
1357      event.skip_in_browser = true;
1358      widgetHost->ForwardKeyboardEvent(event);
1359    }
1360  }
1361
1362  // Possibly autohide the cursor.
1363  if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
1364    [NSCursor setHiddenUntilMouseMoves:YES];
1365}
1366
1367- (void)scrollWheel:(NSEvent *)theEvent {
1368  [self cancelChildPopups];
1369
1370  const WebMouseWheelEvent& event =
1371      WebInputEventFactory::mouseWheelEvent(theEvent, self);
1372  if (renderWidgetHostView_->render_widget_host_)
1373    renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event);
1374}
1375
1376// See the comment in RenderWidgetHostViewMac::Destroy() about cancellation
1377// events. On the Mac we must kill popups on outside events, thus this lovely
1378// case of filicide caused by events on parent views.
1379- (void)cancelChildPopups {
1380  // If this view can be the key view, it is not a popup. Therefore, if it has
1381  // any children, they are popups that need to be canceled.
1382  if (canBeKeyView_) {
1383    for (NSView* subview in [self subviews]) {
1384      if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]])
1385        continue;  // Skip plugin views.
1386
1387      [static_cast<RenderWidgetHostViewCocoa*>(subview)
1388          renderWidgetHostViewMac]->KillSelf();
1389    }
1390  }
1391}
1392
1393- (void)setFrameSize:(NSSize)newSize {
1394  [super setFrameSize:newSize];
1395  if (renderWidgetHostView_->render_widget_host_)
1396    renderWidgetHostView_->render_widget_host_->WasResized();
1397}
1398
1399- (void)setFrame:(NSRect)frameRect {
1400  [super setFrame:frameRect];
1401  if (renderWidgetHostView_->render_widget_host_)
1402    renderWidgetHostView_->render_widget_host_->WasResized();
1403}
1404
1405- (void)setFrameWithDeferredUpdate:(NSRect)frameRect {
1406  [super setFrame:frameRect];
1407  [self performSelector:@selector(renderWidgetHostWasResized)
1408             withObject:nil
1409             afterDelay:0];
1410}
1411
1412- (void)renderWidgetHostWasResized {
1413  if (renderWidgetHostView_->render_widget_host_)
1414    renderWidgetHostView_->render_widget_host_->WasResized();
1415}
1416
1417- (void)callSetNeedsDisplayInRect {
1418  DCHECK([NSThread isMainThread]);
1419  DCHECK(renderWidgetHostView_->call_set_needs_display_in_rect_pending_);
1420  [self setNeedsDisplayInRect:renderWidgetHostView_->invalid_rect_];
1421  renderWidgetHostView_->call_set_needs_display_in_rect_pending_ = false;
1422  renderWidgetHostView_->invalid_rect_ = NSZeroRect;
1423
1424  renderWidgetHostView_->HandleDelayedGpuViewHiding();
1425}
1426
1427// Fills with white the parts of the area to the right and bottom for |rect|
1428// that intersect |damagedRect|.
1429- (void)fillBottomRightRemainderOfRect:(gfx::Rect)rect
1430                             dirtyRect:(gfx::Rect)damagedRect {
1431  if (damagedRect.right() > rect.right()) {
1432    int x = std::max(rect.right(), damagedRect.x());
1433    int y = std::min(rect.bottom(), damagedRect.bottom());
1434    int width = damagedRect.right() - x;
1435    int height = damagedRect.y() - y;
1436
1437    // Extra fun to get around the fact that gfx::Rects can't have
1438    // negative sizes.
1439    if (width < 0) {
1440      x += width;
1441      width = -width;
1442    }
1443    if (height < 0) {
1444      y += height;
1445      height = -height;
1446    }
1447
1448    NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
1449    [[NSColor whiteColor] set];
1450    NSRectFill(r);
1451  }
1452  if (damagedRect.bottom() > rect.bottom()) {
1453    int x = damagedRect.x();
1454    int y = damagedRect.bottom();
1455    int width = damagedRect.right() - x;
1456    int height = std::max(rect.bottom(), damagedRect.y()) - y;
1457
1458    // Extra fun to get around the fact that gfx::Rects can't have
1459    // negative sizes.
1460    if (width < 0) {
1461      x += width;
1462      width = -width;
1463    }
1464    if (height < 0) {
1465      y += height;
1466      height = -height;
1467    }
1468
1469    NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
1470    [[NSColor whiteColor] set];
1471    NSRectFill(r);
1472  }
1473}
1474
1475- (void)drawRect:(NSRect)dirtyRect {
1476  if (!renderWidgetHostView_->render_widget_host_) {
1477    // TODO(shess): Consider using something more noticable?
1478    [[NSColor whiteColor] set];
1479    NSRectFill(dirtyRect);
1480    return;
1481  }
1482
1483  const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
1484
1485  if (renderWidgetHostView_->render_widget_host_->
1486      is_accelerated_compositing_active()) {
1487    gfx::Rect gpuRect;
1488
1489    gfx::PluginWindowHandle root_handle =
1490       renderWidgetHostView_->plugin_container_manager_.root_container_handle();
1491    if (root_handle != gfx::kNullPluginWindow) {
1492      AcceleratedPluginView* view =
1493          renderWidgetHostView_->ViewForPluginWindowHandle(root_handle);
1494      DCHECK(view);
1495      if (view && ![view isHidden]) {
1496        NSRect frame = [view frame];
1497        frame.size = [view cachedSize];
1498        gpuRect = [self flipNSRectToRect:frame];
1499      }
1500    }
1501
1502    [self fillBottomRightRemainderOfRect:gpuRect dirtyRect:damagedRect];
1503    return;
1504  }
1505
1506  DCHECK(!renderWidgetHostView_->about_to_validate_and_paint_);
1507
1508  renderWidgetHostView_->about_to_validate_and_paint_ = true;
1509  BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
1510      renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
1511  renderWidgetHostView_->about_to_validate_and_paint_ = false;
1512
1513  if (backingStore) {
1514    gfx::Rect bitmapRect(0, 0,
1515                         backingStore->size().width(),
1516                         backingStore->size().height());
1517
1518    // Specify the proper y offset to ensure that the view is rooted to the
1519    // upper left corner.  This can be negative, if the window was resized
1520    // smaller and the renderer hasn't yet repainted.
1521    int yOffset = NSHeight([self bounds]) - backingStore->size().height();
1522
1523    gfx::Rect paintRect = bitmapRect.Intersect(damagedRect);
1524    if (!paintRect.IsEmpty()) {
1525      // if we have a CGLayer, draw that into the window
1526      if (backingStore->cg_layer()) {
1527        CGContextRef context = static_cast<CGContextRef>(
1528            [[NSGraphicsContext currentContext] graphicsPort]);
1529
1530        // TODO: add clipping to dirtyRect if it improves drawing performance.
1531        CGContextDrawLayerAtPoint(context, CGPointMake(0.0, yOffset),
1532                                  backingStore->cg_layer());
1533      } else {
1534        // if we haven't created a layer yet, draw the cached bitmap into
1535        // the window.  The CGLayer will be created the next time the renderer
1536        // paints.
1537        CGContextRef context = static_cast<CGContextRef>(
1538            [[NSGraphicsContext currentContext] graphicsPort]);
1539        base::mac::ScopedCFTypeRef<CGImageRef> image(
1540            CGBitmapContextCreateImage(backingStore->cg_bitmap()));
1541        CGRect imageRect = bitmapRect.ToCGRect();
1542        imageRect.origin.y = yOffset;
1543        CGContextDrawImage(context, imageRect, image);
1544      }
1545    }
1546
1547    // Fill the remaining portion of the damagedRect with white
1548    [self fillBottomRightRemainderOfRect:bitmapRect dirtyRect:damagedRect];
1549
1550    if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
1551      base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
1552          renderWidgetHostView_->whiteout_start_time_;
1553      UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
1554
1555      // Reset the start time to 0 so that we start recording again the next
1556      // time the backing store is NULL...
1557      renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks();
1558    }
1559    if (!renderWidgetHostView_->tab_switch_paint_time_.is_null()) {
1560      base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() -
1561          renderWidgetHostView_->tab_switch_paint_time_;
1562      UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1563          tab_switch_paint_duration);
1564      // Reset tab_switch_paint_time_ to 0 so future tab selections are
1565      // recorded.
1566      renderWidgetHostView_->tab_switch_paint_time_ = base::TimeTicks();
1567    }
1568  } else {
1569    [[NSColor whiteColor] set];
1570    NSRectFill(dirtyRect);
1571    if (renderWidgetHostView_->whiteout_start_time_.is_null())
1572      renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks::Now();
1573  }
1574}
1575
1576- (BOOL)canBecomeKeyView {
1577  if (!renderWidgetHostView_->render_widget_host_)
1578    return NO;
1579
1580  return canBeKeyView_;
1581}
1582
1583- (BOOL)acceptsFirstResponder {
1584  if (!renderWidgetHostView_->render_widget_host_)
1585    return NO;
1586
1587  return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
1588}
1589
1590- (BOOL)becomeFirstResponder {
1591  if (!renderWidgetHostView_->render_widget_host_)
1592    return NO;
1593
1594  renderWidgetHostView_->render_widget_host_->Focus();
1595  renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
1596  renderWidgetHostView_->SetTextInputActive(true);
1597
1598  // Cancel any onging composition text which was left before we lost focus.
1599  // TODO(suzhe): We should do it in -resignFirstResponder: method, but
1600  // somehow that method won't be called when switching among different tabs.
1601  // See http://crbug.com/47209
1602  [self cancelComposition];
1603
1604  NSNumber* direction = [NSNumber numberWithUnsignedInteger:
1605      [[self window] keyViewSelectionDirection]];
1606  NSDictionary* userInfo =
1607      [NSDictionary dictionaryWithObject:direction
1608                                  forKey:kSelectionDirection];
1609  [[NSNotificationCenter defaultCenter]
1610      postNotificationName:kViewDidBecomeFirstResponder
1611                    object:self
1612                  userInfo:userInfo];
1613
1614  return YES;
1615}
1616
1617- (BOOL)resignFirstResponder {
1618  renderWidgetHostView_->SetTextInputActive(false);
1619  if (!renderWidgetHostView_->render_widget_host_)
1620    return YES;
1621
1622  if (closeOnDeactivate_)
1623    renderWidgetHostView_->KillSelf();
1624
1625  renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
1626  renderWidgetHostView_->render_widget_host_->Blur();
1627
1628  // We should cancel any onging composition whenever RWH's Blur() method gets
1629  // called, because in this case, webkit will confirm the ongoing composition
1630  // internally.
1631  [self cancelComposition];
1632
1633  return YES;
1634}
1635
1636- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
1637  SEL action = [item action];
1638
1639  // For now, these actions are always enabled for render view,
1640  // this is sub-optimal.
1641  // TODO(suzhe): Plumb the "can*" methods up from WebCore.
1642  if (action == @selector(undo:) ||
1643      action == @selector(redo:) ||
1644      action == @selector(cut:) ||
1645      action == @selector(copy:) ||
1646      action == @selector(copyToFindPboard:) ||
1647      action == @selector(paste:) ||
1648      action == @selector(pasteAsPlainText:) ||
1649      action == @selector(checkSpelling:)) {
1650    return renderWidgetHostView_->render_widget_host_->IsRenderView();
1651  }
1652
1653  if (action == @selector(toggleContinuousSpellChecking:)) {
1654    RenderViewHost::CommandState state;
1655    state.is_enabled = false;
1656    state.checked_state = RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED;
1657    if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
1658      state = static_cast<RenderViewHost*>(
1659          renderWidgetHostView_->render_widget_host_)->
1660              GetStateForCommand(RENDER_VIEW_COMMAND_TOGGLE_SPELL_CHECK);
1661    }
1662    if ([(id)item respondsToSelector:@selector(setState:)]) {
1663      NSCellStateValue checked_state =
1664          RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED;
1665      switch (state.checked_state) {
1666        case RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED:
1667          checked_state = NSOffState;
1668          break;
1669        case RENDER_VIEW_COMMAND_CHECKED_STATE_CHECKED:
1670          checked_state = NSOnState;
1671          break;
1672        case RENDER_VIEW_COMMAND_CHECKED_STATE_MIXED:
1673          checked_state = NSMixedState;
1674          break;
1675      }
1676      [(id)item setState:checked_state];
1677    }
1678    return state.is_enabled;
1679  }
1680
1681  return editCommand_helper_->IsMenuItemEnabled(action, self);
1682}
1683
1684- (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
1685  return renderWidgetHostView_.get();
1686}
1687
1688// Determine whether we should autohide the cursor (i.e., hide it until mouse
1689// move) for the given event. Customize here to be more selective about which
1690// key presses to autohide on.
1691+ (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
1692  return ([event type] == NSKeyDown) ? YES : NO;
1693}
1694
1695- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
1696                                         index:(NSUInteger)index
1697                                      maxCount:(NSUInteger)maxCount {
1698  NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1699  NSUInteger totalLength = [fullArray count];
1700  if (index >= totalLength)
1701    return nil;
1702  NSUInteger length = MIN(totalLength - index, maxCount);
1703  return [fullArray subarrayWithRange:NSMakeRange(index, length)];
1704}
1705
1706- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
1707  NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1708  return [fullArray count];
1709}
1710
1711- (id)accessibilityAttributeValue:(NSString *)attribute {
1712  BrowserAccessibilityManager* manager =
1713      renderWidgetHostView_->browser_accessibility_manager_.get();
1714
1715  // Contents specifies document view of RenderWidgetHostViewCocoa provided by
1716  // BrowserAccessibilityManager. Children includes all subviews in addition to
1717  // contents. Currently we do not have subviews besides the document view.
1718  if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
1719          [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
1720      manager) {
1721    return [NSArray arrayWithObjects:manager->
1722        GetRoot()->toBrowserAccessibilityCocoa(), nil];
1723  } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
1724    return NSAccessibilityScrollAreaRole;
1725  }
1726  id ret = [super accessibilityAttributeValue:attribute];
1727  return ret;
1728}
1729
1730- (NSArray*)accessibilityAttributeNames {
1731  NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1732  [ret addObject:NSAccessibilityContentsAttribute];
1733  [ret addObjectsFromArray:[super accessibilityAttributeNames]];
1734  return ret;
1735}
1736
1737- (id)accessibilityHitTest:(NSPoint)point {
1738  if (!renderWidgetHostView_->browser_accessibility_manager_.get())
1739    return self;
1740  NSPoint pointInWindow = [[self window] convertScreenToBase:point];
1741  NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
1742  localPoint.y = NSHeight([self bounds]) - localPoint.y;
1743  BrowserAccessibilityCocoa* root = renderWidgetHostView_->
1744      browser_accessibility_manager_->
1745          GetRoot()->toBrowserAccessibilityCocoa();
1746  id obj = [root accessibilityHitTest:localPoint];
1747  return obj;
1748}
1749
1750- (BOOL)accessibilityIsIgnored {
1751  return NO;
1752}
1753
1754- (NSUInteger)accessibilityGetIndexOf:(id)child {
1755  BrowserAccessibilityManager* manager =
1756      renderWidgetHostView_->browser_accessibility_manager_.get();
1757  // Only child is root.
1758  if (manager &&
1759      manager->GetRoot()->toBrowserAccessibilityCocoa() == child) {
1760    return 0;
1761  } else {
1762    return NSNotFound;
1763  }
1764}
1765
1766- (id)accessibilityFocusedUIElement {
1767  BrowserAccessibilityManager* manager =
1768      renderWidgetHostView_->browser_accessibility_manager_.get();
1769  if (manager) {
1770    BrowserAccessibility* focused_item = manager->GetFocus(NULL);
1771    DCHECK(focused_item);
1772    if (focused_item) {
1773      BrowserAccessibilityCocoa* focused_item_cocoa =
1774          focused_item->toBrowserAccessibilityCocoa();
1775      DCHECK(focused_item_cocoa);
1776      if (focused_item_cocoa)
1777        return focused_item_cocoa;
1778    }
1779  }
1780  return [super accessibilityFocusedUIElement];
1781}
1782
1783- (void)doDefaultAction:(int32)accessibilityObjectId {
1784  renderWidgetHostView_->render_widget_host_->
1785      AccessibilityDoDefaultAction(accessibilityObjectId);
1786}
1787
1788// Convert a web accessibility's location in web coordinates into a cocoa
1789// screen coordinate.
1790- (NSPoint)accessibilityPointInScreen:
1791    (BrowserAccessibilityCocoa*)accessibility {
1792  NSPoint origin = [accessibility origin];
1793  NSSize size = [accessibility size];
1794  origin.y = NSHeight([self bounds]) - origin.y;
1795  NSPoint originInWindow = [self convertPoint:origin toView:nil];
1796  NSPoint originInScreen = [[self window] convertBaseToScreen:originInWindow];
1797  originInScreen.y = originInScreen.y - size.height;
1798  return originInScreen;
1799}
1800
1801- (void)setAccessibilityFocus:(BOOL)focus
1802              accessibilityId:(int32)accessibilityObjectId {
1803  if (focus) {
1804    renderWidgetHostView_->render_widget_host_->
1805        SetAccessibilityFocus(accessibilityObjectId);
1806  }
1807}
1808
1809// Spellchecking methods
1810// The next three methods are implemented here since this class is the first
1811// responder for anything in the browser.
1812
1813// This message is sent whenever the user specifies that a word should be
1814// changed from the spellChecker.
1815- (void)changeSpelling:(id)sender {
1816  // Grab the currently selected word from the spell panel, as this is the word
1817  // that we want to replace the selected word in the text with.
1818  NSString* newWord = [[sender selectedCell] stringValue];
1819  if (newWord != nil) {
1820    RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
1821    thisHostView->GetRenderWidgetHost()->Replace(
1822        base::SysNSStringToUTF16(newWord));
1823  }
1824}
1825
1826// This message is sent by NSSpellChecker whenever the next word should be
1827// advanced to, either after a correction or clicking the "Find Next" button.
1828// This isn't documented anywhere useful, like in NSSpellProtocol.h with the
1829// other spelling panel methods. This is probably because Apple assumes that the
1830// the spelling panel will be used with an NSText, which will automatically
1831// catch this and advance to the next word for you. Thanks Apple.
1832// This is also called from the Edit -> Spelling -> Check Spelling menu item.
1833- (void)checkSpelling:(id)sender {
1834  RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
1835  thisHostView->GetRenderWidgetHost()->AdvanceToNextMisspelling();
1836}
1837
1838// This message is sent by the spelling panel whenever a word is ignored.
1839- (void)ignoreSpelling:(id)sender {
1840  // Ideally, we would ask the current RenderView for its tag, but that would
1841  // mean making a blocking IPC call from the browser. Instead,
1842  // SpellCheckerPlatform::CheckSpelling remembers the last tag and
1843  // SpellCheckerPlatform::IgnoreWord assumes that is the correct tag.
1844  NSString* wordToIgnore = [sender stringValue];
1845  if (wordToIgnore != nil)
1846    SpellCheckerPlatform::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore));
1847}
1848
1849- (void)showGuessPanel:(id)sender {
1850  RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
1851  thisHostView->GetRenderWidgetHost()->ToggleSpellPanel(
1852      SpellCheckerPlatform::SpellingPanelVisible());
1853}
1854
1855- (void)toggleContinuousSpellChecking:(id)sender {
1856  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
1857    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
1858      ToggleSpellCheck();
1859  }
1860}
1861
1862// END Spellchecking methods
1863
1864// Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
1865// with minor modifications for code style and commenting.
1866//
1867//  The 'public' interface is -setToolTipAtMousePoint:. This differs from
1868// -setToolTip: in that the updated tooltip takes effect immediately,
1869//  without the user's having to move the mouse out of and back into the view.
1870//
1871// Unfortunately, doing this requires sending fake mouseEnter/Exit events to
1872// the view, which in turn requires overriding some internal tracking-rect
1873// methods (to keep track of its owner & userdata, which need to be filled out
1874// in the fake events.) --snej 7/6/09
1875
1876
1877/*
1878 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
1879 *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
1880 *
1881 * Redistribution and use in source and binary forms, with or without
1882 * modification, are permitted provided that the following conditions
1883 * are met:
1884 *
1885 * 1.  Redistributions of source code must retain the above copyright
1886 *     notice, this list of conditions and the following disclaimer.
1887 * 2.  Redistributions in binary form must reproduce the above copyright
1888 *     notice, this list of conditions and the following disclaimer in the
1889 *     documentation and/or other materials provided with the distribution.
1890 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
1891 *     its contributors may be used to endorse or promote products derived
1892 *     from this software without specific prior written permission.
1893 *
1894 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
1895 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1896 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1897 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
1898 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1899 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1900 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1901 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1902 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1903 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1904 */
1905
1906// Any non-zero value will do, but using something recognizable might help us
1907// debug some day.
1908static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
1909
1910// Override of a public NSView method, replacing the inherited functionality.
1911// See above for rationale.
1912- (NSTrackingRectTag)addTrackingRect:(NSRect)rect
1913                               owner:(id)owner
1914                            userData:(void *)data
1915                        assumeInside:(BOOL)assumeInside {
1916  DCHECK(trackingRectOwner_ == nil);
1917  trackingRectOwner_ = owner;
1918  trackingRectUserData_ = data;
1919  return kTrackingRectTag;
1920}
1921
1922// Override of (apparently) a private NSView method(!) See above for rationale.
1923- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
1924                                owner:(id)owner
1925                             userData:(void *)data
1926                         assumeInside:(BOOL)assumeInside
1927                       useTrackingNum:(int)tag {
1928  DCHECK(tag == 0 || tag == kTrackingRectTag);
1929  DCHECK(trackingRectOwner_ == nil);
1930  trackingRectOwner_ = owner;
1931  trackingRectUserData_ = data;
1932  return kTrackingRectTag;
1933}
1934
1935// Override of (apparently) a private NSView method(!) See above for rationale.
1936- (void)_addTrackingRects:(NSRect *)rects
1937                    owner:(id)owner
1938             userDataList:(void **)userDataList
1939         assumeInsideList:(BOOL *)assumeInsideList
1940             trackingNums:(NSTrackingRectTag *)trackingNums
1941                    count:(int)count {
1942  DCHECK(count == 1);
1943  DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
1944  DCHECK(trackingRectOwner_ == nil);
1945  trackingRectOwner_ = owner;
1946  trackingRectUserData_ = userDataList[0];
1947  trackingNums[0] = kTrackingRectTag;
1948}
1949
1950// Override of a public NSView method, replacing the inherited functionality.
1951// See above for rationale.
1952- (void)removeTrackingRect:(NSTrackingRectTag)tag {
1953  if (tag == 0)
1954    return;
1955
1956  if (tag == kTrackingRectTag) {
1957    trackingRectOwner_ = nil;
1958    return;
1959  }
1960
1961  if (tag == lastToolTipTag_) {
1962    [super removeTrackingRect:tag];
1963    lastToolTipTag_ = 0;
1964    return;
1965  }
1966
1967  // If any other tracking rect is being removed, we don't know how it was
1968  // created and it's possible there's a leak involved (see Radar 3500217).
1969  NOTREACHED();
1970}
1971
1972// Override of (apparently) a private NSView method(!)
1973- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
1974  for (int i = 0; i < count; ++i) {
1975    int tag = tags[i];
1976    if (tag == 0)
1977      continue;
1978    DCHECK(tag == kTrackingRectTag);
1979    trackingRectOwner_ = nil;
1980  }
1981}
1982
1983// Sends a fake NSMouseExited event to the view for its current tracking rect.
1984- (void)_sendToolTipMouseExited {
1985  // Nothing matters except window, trackingNumber, and userData.
1986  int windowNumber = [[self window] windowNumber];
1987  NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1988                                              location:NSMakePoint(0, 0)
1989                                         modifierFlags:0
1990                                             timestamp:0
1991                                          windowNumber:windowNumber
1992                                               context:NULL
1993                                           eventNumber:0
1994                                        trackingNumber:kTrackingRectTag
1995                                              userData:trackingRectUserData_];
1996  [trackingRectOwner_ mouseExited:fakeEvent];
1997}
1998
1999// Sends a fake NSMouseEntered event to the view for its current tracking rect.
2000- (void)_sendToolTipMouseEntered {
2001  // Nothing matters except window, trackingNumber, and userData.
2002  int windowNumber = [[self window] windowNumber];
2003  NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2004                                              location:NSMakePoint(0, 0)
2005                                         modifierFlags:0
2006                                             timestamp:0
2007                                          windowNumber:windowNumber
2008                                               context:NULL
2009                                           eventNumber:0
2010                                        trackingNumber:kTrackingRectTag
2011                                              userData:trackingRectUserData_];
2012  [trackingRectOwner_ mouseEntered:fakeEvent];
2013}
2014
2015// Sets the view's current tooltip, to be displayed at the current mouse
2016// location. (This does not make the tooltip appear -- as usual, it only
2017// appears after a delay.) Pass null to remove the tooltip.
2018- (void)setToolTipAtMousePoint:(NSString *)string {
2019  NSString *toolTip = [string length] == 0 ? nil : string;
2020  if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2021      (!toolTip && !toolTip_)) {
2022    return;
2023  }
2024
2025  if (toolTip_) {
2026    [self _sendToolTipMouseExited];
2027  }
2028
2029  toolTip_.reset([toolTip copy]);
2030
2031  if (toolTip) {
2032    // See radar 3500217 for why we remove all tooltips
2033    // rather than just the single one we created.
2034    [self removeAllToolTips];
2035    NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2036    lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2037                                     owner:self
2038                                  userData:NULL];
2039    [self _sendToolTipMouseEntered];
2040  }
2041}
2042
2043// NSView calls this to get the text when displaying the tooltip.
2044- (NSString *)view:(NSView *)view
2045  stringForToolTip:(NSToolTipTag)tag
2046             point:(NSPoint)point
2047          userData:(void *)data {
2048  return [[toolTip_ copy] autorelease];
2049}
2050
2051// Below is our NSTextInput implementation.
2052//
2053// When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2054// functions to process this event.
2055//
2056// [WebHTMLView keyDown] ->
2057//     EventHandler::keyEvent() ->
2058//     ...
2059//     [WebEditorClient handleKeyboardEvent] ->
2060//     [WebHTMLView _interceptEditingKeyEvent] ->
2061//     [NSResponder interpretKeyEvents] ->
2062//     [WebHTMLView insertText] ->
2063//     Editor::insertText()
2064//
2065// Unfortunately, it is hard for Chromium to use this implementation because
2066// it causes key-typing jank.
2067// RenderWidgetHostViewMac is running in a browser process. On the other
2068// hand, Editor and EventHandler are running in a renderer process.
2069// So, if we used this implementation, a NSKeyDown event is dispatched to
2070// the following functions of Chromium.
2071//
2072// [RenderWidgetHostViewMac keyEvent] (browser) ->
2073//     |Sync IPC (KeyDown)| (*1) ->
2074//     EventHandler::keyEvent() (renderer) ->
2075//     ...
2076//     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2077//     |Sync IPC| (*2) ->
2078//     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2079//     [self interpretKeyEvents] ->
2080//     [RenderWidgetHostViewMac insertText] (browser) ->
2081//     |Async IPC| ->
2082//     Editor::insertText() (renderer)
2083//
2084// (*1) we need to wait until this call finishes since WebHTMLView uses the
2085// result of EventHandler::keyEvent().
2086// (*2) we need to wait until this call finishes since WebEditorClient uses
2087// the result of [WebHTMLView _interceptEditingKeyEvent].
2088//
2089// This needs many sync IPC messages sent between a browser and a renderer for
2090// each key event, which would probably result in key-typing jank.
2091// To avoid this problem, this implementation processes key events (and input
2092// method events) totally in a browser process and sends asynchronous input
2093// events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2094// renderer process.
2095//
2096// [RenderWidgetHostViewMac keyEvent] (browser) ->
2097//     |Async IPC (RawKeyDown)| ->
2098//     [self interpretKeyEvents] ->
2099//     [RenderWidgetHostViewMac insertText] (browser) ->
2100//     |Async IPC (Char)| ->
2101//     Editor::insertText() (renderer)
2102//
2103// Since this implementation doesn't have to wait any IPC calls, this doesn't
2104// make any key-typing jank. --hbono 7/23/09
2105//
2106extern "C" {
2107extern NSString *NSTextInputReplacementRangeAttributeName;
2108}
2109
2110- (NSArray *)validAttributesForMarkedText {
2111  // This code is just copied from WebKit except renaming variables.
2112  if (!validAttributesForMarkedText_) {
2113    validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2114        NSUnderlineStyleAttributeName,
2115        NSUnderlineColorAttributeName,
2116        NSMarkedClauseSegmentAttributeName,
2117        NSTextInputReplacementRangeAttributeName,
2118        nil]);
2119  }
2120  return validAttributesForMarkedText_.get();
2121}
2122
2123- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2124  NOTIMPLEMENTED();
2125  return NSNotFound;
2126}
2127
2128- (NSRect)firstRectForCharacterRange:(NSRange)theRange {
2129  // An input method requests a cursor rectangle to display its candidate
2130  // window.
2131  // Calculate the screen coordinate of the cursor rectangle saved in
2132  // RenderWidgetHostViewMac::ImeUpdateTextInputState() and send it to the
2133  // input method.
2134  // Since this window may be moved since we receive the cursor rectangle last
2135  // time we sent the cursor rectangle to the input method, so we should map
2136  // from the view coordinate to the screen coordinate every time when an input
2137  // method need it.
2138  NSRect resultRect = [self convertRect:caretRect_ toView:nil];
2139  NSWindow* window = [self window];
2140  if (window)
2141    resultRect.origin = [window convertBaseToScreen:resultRect.origin];
2142
2143  return resultRect;
2144}
2145
2146- (NSRange)selectedRange {
2147  // Return the selected range saved in the setMarkedText method.
2148  return hasMarkedText_ ? selectedRange_ : NSMakeRange(NSNotFound, 0);
2149}
2150
2151- (NSRange)markedRange {
2152  // An input method calls this method to check if an application really has
2153  // a text being composed when hasMarkedText call returns true.
2154  // Returns the range saved in the setMarkedText method so the input method
2155  // calls the setMarkedText method and we can update the composition node
2156  // there. (When this method returns an empty range, the input method doesn't
2157  // call the setMarkedText method.)
2158  return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2159}
2160
2161- (NSAttributedString *)attributedSubstringFromRange:(NSRange)range {
2162  // TODO(hbono): Even though many input method works without implementing
2163  // this method, we need to save a copy of the string in the setMarkedText
2164  // method and create a NSAttributedString with the given range.
2165  // http://crbug.com/37715
2166  return nil;
2167}
2168
2169- (NSInteger)conversationIdentifier {
2170  return reinterpret_cast<NSInteger>(self);
2171}
2172
2173// Each RenderWidgetHostViewCocoa has its own input context, but we return
2174// nil when the caret is in non-editable content or password box to avoid
2175// making input methods do their work.
2176- (NSTextInputContext *)inputContext {
2177  if (focusedPluginIdentifier_ != -1)
2178    return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2179
2180  switch(renderWidgetHostView_->text_input_type_) {
2181    case WebKit::WebTextInputTypeNone:
2182    case WebKit::WebTextInputTypePassword:
2183      return nil;
2184    default:
2185      return [super inputContext];
2186  }
2187}
2188
2189- (BOOL)hasMarkedText {
2190  // An input method calls this function to figure out whether or not an
2191  // application is really composing a text. If it is composing, it calls
2192  // the markedRange method, and maybe calls the setMarkedText method.
2193  // It seems an input method usually calls this function when it is about to
2194  // cancel an ongoing composition. If an application has a non-empty marked
2195  // range, it calls the setMarkedText method to delete the range.
2196  return hasMarkedText_;
2197}
2198
2199- (void)unmarkText {
2200  // Delete the composition node of the renderer and finish an ongoing
2201  // composition.
2202  // It seems an input method calls the setMarkedText method and set an empty
2203  // text when it cancels an ongoing composition, i.e. I have never seen an
2204  // input method calls this method.
2205  hasMarkedText_ = NO;
2206  markedText_.clear();
2207  underlines_.clear();
2208
2209  // If we are handling a key down event, then ConfirmComposition() will be
2210  // called in keyEvent: method.
2211  if (!handlingKeyDown_)
2212    renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
2213  else
2214    unmarkTextCalled_ = YES;
2215}
2216
2217- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange {
2218  // An input method updates the composition string.
2219  // We send the given text and range to the renderer so it can update the
2220  // composition node of WebKit.
2221  BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2222  NSString* im_text = isAttributedString ? [string string] : string;
2223  int length = [im_text length];
2224
2225  markedRange_ = NSMakeRange(0, length);
2226  selectedRange_ = newSelRange;
2227  markedText_ = base::SysNSStringToUTF16(im_text);
2228  hasMarkedText_ = (length > 0);
2229
2230  underlines_.clear();
2231  if (isAttributedString) {
2232    ExtractUnderlines(string, &underlines_);
2233  } else {
2234    // Use a thin black underline by default.
2235    underlines_.push_back(
2236        WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
2237  }
2238
2239  // If we are handling a key down event, then SetComposition() will be
2240  // called in keyEvent: method.
2241  // Input methods of Mac use setMarkedText calls with an empty text to cancel
2242  // an ongoing composition. So, we should check whether or not the given text
2243  // is empty to update the input method state. (Our input method backend can
2244  // automatically cancels an ongoing composition when we send an empty text.
2245  // So, it is OK to send an empty text to the renderer.)
2246  if (!handlingKeyDown_) {
2247    renderWidgetHostView_->render_widget_host_->ImeSetComposition(
2248        markedText_, underlines_,
2249        newSelRange.location, NSMaxRange(newSelRange));
2250  }
2251}
2252
2253- (void)doCommandBySelector:(SEL)selector {
2254  // An input method calls this function to dispatch an editing command to be
2255  // handled by this view.
2256  if (selector == @selector(noop:))
2257    return;
2258
2259  std::string command(
2260      [RWHVMEditCommandHelper::CommandNameForSelector(selector) UTF8String]);
2261
2262  // If this method is called when handling a key down event, then we need to
2263  // handle the command in the key event handler. Otherwise we can just handle
2264  // it here.
2265  if (handlingKeyDown_) {
2266    hasEditCommands_ = YES;
2267    // We ignore commands that insert characters, because this was causing
2268    // strange behavior (e.g. tab always inserted a tab rather than moving to
2269    // the next field on the page).
2270    if (!StartsWithASCII(command, "insert", false))
2271      editCommands_.push_back(EditCommand(command, ""));
2272  } else {
2273    renderWidgetHostView_->render_widget_host_->ForwardEditCommand(command, "");
2274  }
2275}
2276
2277- (void)insertText:(id)string {
2278  // An input method has characters to be inserted.
2279  // Same as Linux, Mac calls this method not only:
2280  // * when an input method finishs composing text, but also;
2281  // * when we type an ASCII character (without using input methods).
2282  // When we aren't using input methods, we should send the given character as
2283  // a Char event so it is dispatched to an onkeypress() event handler of
2284  // JavaScript.
2285  // On the other hand, when we are using input methods, we should send the
2286  // given characters as an input method event and prevent the characters from
2287  // being dispatched to onkeypress() event handlers.
2288  // Text inserting might be initiated by other source instead of keyboard
2289  // events, such as the Characters dialog. In this case the text should be
2290  // sent as an input method event as well.
2291  BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2292  NSString* im_text = isAttributedString ? [string string] : string;
2293  if (handlingKeyDown_) {
2294    textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
2295  } else {
2296    renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2297        base::SysNSStringToUTF16(im_text));
2298  }
2299
2300  // Inserting text will delete all marked text automatically.
2301  hasMarkedText_ = NO;
2302}
2303
2304- (void)viewDidMoveToWindow {
2305  if (canBeKeyView_) {
2306    NSWindow* newWindow = [self window];
2307    // Pointer comparison only, since we don't know if lastWindow_ is still
2308    // valid.
2309    if (newWindow) {
2310      // If we move into a new window, refresh the frame information. We
2311      // don't need to do it if it was the same window as it used to be in,
2312      // since that case is covered by DidBecomeSelected. We only want to
2313      // do this for real browser views, not popups.
2314      if (newWindow != lastWindow_) {
2315        lastWindow_ = newWindow;
2316        renderWidgetHostView_->WindowFrameChanged();
2317      }
2318      renderWidgetHostView_->ForceTextureReload();
2319    }
2320  }
2321
2322  // If we switch windows (or are removed from the view hierarchy), cancel any
2323  // open mouse-downs.
2324  if (hasOpenMouseDown_) {
2325    WebMouseEvent event;
2326    event.type = WebInputEvent::MouseUp;
2327    event.button = WebMouseEvent::ButtonLeft;
2328    if (renderWidgetHostView_->render_widget_host_)
2329      renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
2330
2331    hasOpenMouseDown_ = NO;
2332  }
2333}
2334
2335- (void)undo:(id)sender {
2336  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2337    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2338      Undo();
2339  }
2340}
2341
2342- (void)redo:(id)sender {
2343  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2344    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2345      Redo();
2346  }
2347}
2348
2349- (void)cut:(id)sender {
2350  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2351    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2352      Cut();
2353  }
2354}
2355
2356- (void)copy:(id)sender {
2357  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2358    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2359      Copy();
2360  }
2361}
2362
2363- (void)copyToFindPboard:(id)sender {
2364  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2365    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2366      CopyToFindPboard();
2367  }
2368}
2369
2370- (void)paste:(id)sender {
2371  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2372    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2373      Paste();
2374  }
2375}
2376
2377- (void)pasteAsPlainText:(id)sender {
2378  if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
2379    static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
2380      ForwardEditCommand("PasteAndMatchStyle", "");
2381  }
2382}
2383
2384- (void)cancelComposition {
2385  if (!hasMarkedText_)
2386    return;
2387
2388  // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
2389  // doesn't call any NSTextInput functions, such as setMarkedText or
2390  // insertText. So, we need to send an IPC message to a renderer so it can
2391  // delete the composition node.
2392  NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2393  [currentInputManager markedTextAbandoned:self];
2394
2395  hasMarkedText_ = NO;
2396  // Should not call [self unmarkText] here, because it'll send unnecessary
2397  // cancel composition IPC message to the renderer.
2398}
2399
2400- (void)confirmComposition {
2401  if (!hasMarkedText_)
2402    return;
2403
2404  if (renderWidgetHostView_->render_widget_host_)
2405    renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
2406
2407  [self cancelComposition];
2408}
2409
2410- (void)setPluginImeActive:(BOOL)active {
2411  if (active == pluginImeActive_)
2412    return;
2413
2414  pluginImeActive_ = active;
2415  if (!active) {
2416    [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
2417    renderWidgetHostView_->PluginImeCompositionCompleted(
2418        string16(), focusedPluginIdentifier_);
2419  }
2420}
2421
2422- (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
2423  if (focused)
2424    focusedPluginIdentifier_ = pluginId;
2425  else if (focusedPluginIdentifier_ == pluginId)
2426    focusedPluginIdentifier_ = -1;
2427
2428  // Whenever plugin focus changes, plugin IME resets.
2429  [self setPluginImeActive:NO];
2430}
2431
2432- (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
2433  if (!pluginImeActive_)
2434    return false;
2435
2436  ComplexTextInputPanel* inputPanel =
2437      [ComplexTextInputPanel sharedComplexTextInputPanel];
2438  NSString* composited_string = nil;
2439  BOOL handled = [inputPanel interpretKeyEvent:event
2440                                        string:&composited_string];
2441  if (composited_string) {
2442    renderWidgetHostView_->PluginImeCompositionCompleted(
2443        base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
2444    pluginImeActive_ = NO;
2445  }
2446  return handled;
2447}
2448
2449- (void)checkForPluginImeCancellation {
2450  if (pluginImeActive_ &&
2451      ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
2452    renderWidgetHostView_->PluginImeCompositionCompleted(
2453        string16(), focusedPluginIdentifier_);
2454    pluginImeActive_ = NO;
2455  }
2456}
2457
2458- (ViewID)viewID {
2459  return VIEW_ID_TAB_CONTAINER_FOCUS_VIEW;
2460}
2461
2462// Overriding a NSResponder method to support application services.
2463
2464- (id)validRequestorForSendType:(NSString*)sendType
2465                     returnType:(NSString*)returnType {
2466  id requestor = nil;
2467  BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
2468  BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
2469  BOOL hasText = !renderWidgetHostView_->selected_text().empty();
2470  BOOL takesText =
2471      renderWidgetHostView_->text_input_type_ != WebKit::WebTextInputTypeNone;
2472
2473  if (sendTypeIsString && hasText && !returnType) {
2474    requestor = self;
2475  } else if (!sendType && returnTypeIsString && takesText) {
2476    requestor = self;
2477  } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
2478    requestor = self;
2479  } else {
2480    requestor = [super validRequestorForSendType:sendType
2481                                      returnType:returnType];
2482  }
2483  return requestor;
2484}
2485
2486@end
2487
2488//
2489// Supporting application services
2490//
2491@implementation RenderWidgetHostViewCocoa(NSServicesRequests)
2492
2493- (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
2494                             types:(NSArray*)types {
2495  const std::string& str = renderWidgetHostView_->selected_text();
2496  if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
2497
2498  scoped_nsobject<NSString> text([[NSString alloc]
2499                                   initWithUTF8String:str.c_str()]);
2500  NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
2501  [pboard declareTypes:toDeclare owner:nil];
2502  return [pboard setString:text forType:NSStringPboardType];
2503}
2504
2505- (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
2506  NSString *string = [pboard stringForType:NSStringPboardType];
2507  if (!string) return NO;
2508
2509  // If the user is currently using an IME, confirm the IME input,
2510  // and then insert the text from the service, the same as TextEdit and Safari.
2511  [self confirmComposition];
2512  [self insertText:string];
2513  return YES;
2514}
2515
2516@end
2517