Upstream version 10.39.225.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::TextInputStateChanged(
944     const ViewHostMsg_TextInputState_Params& params) {
945   if (text_input_type_ != params.type ||
946       can_compose_inline_ != params.can_compose_inline) {
947     text_input_type_ = params.type;
948     can_compose_inline_ = params.can_compose_inline;
949     if (HasFocus()) {
950       SetTextInputActive(true);
951
952       // Let AppKit cache the new input context to make IMEs happy.
953       // See http://crbug.com/73039.
954       [NSApp updateWindows];
955
956 #ifndef __LP64__
957       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
958 #endif
959     }
960   }
961 }
962
963 void RenderWidgetHostViewMac::ImeCancelComposition() {
964   [cocoa_view_ cancelComposition];
965 }
966
967 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
968     const gfx::Range& range,
969     const std::vector<gfx::Rect>& character_bounds) {
970   // The RangeChanged message is only sent with valid values. The current
971   // caret position (start == end) will be sent if there is no IME range.
972   [cocoa_view_ setMarkedRange:range.ToNSRange()];
973   composition_range_ = range;
974   composition_bounds_ = character_bounds;
975 }
976
977 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
978                                                 int error_code) {
979   Destroy();
980 }
981
982 void RenderWidgetHostViewMac::RenderWidgetHostGone() {
983   // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
984   // called on the view.
985   // http://crbug.com/404828
986   ShutdownBrowserCompositor();
987 }
988
989 void RenderWidgetHostViewMac::Destroy() {
990   [[NSNotificationCenter defaultCenter]
991       removeObserver:cocoa_view_
992                 name:NSWindowWillCloseNotification
993               object:popup_window_];
994
995   // We've been told to destroy.
996   [cocoa_view_ retain];
997   [cocoa_view_ removeFromSuperview];
998   [cocoa_view_ autorelease];
999
1000   [popup_window_ close];
1001   popup_window_.autorelease();
1002
1003   [fullscreen_window_manager_ exitFullscreenMode];
1004   fullscreen_window_manager_.reset();
1005   [pepper_fullscreen_window_ close];
1006
1007   // This can be called as part of processing the window's responder
1008   // chain, for instance |-performKeyEquivalent:|.  In that case the
1009   // object needs to survive until the stack unwinds.
1010   pepper_fullscreen_window_.autorelease();
1011
1012   // Delete the delegated frame state, which will reach back into
1013   // render_widget_host_.
1014   ShutdownBrowserCompositor();
1015
1016   // We get this call just before |render_widget_host_| deletes
1017   // itself.  But we are owned by |cocoa_view_|, which may be retained
1018   // by some other code.  Examples are WebContentsViewMac's
1019   // |latent_focus_view_| and TabWindowController's
1020   // |cachedContentView_|.
1021   render_widget_host_ = NULL;
1022 }
1023
1024 // Called from the renderer to tell us what the tooltip text should be. It
1025 // calls us frequently so we need to cache the value to prevent doing a lot
1026 // of repeat work.
1027 void RenderWidgetHostViewMac::SetTooltipText(
1028     const base::string16& tooltip_text) {
1029   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1030     tooltip_text_ = tooltip_text;
1031
1032     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1033     // Windows; we're just trying to be polite. Don't persist the trimmed
1034     // string, as then the comparison above will always fail and we'll try to
1035     // set it again every single time the mouse moves.
1036     base::string16 display_text = tooltip_text_;
1037     if (tooltip_text_.length() > kMaxTooltipLength)
1038       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1039
1040     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1041     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1042   }
1043 }
1044
1045 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1046   return [NSApp respondsToSelector:@selector(speakString:)] &&
1047          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1048 }
1049
1050 void RenderWidgetHostViewMac::SpeakSelection() {
1051   if (![NSApp respondsToSelector:@selector(speakString:)])
1052     return;
1053
1054   if (selected_text_.empty() && render_widget_host_) {
1055     // If there's no selection, speak all text. Send an asynchronous IPC
1056     // request for fetching all the text for a webcontent.
1057     // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1058     render_widget_host_->Send(new ViewMsg_GetRenderedText(
1059         render_widget_host_->GetRoutingID()));
1060     return;
1061   }
1062
1063   SpeakText(selected_text_);
1064 }
1065
1066 bool RenderWidgetHostViewMac::IsSpeaking() const {
1067   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1068          [NSApp isSpeaking];
1069 }
1070
1071 void RenderWidgetHostViewMac::StopSpeaking() {
1072   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1073     [NSApp stopSpeaking:cocoa_view_];
1074 }
1075
1076 //
1077 // RenderWidgetHostViewCocoa uses the stored selection text,
1078 // which implements NSServicesRequests protocol.
1079 //
1080 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1081                                                size_t offset,
1082                                                const gfx::Range& range) {
1083   if (range.is_empty() || text.empty()) {
1084     selected_text_.clear();
1085   } else {
1086     size_t pos = range.GetMin() - offset;
1087     size_t n = range.length();
1088
1089     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1090     if (pos >= text.length()) {
1091       DCHECK(false) << "The text can not cover range.";
1092       return;
1093     }
1094     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1095   }
1096
1097   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1098   // Updates markedRange when there is no marked text so that retrieving
1099   // markedRange immediately after calling setMarkdText: returns the current
1100   // caret position.
1101   if (![cocoa_view_ hasMarkedText]) {
1102     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1103   }
1104
1105   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1106 }
1107
1108 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1109     const ViewHostMsg_SelectionBounds_Params& params) {
1110   if (params.anchor_rect == params.focus_rect)
1111     caret_rect_ = params.anchor_rect;
1112 }
1113
1114 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1115   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1116
1117   // Create a fake mouse event to inform the render widget that the mouse
1118   // left or entered.
1119   NSWindow* window = [cocoa_view_ window];
1120   // TODO(asvitkine): If the location outside of the event stream doesn't
1121   // correspond to the current event (due to delayed event processing), then
1122   // this may result in a cursor flicker if there are later mouse move events
1123   // in the pipeline. Find a way to use the mouse location from the event that
1124   // dismissed the context menu.
1125   NSPoint location = [window mouseLocationOutsideOfEventStream];
1126   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1127                                       location:location
1128                                  modifierFlags:0
1129                                      timestamp:0
1130                                   windowNumber:window_number()
1131                                        context:nil
1132                                    eventNumber:0
1133                                     clickCount:0
1134                                       pressure:0];
1135   WebMouseEvent web_event =
1136       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1137   if (showing)
1138     web_event.type = WebInputEvent::MouseLeave;
1139   ForwardMouseEvent(web_event);
1140 }
1141
1142 bool RenderWidgetHostViewMac::IsPopup() const {
1143   return popup_type_ != blink::WebPopupTypeNone;
1144 }
1145
1146 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1147     const gfx::Rect& src_subrect,
1148     const gfx::Size& dst_size,
1149     const base::Callback<void(bool, const SkBitmap&)>& callback,
1150     const SkColorType color_type) {
1151   if (delegated_frame_host_) {
1152     delegated_frame_host_->CopyFromCompositingSurface(
1153         src_subrect, dst_size, callback, color_type);
1154   }
1155 }
1156
1157 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1158       const gfx::Rect& src_subrect,
1159       const scoped_refptr<media::VideoFrame>& target,
1160       const base::Callback<void(bool)>& callback) {
1161   if (delegated_frame_host_) {
1162     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1163         src_subrect, target, callback);
1164   }
1165 }
1166
1167 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1168   if (delegated_frame_host_)
1169     return delegated_frame_host_->CanCopyToVideoFrame();
1170   return false;
1171 }
1172
1173 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1174   if (delegated_frame_host_)
1175     return delegated_frame_host_->CanSubscribeFrame();
1176   return false;
1177 }
1178
1179 void RenderWidgetHostViewMac::BeginFrameSubscription(
1180     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1181   if (delegated_frame_host_)
1182     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1183 }
1184
1185 void RenderWidgetHostViewMac::EndFrameSubscription() {
1186   if (delegated_frame_host_)
1187     delegated_frame_host_->EndFrameSubscription();
1188 }
1189
1190 // Sets whether or not to accept first responder status.
1191 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1192   [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1193 }
1194
1195 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1196   if (render_widget_host_)
1197     render_widget_host_->ForwardMouseEvent(event);
1198
1199   if (event.type == WebInputEvent::MouseLeave) {
1200     [cocoa_view_ setToolTipAtMousePoint:nil];
1201     tooltip_text_.clear();
1202   }
1203 }
1204
1205 void RenderWidgetHostViewMac::KillSelf() {
1206   if (!weak_factory_.HasWeakPtrs()) {
1207     [cocoa_view_ setHidden:YES];
1208     base::MessageLoop::current()->PostTask(FROM_HERE,
1209         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1210                    weak_factory_.GetWeakPtr()));
1211   }
1212 }
1213
1214 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1215     const NativeWebKeyboardEvent& event) {
1216   // Check WebInputEvent type since multiple types of events can be sent into
1217   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1218   // necessary to avoid double processing.
1219   // Also check the native type, since NSFlagsChanged is considered a key event
1220   // for WebKit purposes, but isn't considered a key event by the OS.
1221   if (event.type == WebInputEvent::RawKeyDown &&
1222       [event.os_event type] == NSKeyDown)
1223     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1224   return false;
1225 }
1226
1227 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1228     const base::string16& text, int plugin_id) {
1229   if (render_widget_host_) {
1230     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1231         render_widget_host_->GetRoutingID(), text, plugin_id));
1232   }
1233 }
1234
1235 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1236     const std::vector<gfx::Rect>& bounds,
1237     const gfx::Range& range,
1238     size_t* line_break_point) {
1239   DCHECK(line_break_point);
1240   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1241     return false;
1242
1243   // We can't check line breaking completely from only rectangle array. Thus we
1244   // assume the line breaking as the next character's y offset is larger than
1245   // a threshold. Currently the threshold is determined as minimum y offset plus
1246   // 75% of maximum height.
1247   // TODO(nona): Check the threshold is reliable or not.
1248   // TODO(nona): Bidi support.
1249   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1250   int max_height = 0;
1251   int min_y_offset = kint32max;
1252   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1253     max_height = std::max(max_height, bounds[idx].height());
1254     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1255   }
1256   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1257   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1258     if (bounds[idx].y() > line_break_threshold) {
1259       *line_break_point = idx;
1260       return true;
1261     }
1262   }
1263   return false;
1264 }
1265
1266 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1267     const gfx::Range& range,
1268     gfx::Range* actual_range) {
1269   DCHECK(actual_range);
1270   DCHECK(!composition_bounds_.empty());
1271   DCHECK(range.start() <= composition_bounds_.size());
1272   DCHECK(range.end() <= composition_bounds_.size());
1273
1274   if (range.is_empty()) {
1275     *actual_range = range;
1276     if (range.start() == composition_bounds_.size()) {
1277       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1278                        composition_bounds_[range.start() - 1].y(),
1279                        0,
1280                        composition_bounds_[range.start() - 1].height());
1281     } else {
1282       return gfx::Rect(composition_bounds_[range.start()].x(),
1283                        composition_bounds_[range.start()].y(),
1284                        0,
1285                        composition_bounds_[range.start()].height());
1286     }
1287   }
1288
1289   size_t end_idx;
1290   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1291     end_idx = range.end();
1292   }
1293   *actual_range = gfx::Range(range.start(), end_idx);
1294   gfx::Rect rect = composition_bounds_[range.start()];
1295   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1296     rect.Union(composition_bounds_[i]);
1297   }
1298   return rect;
1299 }
1300
1301 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1302     const gfx::Range& request_range) {
1303   if (composition_range_.is_empty())
1304     return gfx::Range::InvalidRange();
1305
1306   if (request_range.is_reversed())
1307     return gfx::Range::InvalidRange();
1308
1309   if (request_range.start() < composition_range_.start() ||
1310       request_range.start() > composition_range_.end() ||
1311       request_range.end() > composition_range_.end()) {
1312     return gfx::Range::InvalidRange();
1313   }
1314
1315   return gfx::Range(
1316       request_range.start() - composition_range_.start(),
1317       request_range.end() - composition_range_.start());
1318 }
1319
1320 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1321   if (!render_widget_host_->IsRenderView())
1322     return NULL;
1323
1324   return WebContents::FromRenderViewHost(
1325       RenderViewHost::From(render_widget_host_));
1326 }
1327
1328 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1329     NSRange range,
1330     NSRect* rect,
1331     NSRange* actual_range) {
1332   DCHECK(rect);
1333   // This exists to make IMEs more responsive, see http://crbug.com/115920
1334   TRACE_EVENT0("browser",
1335                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1336
1337   // If requested range is same as caret location, we can just return it.
1338   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1339     if (actual_range)
1340       *actual_range = range;
1341     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1342     return true;
1343   }
1344
1345   const gfx::Range request_range_in_composition =
1346       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1347   if (request_range_in_composition == gfx::Range::InvalidRange())
1348     return false;
1349
1350   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1351   // ImeCompositionRangeChanged will be sent with empty vector.
1352   if (composition_bounds_.empty())
1353     return false;
1354   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1355
1356   gfx::Range ui_actual_range;
1357   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1358                                request_range_in_composition,
1359                                &ui_actual_range).ToCGRect());
1360   if (actual_range) {
1361     *actual_range = gfx::Range(
1362         composition_range_.start() + ui_actual_range.start(),
1363         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1364   }
1365   return true;
1366 }
1367
1368 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1369     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1370     int gpu_host_id) {
1371 }
1372
1373 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1374     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1375     int gpu_host_id) {
1376 }
1377
1378 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1379 }
1380
1381 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1382 }
1383
1384 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1385       const gfx::Size& desired_size) {
1386   if (browser_compositor_view_)
1387     return browser_compositor_view_->HasFrameOfSize(desired_size);
1388   return false;
1389 }
1390
1391 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1392     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1393   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1394
1395   last_scroll_offset_ = frame->metadata.root_scroll_offset;
1396   if (frame->delegated_frame_data) {
1397     float scale_factor = frame->metadata.device_scale_factor;
1398
1399     // Compute the frame size based on the root render pass rect size.
1400     cc::RenderPass* root_pass =
1401         frame->delegated_frame_data->render_pass_list.back();
1402     gfx::Size pixel_size = root_pass->output_rect.size();
1403     gfx::Size dip_size =
1404         ConvertSizeToDIP(scale_factor, pixel_size);
1405
1406     root_layer_->SetBounds(gfx::Rect(dip_size));
1407     if (!render_widget_host_->is_hidden()) {
1408       EnsureBrowserCompositorView();
1409       browser_compositor_view_->GetCompositor()->SetScaleAndSize(
1410           scale_factor, pixel_size);
1411     }
1412
1413     SendVSyncParametersToRenderer();
1414
1415     delegated_frame_host_->SwapDelegatedFrame(
1416         output_surface_id,
1417         frame->delegated_frame_data.Pass(),
1418         frame->metadata.device_scale_factor,
1419         frame->metadata.latency_info);
1420   } else {
1421     DLOG(ERROR) << "Received unexpected frame type.";
1422     RecordAction(
1423         base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1424     render_widget_host_->GetProcess()->ReceivedBadMessage();
1425   }
1426 }
1427
1428 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1429                                                             int route_id) {
1430 }
1431
1432 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1433   *results = GetWebScreenInfo(GetNativeView());
1434 }
1435
1436 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1437   // TODO(shess): In case of !window, the view has been removed from
1438   // the view hierarchy because the tab isn't main.  Could retrieve
1439   // the information from the main tab for our window.
1440   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1441   if (!enclosing_window)
1442     return gfx::Rect();
1443
1444   NSRect bounds = [enclosing_window frame];
1445   return FlipNSRectToRectScreen(bounds);
1446 }
1447
1448 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1449   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1450   // completely on Mac OS.
1451   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
1452 }
1453
1454 bool RenderWidgetHostViewMac::LockMouse() {
1455   if (mouse_locked_)
1456     return true;
1457
1458   mouse_locked_ = true;
1459
1460   // Lock position of mouse cursor and hide it.
1461   CGAssociateMouseAndMouseCursorPosition(NO);
1462   [NSCursor hide];
1463
1464   // Clear the tooltip window.
1465   SetTooltipText(base::string16());
1466
1467   return true;
1468 }
1469
1470 void RenderWidgetHostViewMac::UnlockMouse() {
1471   if (!mouse_locked_)
1472     return;
1473   mouse_locked_ = false;
1474
1475   // Unlock position of mouse cursor and unhide it.
1476   CGAssociateMouseAndMouseCursorPosition(YES);
1477   [NSCursor unhide];
1478
1479   if (render_widget_host_)
1480     render_widget_host_->LostMouseLock();
1481 }
1482
1483 void RenderWidgetHostViewMac::WheelEventAck(
1484     const blink::WebMouseWheelEvent& event,
1485     InputEventAckState ack_result) {
1486   bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1487   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1488   // to see it (no-op wheel events are ignored by the event dispatcher)
1489   if (event.deltaX || event.deltaY)
1490     [cocoa_view_ processedWheelEvent:event consumed:consumed];
1491 }
1492
1493 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1494   if (render_widget_host_)
1495     return render_widget_host_->Send(message);
1496   delete message;
1497   return false;
1498 }
1499
1500 void RenderWidgetHostViewMac::ShutdownHost() {
1501   weak_factory_.InvalidateWeakPtrs();
1502   render_widget_host_->Shutdown();
1503   // Do not touch any members at this point, |this| has been deleted.
1504 }
1505
1506 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1507   DestroyBrowserCompositorView();
1508   delegated_frame_host_.reset();
1509   root_layer_.reset();
1510   browser_compositor_view_placeholder_.reset();
1511 }
1512
1513 void RenderWidgetHostViewMac::SetActive(bool active) {
1514   if (render_widget_host_) {
1515     render_widget_host_->SetActive(active);
1516     if (active) {
1517       if (HasFocus())
1518         render_widget_host_->Focus();
1519     } else {
1520       render_widget_host_->Blur();
1521     }
1522   }
1523   if (HasFocus())
1524     SetTextInputActive(active);
1525   if (!active) {
1526     [cocoa_view_ setPluginImeActive:NO];
1527     UnlockMouse();
1528   }
1529 }
1530
1531 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1532   if (render_widget_host_) {
1533     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1534         render_widget_host_->GetRoutingID(), visible));
1535   }
1536 }
1537
1538 void RenderWidgetHostViewMac::WindowFrameChanged() {
1539   if (render_widget_host_) {
1540     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1541         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1542         GetViewBounds()));
1543   }
1544 }
1545
1546 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1547   RenderWidgetHostViewMacDictionaryHelper helper(this);
1548   helper.ShowDefinitionForSelection();
1549 }
1550
1551 void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
1552   RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
1553   if (render_widget_host_)
1554     render_widget_host_->SetBackgroundOpaque(opaque);
1555 }
1556
1557 BrowserAccessibilityManager*
1558     RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1559         BrowserAccessibilityDelegate* delegate) {
1560   return new BrowserAccessibilityManagerMac(
1561       cocoa_view_,
1562       BrowserAccessibilityManagerMac::GetEmptyDocument(),
1563       delegate);
1564 }
1565
1566 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1567     const gfx::Rect& bounds) {
1568   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1569   NSSize size = NSMakeSize(bounds.width(), bounds.height());
1570   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1571   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1572   NSPoint originInScreen =
1573       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1574   originInScreen.y = originInScreen.y - size.height;
1575   return gfx::Point(originInScreen.x, originInScreen.y);
1576 }
1577
1578 void RenderWidgetHostViewMac::AccessibilityShowMenu(const gfx::Point& point) {
1579   NSPoint location = NSMakePoint(point.x(), point.y());
1580   location = [[cocoa_view_ window] convertScreenToBase:location];
1581   NSEvent* fakeRightClick = [NSEvent
1582                           mouseEventWithType:NSRightMouseDown
1583                                     location:location
1584                                modifierFlags:0
1585                                    timestamp:0
1586                                 windowNumber:[[cocoa_view_ window] windowNumber]
1587                                      context:[NSGraphicsContext currentContext]
1588                                  eventNumber:0
1589                                   clickCount:1
1590                                     pressure:0];
1591
1592   [cocoa_view_ mouseEvent:fakeRightClick];
1593 }
1594
1595 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1596   if (active) {
1597     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1598       EnablePasswordInput();
1599     else
1600       DisablePasswordInput();
1601   } else {
1602     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1603       DisablePasswordInput();
1604   }
1605 }
1606
1607 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1608                                                    int plugin_id) {
1609   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1610 }
1611
1612 void RenderWidgetHostViewMac::OnStartPluginIme() {
1613   [cocoa_view_ setPluginImeActive:YES];
1614 }
1615
1616 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1617     const std::string& text) {
1618   SpeakText(text);
1619 }
1620
1621 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1622   if (!render_widget_host_ || render_widget_host_->is_hidden())
1623     return;
1624
1625   // Pausing for one view prevents others from receiving frames.
1626   // This may lead to large delays, causing overlaps. See crbug.com/352020.
1627   if (!allow_pause_for_resize_or_repaint_)
1628     return;
1629
1630   // Wait for a frame of the right size to come in.
1631   if (browser_compositor_view_)
1632     browser_compositor_view_->BeginPumpingFrames();
1633   render_widget_host_->PauseForPendingResizeOrRepaints();
1634   if (browser_compositor_view_)
1635     browser_compositor_view_->EndPumpingFrames();
1636 }
1637
1638 SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() {
1639   return kN32_SkColorType;
1640 }
1641
1642 ////////////////////////////////////////////////////////////////////////////////
1643 // gfx::DisplayObserver, public:
1644
1645 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1646 }
1647
1648 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1649 }
1650
1651 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1652     const gfx::Display& display, uint32_t metrics) {
1653   gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1654   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1655     return;
1656
1657   UpdateScreenInfo(cocoa_view_);
1658 }
1659
1660 }  // namespace content
1661
1662 // RenderWidgetHostViewCocoa ---------------------------------------------------
1663
1664 @implementation RenderWidgetHostViewCocoa
1665 @synthesize selectedRange = selectedRange_;
1666 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1667 @synthesize markedRange = markedRange_;
1668
1669 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1670   self = [super initWithFrame:NSZeroRect];
1671   if (self) {
1672     self.acceptsTouchEvents = YES;
1673     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1674     editCommand_helper_->AddEditingSelectorsToClass([self class]);
1675
1676     renderWidgetHostView_.reset(r);
1677     canBeKeyView_ = YES;
1678     focusedPluginIdentifier_ = -1;
1679
1680     // OpenGL support:
1681     if ([self respondsToSelector:
1682         @selector(setWantsBestResolutionOpenGLSurface:)]) {
1683       [self setWantsBestResolutionOpenGLSurface:YES];
1684     }
1685     [[NSNotificationCenter defaultCenter]
1686         addObserver:self
1687            selector:@selector(didChangeScreenParameters:)
1688                name:NSApplicationDidChangeScreenParametersNotification
1689              object:nil];
1690   }
1691   return self;
1692 }
1693
1694 - (void)dealloc {
1695   // Unbind the GL context from this view. If this is not done before super's
1696   // dealloc is called then the GL context will crash when it reaches into
1697   // the view in its destructor.
1698   // http://crbug.com/255608
1699   if (renderWidgetHostView_)
1700     renderWidgetHostView_->AcceleratedSurfaceRelease();
1701
1702   if (responderDelegate_ &&
1703       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1704     [responderDelegate_ viewGone:self];
1705   responderDelegate_.reset();
1706
1707   [[NSNotificationCenter defaultCenter] removeObserver:self];
1708
1709   [super dealloc];
1710 }
1711
1712 - (void)didChangeScreenParameters:(NSNotification*)notify {
1713   g_screen_info_up_to_date = false;
1714 }
1715
1716 - (void)setResponderDelegate:
1717             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1718   DCHECK(!responderDelegate_);
1719   responderDelegate_.reset([delegate retain]);
1720 }
1721
1722 - (void)resetCursorRects {
1723   if (currentCursor_) {
1724     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1725     [currentCursor_ setOnMouseEntered:YES];
1726   }
1727 }
1728
1729 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1730                    consumed:(BOOL)consumed {
1731   [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1732 }
1733
1734 - (BOOL)respondsToSelector:(SEL)selector {
1735   // Trickiness: this doesn't mean "does this object's superclass respond to
1736   // this selector" but rather "does the -respondsToSelector impl from the
1737   // superclass say that this class responds to the selector".
1738   if ([super respondsToSelector:selector])
1739     return YES;
1740
1741   if (responderDelegate_)
1742     return [responderDelegate_ respondsToSelector:selector];
1743
1744   return NO;
1745 }
1746
1747 - (id)forwardingTargetForSelector:(SEL)selector {
1748   if ([responderDelegate_ respondsToSelector:selector])
1749     return responderDelegate_.get();
1750
1751   return [super forwardingTargetForSelector:selector];
1752 }
1753
1754 - (void)setCanBeKeyView:(BOOL)can {
1755   canBeKeyView_ = can;
1756 }
1757
1758 - (BOOL)acceptsMouseEventsWhenInactive {
1759   // Some types of windows (balloons, always-on-top panels) want to accept mouse
1760   // clicks w/o the first click being treated as 'activation'. Same applies to
1761   // mouse move events.
1762   return [[self window] level] > NSNormalWindowLevel;
1763 }
1764
1765 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1766   return [self acceptsMouseEventsWhenInactive];
1767 }
1768
1769 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
1770   takesFocusOnlyOnMouseDown_ = b;
1771 }
1772
1773 - (void)setCloseOnDeactivate:(BOOL)b {
1774   closeOnDeactivate_ = b;
1775 }
1776
1777 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1778   NSWindow* window = [self window];
1779   // If this is a background window, don't handle mouse movement events. This
1780   // is the expected behavior on the Mac as evidenced by other applications.
1781   if ([theEvent type] == NSMouseMoved &&
1782       ![self acceptsMouseEventsWhenInactive] &&
1783       ![window isKeyWindow]) {
1784     return YES;
1785   }
1786
1787   // Use hitTest to check whether the mouse is over a nonWebContentView - in
1788   // which case the mouse event should not be handled by the render host.
1789   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1790   NSView* contentView = [window contentView];
1791   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1792   // Traverse the superview hierarchy as the hitTest will return the frontmost
1793   // view, such as an NSTextView, while nonWebContentView may be specified by
1794   // its parent view.
1795   while (view) {
1796     if ([view respondsToSelector:nonWebContentViewSelector] &&
1797         [view performSelector:nonWebContentViewSelector]) {
1798       // The cursor is over a nonWebContentView - ignore this mouse event.
1799       return YES;
1800     }
1801     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1802         !hasOpenMouseDown_) {
1803       // The cursor is over an overlapping render widget. This check is done by
1804       // both views so the one that's returned by -hitTest: will end up
1805       // processing the event.
1806       // Note that while dragging, we only get events for the render view where
1807       // drag started, even if mouse is  actually over another view or outside
1808       // the window. Cocoa does this for us. We should handle these events and
1809       // not ignore (since there is no other render view to handle them). Thus
1810       // the |!hasOpenMouseDown_| check above.
1811       return YES;
1812     }
1813     view = [view superview];
1814   }
1815   return NO;
1816 }
1817
1818 - (void)mouseEvent:(NSEvent*)theEvent {
1819   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1820   if (responderDelegate_ &&
1821       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1822     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1823     if (handled)
1824       return;
1825   }
1826
1827   if ([self shouldIgnoreMouseEvent:theEvent]) {
1828     // If this is the first such event, send a mouse exit to the host view.
1829     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1830       WebMouseEvent exitEvent =
1831           WebInputEventFactory::mouseEvent(theEvent, self);
1832       exitEvent.type = WebInputEvent::MouseLeave;
1833       exitEvent.button = WebMouseEvent::ButtonNone;
1834       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1835     }
1836     mouseEventWasIgnored_ = YES;
1837     return;
1838   }
1839
1840   if (mouseEventWasIgnored_) {
1841     // If this is the first mouse event after a previous event that was ignored
1842     // due to the hitTest, send a mouse enter event to the host view.
1843     if (renderWidgetHostView_->render_widget_host_) {
1844       WebMouseEvent enterEvent =
1845           WebInputEventFactory::mouseEvent(theEvent, self);
1846       enterEvent.type = WebInputEvent::MouseMove;
1847       enterEvent.button = WebMouseEvent::ButtonNone;
1848       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1849     }
1850   }
1851   mouseEventWasIgnored_ = NO;
1852
1853   // TODO(rohitrao): Probably need to handle other mouse down events here.
1854   if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
1855     if (renderWidgetHostView_->render_widget_host_)
1856       renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
1857
1858     // Manually take focus after the click but before forwarding it to the
1859     // renderer.
1860     [[self window] makeFirstResponder:self];
1861   }
1862
1863   // Don't cancel child popups; killing them on a mouse click would prevent the
1864   // user from positioning the insertion point in the text field spawning the
1865   // popup. A click outside the text field would cause the text field to drop
1866   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1867   // the popup anyway, so we're OK.
1868
1869   NSEventType type = [theEvent type];
1870   if (type == NSLeftMouseDown)
1871     hasOpenMouseDown_ = YES;
1872   else if (type == NSLeftMouseUp)
1873     hasOpenMouseDown_ = NO;
1874
1875   // TODO(suzhe): We should send mouse events to the input method first if it
1876   // wants to handle them. But it won't work without implementing method
1877   // - (NSUInteger)characterIndexForPoint:.
1878   // See: http://code.google.com/p/chromium/issues/detail?id=47141
1879   // Instead of sending mouse events to the input method first, we now just
1880   // simply confirm all ongoing composition here.
1881   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1882       type == NSOtherMouseDown) {
1883     [self confirmComposition];
1884   }
1885
1886   const WebMouseEvent event =
1887       WebInputEventFactory::mouseEvent(theEvent, self);
1888   renderWidgetHostView_->ForwardMouseEvent(event);
1889 }
1890
1891 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1892   // |performKeyEquivalent:| is sent to all views of a window, not only down the
1893   // responder chain (cf. "Handling Key Equivalents" in
1894   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1895   // ). We only want to handle key equivalents if we're first responder.
1896   if ([[self window] firstResponder] != self)
1897     return NO;
1898
1899   // If the event is reserved by the system, then do not pass it to web content.
1900   if (EventIsReservedBySystem(theEvent))
1901     return NO;
1902
1903   // If we return |NO| from this function, cocoa will send the key event to
1904   // the menu and only if the menu does not process the event to |keyDown:|. We
1905   // want to send the event to a renderer _before_ sending it to the menu, so
1906   // we need to return |YES| for all events that might be swallowed by the menu.
1907   // We do not return |YES| for every keypress because we don't get |keyDown:|
1908   // events for keys that we handle this way.
1909   NSUInteger modifierFlags = [theEvent modifierFlags];
1910   if ((modifierFlags & NSCommandKeyMask) == 0) {
1911     // Make sure the menu does not contain key equivalents that don't
1912     // contain cmd.
1913     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1914     return NO;
1915   }
1916
1917   // Command key combinations are sent via performKeyEquivalent rather than
1918   // keyDown:. We just forward this on and if WebCore doesn't want to handle
1919   // it, we let the WebContentsView figure out how to reinject it.
1920   [self keyEvent:theEvent wasKeyEquivalent:YES];
1921   return YES;
1922 }
1923
1924 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1925   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1926   // returned NO. If this function returns |YES|, Cocoa sends the event to
1927   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1928   // to us instead of doing key view loop control, ctrl-left/right get handled
1929   // correctly, etc.
1930   // (However, there are still some keys that Cocoa swallows, e.g. the key
1931   // equivalent that Cocoa uses for toggling the input language. In this case,
1932   // that's actually a good thing, though -- see http://crbug.com/26115 .)
1933   return YES;
1934 }
1935
1936 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1937   if (responderDelegate_ &&
1938       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1939     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1940     if (handled)
1941       return kEventHandled;
1942   }
1943
1944   [self keyEvent:theEvent wasKeyEquivalent:NO];
1945   return kEventHandled;
1946 }
1947
1948 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1949   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
1950
1951   // If the user changes the system hotkey mapping after Chrome has been
1952   // launched, then it is possible that a formerly reserved system hotkey is no
1953   // longer reserved. The hotkey would have skipped the renderer, but would
1954   // also have not been handled by the system. If this is the case, immediately
1955   // return.
1956   // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
1957   // api to monitor changes to system hotkeys. This logic will have to be
1958   // updated.
1959   // http://crbug.com/383558.
1960   if (EventIsReservedBySystem(theEvent))
1961     return;
1962
1963   DCHECK([theEvent type] != NSKeyDown ||
1964          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1965
1966   if ([theEvent type] == NSFlagsChanged) {
1967     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1968     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1969     int keyCode = [theEvent keyCode];
1970     if (!keyCode || keyCode == 10 || keyCode == 63)
1971       return;
1972   }
1973
1974   // Don't cancel child popups; the key events are probably what's triggering
1975   // the popup in the first place.
1976
1977   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
1978   DCHECK(widgetHost);
1979
1980   NativeWebKeyboardEvent event(theEvent);
1981
1982   // Force fullscreen windows to close on Escape so they won't keep the keyboard
1983   // grabbed or be stuck onscreen if the renderer is hanging.
1984   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
1985       event.windowsKeyCode == ui::VKEY_ESCAPE &&
1986       renderWidgetHostView_->pepper_fullscreen_window()) {
1987     RenderWidgetHostViewMac* parent =
1988         renderWidgetHostView_->fullscreen_parent_host_view();
1989     if (parent)
1990       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
1991     widgetHost->Shutdown();
1992     return;
1993   }
1994
1995   // Suppress the escape key up event if necessary.
1996   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
1997     if (event.type == NativeWebKeyboardEvent::KeyUp)
1998       suppressNextEscapeKeyUp_ = NO;
1999     return;
2000   }
2001
2002   // We only handle key down events and just simply forward other events.
2003   if ([theEvent type] != NSKeyDown) {
2004     widgetHost->ForwardKeyboardEvent(event);
2005
2006     // Possibly autohide the cursor.
2007     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2008       [NSCursor setHiddenUntilMouseMoves:YES];
2009
2010     return;
2011   }
2012
2013   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2014
2015   // Records the current marked text state, so that we can know if the marked
2016   // text was deleted or not after handling the key down event.
2017   BOOL oldHasMarkedText = hasMarkedText_;
2018
2019   // This method should not be called recursively.
2020   DCHECK(!handlingKeyDown_);
2021
2022   // Tells insertText: and doCommandBySelector: that we are handling a key
2023   // down event.
2024   handlingKeyDown_ = YES;
2025
2026   // These variables might be set when handling the keyboard event.
2027   // Clear them here so that we can know whether they have changed afterwards.
2028   textToBeInserted_.clear();
2029   markedText_.clear();
2030   underlines_.clear();
2031   unmarkTextCalled_ = NO;
2032   hasEditCommands_ = NO;
2033   editCommands_.clear();
2034
2035   // Before doing anything with a key down, check to see if plugin IME has been
2036   // cancelled, since the plugin host needs to be informed of that before
2037   // receiving the keydown.
2038   if ([theEvent type] == NSKeyDown)
2039     [self checkForPluginImeCancellation];
2040
2041   // Sends key down events to input method first, then we can decide what should
2042   // be done according to input method's feedback.
2043   // If a plugin is active, bypass this step since events are forwarded directly
2044   // to the plugin IME.
2045   if (focusedPluginIdentifier_ == -1)
2046     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2047
2048   handlingKeyDown_ = NO;
2049
2050   // Indicates if we should send the key event and corresponding editor commands
2051   // after processing the input method result.
2052   BOOL delayEventUntilAfterImeCompostion = NO;
2053
2054   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2055   // while an input method is composing or inserting a text.
2056   // Gmail checks this code in its onkeydown handler to stop auto-completing
2057   // e-mail addresses while composing a CJK text.
2058   // If the text to be inserted has only one character, then we don't need this
2059   // trick, because we'll send the text as a key press event instead.
2060   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2061     NativeWebKeyboardEvent fakeEvent = event;
2062     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2063     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2064     fakeEvent.skip_in_browser = true;
2065     widgetHost->ForwardKeyboardEvent(fakeEvent);
2066     // If this key event was handled by the input method, but
2067     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2068     // enqueued edit commands, then in order to let webkit handle them
2069     // correctly, we need to send the real key event and corresponding edit
2070     // commands after processing the input method result.
2071     // We shouldn't do this if a new marked text was set by the input method,
2072     // otherwise the new marked text might be cancelled by webkit.
2073     if (hasEditCommands_ && !hasMarkedText_)
2074       delayEventUntilAfterImeCompostion = YES;
2075   } else {
2076     if (!editCommands_.empty()) {
2077       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2078           widgetHost->GetRoutingID(), editCommands_));
2079     }
2080     widgetHost->ForwardKeyboardEvent(event);
2081   }
2082
2083   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2084   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2085   // be set to NULL. So we check it here and return immediately if it's NULL.
2086   if (!renderWidgetHostView_->render_widget_host_)
2087     return;
2088
2089   // Then send keypress and/or composition related events.
2090   // If there was a marked text or the text to be inserted is longer than 1
2091   // character, then we send the text by calling ConfirmComposition().
2092   // Otherwise, if the text to be inserted only contains 1 character, then we
2093   // can just send a keypress event which is fabricated by changing the type of
2094   // the keydown event, so that we can retain all necessary informations, such
2095   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2096   // prevent the browser from handling it again.
2097   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2098   // handle BMP characters here, as we can always insert non-BMP characters as
2099   // text.
2100   BOOL textInserted = NO;
2101   if (textToBeInserted_.length() >
2102       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2103     widgetHost->ImeConfirmComposition(
2104         textToBeInserted_, gfx::Range::InvalidRange(), false);
2105     textInserted = YES;
2106   }
2107
2108   // Updates or cancels the composition. If some text has been inserted, then
2109   // we don't need to cancel the composition explicitly.
2110   if (hasMarkedText_ && markedText_.length()) {
2111     // Sends the updated marked text to the renderer so it can update the
2112     // composition node in WebKit.
2113     // When marked text is available, |selectedRange_| will be the range being
2114     // selected inside the marked text.
2115     widgetHost->ImeSetComposition(markedText_, underlines_,
2116                                   selectedRange_.location,
2117                                   NSMaxRange(selectedRange_));
2118   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2119     if (unmarkTextCalled_) {
2120       widgetHost->ImeConfirmComposition(
2121           base::string16(), gfx::Range::InvalidRange(), false);
2122     } else {
2123       widgetHost->ImeCancelComposition();
2124     }
2125   }
2126
2127   // If the key event was handled by the input method but it also generated some
2128   // edit commands, then we need to send the real key event and corresponding
2129   // edit commands here. This usually occurs when the input method wants to
2130   // finish current composition session but still wants the application to
2131   // handle the key event. See http://crbug.com/48161 for reference.
2132   if (delayEventUntilAfterImeCompostion) {
2133     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2134     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2135     // So before sending the real key down event, we need to send a fake key up
2136     // event to balance it.
2137     NativeWebKeyboardEvent fakeEvent = event;
2138     fakeEvent.type = blink::WebInputEvent::KeyUp;
2139     fakeEvent.skip_in_browser = true;
2140     widgetHost->ForwardKeyboardEvent(fakeEvent);
2141     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2142     // a key event with |skip_in_browser| == true won't be handled by browser,
2143     // thus it won't destroy the widget.
2144
2145     if (!editCommands_.empty()) {
2146       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2147           widgetHost->GetRoutingID(), editCommands_));
2148     }
2149     widgetHost->ForwardKeyboardEvent(event);
2150
2151     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2152     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2153     // be set to NULL. So we check it here and return immediately if it's NULL.
2154     if (!renderWidgetHostView_->render_widget_host_)
2155       return;
2156   }
2157
2158   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2159   // Only send a corresponding key press event if there is no marked text.
2160   if (!hasMarkedText_) {
2161     if (!textInserted && textToBeInserted_.length() == 1) {
2162       // If a single character was inserted, then we just send it as a keypress
2163       // event.
2164       event.type = blink::WebInputEvent::Char;
2165       event.text[0] = textToBeInserted_[0];
2166       event.text[1] = 0;
2167       event.skip_in_browser = true;
2168       widgetHost->ForwardKeyboardEvent(event);
2169     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2170                [[theEvent characters] length] > 0 &&
2171                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2172                 (hasEditCommands_ && editCommands_.empty()))) {
2173       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2174       // generates an insert command. So synthesize a keypress event for these
2175       // cases, unless the key event generated any other command.
2176       event.type = blink::WebInputEvent::Char;
2177       event.skip_in_browser = true;
2178       widgetHost->ForwardKeyboardEvent(event);
2179     }
2180   }
2181
2182   // Possibly autohide the cursor.
2183   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2184     [NSCursor setHiddenUntilMouseMoves:YES];
2185 }
2186
2187 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2188   DCHECK(base::mac::IsOSLionOrLater());
2189
2190   if ([event phase] != NSEventPhaseEnded &&
2191       [event phase] != NSEventPhaseCancelled) {
2192     return;
2193   }
2194
2195   if (renderWidgetHostView_->render_widget_host_) {
2196     // History-swiping is not possible if the logic reaches this point.
2197     // Allow rubber-banding in both directions.
2198     bool canRubberbandLeft = true;
2199     bool canRubberbandRight = true;
2200     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2201         event, self, canRubberbandLeft, canRubberbandRight);
2202     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2203   }
2204
2205   if (endWheelMonitor_) {
2206     [NSEvent removeMonitor:endWheelMonitor_];
2207     endWheelMonitor_ = nil;
2208   }
2209 }
2210
2211 - (void)beginGestureWithEvent:(NSEvent*)event {
2212   [responderDelegate_ beginGestureWithEvent:event];
2213 }
2214 - (void)endGestureWithEvent:(NSEvent*)event {
2215   [responderDelegate_ endGestureWithEvent:event];
2216 }
2217 - (void)touchesMovedWithEvent:(NSEvent*)event {
2218   [responderDelegate_ touchesMovedWithEvent:event];
2219 }
2220 - (void)touchesBeganWithEvent:(NSEvent*)event {
2221   [responderDelegate_ touchesBeganWithEvent:event];
2222 }
2223 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2224   [responderDelegate_ touchesCancelledWithEvent:event];
2225 }
2226 - (void)touchesEndedWithEvent:(NSEvent*)event {
2227   [responderDelegate_ touchesEndedWithEvent:event];
2228 }
2229
2230 // This is invoked only on 10.8 or newer when the user taps a word using
2231 // three fingers.
2232 - (void)quickLookWithEvent:(NSEvent*)event {
2233   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2234   TextInputClientMac::GetInstance()->GetStringAtPoint(
2235       renderWidgetHostView_->render_widget_host_,
2236       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2237       ^(NSAttributedString* string, NSPoint baselinePoint) {
2238           if (string && [string length] > 0) {
2239             dispatch_async(dispatch_get_main_queue(), ^{
2240                 [self showDefinitionForAttributedString:string
2241                                                 atPoint:baselinePoint];
2242             });
2243           }
2244       }
2245   );
2246 }
2247
2248 // This method handles 2 different types of hardware events.
2249 // (Apple does not distinguish between them).
2250 //  a. Scrolling the middle wheel of a mouse.
2251 //  b. Swiping on the track pad.
2252 //
2253 // This method is responsible for 2 types of behavior:
2254 //  a. Scrolling the content of window.
2255 //  b. Navigating forwards/backwards in history.
2256 //
2257 // This is a brief description of the logic:
2258 //  1. If the content can be scrolled, scroll the content.
2259 //     (This requires a roundtrip to blink to determine whether the content
2260 //      can be scrolled.)
2261 //     Once this logic is triggered, the navigate logic cannot be triggered
2262 //     until the gesture finishes.
2263 //  2. If the user is making a horizontal swipe, start the navigate
2264 //     forward/backwards UI.
2265 //     Once this logic is triggered, the user can either cancel or complete
2266 //     the gesture. If the user completes the gesture, all remaining touches
2267 //     are swallowed, and not allowed to scroll the content. If the user
2268 //     cancels the gesture, all remaining touches are forwarded to the content
2269 //     scroll logic. The user cannot trigger the navigation logic again.
2270 - (void)scrollWheel:(NSEvent*)event {
2271   if (responderDelegate_ &&
2272       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2273     BOOL handled = [responderDelegate_ handleEvent:event];
2274     if (handled)
2275       return;
2276   }
2277
2278   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2279   // the event is received even when the mouse cursor is no longer over the view
2280   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2281   // for ending rubber-banding in such cases.
2282   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2283       !endWheelMonitor_) {
2284     endWheelMonitor_ =
2285       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2286       handler:^(NSEvent* blockEvent) {
2287           [self shortCircuitScrollWheelEvent:blockEvent];
2288           return blockEvent;
2289       }];
2290   }
2291
2292   // This is responsible for content scrolling!
2293   if (renderWidgetHostView_->render_widget_host_) {
2294     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2295     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2296     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2297         event, self, canRubberbandLeft, canRubberbandRight);
2298     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2299   }
2300 }
2301
2302 // Called repeatedly during a pinch gesture, with incremental change values.
2303 - (void)magnifyWithEvent:(NSEvent*)event {
2304   if (renderWidgetHostView_->render_widget_host_) {
2305     // Send a GesturePinchUpdate event.
2306     // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
2307     // GestureSrollBegin/End) as is done for touchscreen.  Keeping track of when
2308     // a pinch is active would take a little more work here, and we don't need
2309     // it for anything yet.
2310     const WebGestureEvent& webEvent =
2311         WebInputEventFactory::gestureEvent(event, self);
2312     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
2313   }
2314 }
2315
2316 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2317   NSWindow* oldWindow = [self window];
2318
2319   NSNotificationCenter* notificationCenter =
2320       [NSNotificationCenter defaultCenter];
2321
2322   // Backing property notifications crash on 10.6 when building with the 10.7
2323   // SDK, see http://crbug.com/260595.
2324   static BOOL supportsBackingPropertiesNotification =
2325       SupportsBackingPropertiesChangedNotification();
2326
2327   if (oldWindow) {
2328     if (supportsBackingPropertiesNotification) {
2329       [notificationCenter
2330           removeObserver:self
2331                     name:NSWindowDidChangeBackingPropertiesNotification
2332                   object:oldWindow];
2333     }
2334     [notificationCenter
2335         removeObserver:self
2336                   name:NSWindowDidMoveNotification
2337                 object:oldWindow];
2338     [notificationCenter
2339         removeObserver:self
2340                   name:NSWindowDidEndLiveResizeNotification
2341                 object:oldWindow];
2342   }
2343   if (newWindow) {
2344     if (supportsBackingPropertiesNotification) {
2345       [notificationCenter
2346           addObserver:self
2347              selector:@selector(windowDidChangeBackingProperties:)
2348                  name:NSWindowDidChangeBackingPropertiesNotification
2349                object:newWindow];
2350     }
2351     [notificationCenter
2352         addObserver:self
2353            selector:@selector(windowChangedGlobalFrame:)
2354                name:NSWindowDidMoveNotification
2355              object:newWindow];
2356     [notificationCenter
2357         addObserver:self
2358            selector:@selector(windowChangedGlobalFrame:)
2359                name:NSWindowDidEndLiveResizeNotification
2360              object:newWindow];
2361   }
2362 }
2363
2364 - (void)updateScreenProperties{
2365   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2366   renderWidgetHostView_->UpdateDisplayLink();
2367 }
2368
2369 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2370 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2371   // Background tabs check if their scale factor or vsync properties changed
2372   // when they are added to a window.
2373
2374   // Allocating a CGLayerRef with the current scale factor immediately from
2375   // this handler doesn't work. Schedule the backing store update on the
2376   // next runloop cycle, then things are read for CGLayerRef allocations to
2377   // work.
2378   [self performSelector:@selector(updateScreenProperties)
2379              withObject:nil
2380              afterDelay:0];
2381 }
2382
2383 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2384   renderWidgetHostView_->UpdateScreenInfo(
2385       renderWidgetHostView_->GetNativeView());
2386 }
2387
2388 - (void)setFrameSize:(NSSize)newSize {
2389   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2390
2391   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2392   // -setFrame: isn't neccessary.
2393   [super setFrameSize:newSize];
2394
2395   if (!renderWidgetHostView_->render_widget_host_)
2396     return;
2397
2398   renderWidgetHostView_->render_widget_host_->SendScreenRects();
2399   renderWidgetHostView_->render_widget_host_->WasResized();
2400   if (renderWidgetHostView_->delegated_frame_host_)
2401     renderWidgetHostView_->delegated_frame_host_->WasResized();
2402
2403   // Wait for the frame that WasResize might have requested. If the view is
2404   // being made visible at a new size, then this call will have no effect
2405   // because the view widget is still hidden, and the pause call in WasShown
2406   // will have this effect for us.
2407   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2408 }
2409
2410 - (BOOL)canBecomeKeyView {
2411   if (!renderWidgetHostView_->render_widget_host_)
2412     return NO;
2413
2414   return canBeKeyView_;
2415 }
2416
2417 - (BOOL)acceptsFirstResponder {
2418   if (!renderWidgetHostView_->render_widget_host_)
2419     return NO;
2420
2421   return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
2422 }
2423
2424 - (BOOL)becomeFirstResponder {
2425   if (!renderWidgetHostView_->render_widget_host_)
2426     return NO;
2427
2428   renderWidgetHostView_->render_widget_host_->Focus();
2429   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2430   renderWidgetHostView_->SetTextInputActive(true);
2431
2432   // Cancel any onging composition text which was left before we lost focus.
2433   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2434   // somehow that method won't be called when switching among different tabs.
2435   // See http://crbug.com/47209
2436   [self cancelComposition];
2437
2438   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2439       [[self window] keyViewSelectionDirection]];
2440   NSDictionary* userInfo =
2441       [NSDictionary dictionaryWithObject:direction
2442                                   forKey:kSelectionDirection];
2443   [[NSNotificationCenter defaultCenter]
2444       postNotificationName:kViewDidBecomeFirstResponder
2445                     object:self
2446                   userInfo:userInfo];
2447
2448   return YES;
2449 }
2450
2451 - (BOOL)resignFirstResponder {
2452   renderWidgetHostView_->SetTextInputActive(false);
2453   if (!renderWidgetHostView_->render_widget_host_)
2454     return YES;
2455
2456   if (closeOnDeactivate_)
2457     renderWidgetHostView_->KillSelf();
2458
2459   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2460   renderWidgetHostView_->render_widget_host_->Blur();
2461
2462   // We should cancel any onging composition whenever RWH's Blur() method gets
2463   // called, because in this case, webkit will confirm the ongoing composition
2464   // internally.
2465   [self cancelComposition];
2466
2467   return YES;
2468 }
2469
2470 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2471   if (responderDelegate_ &&
2472       [responderDelegate_
2473           respondsToSelector:@selector(validateUserInterfaceItem:
2474                                                      isValidItem:)]) {
2475     BOOL valid;
2476     BOOL known =
2477         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2478     if (known)
2479       return valid;
2480   }
2481
2482   SEL action = [item action];
2483
2484   if (action == @selector(stopSpeaking:)) {
2485     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2486            renderWidgetHostView_->IsSpeaking();
2487   }
2488   if (action == @selector(startSpeaking:)) {
2489     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2490            renderWidgetHostView_->SupportsSpeech();
2491   }
2492
2493   // For now, these actions are always enabled for render view,
2494   // this is sub-optimal.
2495   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2496   if (action == @selector(undo:) ||
2497       action == @selector(redo:) ||
2498       action == @selector(cut:) ||
2499       action == @selector(copy:) ||
2500       action == @selector(copyToFindPboard:) ||
2501       action == @selector(paste:) ||
2502       action == @selector(pasteAndMatchStyle:)) {
2503     return renderWidgetHostView_->render_widget_host_->IsRenderView();
2504   }
2505
2506   return editCommand_helper_->IsMenuItemEnabled(action, self);
2507 }
2508
2509 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2510   return renderWidgetHostView_.get();
2511 }
2512
2513 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2514 // move) for the given event. Customize here to be more selective about which
2515 // key presses to autohide on.
2516 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2517   return ([event type] == NSKeyDown &&
2518              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2519 }
2520
2521 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2522                                          index:(NSUInteger)index
2523                                       maxCount:(NSUInteger)maxCount {
2524   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2525   NSUInteger totalLength = [fullArray count];
2526   if (index >= totalLength)
2527     return nil;
2528   NSUInteger length = MIN(totalLength - index, maxCount);
2529   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2530 }
2531
2532 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2533   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2534   return [fullArray count];
2535 }
2536
2537 - (id)accessibilityAttributeValue:(NSString *)attribute {
2538   BrowserAccessibilityManager* manager =
2539       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2540
2541   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2542   // BrowserAccessibilityManager. Children includes all subviews in addition to
2543   // contents. Currently we do not have subviews besides the document view.
2544   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2545           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2546       manager) {
2547     return [NSArray arrayWithObjects:manager->
2548         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2549   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2550     return NSAccessibilityScrollAreaRole;
2551   }
2552   id ret = [super accessibilityAttributeValue:attribute];
2553   return ret;
2554 }
2555
2556 - (NSArray*)accessibilityAttributeNames {
2557   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2558   [ret addObject:NSAccessibilityContentsAttribute];
2559   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2560   return ret;
2561 }
2562
2563 - (id)accessibilityHitTest:(NSPoint)point {
2564   BrowserAccessibilityManager* manager =
2565       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2566   if (!manager)
2567     return self;
2568   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2569   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2570   localPoint.y = NSHeight([self bounds]) - localPoint.y;
2571   BrowserAccessibilityCocoa* root =
2572       manager->GetRoot()->ToBrowserAccessibilityCocoa();
2573   id obj = [root accessibilityHitTest:localPoint];
2574   return obj;
2575 }
2576
2577 - (BOOL)accessibilityIsIgnored {
2578   BrowserAccessibilityManager* manager =
2579       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2580   return !manager;
2581 }
2582
2583 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2584   BrowserAccessibilityManager* manager =
2585       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2586   // Only child is root.
2587   if (manager &&
2588       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2589     return 0;
2590   } else {
2591     return NSNotFound;
2592   }
2593 }
2594
2595 - (id)accessibilityFocusedUIElement {
2596   BrowserAccessibilityManager* manager =
2597       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2598   if (manager) {
2599     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2600     DCHECK(focused_item);
2601     if (focused_item) {
2602       BrowserAccessibilityCocoa* focused_item_cocoa =
2603           focused_item->ToBrowserAccessibilityCocoa();
2604       DCHECK(focused_item_cocoa);
2605       if (focused_item_cocoa)
2606         return focused_item_cocoa;
2607     }
2608   }
2609   return [super accessibilityFocusedUIElement];
2610 }
2611
2612 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
2613 // with minor modifications for code style and commenting.
2614 //
2615 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
2616 // -setToolTip: in that the updated tooltip takes effect immediately,
2617 //  without the user's having to move the mouse out of and back into the view.
2618 //
2619 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
2620 // the view, which in turn requires overriding some internal tracking-rect
2621 // methods (to keep track of its owner & userdata, which need to be filled out
2622 // in the fake events.) --snej 7/6/09
2623
2624
2625 /*
2626  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
2627  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
2628  *
2629  * Redistribution and use in source and binary forms, with or without
2630  * modification, are permitted provided that the following conditions
2631  * are met:
2632  *
2633  * 1.  Redistributions of source code must retain the above copyright
2634  *     notice, this list of conditions and the following disclaimer.
2635  * 2.  Redistributions in binary form must reproduce the above copyright
2636  *     notice, this list of conditions and the following disclaimer in the
2637  *     documentation and/or other materials provided with the distribution.
2638  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
2639  *     its contributors may be used to endorse or promote products derived
2640  *     from this software without specific prior written permission.
2641  *
2642  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
2643  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2644  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2645  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2646  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2647  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2648  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2649  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2650  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2651  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2652  */
2653
2654 // Any non-zero value will do, but using something recognizable might help us
2655 // debug some day.
2656 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
2657
2658 // Override of a public NSView method, replacing the inherited functionality.
2659 // See above for rationale.
2660 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
2661                                owner:(id)owner
2662                             userData:(void *)data
2663                         assumeInside:(BOOL)assumeInside {
2664   DCHECK(trackingRectOwner_ == nil);
2665   trackingRectOwner_ = owner;
2666   trackingRectUserData_ = data;
2667   return kTrackingRectTag;
2668 }
2669
2670 // Override of (apparently) a private NSView method(!) See above for rationale.
2671 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
2672                                 owner:(id)owner
2673                              userData:(void *)data
2674                          assumeInside:(BOOL)assumeInside
2675                        useTrackingNum:(int)tag {
2676   DCHECK(tag == 0 || tag == kTrackingRectTag);
2677   DCHECK(trackingRectOwner_ == nil);
2678   trackingRectOwner_ = owner;
2679   trackingRectUserData_ = data;
2680   return kTrackingRectTag;
2681 }
2682
2683 // Override of (apparently) a private NSView method(!) See above for rationale.
2684 - (void)_addTrackingRects:(NSRect *)rects
2685                     owner:(id)owner
2686              userDataList:(void **)userDataList
2687          assumeInsideList:(BOOL *)assumeInsideList
2688              trackingNums:(NSTrackingRectTag *)trackingNums
2689                     count:(int)count {
2690   DCHECK(count == 1);
2691   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
2692   DCHECK(trackingRectOwner_ == nil);
2693   trackingRectOwner_ = owner;
2694   trackingRectUserData_ = userDataList[0];
2695   trackingNums[0] = kTrackingRectTag;
2696 }
2697
2698 // Override of a public NSView method, replacing the inherited functionality.
2699 // See above for rationale.
2700 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
2701   if (tag == 0)
2702     return;
2703
2704   if (tag == kTrackingRectTag) {
2705     trackingRectOwner_ = nil;
2706     return;
2707   }
2708
2709   if (tag == lastToolTipTag_) {
2710     [super removeTrackingRect:tag];
2711     lastToolTipTag_ = 0;
2712     return;
2713   }
2714
2715   // If any other tracking rect is being removed, we don't know how it was
2716   // created and it's possible there's a leak involved (see Radar 3500217).
2717   NOTREACHED();
2718 }
2719
2720 // Override of (apparently) a private NSView method(!)
2721 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
2722   for (int i = 0; i < count; ++i) {
2723     int tag = tags[i];
2724     if (tag == 0)
2725       continue;
2726     DCHECK(tag == kTrackingRectTag);
2727     trackingRectOwner_ = nil;
2728   }
2729 }
2730
2731 // Sends a fake NSMouseExited event to the view for its current tracking rect.
2732 - (void)_sendToolTipMouseExited {
2733   // Nothing matters except window, trackingNumber, and userData.
2734   int windowNumber = [[self window] windowNumber];
2735   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2736                                               location:NSZeroPoint
2737                                          modifierFlags:0
2738                                              timestamp:0
2739                                           windowNumber:windowNumber
2740                                                context:NULL
2741                                            eventNumber:0
2742                                         trackingNumber:kTrackingRectTag
2743                                               userData:trackingRectUserData_];
2744   [trackingRectOwner_ mouseExited:fakeEvent];
2745 }
2746
2747 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
2748 - (void)_sendToolTipMouseEntered {
2749   // Nothing matters except window, trackingNumber, and userData.
2750   int windowNumber = [[self window] windowNumber];
2751   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2752                                               location:NSZeroPoint
2753                                          modifierFlags:0
2754                                              timestamp:0
2755                                           windowNumber:windowNumber
2756                                                context:NULL
2757                                            eventNumber:0
2758                                         trackingNumber:kTrackingRectTag
2759                                               userData:trackingRectUserData_];
2760   [trackingRectOwner_ mouseEntered:fakeEvent];
2761 }
2762
2763 // Sets the view's current tooltip, to be displayed at the current mouse
2764 // location. (This does not make the tooltip appear -- as usual, it only
2765 // appears after a delay.) Pass null to remove the tooltip.
2766 - (void)setToolTipAtMousePoint:(NSString *)string {
2767   NSString *toolTip = [string length] == 0 ? nil : string;
2768   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2769       (!toolTip && !toolTip_)) {
2770     return;
2771   }
2772
2773   if (toolTip_) {
2774     [self _sendToolTipMouseExited];
2775   }
2776
2777   toolTip_.reset([toolTip copy]);
2778
2779   if (toolTip) {
2780     // See radar 3500217 for why we remove all tooltips
2781     // rather than just the single one we created.
2782     [self removeAllToolTips];
2783     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2784     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2785                                      owner:self
2786                                   userData:NULL];
2787     [self _sendToolTipMouseEntered];
2788   }
2789 }
2790
2791 // NSView calls this to get the text when displaying the tooltip.
2792 - (NSString *)view:(NSView *)view
2793   stringForToolTip:(NSToolTipTag)tag
2794              point:(NSPoint)point
2795           userData:(void *)data {
2796   return [[toolTip_ copy] autorelease];
2797 }
2798
2799 // Below is our NSTextInputClient implementation.
2800 //
2801 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2802 // functions to process this event.
2803 //
2804 // [WebHTMLView keyDown] ->
2805 //     EventHandler::keyEvent() ->
2806 //     ...
2807 //     [WebEditorClient handleKeyboardEvent] ->
2808 //     [WebHTMLView _interceptEditingKeyEvent] ->
2809 //     [NSResponder interpretKeyEvents] ->
2810 //     [WebHTMLView insertText] ->
2811 //     Editor::insertText()
2812 //
2813 // Unfortunately, it is hard for Chromium to use this implementation because
2814 // it causes key-typing jank.
2815 // RenderWidgetHostViewMac is running in a browser process. On the other
2816 // hand, Editor and EventHandler are running in a renderer process.
2817 // So, if we used this implementation, a NSKeyDown event is dispatched to
2818 // the following functions of Chromium.
2819 //
2820 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2821 //     |Sync IPC (KeyDown)| (*1) ->
2822 //     EventHandler::keyEvent() (renderer) ->
2823 //     ...
2824 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2825 //     |Sync IPC| (*2) ->
2826 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2827 //     [self interpretKeyEvents] ->
2828 //     [RenderWidgetHostViewMac insertText] (browser) ->
2829 //     |Async IPC| ->
2830 //     Editor::insertText() (renderer)
2831 //
2832 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2833 // result of EventHandler::keyEvent().
2834 // (*2) we need to wait until this call finishes since WebEditorClient uses
2835 // the result of [WebHTMLView _interceptEditingKeyEvent].
2836 //
2837 // This needs many sync IPC messages sent between a browser and a renderer for
2838 // each key event, which would probably result in key-typing jank.
2839 // To avoid this problem, this implementation processes key events (and input
2840 // method events) totally in a browser process and sends asynchronous input
2841 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2842 // renderer process.
2843 //
2844 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2845 //     |Async IPC (RawKeyDown)| ->
2846 //     [self interpretKeyEvents] ->
2847 //     [RenderWidgetHostViewMac insertText] (browser) ->
2848 //     |Async IPC (Char)| ->
2849 //     Editor::insertText() (renderer)
2850 //
2851 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2852 // make any key-typing jank. --hbono 7/23/09
2853 //
2854 extern "C" {
2855 extern NSString *NSTextInputReplacementRangeAttributeName;
2856 }
2857
2858 - (NSArray *)validAttributesForMarkedText {
2859   // This code is just copied from WebKit except renaming variables.
2860   if (!validAttributesForMarkedText_) {
2861     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2862         NSUnderlineStyleAttributeName,
2863         NSUnderlineColorAttributeName,
2864         NSMarkedClauseSegmentAttributeName,
2865         NSTextInputReplacementRangeAttributeName,
2866         nil]);
2867   }
2868   return validAttributesForMarkedText_.get();
2869 }
2870
2871 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2872   DCHECK([self window]);
2873   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2874   // coordinates (upper left origin). Scroll offsets will be taken care of in
2875   // the renderer.
2876   thePoint = [[self window] convertScreenToBase:thePoint];
2877   thePoint = [self convertPoint:thePoint fromView:nil];
2878   thePoint.y = NSHeight([self frame]) - thePoint.y;
2879
2880   NSUInteger index =
2881       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2882           renderWidgetHostView_->render_widget_host_,
2883           gfx::Point(thePoint.x, thePoint.y));
2884   return index;
2885 }
2886
2887 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2888                              actualRange:(NSRangePointer)actualRange {
2889   NSRect rect;
2890   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2891           theRange,
2892           &rect,
2893           actualRange)) {
2894     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2895         renderWidgetHostView_->render_widget_host_, theRange);
2896
2897     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2898     if (actualRange)
2899       *actualRange = theRange;
2900   }
2901
2902   // The returned rectangle is in WebKit coordinates (upper left origin), so
2903   // flip the coordinate system.
2904   NSRect viewFrame = [self frame];
2905   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2906   return rect;
2907 }
2908
2909 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2910                          actualRange:(NSRangePointer)actualRange {
2911   NSRect rect = [self firstViewRectForCharacterRange:theRange
2912                                          actualRange:actualRange];
2913
2914   // Convert into screen coordinates for return.
2915   rect = [self convertRect:rect toView:nil];
2916   rect.origin = [[self window] convertBaseToScreen:rect.origin];
2917   return rect;
2918 }
2919
2920 - (NSRange)markedRange {
2921   // An input method calls this method to check if an application really has
2922   // a text being composed when hasMarkedText call returns true.
2923   // Returns the range saved in the setMarkedText method so the input method
2924   // calls the setMarkedText method and we can update the composition node
2925   // there. (When this method returns an empty range, the input method doesn't
2926   // call the setMarkedText method.)
2927   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2928 }
2929
2930 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2931     actualRange:(NSRangePointer)actualRange {
2932   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2933   if (actualRange)
2934     *actualRange = range;
2935   NSAttributedString* str =
2936       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2937           renderWidgetHostView_->render_widget_host_, range);
2938   return str;
2939 }
2940
2941 - (NSInteger)conversationIdentifier {
2942   return reinterpret_cast<NSInteger>(self);
2943 }
2944
2945 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2946 // nil when the caret is in non-editable content or password box to avoid
2947 // making input methods do their work.
2948 - (NSTextInputContext *)inputContext {
2949   if (focusedPluginIdentifier_ != -1)
2950     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2951
2952   switch(renderWidgetHostView_->text_input_type_) {
2953     case ui::TEXT_INPUT_TYPE_NONE:
2954     case ui::TEXT_INPUT_TYPE_PASSWORD:
2955       return nil;
2956     default:
2957       return [super inputContext];
2958   }
2959 }
2960
2961 - (BOOL)hasMarkedText {
2962   // An input method calls this function to figure out whether or not an
2963   // application is really composing a text. If it is composing, it calls
2964   // the markedRange method, and maybe calls the setMarkedText method.
2965   // It seems an input method usually calls this function when it is about to
2966   // cancel an ongoing composition. If an application has a non-empty marked
2967   // range, it calls the setMarkedText method to delete the range.
2968   return hasMarkedText_;
2969 }
2970
2971 - (void)unmarkText {
2972   // Delete the composition node of the renderer and finish an ongoing
2973   // composition.
2974   // It seems an input method calls the setMarkedText method and set an empty
2975   // text when it cancels an ongoing composition, i.e. I have never seen an
2976   // input method calls this method.
2977   hasMarkedText_ = NO;
2978   markedText_.clear();
2979   underlines_.clear();
2980
2981   // If we are handling a key down event, then ConfirmComposition() will be
2982   // called in keyEvent: method.
2983   if (!handlingKeyDown_) {
2984     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2985         base::string16(), gfx::Range::InvalidRange(), false);
2986   } else {
2987     unmarkTextCalled_ = YES;
2988   }
2989 }
2990
2991 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2992                               replacementRange:(NSRange)replacementRange {
2993   // An input method updates the composition string.
2994   // We send the given text and range to the renderer so it can update the
2995   // composition node of WebKit.
2996   // TODO(suzhe): It's hard for us to support replacementRange without accessing
2997   // the full web content.
2998   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2999   NSString* im_text = isAttributedString ? [string string] : string;
3000   int length = [im_text length];
3001
3002   // |markedRange_| will get set on a callback from ImeSetComposition().
3003   selectedRange_ = newSelRange;
3004   markedText_ = base::SysNSStringToUTF16(im_text);
3005   hasMarkedText_ = (length > 0);
3006
3007   underlines_.clear();
3008   if (isAttributedString) {
3009     ExtractUnderlines(string, &underlines_);
3010   } else {
3011     // Use a thin black underline by default.
3012     underlines_.push_back(blink::WebCompositionUnderline(
3013         0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3014   }
3015
3016   // If we are handling a key down event, then SetComposition() will be
3017   // called in keyEvent: method.
3018   // Input methods of Mac use setMarkedText calls with an empty text to cancel
3019   // an ongoing composition. So, we should check whether or not the given text
3020   // is empty to update the input method state. (Our input method backend can
3021   // automatically cancels an ongoing composition when we send an empty text.
3022   // So, it is OK to send an empty text to the renderer.)
3023   if (!handlingKeyDown_) {
3024     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3025         markedText_, underlines_,
3026         newSelRange.location, NSMaxRange(newSelRange));
3027   }
3028 }
3029
3030 - (void)doCommandBySelector:(SEL)selector {
3031   // An input method calls this function to dispatch an editing command to be
3032   // handled by this view.
3033   if (selector == @selector(noop:))
3034     return;
3035
3036   std::string command(
3037       [RenderWidgetHostViewMacEditCommandHelper::
3038           CommandNameForSelector(selector) UTF8String]);
3039
3040   // If this method is called when handling a key down event, then we need to
3041   // handle the command in the key event handler. Otherwise we can just handle
3042   // it here.
3043   if (handlingKeyDown_) {
3044     hasEditCommands_ = YES;
3045     // We ignore commands that insert characters, because this was causing
3046     // strange behavior (e.g. tab always inserted a tab rather than moving to
3047     // the next field on the page).
3048     if (!StartsWithASCII(command, "insert", false))
3049       editCommands_.push_back(EditCommand(command, ""));
3050   } else {
3051     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3052     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3053                                               command, ""));
3054   }
3055 }
3056
3057 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3058   // An input method has characters to be inserted.
3059   // Same as Linux, Mac calls this method not only:
3060   // * when an input method finishs composing text, but also;
3061   // * when we type an ASCII character (without using input methods).
3062   // When we aren't using input methods, we should send the given character as
3063   // a Char event so it is dispatched to an onkeypress() event handler of
3064   // JavaScript.
3065   // On the other hand, when we are using input methods, we should send the
3066   // given characters as an input method event and prevent the characters from
3067   // being dispatched to onkeypress() event handlers.
3068   // Text inserting might be initiated by other source instead of keyboard
3069   // events, such as the Characters dialog. In this case the text should be
3070   // sent as an input method event as well.
3071   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3072   // the full web content.
3073   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3074   NSString* im_text = isAttributedString ? [string string] : string;
3075   if (handlingKeyDown_) {
3076     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3077   } else {
3078     gfx::Range replacement_range(replacementRange);
3079     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3080         base::SysNSStringToUTF16(im_text), replacement_range, false);
3081   }
3082
3083   // Inserting text will delete all marked text automatically.
3084   hasMarkedText_ = NO;
3085 }
3086
3087 - (void)insertText:(id)string {
3088   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3089 }
3090
3091 - (void)viewDidMoveToWindow {
3092   if ([self window])
3093     [self updateScreenProperties];
3094
3095   if (canBeKeyView_) {
3096     NSWindow* newWindow = [self window];
3097     // Pointer comparison only, since we don't know if lastWindow_ is still
3098     // valid.
3099     if (newWindow) {
3100       // If we move into a new window, refresh the frame information. We
3101       // don't need to do it if it was the same window as it used to be in,
3102       // since that case is covered by WasShown(). We only want to do this for
3103       // real browser views, not popups.
3104       if (newWindow != lastWindow_) {
3105         lastWindow_ = newWindow;
3106         renderWidgetHostView_->WindowFrameChanged();
3107       }
3108     }
3109   }
3110
3111   // If we switch windows (or are removed from the view hierarchy), cancel any
3112   // open mouse-downs.
3113   if (hasOpenMouseDown_) {
3114     WebMouseEvent event;
3115     event.type = WebInputEvent::MouseUp;
3116     event.button = WebMouseEvent::ButtonLeft;
3117     renderWidgetHostView_->ForwardMouseEvent(event);
3118
3119     hasOpenMouseDown_ = NO;
3120   }
3121 }
3122
3123 - (void)undo:(id)sender {
3124   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3125   if (web_contents)
3126     web_contents->Undo();
3127 }
3128
3129 - (void)redo:(id)sender {
3130   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3131   if (web_contents)
3132     web_contents->Redo();
3133 }
3134
3135 - (void)cut:(id)sender {
3136   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3137   if (web_contents)
3138     web_contents->Cut();
3139 }
3140
3141 - (void)copy:(id)sender {
3142   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3143   if (web_contents)
3144     web_contents->Copy();
3145 }
3146
3147 - (void)copyToFindPboard:(id)sender {
3148   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3149   if (web_contents)
3150     web_contents->CopyToFindPboard();
3151 }
3152
3153 - (void)paste:(id)sender {
3154   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3155   if (web_contents)
3156     web_contents->Paste();
3157 }
3158
3159 - (void)pasteAndMatchStyle:(id)sender {
3160   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3161   if (web_contents)
3162     web_contents->PasteAndMatchStyle();
3163 }
3164
3165 - (void)selectAll:(id)sender {
3166   // editCommand_helper_ adds implementations for most NSResponder methods
3167   // dynamically. But the renderer side only sends selection results back to
3168   // the browser if they were triggered by a keyboard event or went through
3169   // one of the Select methods on RWH. Since selectAll: is called from the
3170   // menu handler, neither is true.
3171   // Explicitly call SelectAll() here to make sure the renderer returns
3172   // selection results.
3173   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3174   if (web_contents)
3175     web_contents->SelectAll();
3176 }
3177
3178 - (void)startSpeaking:(id)sender {
3179   renderWidgetHostView_->SpeakSelection();
3180 }
3181
3182 - (void)stopSpeaking:(id)sender {
3183   renderWidgetHostView_->StopSpeaking();
3184 }
3185
3186 - (void)cancelComposition {
3187   if (!hasMarkedText_)
3188     return;
3189
3190   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3191   // doesn't call any NSTextInput functions, such as setMarkedText or
3192   // insertText. So, we need to send an IPC message to a renderer so it can
3193   // delete the composition node.
3194   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3195   [currentInputManager markedTextAbandoned:self];
3196
3197   hasMarkedText_ = NO;
3198   // Should not call [self unmarkText] here, because it'll send unnecessary
3199   // cancel composition IPC message to the renderer.
3200 }
3201
3202 - (void)confirmComposition {
3203   if (!hasMarkedText_)
3204     return;
3205
3206   if (renderWidgetHostView_->render_widget_host_)
3207     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3208         base::string16(), gfx::Range::InvalidRange(), false);
3209
3210   [self cancelComposition];
3211 }
3212
3213 - (void)setPluginImeActive:(BOOL)active {
3214   if (active == pluginImeActive_)
3215     return;
3216
3217   pluginImeActive_ = active;
3218   if (!active) {
3219     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3220     renderWidgetHostView_->PluginImeCompositionCompleted(
3221         base::string16(), focusedPluginIdentifier_);
3222   }
3223 }
3224
3225 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3226   if (focused)
3227     focusedPluginIdentifier_ = pluginId;
3228   else if (focusedPluginIdentifier_ == pluginId)
3229     focusedPluginIdentifier_ = -1;
3230
3231   // Whenever plugin focus changes, plugin IME resets.
3232   [self setPluginImeActive:NO];
3233 }
3234
3235 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3236   if (!pluginImeActive_)
3237     return false;
3238
3239   ComplexTextInputPanel* inputPanel =
3240       [ComplexTextInputPanel sharedComplexTextInputPanel];
3241   NSString* composited_string = nil;
3242   BOOL handled = [inputPanel interpretKeyEvent:event
3243                                         string:&composited_string];
3244   if (composited_string) {
3245     renderWidgetHostView_->PluginImeCompositionCompleted(
3246         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3247     pluginImeActive_ = NO;
3248   }
3249   return handled;
3250 }
3251
3252 - (void)checkForPluginImeCancellation {
3253   if (pluginImeActive_ &&
3254       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3255     renderWidgetHostView_->PluginImeCompositionCompleted(
3256         base::string16(), focusedPluginIdentifier_);
3257     pluginImeActive_ = NO;
3258   }
3259 }
3260
3261 // Overriding a NSResponder method to support application services.
3262
3263 - (id)validRequestorForSendType:(NSString*)sendType
3264                      returnType:(NSString*)returnType {
3265   id requestor = nil;
3266   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3267   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3268   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3269   BOOL takesText =
3270       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3271
3272   if (sendTypeIsString && hasText && !returnType) {
3273     requestor = self;
3274   } else if (!sendType && returnTypeIsString && takesText) {
3275     requestor = self;
3276   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3277     requestor = self;
3278   } else {
3279     requestor = [super validRequestorForSendType:sendType
3280                                       returnType:returnType];
3281   }
3282   return requestor;
3283 }
3284
3285 - (void)viewWillStartLiveResize {
3286   [super viewWillStartLiveResize];
3287   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3288   if (widget)
3289     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3290 }
3291
3292 - (void)viewDidEndLiveResize {
3293   [super viewDidEndLiveResize];
3294   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3295   if (widget)
3296     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3297 }
3298
3299 - (void)updateCursor:(NSCursor*)cursor {
3300   if (currentCursor_ == cursor)
3301     return;
3302
3303   currentCursor_.reset([cursor retain]);
3304   [[self window] invalidateCursorRectsForView:self];
3305 }
3306
3307 - (void)popupWindowWillClose:(NSNotification *)notification {
3308   renderWidgetHostView_->KillSelf();
3309 }
3310
3311 @end
3312
3313 //
3314 // Supporting application services
3315 //
3316 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3317
3318 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3319                              types:(NSArray*)types {
3320   const std::string& str = renderWidgetHostView_->selected_text();
3321   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3322
3323   base::scoped_nsobject<NSString> text(
3324       [[NSString alloc] initWithUTF8String:str.c_str()]);
3325   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3326   [pboard declareTypes:toDeclare owner:nil];
3327   return [pboard setString:text forType:NSStringPboardType];
3328 }
3329
3330 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3331   NSString *string = [pboard stringForType:NSStringPboardType];
3332   if (!string) return NO;
3333
3334   // If the user is currently using an IME, confirm the IME input,
3335   // and then insert the text from the service, the same as TextEdit and Safari.
3336   [self confirmComposition];
3337   [self insertText:string];
3338   return YES;
3339 }
3340
3341 - (BOOL)isOpaque {
3342   return YES;
3343 }
3344
3345 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3346 // regions that are not draggable. (See ControlRegionView in
3347 // native_app_window_cocoa.mm). This requires the render host view to be
3348 // draggable by default.
3349 - (BOOL)mouseDownCanMoveWindow {
3350   return YES;
3351 }
3352
3353 @end