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