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