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