Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / render_widget_host_view_mac.mm
1 // Copyright (c) 2012 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 "content/browser/renderer_host/render_widget_host_view_mac.h"
6
7 #import <objc/runtime.h>
8 #include <OpenGL/gl.h>
9 #include <QuartzCore/QuartzCore.h>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/debug/crash_logging.h"
16 #include "base/debug/trace_event.h"
17 #include "base/logging.h"
18 #include "base/mac/mac_util.h"
19 #include "base/mac/scoped_cftyperef.h"
20 #import "base/mac/scoped_nsobject.h"
21 #include "base/mac/sdk_forward_declarations.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/histogram.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/sys_string_conversions.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/sys_info.h"
29 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
30 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
31 #import "content/browser/cocoa/system_hotkey_helper_mac.h"
32 #import "content/browser/cocoa/system_hotkey_map.h"
33 #include "content/browser/compositor/io_surface_layer_mac.h"
34 #include "content/browser/compositor/resize_lock.h"
35 #include "content/browser/compositor/software_layer_mac.h"
36 #include "content/browser/frame_host/frame_tree.h"
37 #include "content/browser/frame_host/frame_tree_node.h"
38 #include "content/browser/frame_host/render_frame_host_impl.h"
39 #include "content/browser/gpu/compositor_util.h"
40 #include "content/browser/renderer_host/render_widget_helper.h"
41 #include "content/browser/renderer_host/render_view_host_impl.h"
42 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
43 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
44 #import "content/browser/renderer_host/text_input_client_mac.h"
45 #include "content/common/accessibility_messages.h"
46 #include "content/common/edit_command.h"
47 #include "content/common/gpu/gpu_messages.h"
48 #include "content/common/gpu/surface_handle_types_mac.h"
49 #include "content/common/input_messages.h"
50 #include "content/common/view_messages.h"
51 #include "content/common/webplugin_geometry.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "content/public/browser/native_web_keyboard_event.h"
54 #include "content/public/browser/notification_service.h"
55 #include "content/public/browser/notification_types.h"
56 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
57 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
58 #include "content/public/browser/user_metrics.h"
59 #include "content/public/browser/web_contents.h"
60 #include "skia/ext/platform_canvas.h"
61 #include "skia/ext/skia_utils_mac.h"
62 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
63 #include "third_party/WebKit/public/web/WebInputEvent.h"
64 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
65 #import "third_party/mozilla/ComplexTextInputPanel.h"
66 #include "ui/base/cocoa/animation_utils.h"
67 #import "ui/base/cocoa/fullscreen_window_manager.h"
68 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
69 #include "ui/events/keycodes/keyboard_codes.h"
70 #include "ui/base/layout.h"
71 #include "ui/compositor/compositor.h"
72 #include "ui/compositor/layer.h"
73 #include "ui/gfx/display.h"
74 #include "ui/gfx/frame_time.h"
75 #include "ui/gfx/point.h"
76 #include "ui/gfx/rect_conversions.h"
77 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
78 #include "ui/gfx/screen.h"
79 #include "ui/gfx/size_conversions.h"
80 #include "ui/gl/gl_switches.h"
81
82 using content::BrowserAccessibility;
83 using content::BrowserAccessibilityManager;
84 using content::EditCommand;
85 using content::FrameTreeNode;
86 using content::NativeWebKeyboardEvent;
87 using content::RenderFrameHost;
88 using content::RenderViewHost;
89 using content::RenderViewHostImpl;
90 using content::RenderWidgetHostImpl;
91 using content::RenderWidgetHostViewMac;
92 using content::RenderWidgetHostViewMacEditCommandHelper;
93 using content::TextInputClientMac;
94 using content::WebContents;
95 using blink::WebInputEvent;
96 using blink::WebInputEventFactory;
97 using blink::WebMouseEvent;
98 using blink::WebMouseWheelEvent;
99 using blink::WebGestureEvent;
100
101 namespace {
102
103 // Whether a keyboard event has been reserved by OSX.
104 BOOL EventIsReservedBySystem(NSEvent* event) {
105   content::SystemHotkeyHelperMac* helper =
106       content::SystemHotkeyHelperMac::GetInstance();
107   return helper->map()->IsEventReserved(event);
108 }
109
110 }  // namespace
111
112 // These are not documented, so use only after checking -respondsToSelector:.
113 @interface NSApplication (UndocumentedSpeechMethods)
114 - (void)speakString:(NSString*)string;
115 - (void)stopSpeaking:(id)sender;
116 - (BOOL)isSpeaking;
117 @end
118
119 // This method will return YES for OS X versions 10.7.3 and later, and NO
120 // otherwise.
121 // Used to prevent a crash when building with the 10.7 SDK and accessing the
122 // notification below. See: http://crbug.com/260595.
123 static BOOL SupportsBackingPropertiesChangedNotification() {
124   // windowDidChangeBackingProperties: method has been added to the
125   // NSWindowDelegate protocol in 10.7.3, at the same time as the
126   // NSWindowDidChangeBackingPropertiesNotification notification was added.
127   // If the protocol contains this method description, the notification should
128   // be supported as well.
129   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
130   struct objc_method_description methodDescription =
131       protocol_getMethodDescription(
132           windowDelegateProtocol,
133           @selector(windowDidChangeBackingProperties:),
134           NO,
135           YES);
136
137   // If the protocol does not contain the method, the returned method
138   // description is {NULL, NULL}
139   return methodDescription.name != NULL || methodDescription.types != NULL;
140 }
141
142 // Private methods:
143 @interface RenderWidgetHostViewCocoa ()
144 @property(nonatomic, assign) NSRange selectedRange;
145 @property(nonatomic, assign) NSRange markedRange;
146
147 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
148 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
149 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
150                    consumed:(BOOL)consumed;
151
152 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
153 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
154 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
155 - (void)checkForPluginImeCancellation;
156 - (void)updateScreenProperties;
157 - (void)setResponderDelegate:
158         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
159 @end
160
161 // A window subclass that allows the fullscreen window to become main and gain
162 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
163 // handled by the browser.
164 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
165 @end
166
167 @implementation PepperFlashFullscreenWindow
168
169 - (BOOL)canBecomeKeyWindow {
170   return YES;
171 }
172
173 - (BOOL)canBecomeMainWindow {
174   return YES;
175 }
176
177 @end
178
179 @interface RenderWidgetPopupWindow : NSWindow {
180    // The event tap that allows monitoring of all events, to properly close with
181    // a click outside the bounds of the window.
182   id clickEventTap_;
183 }
184 @end
185
186 @implementation RenderWidgetPopupWindow
187
188 - (id)initWithContentRect:(NSRect)contentRect
189                 styleMask:(NSUInteger)windowStyle
190                   backing:(NSBackingStoreType)bufferingType
191                     defer:(BOOL)deferCreation {
192   if (self = [super initWithContentRect:contentRect
193                               styleMask:windowStyle
194                                 backing:bufferingType
195                                   defer:deferCreation]) {
196     [self setOpaque:NO];
197     [self setBackgroundColor:[NSColor clearColor]];
198     [self startObservingClicks];
199   }
200   return self;
201 }
202
203 - (void)close {
204   [self stopObservingClicks];
205   [super close];
206 }
207
208 // Gets called when the menubar is clicked.
209 // Needed because the local event monitor doesn't see the click on the menubar.
210 - (void)beganTracking:(NSNotification*)notification {
211   [self close];
212 }
213
214 // Install the callback.
215 - (void)startObservingClicks {
216   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
217       handler:^NSEvent* (NSEvent* event) {
218           if ([event window] == self)
219             return event;
220           NSEventType eventType = [event type];
221           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
222             [self close];
223           return event;
224   }];
225
226   NSNotificationCenter* notificationCenter =
227       [NSNotificationCenter defaultCenter];
228   [notificationCenter addObserver:self
229          selector:@selector(beganTracking:)
230              name:NSMenuDidBeginTrackingNotification
231            object:[NSApp mainMenu]];
232 }
233
234 // Remove the callback.
235 - (void)stopObservingClicks {
236   if (!clickEventTap_)
237     return;
238
239   [NSEvent removeMonitor:clickEventTap_];
240    clickEventTap_ = nil;
241
242   NSNotificationCenter* notificationCenter =
243       [NSNotificationCenter defaultCenter];
244   [notificationCenter removeObserver:self
245                 name:NSMenuDidBeginTrackingNotification
246               object:[NSApp mainMenu]];
247 }
248
249 @end
250
251 namespace {
252
253 // Maximum number of characters we allow in a tooltip.
254 const size_t kMaxTooltipLength = 1024;
255
256 // TODO(suzhe): Upstream this function.
257 blink::WebColor WebColorFromNSColor(NSColor *color) {
258   CGFloat r, g, b, a;
259   [color getRed:&r green:&g blue:&b alpha:&a];
260
261   return
262       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
263       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
264       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
265       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
266 }
267
268 // Extract underline information from an attributed string. Mostly copied from
269 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
270 void ExtractUnderlines(
271     NSAttributedString* string,
272     std::vector<blink::WebCompositionUnderline>* underlines) {
273   int length = [[string string] length];
274   int i = 0;
275   while (i < length) {
276     NSRange range;
277     NSDictionary* attrs = [string attributesAtIndex:i
278                               longestEffectiveRange:&range
279                                             inRange:NSMakeRange(i, length - i)];
280     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
281       blink::WebColor color = SK_ColorBLACK;
282       if (NSColor *colorAttr =
283           [attrs objectForKey:NSUnderlineColorAttributeName]) {
284         color = WebColorFromNSColor(
285             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
286       }
287       underlines->push_back(
288           blink::WebCompositionUnderline(range.location,
289                                          NSMaxRange(range),
290                                          color,
291                                          [style intValue] > 1,
292                                          SK_ColorTRANSPARENT));
293     }
294     i = range.location + range.length;
295   }
296 }
297
298 // EnablePasswordInput() and DisablePasswordInput() are copied from
299 // enableSecureTextInput() and disableSecureTextInput() functions in
300 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
301 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
302 // here, because they are already called in webkit and they are system wide
303 // functions.
304 void EnablePasswordInput() {
305   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
306   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
307                          sizeof(CFArrayRef), &inputSources);
308   CFRelease(inputSources);
309 }
310
311 void DisablePasswordInput() {
312   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
313 }
314
315 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
316 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
317 // value when screen info changes.
318 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
319 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
320 // is necessary.
321 bool g_screen_info_up_to_date = false;
322
323 float FlipYFromRectToScreen(float y, float rect_height) {
324   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
325   static CGFloat screen_zero_height = 0;
326   if (!g_screen_info_up_to_date) {
327     if ([[NSScreen screens] count] > 0) {
328       screen_zero_height =
329           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
330       g_screen_info_up_to_date = true;
331     } else {
332       return y;
333     }
334   }
335   return screen_zero_height - y - rect_height;
336 }
337
338 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
339 // left of the primary screen (Carbon coordinates), and stuffs it into a
340 // gfx::Rect.
341 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
342   gfx::Rect new_rect(NSRectToCGRect(rect));
343   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
344   return new_rect;
345 }
346
347 // Returns the window that visually contains the given view. This is different
348 // from [view window] in the case of tab dragging, where the view's owning
349 // window is a floating panel attached to the actual browser window that the tab
350 // is visually part of.
351 NSWindow* ApparentWindowForView(NSView* view) {
352   // TODO(shess): In case of !window, the view has been removed from
353   // the view hierarchy because the tab isn't main.  Could retrieve
354   // the information from the main tab for our window.
355   NSWindow* enclosing_window = [view window];
356
357   // See if this is a tab drag window. The width check is to distinguish that
358   // case from extension popup windows.
359   NSWindow* ancestor_window = [enclosing_window parentWindow];
360   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
361                           NSWidth([ancestor_window frame]))) {
362     enclosing_window = ancestor_window;
363   }
364
365   return enclosing_window;
366 }
367
368 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
369   gfx::Display display =
370       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
371
372   NSScreen* screen = [NSScreen deepestScreen];
373
374   blink::WebScreenInfo results;
375
376   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
377   results.depth = NSBitsPerPixelFromDepth([screen depth]);
378   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
379   results.isMonochrome =
380       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
381   results.rect = display.bounds();
382   results.availableRect = display.work_area();
383   results.orientationAngle = display.RotationAsDegree();
384   results.orientationType =
385       content::RenderWidgetHostViewBase::GetOrientationTypeForDesktop(display);
386
387   return results;
388 }
389
390 }  // namespace
391
392 namespace content {
393
394 ////////////////////////////////////////////////////////////////////////////////
395 // DelegatedFrameHost, public:
396
397 ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
398   // When |browser_compositor_view_| is suspended or destroyed, the connection
399   // between its ui::Compositor and |delegated_frame_host_| has been severed.
400   if (browser_compositor_state_ == BrowserCompositorActive)
401     return browser_compositor_view_->GetCompositor();
402   return NULL;
403 }
404
405 ui::Layer* RenderWidgetHostViewMac::GetLayer() {
406   return root_layer_.get();
407 }
408
409 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
410   return render_widget_host_;
411 }
412
413 bool RenderWidgetHostViewMac::IsVisible() {
414   return !render_widget_host_->is_hidden();
415 }
416
417 gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
418   return GetViewBounds().size();
419 }
420
421 float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
422   return ViewScaleFactor();
423 }
424
425 gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
426     const gfx::Size& size) {
427   return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
428                                              ViewScaleFactor())).size();
429 }
430
431 scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
432     bool defer_compositor_lock) {
433   NOTREACHED();
434   ResizeLock* lock = NULL;
435   return scoped_ptr<ResizeLock>(lock);
436 }
437
438 DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
439   return delegated_frame_host_.get();
440 }
441
442 ////////////////////////////////////////////////////////////////////////////////
443 // BrowserCompositorViewMacClient, public:
444
445 bool RenderWidgetHostViewMac::BrowserCompositorViewShouldAckImmediately()
446     const {
447   // If vsync is disabled, then always draw and ack frames immediately.
448   static bool is_vsync_disabled =
449       base::CommandLine::ForCurrentProcess()->HasSwitch(
450           switches::kDisableGpuVsync);
451   if (is_vsync_disabled)
452     return true;
453
454   // If the window is occluded, then this frame's display call may be severely
455   // throttled. This is a good thing, unless tab capture may be active, because
456   // the broadcast will be inappropriately throttled.
457   // http://crbug.com/350410
458
459   // If tab capture isn't active then only ack frames when we draw them.
460   if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
461     return false;
462
463   NSWindow* window = [cocoa_view_ window];
464   // If the view isn't even in the heirarchy then frames will never be drawn,
465   // so ack them immediately.
466   if (!window)
467     return true;
468
469   // Check the window occlusion API.
470   if ([window respondsToSelector:@selector(occlusionState)]) {
471     if ([window occlusionState] & NSWindowOcclusionStateVisible) {
472       // If the window is visible then it is safe to wait until frames are
473       // drawn to ack them.
474       return false;
475     } else {
476       // If the window is occluded then frames may never be drawn, so ack them
477       // immediately.
478       return true;
479     }
480   }
481
482   // If the window occlusion API is not present then ack frames when we draw
483   // them.
484   return false;
485 }
486
487 void RenderWidgetHostViewMac::BrowserCompositorViewFrameSwapped(
488     const std::vector<ui::LatencyInfo>& all_latency_info) {
489   if (!render_widget_host_)
490     return;
491   for (auto latency_info : all_latency_info) {
492     latency_info.AddLatencyNumber(
493         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
494     render_widget_host_->FrameSwapped(latency_info);
495   }
496 }
497
498 ///////////////////////////////////////////////////////////////////////////////
499 // RenderWidgetHostViewBase, public:
500
501 // static
502 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
503     blink::WebScreenInfo* results) {
504   *results = GetWebScreenInfo(NULL);
505 }
506
507 ///////////////////////////////////////////////////////////////////////////////
508 // RenderWidgetHostViewMac, public:
509
510 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget,
511                                                  bool is_guest_view_hack)
512     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
513       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
514       can_compose_inline_(true),
515       browser_compositor_state_(BrowserCompositorDestroyed),
516       browser_compositor_view_placeholder_(
517           new BrowserCompositorViewPlaceholderMac),
518       is_loading_(false),
519       allow_pause_for_resize_or_repaint_(true),
520       is_guest_view_hack_(is_guest_view_hack),
521       weak_factory_(this),
522       fullscreen_parent_host_view_(NULL) {
523   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
524   // goes away.  Since we autorelease it, our caller must put
525   // |GetNativeView()| into the view hierarchy right after calling us.
526   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
527                   initWithRenderWidgetHostViewMac:this] autorelease];
528
529   // Paint this view host with |background_color_| when there is no content
530   // ready to draw.
531   background_layer_.reset([[CALayer alloc] init]);
532   [background_layer_
533       setBackgroundColor:gfx::CGColorCreateFromSkColor(background_color_)];
534   [cocoa_view_ setLayer:background_layer_];
535   [cocoa_view_ setWantsLayer:YES];
536
537   if (IsDelegatedRendererEnabled()) {
538     root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
539     delegated_frame_host_.reset(new DelegatedFrameHost(this));
540   }
541
542   gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
543
544   if (!is_guest_view_hack_)
545     render_widget_host_->SetView(this);
546 }
547
548 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
549   gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
550
551   // This is being called from |cocoa_view_|'s destructor, so invalidate the
552   // pointer.
553   cocoa_view_ = nil;
554
555   UnlockMouse();
556
557   // Ensure that the browser compositor is destroyed in a safe order.
558   ShutdownBrowserCompositor();
559
560   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
561   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
562   // us.
563   if (render_widget_host_) {
564     // If this is a RenderWidgetHostViewGuest's platform_view_, we're not the
565     // RWH's view, the RenderWidgetHostViewGuest is. So don't reset the RWH's
566     // view, the RenderWidgetHostViewGuest will do it.
567     if (!is_guest_view_hack_)
568       render_widget_host_->SetView(NULL);
569   }
570 }
571
572 void RenderWidgetHostViewMac::SetDelegate(
573     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
574   [cocoa_view_ setResponderDelegate:delegate];
575 }
576
577 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
578   allow_pause_for_resize_or_repaint_ = allow;
579 }
580
581 ///////////////////////////////////////////////////////////////////////////////
582 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
583
584 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
585   TRACE_EVENT0("browser",
586                "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
587
588   // Create the view, to transition from Destroyed -> Suspended.
589   if (browser_compositor_state_ == BrowserCompositorDestroyed) {
590     browser_compositor_view_.reset(
591         new BrowserCompositorViewMac(this, cocoa_view_, root_layer_.get()));
592     browser_compositor_state_ = BrowserCompositorSuspended;
593   }
594
595   // Show the DelegatedFrameHost to transition from Suspended -> Active.
596   if (browser_compositor_state_ == BrowserCompositorSuspended) {
597     delegated_frame_host_->AddedToWindow();
598     delegated_frame_host_->WasShown(ui::LatencyInfo());
599     browser_compositor_state_ = BrowserCompositorActive;
600   }
601 }
602
603 void RenderWidgetHostViewMac::SuspendBrowserCompositorView() {
604   TRACE_EVENT0("browser",
605                "RenderWidgetHostViewMac::SuspendBrowserCompositorView");
606
607   // Hide the DelegatedFrameHost to transition from Active -> Suspended.
608   if (browser_compositor_state_ == BrowserCompositorActive) {
609     // Marking the DelegatedFrameHost as removed from the window hierarchy is
610     // necessary to remove all connections to its old ui::Compositor.
611     delegated_frame_host_->WasHidden();
612     delegated_frame_host_->RemovingFromWindow();
613     browser_compositor_state_ = BrowserCompositorSuspended;
614   }
615 }
616
617 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
618   TRACE_EVENT0("browser",
619                "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
620
621   // Transition from Active -> Suspended if need be.
622   SuspendBrowserCompositorView();
623
624   // Destroy the BrowserCompositorView to transition Suspended -> Destroyed.
625   if (browser_compositor_state_ == BrowserCompositorSuspended) {
626     browser_compositor_view_.reset();
627     browser_compositor_state_ = BrowserCompositorDestroyed;
628   }
629 }
630
631 void RenderWidgetHostViewMac::DestroySuspendedBrowserCompositorViewIfNeeded() {
632   if (browser_compositor_state_ != BrowserCompositorSuspended)
633     return;
634
635   // If this view is in a window that is visible, keep around the suspended
636   // BrowserCompositorView in case |cocoa_view_| is suddenly revealed (so that
637   // we don't flash white).
638   NSWindow* window = [cocoa_view_ window];
639   if (window)
640     return;
641
642   // This should only be reached if |render_widget_host_| is hidden, destroyed,
643   // or in the process of being destroyed.
644   DestroyBrowserCompositorView();
645 }
646
647 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
648   bool handled = true;
649   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
650     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
651     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
652     IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
653         OnGetRenderedTextCompleted)
654     IPC_MESSAGE_UNHANDLED(handled = false)
655   IPC_END_MESSAGE_MAP()
656   return handled;
657 }
658
659 void RenderWidgetHostViewMac::InitAsChild(
660     gfx::NativeView parent_view) {
661 }
662
663 void RenderWidgetHostViewMac::InitAsPopup(
664     RenderWidgetHostView* parent_host_view,
665     const gfx::Rect& pos) {
666   bool activatable = popup_type_ == blink::WebPopupTypeNone;
667   [cocoa_view_ setCloseOnDeactivate:YES];
668   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
669
670   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
671   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
672
673   popup_window_.reset([[RenderWidgetPopupWindow alloc]
674       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
675                                      pos.width(), pos.height())
676                 styleMask:NSBorderlessWindowMask
677                   backing:NSBackingStoreBuffered
678                     defer:NO]);
679   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
680   [popup_window_ setReleasedWhenClosed:NO];
681   [popup_window_ makeKeyAndOrderFront:nil];
682   [[popup_window_ contentView] addSubview:cocoa_view_];
683   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
684   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
685   [[NSNotificationCenter defaultCenter]
686       addObserver:cocoa_view_
687          selector:@selector(popupWindowWillClose:)
688              name:NSWindowWillCloseNotification
689            object:popup_window_];
690 }
691
692 // This function creates the fullscreen window and hides the dock and menubar if
693 // necessary. Note, this codepath is only used for pepper flash when
694 // pp::FlashFullScreen::SetFullscreen() is called. If
695 // pp::FullScreen::SetFullscreen() is called then the entire browser window
696 // will enter fullscreen instead.
697 void RenderWidgetHostViewMac::InitAsFullscreen(
698     RenderWidgetHostView* reference_host_view) {
699   fullscreen_parent_host_view_ =
700       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
701   NSWindow* parent_window = nil;
702   if (reference_host_view)
703     parent_window = [reference_host_view->GetNativeView() window];
704   NSScreen* screen = [parent_window screen];
705   if (!screen)
706     screen = [NSScreen mainScreen];
707
708   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
709       initWithContentRect:[screen frame]
710                 styleMask:NSBorderlessWindowMask
711                   backing:NSBackingStoreBuffered
712                     defer:NO]);
713   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
714   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
715   [cocoa_view_ setCanBeKeyView:YES];
716   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
717   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
718   // If the pepper fullscreen window isn't opaque then there are performance
719   // issues when it's on the discrete GPU and the Chrome window is being drawn
720   // to. http://crbug.com/171911
721   [pepper_fullscreen_window_ setOpaque:YES];
722
723   // Note that this forms a reference cycle between the fullscreen window and
724   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
725   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
726   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
727   // explicitly calls Shutdown on the render_widget_host_, which calls
728   // Destroy() on RWHVMac, which drops the reference to
729   // pepper_fullscreen_window_.
730   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
731
732   // Note that this keeps another reference to pepper_fullscreen_window_.
733   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
734       initWithWindow:pepper_fullscreen_window_.get()
735        desiredScreen:screen]);
736   [fullscreen_window_manager_ enterFullscreenMode];
737   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
738 }
739
740 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
741   // See comment in InitAsFullscreen(): There is a reference cycle between
742   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
743   // Tests that test pepper fullscreen mode without sending an <esc> event
744   // need to call this method to break the reference cycle.
745   [fullscreen_window_manager_ exitFullscreenMode];
746   fullscreen_window_manager_.reset();
747   [pepper_fullscreen_window_ close];
748   pepper_fullscreen_window_.reset();
749 }
750
751 int RenderWidgetHostViewMac::window_number() const {
752   NSWindow* window = [cocoa_view_ window];
753   if (!window)
754     return -1;
755   return [window windowNumber];
756 }
757
758 float RenderWidgetHostViewMac::ViewScaleFactor() const {
759   return ui::GetScaleFactorForNativeView(cocoa_view_);
760 }
761
762 void RenderWidgetHostViewMac::UpdateDisplayLink() {
763   static bool is_vsync_disabled =
764       base::CommandLine::ForCurrentProcess()->HasSwitch(
765           switches::kDisableGpuVsync);
766   if (is_vsync_disabled)
767     return;
768
769   NSScreen* screen = [[cocoa_view_ window] screen];
770   NSDictionary* screen_description = [screen deviceDescription];
771   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
772   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
773
774   display_link_ = DisplayLinkMac::GetForDisplay(display_id);
775   if (!display_link_.get()) {
776     // Note that on some headless systems, the display link will fail to be
777     // created, so this should not be a fatal error.
778     LOG(ERROR) << "Failed to create display link.";
779   }
780 }
781
782 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
783   if (!render_widget_host_ || !display_link_.get())
784     return;
785
786   if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
787     vsync_timebase_ = base::TimeTicks();
788     vsync_interval_ = base::TimeDelta();
789     return;
790   }
791
792   render_widget_host_->UpdateVSyncParameters(vsync_timebase_, vsync_interval_);
793 }
794
795 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
796   [NSApp speakString:base::SysUTF8ToNSString(text)];
797 }
798
799 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
800   if (!render_widget_host_)
801     return;
802   render_widget_host_->NotifyScreenInfoChanged();
803 }
804
805 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
806   return render_widget_host_;
807 }
808
809 void RenderWidgetHostViewMac::WasShown() {
810   if (!render_widget_host_->is_hidden())
811     return;
812
813   ui::LatencyInfo renderer_latency_info;
814   renderer_latency_info.AddLatencyNumber(
815       ui::TAB_SHOW_COMPONENT,
816       render_widget_host_->GetLatencyComponentId(),
817       0);
818   render_widget_host_->WasShown(renderer_latency_info);
819
820   // If there is not a frame being currently drawn, kick one, so that the below
821   // pause will have a frame to wait on.
822   render_widget_host_->ScheduleComposite();
823   PauseForPendingResizeOrRepaintsAndDraw();
824 }
825
826 void RenderWidgetHostViewMac::WasHidden() {
827   if (render_widget_host_->is_hidden())
828     return;
829
830   // If we have a renderer, then inform it that we are being hidden so it can
831   // reduce its resource utilization.
832   render_widget_host_->WasHidden();
833
834   SuspendBrowserCompositorView();
835   DestroySuspendedBrowserCompositorViewIfNeeded();
836 }
837
838 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
839   gfx::Rect rect = GetViewBounds();
840   rect.set_size(size);
841   SetBounds(rect);
842 }
843
844 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
845   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
846   // TODO(thakis): fix, http://crbug.com/73362
847   if (render_widget_host_->is_hidden())
848     return;
849
850   // During the initial creation of the RenderWidgetHostView in
851   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
852   // an empty size. In the Windows code flow, it is not ignored because
853   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
854   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
855   // flags to keep things sized properly. On the other hand, if the size is not
856   // empty then this is a valid request for a pop-up.
857   if (rect.size().IsEmpty())
858     return;
859
860   // Ignore the position of |rect| for non-popup rwhvs. This is because
861   // background tabs do not have a window, but the window is required for the
862   // coordinate conversions. Popups are always for a visible tab.
863   //
864   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
865   // valid for resizing to be requested (e.g., during tab capture, to size the
866   // view to screen-capture resolution). In this case, simply treat the view as
867   // relative to the screen.
868   BOOL isRelativeToScreen = IsPopup() ||
869       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
870   if (isRelativeToScreen) {
871     // The position of |rect| is screen coordinate system and we have to
872     // consider Cocoa coordinate system is upside-down and also multi-screen.
873     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
874     NSSize size = NSMakeSize(rect.width(), rect.height());
875     size = [cocoa_view_ convertSize:size toView:nil];
876     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
877     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
878                               size.width, size.height);
879     if (IsPopup())
880       [popup_window_ setFrame:frame display:YES];
881     else
882       [cocoa_view_ setFrame:frame];
883   } else {
884     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
885     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
886     rect2.set_width(rect.width());
887     rect2.set_height(rect.height());
888     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
889   }
890 }
891
892 gfx::Vector2dF RenderWidgetHostViewMac::GetLastScrollOffset() const {
893   return last_scroll_offset_;
894 }
895
896 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
897   return cocoa_view_;
898 }
899
900 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
901   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
902 }
903
904 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
905   NOTIMPLEMENTED();
906   return static_cast<gfx::NativeViewAccessible>(NULL);
907 }
908
909 void RenderWidgetHostViewMac::MovePluginWindows(
910     const std::vector<WebPluginGeometry>& moves) {
911   // Must be overridden, but unused on this platform. Core Animation
912   // plugins are drawn by the GPU process (through the compositor),
913   // and Core Graphics plugins are drawn by the renderer process.
914   DCHECK_CURRENTLY_ON(BrowserThread::UI);
915 }
916
917 void RenderWidgetHostViewMac::Focus() {
918   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
919 }
920
921 void RenderWidgetHostViewMac::Blur() {
922   UnlockMouse();
923   [[cocoa_view_ window] makeFirstResponder:nil];
924 }
925
926 bool RenderWidgetHostViewMac::HasFocus() const {
927   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
928 }
929
930 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
931   if (delegated_frame_host_)
932     return delegated_frame_host_->CanCopyToBitmap();
933   return false;
934 }
935
936 void RenderWidgetHostViewMac::Show() {
937   [cocoa_view_ setHidden:NO];
938
939   WasShown();
940 }
941
942 void RenderWidgetHostViewMac::Hide() {
943   [cocoa_view_ setHidden:YES];
944
945   WasHidden();
946 }
947
948 bool RenderWidgetHostViewMac::IsShowing() {
949   return ![cocoa_view_ isHidden];
950 }
951
952 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
953   NSRect bounds = [cocoa_view_ bounds];
954   // TODO(shess): In case of !window, the view has been removed from
955   // the view hierarchy because the tab isn't main.  Could retrieve
956   // the information from the main tab for our window.
957   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
958   if (!enclosing_window)
959     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
960
961   bounds = [cocoa_view_ convertRect:bounds toView:nil];
962   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
963   return FlipNSRectToRectScreen(bounds);
964 }
965
966 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
967   WebCursor web_cursor = cursor;
968   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
969 }
970
971 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
972   is_loading_ = is_loading;
973   // If we ever decide to show the waiting cursor while the page is loading
974   // like Chrome does on Windows, call |UpdateCursor()| here.
975 }
976
977 void RenderWidgetHostViewMac::TextInputTypeChanged(
978     ui::TextInputType type,
979     ui::TextInputMode input_mode,
980     bool can_compose_inline,
981     int flags) {
982   if (text_input_type_ != type
983       || can_compose_inline_ != can_compose_inline) {
984     text_input_type_ = type;
985     can_compose_inline_ = can_compose_inline;
986     if (HasFocus()) {
987       SetTextInputActive(true);
988
989       // Let AppKit cache the new input context to make IMEs happy.
990       // See http://crbug.com/73039.
991       [NSApp updateWindows];
992
993 #ifndef __LP64__
994       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
995 #endif
996     }
997   }
998 }
999
1000 void RenderWidgetHostViewMac::ImeCancelComposition() {
1001   [cocoa_view_ cancelComposition];
1002 }
1003
1004 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
1005     const gfx::Range& range,
1006     const std::vector<gfx::Rect>& character_bounds) {
1007   // The RangeChanged message is only sent with valid values. The current
1008   // caret position (start == end) will be sent if there is no IME range.
1009   [cocoa_view_ setMarkedRange:range.ToNSRange()];
1010   composition_range_ = range;
1011   composition_bounds_ = character_bounds;
1012 }
1013
1014 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
1015                                                 int error_code) {
1016   Destroy();
1017 }
1018
1019 void RenderWidgetHostViewMac::RenderWidgetHostGone() {
1020   // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
1021   // called on the view.
1022   // http://crbug.com/404828
1023   ShutdownBrowserCompositor();
1024 }
1025
1026 void RenderWidgetHostViewMac::Destroy() {
1027   [[NSNotificationCenter defaultCenter]
1028       removeObserver:cocoa_view_
1029                 name:NSWindowWillCloseNotification
1030               object:popup_window_];
1031
1032   // We've been told to destroy.
1033   [cocoa_view_ retain];
1034   [cocoa_view_ removeFromSuperview];
1035   [cocoa_view_ autorelease];
1036
1037   [popup_window_ close];
1038   popup_window_.autorelease();
1039
1040   [fullscreen_window_manager_ exitFullscreenMode];
1041   fullscreen_window_manager_.reset();
1042   [pepper_fullscreen_window_ close];
1043
1044   // This can be called as part of processing the window's responder
1045   // chain, for instance |-performKeyEquivalent:|.  In that case the
1046   // object needs to survive until the stack unwinds.
1047   pepper_fullscreen_window_.autorelease();
1048
1049   // Delete the delegated frame state, which will reach back into
1050   // render_widget_host_.
1051   ShutdownBrowserCompositor();
1052
1053   // We get this call just before |render_widget_host_| deletes
1054   // itself.  But we are owned by |cocoa_view_|, which may be retained
1055   // by some other code.  Examples are WebContentsViewMac's
1056   // |latent_focus_view_| and TabWindowController's
1057   // |cachedContentView_|.
1058   render_widget_host_ = NULL;
1059 }
1060
1061 // Called from the renderer to tell us what the tooltip text should be. It
1062 // calls us frequently so we need to cache the value to prevent doing a lot
1063 // of repeat work.
1064 void RenderWidgetHostViewMac::SetTooltipText(
1065     const base::string16& tooltip_text) {
1066   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1067     tooltip_text_ = tooltip_text;
1068
1069     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1070     // Windows; we're just trying to be polite. Don't persist the trimmed
1071     // string, as then the comparison above will always fail and we'll try to
1072     // set it again every single time the mouse moves.
1073     base::string16 display_text = tooltip_text_;
1074     if (tooltip_text_.length() > kMaxTooltipLength)
1075       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1076
1077     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1078     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1079   }
1080 }
1081
1082 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1083   return [NSApp respondsToSelector:@selector(speakString:)] &&
1084          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1085 }
1086
1087 void RenderWidgetHostViewMac::SpeakSelection() {
1088   if (![NSApp respondsToSelector:@selector(speakString:)])
1089     return;
1090
1091   if (selected_text_.empty() && render_widget_host_) {
1092     // If there's no selection, speak all text. Send an asynchronous IPC
1093     // request for fetching all the text for a webcontent.
1094     // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1095     render_widget_host_->Send(new ViewMsg_GetRenderedText(
1096         render_widget_host_->GetRoutingID()));
1097     return;
1098   }
1099
1100   SpeakText(selected_text_);
1101 }
1102
1103 bool RenderWidgetHostViewMac::IsSpeaking() const {
1104   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1105          [NSApp isSpeaking];
1106 }
1107
1108 void RenderWidgetHostViewMac::StopSpeaking() {
1109   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1110     [NSApp stopSpeaking:cocoa_view_];
1111 }
1112
1113 //
1114 // RenderWidgetHostViewCocoa uses the stored selection text,
1115 // which implements NSServicesRequests protocol.
1116 //
1117 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1118                                                size_t offset,
1119                                                const gfx::Range& range) {
1120   if (range.is_empty() || text.empty()) {
1121     selected_text_.clear();
1122   } else {
1123     size_t pos = range.GetMin() - offset;
1124     size_t n = range.length();
1125
1126     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1127     if (pos >= text.length()) {
1128       DCHECK(false) << "The text can not cover range.";
1129       return;
1130     }
1131     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1132   }
1133
1134   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1135   // Updates markedRange when there is no marked text so that retrieving
1136   // markedRange immediately after calling setMarkdText: returns the current
1137   // caret position.
1138   if (![cocoa_view_ hasMarkedText]) {
1139     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1140   }
1141
1142   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1143 }
1144
1145 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1146     const ViewHostMsg_SelectionBounds_Params& params) {
1147   if (params.anchor_rect == params.focus_rect)
1148     caret_rect_ = params.anchor_rect;
1149 }
1150
1151 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1152   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1153
1154   // Create a fake mouse event to inform the render widget that the mouse
1155   // left or entered.
1156   NSWindow* window = [cocoa_view_ window];
1157   // TODO(asvitkine): If the location outside of the event stream doesn't
1158   // correspond to the current event (due to delayed event processing), then
1159   // this may result in a cursor flicker if there are later mouse move events
1160   // in the pipeline. Find a way to use the mouse location from the event that
1161   // dismissed the context menu.
1162   NSPoint location = [window mouseLocationOutsideOfEventStream];
1163   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1164                                       location:location
1165                                  modifierFlags:0
1166                                      timestamp:0
1167                                   windowNumber:window_number()
1168                                        context:nil
1169                                    eventNumber:0
1170                                     clickCount:0
1171                                       pressure:0];
1172   WebMouseEvent web_event =
1173       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1174   if (showing)
1175     web_event.type = WebInputEvent::MouseLeave;
1176   ForwardMouseEvent(web_event);
1177 }
1178
1179 bool RenderWidgetHostViewMac::IsPopup() const {
1180   return popup_type_ != blink::WebPopupTypeNone;
1181 }
1182
1183 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1184     const gfx::Rect& src_subrect,
1185     const gfx::Size& dst_size,
1186     const base::Callback<void(bool, const SkBitmap&)>& callback,
1187     const SkColorType color_type) {
1188   if (delegated_frame_host_) {
1189     delegated_frame_host_->CopyFromCompositingSurface(
1190         src_subrect, dst_size, callback, color_type);
1191   }
1192 }
1193
1194 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1195       const gfx::Rect& src_subrect,
1196       const scoped_refptr<media::VideoFrame>& target,
1197       const base::Callback<void(bool)>& callback) {
1198   if (delegated_frame_host_) {
1199     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1200         src_subrect, target, callback);
1201   }
1202 }
1203
1204 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1205   if (delegated_frame_host_)
1206     return delegated_frame_host_->CanCopyToVideoFrame();
1207   return false;
1208 }
1209
1210 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1211   if (delegated_frame_host_)
1212     return delegated_frame_host_->CanSubscribeFrame();
1213   return false;
1214 }
1215
1216 void RenderWidgetHostViewMac::BeginFrameSubscription(
1217     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1218   if (delegated_frame_host_)
1219     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1220 }
1221
1222 void RenderWidgetHostViewMac::EndFrameSubscription() {
1223   if (delegated_frame_host_)
1224     delegated_frame_host_->EndFrameSubscription();
1225 }
1226
1227 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1228   if (render_widget_host_)
1229     render_widget_host_->ForwardMouseEvent(event);
1230
1231   if (event.type == WebInputEvent::MouseLeave) {
1232     [cocoa_view_ setToolTipAtMousePoint:nil];
1233     tooltip_text_.clear();
1234   }
1235 }
1236
1237 void RenderWidgetHostViewMac::KillSelf() {
1238   if (!weak_factory_.HasWeakPtrs()) {
1239     [cocoa_view_ setHidden:YES];
1240     base::MessageLoop::current()->PostTask(FROM_HERE,
1241         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1242                    weak_factory_.GetWeakPtr()));
1243   }
1244 }
1245
1246 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1247     const NativeWebKeyboardEvent& event) {
1248   // Check WebInputEvent type since multiple types of events can be sent into
1249   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1250   // necessary to avoid double processing.
1251   // Also check the native type, since NSFlagsChanged is considered a key event
1252   // for WebKit purposes, but isn't considered a key event by the OS.
1253   if (event.type == WebInputEvent::RawKeyDown &&
1254       [event.os_event type] == NSKeyDown)
1255     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1256   return false;
1257 }
1258
1259 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1260     const base::string16& text, int plugin_id) {
1261   if (render_widget_host_) {
1262     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1263         render_widget_host_->GetRoutingID(), text, plugin_id));
1264   }
1265 }
1266
1267 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1268     const std::vector<gfx::Rect>& bounds,
1269     const gfx::Range& range,
1270     size_t* line_break_point) {
1271   DCHECK(line_break_point);
1272   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1273     return false;
1274
1275   // We can't check line breaking completely from only rectangle array. Thus we
1276   // assume the line breaking as the next character's y offset is larger than
1277   // a threshold. Currently the threshold is determined as minimum y offset plus
1278   // 75% of maximum height.
1279   // TODO(nona): Check the threshold is reliable or not.
1280   // TODO(nona): Bidi support.
1281   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1282   int max_height = 0;
1283   int min_y_offset = kint32max;
1284   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1285     max_height = std::max(max_height, bounds[idx].height());
1286     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1287   }
1288   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1289   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1290     if (bounds[idx].y() > line_break_threshold) {
1291       *line_break_point = idx;
1292       return true;
1293     }
1294   }
1295   return false;
1296 }
1297
1298 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1299     const gfx::Range& range,
1300     gfx::Range* actual_range) {
1301   DCHECK(actual_range);
1302   DCHECK(!composition_bounds_.empty());
1303   DCHECK(range.start() <= composition_bounds_.size());
1304   DCHECK(range.end() <= composition_bounds_.size());
1305
1306   if (range.is_empty()) {
1307     *actual_range = range;
1308     if (range.start() == composition_bounds_.size()) {
1309       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1310                        composition_bounds_[range.start() - 1].y(),
1311                        0,
1312                        composition_bounds_[range.start() - 1].height());
1313     } else {
1314       return gfx::Rect(composition_bounds_[range.start()].x(),
1315                        composition_bounds_[range.start()].y(),
1316                        0,
1317                        composition_bounds_[range.start()].height());
1318     }
1319   }
1320
1321   size_t end_idx;
1322   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1323     end_idx = range.end();
1324   }
1325   *actual_range = gfx::Range(range.start(), end_idx);
1326   gfx::Rect rect = composition_bounds_[range.start()];
1327   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1328     rect.Union(composition_bounds_[i]);
1329   }
1330   return rect;
1331 }
1332
1333 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1334     const gfx::Range& request_range) {
1335   if (composition_range_.is_empty())
1336     return gfx::Range::InvalidRange();
1337
1338   if (request_range.is_reversed())
1339     return gfx::Range::InvalidRange();
1340
1341   if (request_range.start() < composition_range_.start() ||
1342       request_range.start() > composition_range_.end() ||
1343       request_range.end() > composition_range_.end()) {
1344     return gfx::Range::InvalidRange();
1345   }
1346
1347   return gfx::Range(
1348       request_range.start() - composition_range_.start(),
1349       request_range.end() - composition_range_.start());
1350 }
1351
1352 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1353   if (!render_widget_host_->IsRenderView())
1354     return NULL;
1355
1356   return WebContents::FromRenderViewHost(
1357       RenderViewHost::From(render_widget_host_));
1358 }
1359
1360 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1361     NSRange range,
1362     NSRect* rect,
1363     NSRange* actual_range) {
1364   DCHECK(rect);
1365   // This exists to make IMEs more responsive, see http://crbug.com/115920
1366   TRACE_EVENT0("browser",
1367                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1368
1369   // If requested range is same as caret location, we can just return it.
1370   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1371     if (actual_range)
1372       *actual_range = range;
1373     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1374     return true;
1375   }
1376
1377   const gfx::Range request_range_in_composition =
1378       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1379   if (request_range_in_composition == gfx::Range::InvalidRange())
1380     return false;
1381
1382   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1383   // ImeCompositionRangeChanged will be sent with empty vector.
1384   if (composition_bounds_.empty())
1385     return false;
1386   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1387
1388   gfx::Range ui_actual_range;
1389   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1390                                request_range_in_composition,
1391                                &ui_actual_range).ToCGRect());
1392   if (actual_range) {
1393     *actual_range = gfx::Range(
1394         composition_range_.start() + ui_actual_range.start(),
1395         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1396   }
1397   return true;
1398 }
1399
1400 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1401       const gfx::Size& desired_size) {
1402   if (browser_compositor_view_)
1403     return browser_compositor_view_->HasFrameOfSize(desired_size);
1404   return false;
1405 }
1406
1407 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1408     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1409   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1410
1411   last_scroll_offset_ = frame->metadata.root_scroll_offset;
1412   if (frame->delegated_frame_data) {
1413     float scale_factor = frame->metadata.device_scale_factor;
1414
1415     // Compute the frame size based on the root render pass rect size.
1416     cc::RenderPass* root_pass =
1417         frame->delegated_frame_data->render_pass_list.back();
1418     gfx::Size pixel_size = root_pass->output_rect.size();
1419     gfx::Size dip_size =
1420         ConvertSizeToDIP(scale_factor, pixel_size);
1421
1422     root_layer_->SetBounds(gfx::Rect(dip_size));
1423     if (!render_widget_host_->is_hidden()) {
1424       EnsureBrowserCompositorView();
1425       browser_compositor_view_->GetCompositor()->SetScaleAndSize(
1426           scale_factor, pixel_size);
1427     }
1428
1429     SendVSyncParametersToRenderer();
1430
1431     delegated_frame_host_->SwapDelegatedFrame(
1432         output_surface_id,
1433         frame->delegated_frame_data.Pass(),
1434         frame->metadata.device_scale_factor,
1435         frame->metadata.latency_info);
1436   } else {
1437     DLOG(ERROR) << "Received unexpected frame type.";
1438     RecordAction(
1439         base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1440     render_widget_host_->GetProcess()->ReceivedBadMessage();
1441   }
1442 }
1443
1444 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1445   *results = GetWebScreenInfo(GetNativeView());
1446 }
1447
1448 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1449   // TODO(shess): In case of !window, the view has been removed from
1450   // the view hierarchy because the tab isn't main.  Could retrieve
1451   // the information from the main tab for our window.
1452   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1453   if (!enclosing_window)
1454     return gfx::Rect();
1455
1456   NSRect bounds = [enclosing_window frame];
1457   return FlipNSRectToRectScreen(bounds);
1458 }
1459
1460 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1461   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1462   // completely on Mac OS.
1463   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NULL_TRANSPORT);
1464 }
1465
1466 bool RenderWidgetHostViewMac::LockMouse() {
1467   if (mouse_locked_)
1468     return true;
1469
1470   mouse_locked_ = true;
1471
1472   // Lock position of mouse cursor and hide it.
1473   CGAssociateMouseAndMouseCursorPosition(NO);
1474   [NSCursor hide];
1475
1476   // Clear the tooltip window.
1477   SetTooltipText(base::string16());
1478
1479   return true;
1480 }
1481
1482 void RenderWidgetHostViewMac::UnlockMouse() {
1483   if (!mouse_locked_)
1484     return;
1485   mouse_locked_ = false;
1486
1487   // Unlock position of mouse cursor and unhide it.
1488   CGAssociateMouseAndMouseCursorPosition(YES);
1489   [NSCursor unhide];
1490
1491   if (render_widget_host_)
1492     render_widget_host_->LostMouseLock();
1493 }
1494
1495 void RenderWidgetHostViewMac::WheelEventAck(
1496     const blink::WebMouseWheelEvent& event,
1497     InputEventAckState ack_result) {
1498   bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1499   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1500   // to see it (no-op wheel events are ignored by the event dispatcher)
1501   if (event.deltaX || event.deltaY)
1502     [cocoa_view_ processedWheelEvent:event consumed:consumed];
1503 }
1504
1505 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1506   if (render_widget_host_)
1507     return render_widget_host_->Send(message);
1508   delete message;
1509   return false;
1510 }
1511
1512 void RenderWidgetHostViewMac::ShutdownHost() {
1513   weak_factory_.InvalidateWeakPtrs();
1514   render_widget_host_->Shutdown();
1515   // Do not touch any members at this point, |this| has been deleted.
1516 }
1517
1518 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1519   DestroyBrowserCompositorView();
1520   delegated_frame_host_.reset();
1521   root_layer_.reset();
1522   browser_compositor_view_placeholder_.reset();
1523 }
1524
1525 void RenderWidgetHostViewMac::SetActive(bool active) {
1526   if (render_widget_host_) {
1527     render_widget_host_->SetActive(active);
1528     if (active) {
1529       if (HasFocus())
1530         render_widget_host_->Focus();
1531     } else {
1532       render_widget_host_->Blur();
1533     }
1534   }
1535   if (HasFocus())
1536     SetTextInputActive(active);
1537   if (!active) {
1538     [cocoa_view_ setPluginImeActive:NO];
1539     UnlockMouse();
1540   }
1541 }
1542
1543 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1544   if (render_widget_host_) {
1545     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1546         render_widget_host_->GetRoutingID(), visible));
1547   }
1548 }
1549
1550 void RenderWidgetHostViewMac::WindowFrameChanged() {
1551   if (render_widget_host_) {
1552     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1553         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1554         GetViewBounds()));
1555   }
1556 }
1557
1558 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1559   RenderWidgetHostViewMacDictionaryHelper helper(this);
1560   helper.ShowDefinitionForSelection();
1561 }
1562
1563 void RenderWidgetHostViewMac::SetBackgroundColor(SkColor color) {
1564   RenderWidgetHostViewBase::SetBackgroundColor(color);
1565   if (render_widget_host_)
1566     render_widget_host_->SetBackgroundOpaque(GetBackgroundOpaque());
1567
1568   if (background_layer_) {
1569     [background_layer_
1570         setBackgroundColor:gfx::CGColorCreateFromSkColor(background_color_)];
1571   }
1572 }
1573
1574 BrowserAccessibilityManager*
1575     RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1576         BrowserAccessibilityDelegate* delegate) {
1577   return new BrowserAccessibilityManagerMac(
1578       cocoa_view_,
1579       BrowserAccessibilityManagerMac::GetEmptyDocument(),
1580       delegate);
1581 }
1582
1583 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1584     const gfx::Rect& bounds) {
1585   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1586   NSSize size = NSMakeSize(bounds.width(), bounds.height());
1587   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1588   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1589   NSPoint originInScreen =
1590       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1591   originInScreen.y = originInScreen.y - size.height;
1592   return gfx::Point(originInScreen.x, originInScreen.y);
1593 }
1594
1595 void RenderWidgetHostViewMac::AccessibilityShowMenu(const gfx::Point& point) {
1596   NSPoint location = NSMakePoint(point.x(), point.y());
1597   location = [[cocoa_view_ window] convertScreenToBase:location];
1598   NSEvent* fakeRightClick = [NSEvent
1599                           mouseEventWithType:NSRightMouseDown
1600                                     location:location
1601                                modifierFlags:0
1602                                    timestamp:0
1603                                 windowNumber:[[cocoa_view_ window] windowNumber]
1604                                      context:[NSGraphicsContext currentContext]
1605                                  eventNumber:0
1606                                   clickCount:1
1607                                     pressure:0];
1608
1609   [cocoa_view_ mouseEvent:fakeRightClick];
1610 }
1611
1612 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1613   if (active) {
1614     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1615       EnablePasswordInput();
1616     else
1617       DisablePasswordInput();
1618   } else {
1619     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1620       DisablePasswordInput();
1621   }
1622 }
1623
1624 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1625                                                    int plugin_id) {
1626   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1627 }
1628
1629 void RenderWidgetHostViewMac::OnStartPluginIme() {
1630   [cocoa_view_ setPluginImeActive:YES];
1631 }
1632
1633 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1634     const std::string& text) {
1635   SpeakText(text);
1636 }
1637
1638 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1639   if (!render_widget_host_ || render_widget_host_->is_hidden())
1640     return;
1641
1642   // Pausing for one view prevents others from receiving frames.
1643   // This may lead to large delays, causing overlaps. See crbug.com/352020.
1644   if (!allow_pause_for_resize_or_repaint_)
1645     return;
1646
1647   // Wait for a frame of the right size to come in.
1648   if (browser_compositor_view_)
1649     browser_compositor_view_->BeginPumpingFrames();
1650   render_widget_host_->PauseForPendingResizeOrRepaints();
1651   if (browser_compositor_view_)
1652     browser_compositor_view_->EndPumpingFrames();
1653 }
1654
1655 SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() {
1656   return kN32_SkColorType;
1657 }
1658
1659 ////////////////////////////////////////////////////////////////////////////////
1660 // gfx::DisplayObserver, public:
1661
1662 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1663 }
1664
1665 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1666 }
1667
1668 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1669     const gfx::Display& display, uint32_t metrics) {
1670   gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1671   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1672     return;
1673
1674   UpdateScreenInfo(cocoa_view_);
1675 }
1676
1677 }  // namespace content
1678
1679 // RenderWidgetHostViewCocoa ---------------------------------------------------
1680
1681 @implementation RenderWidgetHostViewCocoa
1682 @synthesize selectedRange = selectedRange_;
1683 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1684 @synthesize markedRange = markedRange_;
1685
1686 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1687   self = [super initWithFrame:NSZeroRect];
1688   if (self) {
1689     self.acceptsTouchEvents = YES;
1690     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1691     editCommand_helper_->AddEditingSelectorsToClass([self class]);
1692
1693     renderWidgetHostView_.reset(r);
1694     canBeKeyView_ = YES;
1695     focusedPluginIdentifier_ = -1;
1696
1697     // OpenGL support:
1698     if ([self respondsToSelector:
1699         @selector(setWantsBestResolutionOpenGLSurface:)]) {
1700       [self setWantsBestResolutionOpenGLSurface:YES];
1701     }
1702     [[NSNotificationCenter defaultCenter]
1703         addObserver:self
1704            selector:@selector(didChangeScreenParameters:)
1705                name:NSApplicationDidChangeScreenParametersNotification
1706              object:nil];
1707   }
1708   return self;
1709 }
1710
1711 - (void)dealloc {
1712   if (responderDelegate_ &&
1713       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1714     [responderDelegate_ viewGone:self];
1715   responderDelegate_.reset();
1716
1717   [[NSNotificationCenter defaultCenter] removeObserver:self];
1718
1719   [super dealloc];
1720 }
1721
1722 - (void)didChangeScreenParameters:(NSNotification*)notify {
1723   g_screen_info_up_to_date = false;
1724 }
1725
1726 - (void)setResponderDelegate:
1727             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1728   DCHECK(!responderDelegate_);
1729   responderDelegate_.reset([delegate retain]);
1730 }
1731
1732 - (void)resetCursorRects {
1733   if (currentCursor_) {
1734     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1735     [currentCursor_ setOnMouseEntered:YES];
1736   }
1737 }
1738
1739 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1740                    consumed:(BOOL)consumed {
1741   [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1742 }
1743
1744 - (BOOL)respondsToSelector:(SEL)selector {
1745   // Trickiness: this doesn't mean "does this object's superclass respond to
1746   // this selector" but rather "does the -respondsToSelector impl from the
1747   // superclass say that this class responds to the selector".
1748   if ([super respondsToSelector:selector])
1749     return YES;
1750
1751   if (responderDelegate_)
1752     return [responderDelegate_ respondsToSelector:selector];
1753
1754   return NO;
1755 }
1756
1757 - (id)forwardingTargetForSelector:(SEL)selector {
1758   if ([responderDelegate_ respondsToSelector:selector])
1759     return responderDelegate_.get();
1760
1761   return [super forwardingTargetForSelector:selector];
1762 }
1763
1764 - (void)setCanBeKeyView:(BOOL)can {
1765   canBeKeyView_ = can;
1766 }
1767
1768 - (BOOL)acceptsMouseEventsWhenInactive {
1769   // Some types of windows (balloons, always-on-top panels) want to accept mouse
1770   // clicks w/o the first click being treated as 'activation'. Same applies to
1771   // mouse move events.
1772   return [[self window] level] > NSNormalWindowLevel;
1773 }
1774
1775 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1776   return [self acceptsMouseEventsWhenInactive];
1777 }
1778
1779 - (void)setCloseOnDeactivate:(BOOL)b {
1780   closeOnDeactivate_ = b;
1781 }
1782
1783 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1784   NSWindow* window = [self window];
1785   // If this is a background window, don't handle mouse movement events. This
1786   // is the expected behavior on the Mac as evidenced by other applications.
1787   if ([theEvent type] == NSMouseMoved &&
1788       ![self acceptsMouseEventsWhenInactive] &&
1789       ![window isKeyWindow]) {
1790     return YES;
1791   }
1792
1793   // Use hitTest to check whether the mouse is over a nonWebContentView - in
1794   // which case the mouse event should not be handled by the render host.
1795   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1796   NSView* contentView = [window contentView];
1797   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1798   // Traverse the superview hierarchy as the hitTest will return the frontmost
1799   // view, such as an NSTextView, while nonWebContentView may be specified by
1800   // its parent view.
1801   while (view) {
1802     if ([view respondsToSelector:nonWebContentViewSelector] &&
1803         [view performSelector:nonWebContentViewSelector]) {
1804       // The cursor is over a nonWebContentView - ignore this mouse event.
1805       return YES;
1806     }
1807     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1808         !hasOpenMouseDown_) {
1809       // The cursor is over an overlapping render widget. This check is done by
1810       // both views so the one that's returned by -hitTest: will end up
1811       // processing the event.
1812       // Note that while dragging, we only get events for the render view where
1813       // drag started, even if mouse is  actually over another view or outside
1814       // the window. Cocoa does this for us. We should handle these events and
1815       // not ignore (since there is no other render view to handle them). Thus
1816       // the |!hasOpenMouseDown_| check above.
1817       return YES;
1818     }
1819     view = [view superview];
1820   }
1821   return NO;
1822 }
1823
1824 - (void)mouseEvent:(NSEvent*)theEvent {
1825   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1826   if (responderDelegate_ &&
1827       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1828     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1829     if (handled)
1830       return;
1831   }
1832
1833   if ([self shouldIgnoreMouseEvent:theEvent]) {
1834     // If this is the first such event, send a mouse exit to the host view.
1835     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1836       WebMouseEvent exitEvent =
1837           WebInputEventFactory::mouseEvent(theEvent, self);
1838       exitEvent.type = WebInputEvent::MouseLeave;
1839       exitEvent.button = WebMouseEvent::ButtonNone;
1840       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1841     }
1842     mouseEventWasIgnored_ = YES;
1843     return;
1844   }
1845
1846   if (mouseEventWasIgnored_) {
1847     // If this is the first mouse event after a previous event that was ignored
1848     // due to the hitTest, send a mouse enter event to the host view.
1849     if (renderWidgetHostView_->render_widget_host_) {
1850       WebMouseEvent enterEvent =
1851           WebInputEventFactory::mouseEvent(theEvent, self);
1852       enterEvent.type = WebInputEvent::MouseMove;
1853       enterEvent.button = WebMouseEvent::ButtonNone;
1854       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1855     }
1856   }
1857   mouseEventWasIgnored_ = NO;
1858
1859   // Don't cancel child popups; killing them on a mouse click would prevent the
1860   // user from positioning the insertion point in the text field spawning the
1861   // popup. A click outside the text field would cause the text field to drop
1862   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1863   // the popup anyway, so we're OK.
1864
1865   NSEventType type = [theEvent type];
1866   if (type == NSLeftMouseDown)
1867     hasOpenMouseDown_ = YES;
1868   else if (type == NSLeftMouseUp)
1869     hasOpenMouseDown_ = NO;
1870
1871   // TODO(suzhe): We should send mouse events to the input method first if it
1872   // wants to handle them. But it won't work without implementing method
1873   // - (NSUInteger)characterIndexForPoint:.
1874   // See: http://code.google.com/p/chromium/issues/detail?id=47141
1875   // Instead of sending mouse events to the input method first, we now just
1876   // simply confirm all ongoing composition here.
1877   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1878       type == NSOtherMouseDown) {
1879     [self confirmComposition];
1880   }
1881
1882   const WebMouseEvent event =
1883       WebInputEventFactory::mouseEvent(theEvent, self);
1884   renderWidgetHostView_->ForwardMouseEvent(event);
1885 }
1886
1887 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1888   // |performKeyEquivalent:| is sent to all views of a window, not only down the
1889   // responder chain (cf. "Handling Key Equivalents" in
1890   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1891   // ). We only want to handle key equivalents if we're first responder.
1892   if ([[self window] firstResponder] != self)
1893     return NO;
1894
1895   // If the event is reserved by the system, then do not pass it to web content.
1896   if (EventIsReservedBySystem(theEvent))
1897     return NO;
1898
1899   // If we return |NO| from this function, cocoa will send the key event to
1900   // the menu and only if the menu does not process the event to |keyDown:|. We
1901   // want to send the event to a renderer _before_ sending it to the menu, so
1902   // we need to return |YES| for all events that might be swallowed by the menu.
1903   // We do not return |YES| for every keypress because we don't get |keyDown:|
1904   // events for keys that we handle this way.
1905   NSUInteger modifierFlags = [theEvent modifierFlags];
1906   if ((modifierFlags & NSCommandKeyMask) == 0) {
1907     // Make sure the menu does not contain key equivalents that don't
1908     // contain cmd.
1909     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1910     return NO;
1911   }
1912
1913   // Command key combinations are sent via performKeyEquivalent rather than
1914   // keyDown:. We just forward this on and if WebCore doesn't want to handle
1915   // it, we let the WebContentsView figure out how to reinject it.
1916   [self keyEvent:theEvent wasKeyEquivalent:YES];
1917   return YES;
1918 }
1919
1920 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1921   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1922   // returned NO. If this function returns |YES|, Cocoa sends the event to
1923   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1924   // to us instead of doing key view loop control, ctrl-left/right get handled
1925   // correctly, etc.
1926   // (However, there are still some keys that Cocoa swallows, e.g. the key
1927   // equivalent that Cocoa uses for toggling the input language. In this case,
1928   // that's actually a good thing, though -- see http://crbug.com/26115 .)
1929   return YES;
1930 }
1931
1932 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1933   if (responderDelegate_ &&
1934       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1935     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1936     if (handled)
1937       return kEventHandled;
1938   }
1939
1940   [self keyEvent:theEvent wasKeyEquivalent:NO];
1941   return kEventHandled;
1942 }
1943
1944 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1945   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
1946
1947   // If the user changes the system hotkey mapping after Chrome has been
1948   // launched, then it is possible that a formerly reserved system hotkey is no
1949   // longer reserved. The hotkey would have skipped the renderer, but would
1950   // also have not been handled by the system. If this is the case, immediately
1951   // return.
1952   // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
1953   // api to monitor changes to system hotkeys. This logic will have to be
1954   // updated.
1955   // http://crbug.com/383558.
1956   if (EventIsReservedBySystem(theEvent))
1957     return;
1958
1959   DCHECK([theEvent type] != NSKeyDown ||
1960          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1961
1962   if ([theEvent type] == NSFlagsChanged) {
1963     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1964     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1965     int keyCode = [theEvent keyCode];
1966     if (!keyCode || keyCode == 10 || keyCode == 63)
1967       return;
1968   }
1969
1970   // Don't cancel child popups; the key events are probably what's triggering
1971   // the popup in the first place.
1972
1973   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
1974   DCHECK(widgetHost);
1975
1976   NativeWebKeyboardEvent event(theEvent);
1977
1978   // Force fullscreen windows to close on Escape so they won't keep the keyboard
1979   // grabbed or be stuck onscreen if the renderer is hanging.
1980   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
1981       event.windowsKeyCode == ui::VKEY_ESCAPE &&
1982       renderWidgetHostView_->pepper_fullscreen_window()) {
1983     RenderWidgetHostViewMac* parent =
1984         renderWidgetHostView_->fullscreen_parent_host_view();
1985     if (parent)
1986       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
1987     widgetHost->Shutdown();
1988     return;
1989   }
1990
1991   // Suppress the escape key up event if necessary.
1992   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
1993     if (event.type == NativeWebKeyboardEvent::KeyUp)
1994       suppressNextEscapeKeyUp_ = NO;
1995     return;
1996   }
1997
1998   // We only handle key down events and just simply forward other events.
1999   if ([theEvent type] != NSKeyDown) {
2000     widgetHost->ForwardKeyboardEvent(event);
2001
2002     // Possibly autohide the cursor.
2003     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2004       [NSCursor setHiddenUntilMouseMoves:YES];
2005
2006     return;
2007   }
2008
2009   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2010
2011   // Records the current marked text state, so that we can know if the marked
2012   // text was deleted or not after handling the key down event.
2013   BOOL oldHasMarkedText = hasMarkedText_;
2014
2015   // This method should not be called recursively.
2016   DCHECK(!handlingKeyDown_);
2017
2018   // Tells insertText: and doCommandBySelector: that we are handling a key
2019   // down event.
2020   handlingKeyDown_ = YES;
2021
2022   // These variables might be set when handling the keyboard event.
2023   // Clear them here so that we can know whether they have changed afterwards.
2024   textToBeInserted_.clear();
2025   markedText_.clear();
2026   underlines_.clear();
2027   unmarkTextCalled_ = NO;
2028   hasEditCommands_ = NO;
2029   editCommands_.clear();
2030
2031   // Before doing anything with a key down, check to see if plugin IME has been
2032   // cancelled, since the plugin host needs to be informed of that before
2033   // receiving the keydown.
2034   if ([theEvent type] == NSKeyDown)
2035     [self checkForPluginImeCancellation];
2036
2037   // Sends key down events to input method first, then we can decide what should
2038   // be done according to input method's feedback.
2039   // If a plugin is active, bypass this step since events are forwarded directly
2040   // to the plugin IME.
2041   if (focusedPluginIdentifier_ == -1)
2042     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2043
2044   handlingKeyDown_ = NO;
2045
2046   // Indicates if we should send the key event and corresponding editor commands
2047   // after processing the input method result.
2048   BOOL delayEventUntilAfterImeCompostion = NO;
2049
2050   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2051   // while an input method is composing or inserting a text.
2052   // Gmail checks this code in its onkeydown handler to stop auto-completing
2053   // e-mail addresses while composing a CJK text.
2054   // If the text to be inserted has only one character, then we don't need this
2055   // trick, because we'll send the text as a key press event instead.
2056   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2057     NativeWebKeyboardEvent fakeEvent = event;
2058     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2059     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2060     fakeEvent.skip_in_browser = true;
2061     widgetHost->ForwardKeyboardEvent(fakeEvent);
2062     // If this key event was handled by the input method, but
2063     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2064     // enqueued edit commands, then in order to let webkit handle them
2065     // correctly, we need to send the real key event and corresponding edit
2066     // commands after processing the input method result.
2067     // We shouldn't do this if a new marked text was set by the input method,
2068     // otherwise the new marked text might be cancelled by webkit.
2069     if (hasEditCommands_ && !hasMarkedText_)
2070       delayEventUntilAfterImeCompostion = YES;
2071   } else {
2072     if (!editCommands_.empty()) {
2073       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2074           widgetHost->GetRoutingID(), editCommands_));
2075     }
2076     widgetHost->ForwardKeyboardEvent(event);
2077   }
2078
2079   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2080   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2081   // be set to NULL. So we check it here and return immediately if it's NULL.
2082   if (!renderWidgetHostView_->render_widget_host_)
2083     return;
2084
2085   // Then send keypress and/or composition related events.
2086   // If there was a marked text or the text to be inserted is longer than 1
2087   // character, then we send the text by calling ConfirmComposition().
2088   // Otherwise, if the text to be inserted only contains 1 character, then we
2089   // can just send a keypress event which is fabricated by changing the type of
2090   // the keydown event, so that we can retain all necessary informations, such
2091   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2092   // prevent the browser from handling it again.
2093   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2094   // handle BMP characters here, as we can always insert non-BMP characters as
2095   // text.
2096   BOOL textInserted = NO;
2097   if (textToBeInserted_.length() >
2098       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2099     widgetHost->ImeConfirmComposition(
2100         textToBeInserted_, gfx::Range::InvalidRange(), false);
2101     textInserted = YES;
2102   }
2103
2104   // Updates or cancels the composition. If some text has been inserted, then
2105   // we don't need to cancel the composition explicitly.
2106   if (hasMarkedText_ && markedText_.length()) {
2107     // Sends the updated marked text to the renderer so it can update the
2108     // composition node in WebKit.
2109     // When marked text is available, |selectedRange_| will be the range being
2110     // selected inside the marked text.
2111     widgetHost->ImeSetComposition(markedText_, underlines_,
2112                                   selectedRange_.location,
2113                                   NSMaxRange(selectedRange_));
2114   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2115     if (unmarkTextCalled_) {
2116       widgetHost->ImeConfirmComposition(
2117           base::string16(), gfx::Range::InvalidRange(), false);
2118     } else {
2119       widgetHost->ImeCancelComposition();
2120     }
2121   }
2122
2123   // If the key event was handled by the input method but it also generated some
2124   // edit commands, then we need to send the real key event and corresponding
2125   // edit commands here. This usually occurs when the input method wants to
2126   // finish current composition session but still wants the application to
2127   // handle the key event. See http://crbug.com/48161 for reference.
2128   if (delayEventUntilAfterImeCompostion) {
2129     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2130     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2131     // So before sending the real key down event, we need to send a fake key up
2132     // event to balance it.
2133     NativeWebKeyboardEvent fakeEvent = event;
2134     fakeEvent.type = blink::WebInputEvent::KeyUp;
2135     fakeEvent.skip_in_browser = true;
2136     widgetHost->ForwardKeyboardEvent(fakeEvent);
2137     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2138     // a key event with |skip_in_browser| == true won't be handled by browser,
2139     // thus it won't destroy the widget.
2140
2141     if (!editCommands_.empty()) {
2142       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2143           widgetHost->GetRoutingID(), editCommands_));
2144     }
2145     widgetHost->ForwardKeyboardEvent(event);
2146
2147     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2148     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2149     // be set to NULL. So we check it here and return immediately if it's NULL.
2150     if (!renderWidgetHostView_->render_widget_host_)
2151       return;
2152   }
2153
2154   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2155   // Only send a corresponding key press event if there is no marked text.
2156   if (!hasMarkedText_) {
2157     if (!textInserted && textToBeInserted_.length() == 1) {
2158       // If a single character was inserted, then we just send it as a keypress
2159       // event.
2160       event.type = blink::WebInputEvent::Char;
2161       event.text[0] = textToBeInserted_[0];
2162       event.text[1] = 0;
2163       event.skip_in_browser = true;
2164       widgetHost->ForwardKeyboardEvent(event);
2165     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2166                [[theEvent characters] length] > 0 &&
2167                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2168                 (hasEditCommands_ && editCommands_.empty()))) {
2169       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2170       // generates an insert command. So synthesize a keypress event for these
2171       // cases, unless the key event generated any other command.
2172       event.type = blink::WebInputEvent::Char;
2173       event.skip_in_browser = true;
2174       widgetHost->ForwardKeyboardEvent(event);
2175     }
2176   }
2177
2178   // Possibly autohide the cursor.
2179   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2180     [NSCursor setHiddenUntilMouseMoves:YES];
2181 }
2182
2183 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2184   DCHECK(base::mac::IsOSLionOrLater());
2185
2186   if ([event phase] != NSEventPhaseEnded &&
2187       [event phase] != NSEventPhaseCancelled) {
2188     return;
2189   }
2190
2191   if (renderWidgetHostView_->render_widget_host_) {
2192     // History-swiping is not possible if the logic reaches this point.
2193     // Allow rubber-banding in both directions.
2194     bool canRubberbandLeft = true;
2195     bool canRubberbandRight = true;
2196     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2197         event, self, canRubberbandLeft, canRubberbandRight);
2198     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2199   }
2200
2201   if (endWheelMonitor_) {
2202     [NSEvent removeMonitor:endWheelMonitor_];
2203     endWheelMonitor_ = nil;
2204   }
2205 }
2206
2207 - (void)beginGestureWithEvent:(NSEvent*)event {
2208   [responderDelegate_ beginGestureWithEvent:event];
2209 }
2210 - (void)endGestureWithEvent:(NSEvent*)event {
2211   [responderDelegate_ endGestureWithEvent:event];
2212 }
2213 - (void)touchesMovedWithEvent:(NSEvent*)event {
2214   [responderDelegate_ touchesMovedWithEvent:event];
2215 }
2216 - (void)touchesBeganWithEvent:(NSEvent*)event {
2217   [responderDelegate_ touchesBeganWithEvent:event];
2218 }
2219 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2220   [responderDelegate_ touchesCancelledWithEvent:event];
2221 }
2222 - (void)touchesEndedWithEvent:(NSEvent*)event {
2223   [responderDelegate_ touchesEndedWithEvent:event];
2224 }
2225
2226 // This is invoked only on 10.8 or newer when the user taps a word using
2227 // three fingers.
2228 - (void)quickLookWithEvent:(NSEvent*)event {
2229   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2230   TextInputClientMac::GetInstance()->GetStringAtPoint(
2231       renderWidgetHostView_->render_widget_host_,
2232       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2233       ^(NSAttributedString* string, NSPoint baselinePoint) {
2234           if (string && [string length] > 0) {
2235             dispatch_async(dispatch_get_main_queue(), ^{
2236                 [self showDefinitionForAttributedString:string
2237                                                 atPoint:baselinePoint];
2238             });
2239           }
2240       }
2241   );
2242 }
2243
2244 // This method handles 2 different types of hardware events.
2245 // (Apple does not distinguish between them).
2246 //  a. Scrolling the middle wheel of a mouse.
2247 //  b. Swiping on the track pad.
2248 //
2249 // This method is responsible for 2 types of behavior:
2250 //  a. Scrolling the content of window.
2251 //  b. Navigating forwards/backwards in history.
2252 //
2253 // This is a brief description of the logic:
2254 //  1. If the content can be scrolled, scroll the content.
2255 //     (This requires a roundtrip to blink to determine whether the content
2256 //      can be scrolled.)
2257 //     Once this logic is triggered, the navigate logic cannot be triggered
2258 //     until the gesture finishes.
2259 //  2. If the user is making a horizontal swipe, start the navigate
2260 //     forward/backwards UI.
2261 //     Once this logic is triggered, the user can either cancel or complete
2262 //     the gesture. If the user completes the gesture, all remaining touches
2263 //     are swallowed, and not allowed to scroll the content. If the user
2264 //     cancels the gesture, all remaining touches are forwarded to the content
2265 //     scroll logic. The user cannot trigger the navigation logic again.
2266 - (void)scrollWheel:(NSEvent*)event {
2267   if (responderDelegate_ &&
2268       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2269     BOOL handled = [responderDelegate_ handleEvent:event];
2270     if (handled)
2271       return;
2272   }
2273
2274   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2275   // the event is received even when the mouse cursor is no longer over the view
2276   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2277   // for ending rubber-banding in such cases.
2278   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2279       !endWheelMonitor_) {
2280     endWheelMonitor_ =
2281       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2282       handler:^(NSEvent* blockEvent) {
2283           [self shortCircuitScrollWheelEvent:blockEvent];
2284           return blockEvent;
2285       }];
2286   }
2287
2288   // This is responsible for content scrolling!
2289   if (renderWidgetHostView_->render_widget_host_) {
2290     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2291     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2292     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2293         event, self, canRubberbandLeft, canRubberbandRight);
2294     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2295   }
2296 }
2297
2298 // Called repeatedly during a pinch gesture, with incremental change values.
2299 - (void)magnifyWithEvent:(NSEvent*)event {
2300   if (renderWidgetHostView_->render_widget_host_) {
2301     // Send a GesturePinchUpdate event.
2302     // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
2303     // GestureSrollBegin/End) as is done for touchscreen.  Keeping track of when
2304     // a pinch is active would take a little more work here, and we don't need
2305     // it for anything yet.
2306     const WebGestureEvent& webEvent =
2307         WebInputEventFactory::gestureEvent(event, self);
2308     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
2309   }
2310 }
2311
2312 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2313   NSWindow* oldWindow = [self window];
2314
2315   NSNotificationCenter* notificationCenter =
2316       [NSNotificationCenter defaultCenter];
2317
2318   // Backing property notifications crash on 10.6 when building with the 10.7
2319   // SDK, see http://crbug.com/260595.
2320   static BOOL supportsBackingPropertiesNotification =
2321       SupportsBackingPropertiesChangedNotification();
2322
2323   if (oldWindow) {
2324     if (supportsBackingPropertiesNotification) {
2325       [notificationCenter
2326           removeObserver:self
2327                     name:NSWindowDidChangeBackingPropertiesNotification
2328                   object:oldWindow];
2329     }
2330     [notificationCenter
2331         removeObserver:self
2332                   name:NSWindowDidMoveNotification
2333                 object:oldWindow];
2334     [notificationCenter
2335         removeObserver:self
2336                   name:NSWindowDidEndLiveResizeNotification
2337                 object:oldWindow];
2338   }
2339   if (newWindow) {
2340     if (supportsBackingPropertiesNotification) {
2341       [notificationCenter
2342           addObserver:self
2343              selector:@selector(windowDidChangeBackingProperties:)
2344                  name:NSWindowDidChangeBackingPropertiesNotification
2345                object:newWindow];
2346     }
2347     [notificationCenter
2348         addObserver:self
2349            selector:@selector(windowChangedGlobalFrame:)
2350                name:NSWindowDidMoveNotification
2351              object:newWindow];
2352     [notificationCenter
2353         addObserver:self
2354            selector:@selector(windowChangedGlobalFrame:)
2355                name:NSWindowDidEndLiveResizeNotification
2356              object:newWindow];
2357   }
2358 }
2359
2360 - (void)updateScreenProperties{
2361   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2362   renderWidgetHostView_->UpdateDisplayLink();
2363 }
2364
2365 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2366 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2367   // Background tabs check if their scale factor or vsync properties changed
2368   // when they are added to a window.
2369
2370   // Allocating a CGLayerRef with the current scale factor immediately from
2371   // this handler doesn't work. Schedule the backing store update on the
2372   // next runloop cycle, then things are read for CGLayerRef allocations to
2373   // work.
2374   [self performSelector:@selector(updateScreenProperties)
2375              withObject:nil
2376              afterDelay:0];
2377 }
2378
2379 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2380   renderWidgetHostView_->UpdateScreenInfo(
2381       renderWidgetHostView_->GetNativeView());
2382 }
2383
2384 - (void)setFrameSize:(NSSize)newSize {
2385   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2386
2387   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2388   // -setFrame: isn't neccessary.
2389   [super setFrameSize:newSize];
2390
2391   if (!renderWidgetHostView_->render_widget_host_)
2392     return;
2393
2394   renderWidgetHostView_->render_widget_host_->SendScreenRects();
2395   renderWidgetHostView_->render_widget_host_->WasResized();
2396   if (renderWidgetHostView_->delegated_frame_host_)
2397     renderWidgetHostView_->delegated_frame_host_->WasResized();
2398
2399   // Wait for the frame that WasResize might have requested. If the view is
2400   // being made visible at a new size, then this call will have no effect
2401   // because the view widget is still hidden, and the pause call in WasShown
2402   // will have this effect for us.
2403   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2404 }
2405
2406 - (BOOL)canBecomeKeyView {
2407   if (!renderWidgetHostView_->render_widget_host_)
2408     return NO;
2409
2410   return canBeKeyView_;
2411 }
2412
2413 - (BOOL)acceptsFirstResponder {
2414   if (!renderWidgetHostView_->render_widget_host_)
2415     return NO;
2416
2417   return canBeKeyView_;
2418 }
2419
2420 - (BOOL)becomeFirstResponder {
2421   if (!renderWidgetHostView_->render_widget_host_)
2422     return NO;
2423
2424   renderWidgetHostView_->render_widget_host_->Focus();
2425   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2426   renderWidgetHostView_->SetTextInputActive(true);
2427
2428   // Cancel any onging composition text which was left before we lost focus.
2429   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2430   // somehow that method won't be called when switching among different tabs.
2431   // See http://crbug.com/47209
2432   [self cancelComposition];
2433
2434   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2435       [[self window] keyViewSelectionDirection]];
2436   NSDictionary* userInfo =
2437       [NSDictionary dictionaryWithObject:direction
2438                                   forKey:kSelectionDirection];
2439   [[NSNotificationCenter defaultCenter]
2440       postNotificationName:kViewDidBecomeFirstResponder
2441                     object:self
2442                   userInfo:userInfo];
2443
2444   return YES;
2445 }
2446
2447 - (BOOL)resignFirstResponder {
2448   renderWidgetHostView_->SetTextInputActive(false);
2449   if (!renderWidgetHostView_->render_widget_host_)
2450     return YES;
2451
2452   if (closeOnDeactivate_)
2453     renderWidgetHostView_->KillSelf();
2454
2455   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2456   renderWidgetHostView_->render_widget_host_->Blur();
2457
2458   // We should cancel any onging composition whenever RWH's Blur() method gets
2459   // called, because in this case, webkit will confirm the ongoing composition
2460   // internally.
2461   [self cancelComposition];
2462
2463   return YES;
2464 }
2465
2466 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2467   if (responderDelegate_ &&
2468       [responderDelegate_
2469           respondsToSelector:@selector(validateUserInterfaceItem:
2470                                                      isValidItem:)]) {
2471     BOOL valid;
2472     BOOL known =
2473         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2474     if (known)
2475       return valid;
2476   }
2477
2478   SEL action = [item action];
2479
2480   if (action == @selector(stopSpeaking:)) {
2481     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2482            renderWidgetHostView_->IsSpeaking();
2483   }
2484   if (action == @selector(startSpeaking:)) {
2485     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2486            renderWidgetHostView_->SupportsSpeech();
2487   }
2488
2489   // For now, these actions are always enabled for render view,
2490   // this is sub-optimal.
2491   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2492   if (action == @selector(undo:) ||
2493       action == @selector(redo:) ||
2494       action == @selector(cut:) ||
2495       action == @selector(copy:) ||
2496       action == @selector(copyToFindPboard:) ||
2497       action == @selector(paste:) ||
2498       action == @selector(pasteAndMatchStyle:)) {
2499     return renderWidgetHostView_->render_widget_host_->IsRenderView();
2500   }
2501
2502   return editCommand_helper_->IsMenuItemEnabled(action, self);
2503 }
2504
2505 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2506   return renderWidgetHostView_.get();
2507 }
2508
2509 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2510 // move) for the given event. Customize here to be more selective about which
2511 // key presses to autohide on.
2512 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2513   return ([event type] == NSKeyDown &&
2514              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2515 }
2516
2517 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2518                                          index:(NSUInteger)index
2519                                       maxCount:(NSUInteger)maxCount {
2520   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2521   NSUInteger totalLength = [fullArray count];
2522   if (index >= totalLength)
2523     return nil;
2524   NSUInteger length = MIN(totalLength - index, maxCount);
2525   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2526 }
2527
2528 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2529   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2530   return [fullArray count];
2531 }
2532
2533 - (id)accessibilityAttributeValue:(NSString *)attribute {
2534   BrowserAccessibilityManager* manager =
2535       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2536
2537   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2538   // BrowserAccessibilityManager. Children includes all subviews in addition to
2539   // contents. Currently we do not have subviews besides the document view.
2540   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2541           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2542       manager) {
2543     return [NSArray arrayWithObjects:manager->
2544         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2545   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2546     return NSAccessibilityScrollAreaRole;
2547   }
2548   id ret = [super accessibilityAttributeValue:attribute];
2549   return ret;
2550 }
2551
2552 - (NSArray*)accessibilityAttributeNames {
2553   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2554   [ret addObject:NSAccessibilityContentsAttribute];
2555   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2556   return ret;
2557 }
2558
2559 - (id)accessibilityHitTest:(NSPoint)point {
2560   BrowserAccessibilityManager* manager =
2561       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2562   if (!manager)
2563     return self;
2564   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2565   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2566   localPoint.y = NSHeight([self bounds]) - localPoint.y;
2567   BrowserAccessibilityCocoa* root =
2568       manager->GetRoot()->ToBrowserAccessibilityCocoa();
2569   id obj = [root accessibilityHitTest:localPoint];
2570   return obj;
2571 }
2572
2573 - (BOOL)accessibilityIsIgnored {
2574   BrowserAccessibilityManager* manager =
2575       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2576   return !manager;
2577 }
2578
2579 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2580   BrowserAccessibilityManager* manager =
2581       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2582   // Only child is root.
2583   if (manager &&
2584       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2585     return 0;
2586   } else {
2587     return NSNotFound;
2588   }
2589 }
2590
2591 - (id)accessibilityFocusedUIElement {
2592   BrowserAccessibilityManager* manager =
2593       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2594   if (manager) {
2595     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2596     DCHECK(focused_item);
2597     if (focused_item) {
2598       BrowserAccessibilityCocoa* focused_item_cocoa =
2599           focused_item->ToBrowserAccessibilityCocoa();
2600       DCHECK(focused_item_cocoa);
2601       if (focused_item_cocoa)
2602         return focused_item_cocoa;
2603     }
2604   }
2605   return [super accessibilityFocusedUIElement];
2606 }
2607
2608 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
2609 // with minor modifications for code style and commenting.
2610 //
2611 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
2612 // -setToolTip: in that the updated tooltip takes effect immediately,
2613 //  without the user's having to move the mouse out of and back into the view.
2614 //
2615 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
2616 // the view, which in turn requires overriding some internal tracking-rect
2617 // methods (to keep track of its owner & userdata, which need to be filled out
2618 // in the fake events.) --snej 7/6/09
2619
2620
2621 /*
2622  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
2623  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
2624  *
2625  * Redistribution and use in source and binary forms, with or without
2626  * modification, are permitted provided that the following conditions
2627  * are met:
2628  *
2629  * 1.  Redistributions of source code must retain the above copyright
2630  *     notice, this list of conditions and the following disclaimer.
2631  * 2.  Redistributions in binary form must reproduce the above copyright
2632  *     notice, this list of conditions and the following disclaimer in the
2633  *     documentation and/or other materials provided with the distribution.
2634  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
2635  *     its contributors may be used to endorse or promote products derived
2636  *     from this software without specific prior written permission.
2637  *
2638  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
2639  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2640  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2641  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2642  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2643  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2644  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2645  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2646  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2647  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2648  */
2649
2650 // Any non-zero value will do, but using something recognizable might help us
2651 // debug some day.
2652 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
2653
2654 // Override of a public NSView method, replacing the inherited functionality.
2655 // See above for rationale.
2656 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
2657                                owner:(id)owner
2658                             userData:(void *)data
2659                         assumeInside:(BOOL)assumeInside {
2660   DCHECK(trackingRectOwner_ == nil);
2661   trackingRectOwner_ = owner;
2662   trackingRectUserData_ = data;
2663   return kTrackingRectTag;
2664 }
2665
2666 // Override of (apparently) a private NSView method(!) See above for rationale.
2667 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
2668                                 owner:(id)owner
2669                              userData:(void *)data
2670                          assumeInside:(BOOL)assumeInside
2671                        useTrackingNum:(int)tag {
2672   DCHECK(tag == 0 || tag == kTrackingRectTag);
2673   DCHECK(trackingRectOwner_ == nil);
2674   trackingRectOwner_ = owner;
2675   trackingRectUserData_ = data;
2676   return kTrackingRectTag;
2677 }
2678
2679 // Override of (apparently) a private NSView method(!) See above for rationale.
2680 - (void)_addTrackingRects:(NSRect *)rects
2681                     owner:(id)owner
2682              userDataList:(void **)userDataList
2683          assumeInsideList:(BOOL *)assumeInsideList
2684              trackingNums:(NSTrackingRectTag *)trackingNums
2685                     count:(int)count {
2686   DCHECK(count == 1);
2687   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
2688   DCHECK(trackingRectOwner_ == nil);
2689   trackingRectOwner_ = owner;
2690   trackingRectUserData_ = userDataList[0];
2691   trackingNums[0] = kTrackingRectTag;
2692 }
2693
2694 // Override of a public NSView method, replacing the inherited functionality.
2695 // See above for rationale.
2696 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
2697   if (tag == 0)
2698     return;
2699
2700   if (tag == kTrackingRectTag) {
2701     trackingRectOwner_ = nil;
2702     return;
2703   }
2704
2705   if (tag == lastToolTipTag_) {
2706     [super removeTrackingRect:tag];
2707     lastToolTipTag_ = 0;
2708     return;
2709   }
2710
2711   // If any other tracking rect is being removed, we don't know how it was
2712   // created and it's possible there's a leak involved (see Radar 3500217).
2713   NOTREACHED();
2714 }
2715
2716 // Override of (apparently) a private NSView method(!)
2717 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
2718   for (int i = 0; i < count; ++i) {
2719     int tag = tags[i];
2720     if (tag == 0)
2721       continue;
2722     DCHECK(tag == kTrackingRectTag);
2723     trackingRectOwner_ = nil;
2724   }
2725 }
2726
2727 // Sends a fake NSMouseExited event to the view for its current tracking rect.
2728 - (void)_sendToolTipMouseExited {
2729   // Nothing matters except window, trackingNumber, and userData.
2730   int windowNumber = [[self window] windowNumber];
2731   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2732                                               location:NSZeroPoint
2733                                          modifierFlags:0
2734                                              timestamp:0
2735                                           windowNumber:windowNumber
2736                                                context:NULL
2737                                            eventNumber:0
2738                                         trackingNumber:kTrackingRectTag
2739                                               userData:trackingRectUserData_];
2740   [trackingRectOwner_ mouseExited:fakeEvent];
2741 }
2742
2743 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
2744 - (void)_sendToolTipMouseEntered {
2745   // Nothing matters except window, trackingNumber, and userData.
2746   int windowNumber = [[self window] windowNumber];
2747   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2748                                               location:NSZeroPoint
2749                                          modifierFlags:0
2750                                              timestamp:0
2751                                           windowNumber:windowNumber
2752                                                context:NULL
2753                                            eventNumber:0
2754                                         trackingNumber:kTrackingRectTag
2755                                               userData:trackingRectUserData_];
2756   [trackingRectOwner_ mouseEntered:fakeEvent];
2757 }
2758
2759 // Sets the view's current tooltip, to be displayed at the current mouse
2760 // location. (This does not make the tooltip appear -- as usual, it only
2761 // appears after a delay.) Pass null to remove the tooltip.
2762 - (void)setToolTipAtMousePoint:(NSString *)string {
2763   NSString *toolTip = [string length] == 0 ? nil : string;
2764   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2765       (!toolTip && !toolTip_)) {
2766     return;
2767   }
2768
2769   if (toolTip_) {
2770     [self _sendToolTipMouseExited];
2771   }
2772
2773   toolTip_.reset([toolTip copy]);
2774
2775   if (toolTip) {
2776     // See radar 3500217 for why we remove all tooltips
2777     // rather than just the single one we created.
2778     [self removeAllToolTips];
2779     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2780     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2781                                      owner:self
2782                                   userData:NULL];
2783     [self _sendToolTipMouseEntered];
2784   }
2785 }
2786
2787 // NSView calls this to get the text when displaying the tooltip.
2788 - (NSString *)view:(NSView *)view
2789   stringForToolTip:(NSToolTipTag)tag
2790              point:(NSPoint)point
2791           userData:(void *)data {
2792   return [[toolTip_ copy] autorelease];
2793 }
2794
2795 // Below is our NSTextInputClient implementation.
2796 //
2797 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2798 // functions to process this event.
2799 //
2800 // [WebHTMLView keyDown] ->
2801 //     EventHandler::keyEvent() ->
2802 //     ...
2803 //     [WebEditorClient handleKeyboardEvent] ->
2804 //     [WebHTMLView _interceptEditingKeyEvent] ->
2805 //     [NSResponder interpretKeyEvents] ->
2806 //     [WebHTMLView insertText] ->
2807 //     Editor::insertText()
2808 //
2809 // Unfortunately, it is hard for Chromium to use this implementation because
2810 // it causes key-typing jank.
2811 // RenderWidgetHostViewMac is running in a browser process. On the other
2812 // hand, Editor and EventHandler are running in a renderer process.
2813 // So, if we used this implementation, a NSKeyDown event is dispatched to
2814 // the following functions of Chromium.
2815 //
2816 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2817 //     |Sync IPC (KeyDown)| (*1) ->
2818 //     EventHandler::keyEvent() (renderer) ->
2819 //     ...
2820 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2821 //     |Sync IPC| (*2) ->
2822 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2823 //     [self interpretKeyEvents] ->
2824 //     [RenderWidgetHostViewMac insertText] (browser) ->
2825 //     |Async IPC| ->
2826 //     Editor::insertText() (renderer)
2827 //
2828 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2829 // result of EventHandler::keyEvent().
2830 // (*2) we need to wait until this call finishes since WebEditorClient uses
2831 // the result of [WebHTMLView _interceptEditingKeyEvent].
2832 //
2833 // This needs many sync IPC messages sent between a browser and a renderer for
2834 // each key event, which would probably result in key-typing jank.
2835 // To avoid this problem, this implementation processes key events (and input
2836 // method events) totally in a browser process and sends asynchronous input
2837 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2838 // renderer process.
2839 //
2840 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2841 //     |Async IPC (RawKeyDown)| ->
2842 //     [self interpretKeyEvents] ->
2843 //     [RenderWidgetHostViewMac insertText] (browser) ->
2844 //     |Async IPC (Char)| ->
2845 //     Editor::insertText() (renderer)
2846 //
2847 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2848 // make any key-typing jank. --hbono 7/23/09
2849 //
2850 extern "C" {
2851 extern NSString *NSTextInputReplacementRangeAttributeName;
2852 }
2853
2854 - (NSArray *)validAttributesForMarkedText {
2855   // This code is just copied from WebKit except renaming variables.
2856   if (!validAttributesForMarkedText_) {
2857     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2858         NSUnderlineStyleAttributeName,
2859         NSUnderlineColorAttributeName,
2860         NSMarkedClauseSegmentAttributeName,
2861         NSTextInputReplacementRangeAttributeName,
2862         nil]);
2863   }
2864   return validAttributesForMarkedText_.get();
2865 }
2866
2867 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2868   DCHECK([self window]);
2869   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2870   // coordinates (upper left origin). Scroll offsets will be taken care of in
2871   // the renderer.
2872   thePoint = [[self window] convertScreenToBase:thePoint];
2873   thePoint = [self convertPoint:thePoint fromView:nil];
2874   thePoint.y = NSHeight([self frame]) - thePoint.y;
2875
2876   NSUInteger index =
2877       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2878           renderWidgetHostView_->render_widget_host_,
2879           gfx::Point(thePoint.x, thePoint.y));
2880   return index;
2881 }
2882
2883 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2884                              actualRange:(NSRangePointer)actualRange {
2885   NSRect rect;
2886   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2887           theRange,
2888           &rect,
2889           actualRange)) {
2890     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2891         renderWidgetHostView_->render_widget_host_, theRange);
2892
2893     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2894     if (actualRange)
2895       *actualRange = theRange;
2896   }
2897
2898   // The returned rectangle is in WebKit coordinates (upper left origin), so
2899   // flip the coordinate system.
2900   NSRect viewFrame = [self frame];
2901   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2902   return rect;
2903 }
2904
2905 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2906                          actualRange:(NSRangePointer)actualRange {
2907   NSRect rect = [self firstViewRectForCharacterRange:theRange
2908                                          actualRange:actualRange];
2909
2910   // Convert into screen coordinates for return.
2911   rect = [self convertRect:rect toView:nil];
2912   rect.origin = [[self window] convertBaseToScreen:rect.origin];
2913   return rect;
2914 }
2915
2916 - (NSRange)markedRange {
2917   // An input method calls this method to check if an application really has
2918   // a text being composed when hasMarkedText call returns true.
2919   // Returns the range saved in the setMarkedText method so the input method
2920   // calls the setMarkedText method and we can update the composition node
2921   // there. (When this method returns an empty range, the input method doesn't
2922   // call the setMarkedText method.)
2923   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2924 }
2925
2926 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2927     actualRange:(NSRangePointer)actualRange {
2928   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2929   if (actualRange)
2930     *actualRange = range;
2931   NSAttributedString* str =
2932       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2933           renderWidgetHostView_->render_widget_host_, range);
2934   return str;
2935 }
2936
2937 - (NSInteger)conversationIdentifier {
2938   return reinterpret_cast<NSInteger>(self);
2939 }
2940
2941 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2942 // nil when the caret is in non-editable content or password box to avoid
2943 // making input methods do their work.
2944 - (NSTextInputContext *)inputContext {
2945   if (focusedPluginIdentifier_ != -1)
2946     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2947
2948   switch(renderWidgetHostView_->text_input_type_) {
2949     case ui::TEXT_INPUT_TYPE_NONE:
2950     case ui::TEXT_INPUT_TYPE_PASSWORD:
2951       return nil;
2952     default:
2953       return [super inputContext];
2954   }
2955 }
2956
2957 - (BOOL)hasMarkedText {
2958   // An input method calls this function to figure out whether or not an
2959   // application is really composing a text. If it is composing, it calls
2960   // the markedRange method, and maybe calls the setMarkedText method.
2961   // It seems an input method usually calls this function when it is about to
2962   // cancel an ongoing composition. If an application has a non-empty marked
2963   // range, it calls the setMarkedText method to delete the range.
2964   return hasMarkedText_;
2965 }
2966
2967 - (void)unmarkText {
2968   // Delete the composition node of the renderer and finish an ongoing
2969   // composition.
2970   // It seems an input method calls the setMarkedText method and set an empty
2971   // text when it cancels an ongoing composition, i.e. I have never seen an
2972   // input method calls this method.
2973   hasMarkedText_ = NO;
2974   markedText_.clear();
2975   underlines_.clear();
2976
2977   // If we are handling a key down event, then ConfirmComposition() will be
2978   // called in keyEvent: method.
2979   if (!handlingKeyDown_) {
2980     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2981         base::string16(), gfx::Range::InvalidRange(), false);
2982   } else {
2983     unmarkTextCalled_ = YES;
2984   }
2985 }
2986
2987 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2988                               replacementRange:(NSRange)replacementRange {
2989   // An input method updates the composition string.
2990   // We send the given text and range to the renderer so it can update the
2991   // composition node of WebKit.
2992   // TODO(suzhe): It's hard for us to support replacementRange without accessing
2993   // the full web content.
2994   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2995   NSString* im_text = isAttributedString ? [string string] : string;
2996   int length = [im_text length];
2997
2998   // |markedRange_| will get set on a callback from ImeSetComposition().
2999   selectedRange_ = newSelRange;
3000   markedText_ = base::SysNSStringToUTF16(im_text);
3001   hasMarkedText_ = (length > 0);
3002
3003   underlines_.clear();
3004   if (isAttributedString) {
3005     ExtractUnderlines(string, &underlines_);
3006   } else {
3007     // Use a thin black underline by default.
3008     underlines_.push_back(blink::WebCompositionUnderline(
3009         0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3010   }
3011
3012   // If we are handling a key down event, then SetComposition() will be
3013   // called in keyEvent: method.
3014   // Input methods of Mac use setMarkedText calls with an empty text to cancel
3015   // an ongoing composition. So, we should check whether or not the given text
3016   // is empty to update the input method state. (Our input method backend can
3017   // automatically cancels an ongoing composition when we send an empty text.
3018   // So, it is OK to send an empty text to the renderer.)
3019   if (!handlingKeyDown_) {
3020     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3021         markedText_, underlines_,
3022         newSelRange.location, NSMaxRange(newSelRange));
3023   }
3024 }
3025
3026 - (void)doCommandBySelector:(SEL)selector {
3027   // An input method calls this function to dispatch an editing command to be
3028   // handled by this view.
3029   if (selector == @selector(noop:))
3030     return;
3031
3032   std::string command(
3033       [RenderWidgetHostViewMacEditCommandHelper::
3034           CommandNameForSelector(selector) UTF8String]);
3035
3036   // If this method is called when handling a key down event, then we need to
3037   // handle the command in the key event handler. Otherwise we can just handle
3038   // it here.
3039   if (handlingKeyDown_) {
3040     hasEditCommands_ = YES;
3041     // We ignore commands that insert characters, because this was causing
3042     // strange behavior (e.g. tab always inserted a tab rather than moving to
3043     // the next field on the page).
3044     if (!StartsWithASCII(command, "insert", false))
3045       editCommands_.push_back(EditCommand(command, ""));
3046   } else {
3047     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3048     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3049                                               command, ""));
3050   }
3051 }
3052
3053 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3054   // An input method has characters to be inserted.
3055   // Same as Linux, Mac calls this method not only:
3056   // * when an input method finishs composing text, but also;
3057   // * when we type an ASCII character (without using input methods).
3058   // When we aren't using input methods, we should send the given character as
3059   // a Char event so it is dispatched to an onkeypress() event handler of
3060   // JavaScript.
3061   // On the other hand, when we are using input methods, we should send the
3062   // given characters as an input method event and prevent the characters from
3063   // being dispatched to onkeypress() event handlers.
3064   // Text inserting might be initiated by other source instead of keyboard
3065   // events, such as the Characters dialog. In this case the text should be
3066   // sent as an input method event as well.
3067   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3068   // the full web content.
3069   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3070   NSString* im_text = isAttributedString ? [string string] : string;
3071   if (handlingKeyDown_) {
3072     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3073   } else {
3074     gfx::Range replacement_range(replacementRange);
3075     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3076         base::SysNSStringToUTF16(im_text), replacement_range, false);
3077   }
3078
3079   // Inserting text will delete all marked text automatically.
3080   hasMarkedText_ = NO;
3081 }
3082
3083 - (void)insertText:(id)string {
3084   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3085 }
3086
3087 - (void)viewDidMoveToWindow {
3088   if ([self window]) {
3089     [self updateScreenProperties];
3090   } else {
3091     // If the RenderWidgetHostViewCocoa is being removed from its window, tear
3092     // down its browser compositor resources, if needed.
3093     renderWidgetHostView_->DestroySuspendedBrowserCompositorViewIfNeeded();
3094   }
3095
3096   if (canBeKeyView_) {
3097     NSWindow* newWindow = [self window];
3098     // Pointer comparison only, since we don't know if lastWindow_ is still
3099     // valid.
3100     if (newWindow) {
3101       // If we move into a new window, refresh the frame information. We
3102       // don't need to do it if it was the same window as it used to be in,
3103       // since that case is covered by WasShown(). We only want to do this for
3104       // real browser views, not popups.
3105       if (newWindow != lastWindow_) {
3106         lastWindow_ = newWindow;
3107         renderWidgetHostView_->WindowFrameChanged();
3108       }
3109     }
3110   }
3111
3112   // If we switch windows (or are removed from the view hierarchy), cancel any
3113   // open mouse-downs.
3114   if (hasOpenMouseDown_) {
3115     WebMouseEvent event;
3116     event.type = WebInputEvent::MouseUp;
3117     event.button = WebMouseEvent::ButtonLeft;
3118     renderWidgetHostView_->ForwardMouseEvent(event);
3119
3120     hasOpenMouseDown_ = NO;
3121   }
3122 }
3123
3124 - (void)undo:(id)sender {
3125   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3126   if (web_contents)
3127     web_contents->Undo();
3128 }
3129
3130 - (void)redo:(id)sender {
3131   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3132   if (web_contents)
3133     web_contents->Redo();
3134 }
3135
3136 - (void)cut:(id)sender {
3137   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3138   if (web_contents)
3139     web_contents->Cut();
3140 }
3141
3142 - (void)copy:(id)sender {
3143   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3144   if (web_contents)
3145     web_contents->Copy();
3146 }
3147
3148 - (void)copyToFindPboard:(id)sender {
3149   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3150   if (web_contents)
3151     web_contents->CopyToFindPboard();
3152 }
3153
3154 - (void)paste:(id)sender {
3155   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3156   if (web_contents)
3157     web_contents->Paste();
3158 }
3159
3160 - (void)pasteAndMatchStyle:(id)sender {
3161   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3162   if (web_contents)
3163     web_contents->PasteAndMatchStyle();
3164 }
3165
3166 - (void)selectAll:(id)sender {
3167   // editCommand_helper_ adds implementations for most NSResponder methods
3168   // dynamically. But the renderer side only sends selection results back to
3169   // the browser if they were triggered by a keyboard event or went through
3170   // one of the Select methods on RWH. Since selectAll: is called from the
3171   // menu handler, neither is true.
3172   // Explicitly call SelectAll() here to make sure the renderer returns
3173   // selection results.
3174   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3175   if (web_contents)
3176     web_contents->SelectAll();
3177 }
3178
3179 - (void)startSpeaking:(id)sender {
3180   renderWidgetHostView_->SpeakSelection();
3181 }
3182
3183 - (void)stopSpeaking:(id)sender {
3184   renderWidgetHostView_->StopSpeaking();
3185 }
3186
3187 - (void)cancelComposition {
3188   if (!hasMarkedText_)
3189     return;
3190
3191   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3192   // doesn't call any NSTextInput functions, such as setMarkedText or
3193   // insertText. So, we need to send an IPC message to a renderer so it can
3194   // delete the composition node.
3195   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3196   [currentInputManager markedTextAbandoned:self];
3197
3198   hasMarkedText_ = NO;
3199   // Should not call [self unmarkText] here, because it'll send unnecessary
3200   // cancel composition IPC message to the renderer.
3201 }
3202
3203 - (void)confirmComposition {
3204   if (!hasMarkedText_)
3205     return;
3206
3207   if (renderWidgetHostView_->render_widget_host_)
3208     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3209         base::string16(), gfx::Range::InvalidRange(), false);
3210
3211   [self cancelComposition];
3212 }
3213
3214 - (void)setPluginImeActive:(BOOL)active {
3215   if (active == pluginImeActive_)
3216     return;
3217
3218   pluginImeActive_ = active;
3219   if (!active) {
3220     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3221     renderWidgetHostView_->PluginImeCompositionCompleted(
3222         base::string16(), focusedPluginIdentifier_);
3223   }
3224 }
3225
3226 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3227   if (focused)
3228     focusedPluginIdentifier_ = pluginId;
3229   else if (focusedPluginIdentifier_ == pluginId)
3230     focusedPluginIdentifier_ = -1;
3231
3232   // Whenever plugin focus changes, plugin IME resets.
3233   [self setPluginImeActive:NO];
3234 }
3235
3236 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3237   if (!pluginImeActive_)
3238     return false;
3239
3240   ComplexTextInputPanel* inputPanel =
3241       [ComplexTextInputPanel sharedComplexTextInputPanel];
3242   NSString* composited_string = nil;
3243   BOOL handled = [inputPanel interpretKeyEvent:event
3244                                         string:&composited_string];
3245   if (composited_string) {
3246     renderWidgetHostView_->PluginImeCompositionCompleted(
3247         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3248     pluginImeActive_ = NO;
3249   }
3250   return handled;
3251 }
3252
3253 - (void)checkForPluginImeCancellation {
3254   if (pluginImeActive_ &&
3255       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3256     renderWidgetHostView_->PluginImeCompositionCompleted(
3257         base::string16(), focusedPluginIdentifier_);
3258     pluginImeActive_ = NO;
3259   }
3260 }
3261
3262 // Overriding a NSResponder method to support application services.
3263
3264 - (id)validRequestorForSendType:(NSString*)sendType
3265                      returnType:(NSString*)returnType {
3266   id requestor = nil;
3267   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3268   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3269   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3270   BOOL takesText =
3271       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3272
3273   if (sendTypeIsString && hasText && !returnType) {
3274     requestor = self;
3275   } else if (!sendType && returnTypeIsString && takesText) {
3276     requestor = self;
3277   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3278     requestor = self;
3279   } else {
3280     requestor = [super validRequestorForSendType:sendType
3281                                       returnType:returnType];
3282   }
3283   return requestor;
3284 }
3285
3286 - (void)viewWillStartLiveResize {
3287   [super viewWillStartLiveResize];
3288   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3289   if (widget)
3290     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3291 }
3292
3293 - (void)viewDidEndLiveResize {
3294   [super viewDidEndLiveResize];
3295   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3296   if (widget)
3297     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3298 }
3299
3300 - (void)updateCursor:(NSCursor*)cursor {
3301   if (currentCursor_ == cursor)
3302     return;
3303
3304   currentCursor_.reset([cursor retain]);
3305   [[self window] invalidateCursorRectsForView:self];
3306 }
3307
3308 - (void)popupWindowWillClose:(NSNotification *)notification {
3309   renderWidgetHostView_->KillSelf();
3310 }
3311
3312 @end
3313
3314 //
3315 // Supporting application services
3316 //
3317 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3318
3319 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3320                              types:(NSArray*)types {
3321   const std::string& str = renderWidgetHostView_->selected_text();
3322   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3323
3324   base::scoped_nsobject<NSString> text(
3325       [[NSString alloc] initWithUTF8String:str.c_str()]);
3326   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3327   [pboard declareTypes:toDeclare owner:nil];
3328   return [pboard setString:text forType:NSStringPboardType];
3329 }
3330
3331 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3332   NSString *string = [pboard stringForType:NSStringPboardType];
3333   if (!string) return NO;
3334
3335   // If the user is currently using an IME, confirm the IME input,
3336   // and then insert the text from the service, the same as TextEdit and Safari.
3337   [self confirmComposition];
3338   [self insertText:string];
3339   return YES;
3340 }
3341
3342 - (BOOL)isOpaque {
3343   return YES;
3344 }
3345
3346 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3347 // regions that are not draggable. (See ControlRegionView in
3348 // native_app_window_cocoa.mm). This requires the render host view to be
3349 // draggable by default.
3350 - (BOOL)mouseDownCanMoveWindow {
3351   return YES;
3352 }
3353
3354 @end